Linq包含关键字的不同记录

我需要根据汽车关键字搜索返回一个独特的记录列表,如:“Alfa 147”

问题是,因为我有3辆“Alfa”汽车,它会返回1 + 3条记录(阿尔法似乎为1,结果为147,Alfa结果为3)

编辑:

SQL-Server查询看起来像这样:

SELECT DISTINCT c.Id, c.Name /*, COUNT(Number of Ads in the KeywordAdCategories table with those 2 keywords) */ FROM Categories AS c INNER JOIN KeywordAdCategories AS kac ON kac.Category_Id = c.Id INNER JOIN KeywordAdCategories AS kac1 ON kac.Ad_Id = kac1.Ad_Id AND kac1.Keyword_Id = (SELECT Id FROM Keywords WHERE Name = 'ALFA') INNER JOIN KeywordAdCategories AS kac2 ON kac1.Ad_Id = kac2.Ad_Id AND kac2.Keyword_Id = (SELECT Id FROM Keywords WHERE Name = '147') 

我的LINQ查询是:

  var query = from k in keywordQuery where splitKeywords.Contains(k.Name) join kac in keywordAdCategoryQuery on k.Id equals kac.Keyword_Id join c in categoryQuery on kac.Category_Id equals c.Id join a in adQuery on kac.Ad_Id equals a.Id select new CategoryListByKeywordsDetailDto { Id = c.Id, Name = c.Name, SearchCount = keywordAdCategoryQuery.Where(s => s.Category_Id == c.Id).Where(s => s.Keyword_Id == k.Id).Distinct().Count(), ListController = c.ListController, ListAction = c.ListAction }; var searchResults = new CategoryListByBeywordsListDto(); searchResults.CategoryListByKeywordsDetails = query.Distinct().ToList(); 

实体是:

 public class Keyword { // Primary properties public int Id { get; set; } public string Name { get; set; } } // Keyword Sample Data: // 1356 ALFA // 1357 ROMEO // 1358 145 // 1373 147 public class Category { // Primary properties public int Id { get; set; } public string Name { get; set; } } // Category Sample Data // 1 NULL 1 Carros // 2 NULL 1 Motos // 3 NULL 2 Oficinas // 4 NULL 2 Stands // 5 NULL 1 Comerciais // 8 NULL 1 Barcos // 9 NULL 1 Máquinas // 10 NULL 1 Caravanas e Autocaravanas // 11 NULL 1 Peças e Acessórios // 12 1 1 Citadino // 13 1 1 Utilitário // 14 1 1 Monovolume public class KeywordAdCategory { [Key] [Column("Keyword_Id", Order = 0)] public int Keyword_Id { get; set; } [Key] [Column("Ad_Id", Order = 1)] public int Ad_Id { get; set; } [Key] [Column("Category_Id", Order = 2)] public int Category_Id { get; set; } } // KeywordAdCategory Sample Data // 1356 1017 1 // 1356 1018 1 // 1356 1019 1 // 1357 1017 1 // 1357 1018 1 // 1357 1019 1 // 1358 1017 1 // 1373 1019 1 public class Ad { // Primary properties public int Id { get; set; } public string Title { get; set; } public string TitleStandard { get; set; } public string Version { get; set; } public int Year { get; set; } public decimal Price { get; set; } // Navigation properties public Member Member { get; set; } public Category Category { get; set; } public IList Features { get; set; } public IList Pictures { get; set; } public IList Operations { get; set; } } public class AdCar : Ad { public int Kms { get; set; } public Make Make { get; set; } public Model Model { get; set; } public Fuel Fuel { get; set; } public Color Color { get; set; } } // AdCar Sample Data // 1017 Alfa Romeo 145 1.6TDI 2013 ALFA ROMEO 145 1.6TDI 2013 12 2 1.6TDI 1000 1 2013 1 20000,0000 2052 AdCar // 1018 Alfa Romeo 146 1.6TDI 2013 ALFA ROMEO 146 1.6TDI 2013 12 2 5 1.6TDI 1000 2 2013 1 20000,0000 2052 AdCar // 1019 Alfa Romeo 147 1.6TDI 2013 ALFA ROMEO 147 1.6TDI 2013 12 2 6 1.6TDI 1000 3 2013 1 20000,0000 2052 AdCar 

我期望搜索“ALFA”的结果是“汽车:3”,搜索“ALFA 147”的结果是“汽车:1”,实际上我得到的结果是“汽车:1 \ n汽车:3”

Fiuu,这是脑力残骸。 我将查询拆分成几个部分,但它最后作为一个整体执行(var结果)。 我回到了匿名课堂,但意图很明确。

这是解决方案:

 var keywordIds = from k in keywordQuery where splitKeywords.Contains(k.Name) select k.Id; var matchingKac = from kac in keywordAdCategories where keywordIds.Contains(kac.Keyword_Id) select kac; var addIDs = from kac in matchingKac group kac by kac.Ad_Id into d where d.Count() == splitKeywords.Length select d.Key; var groupedKac = from kac in keywordAdCategoryQuery where addIDs.Contains(kac.Ad_Id) group kac by new { kac.Category_Id, kac.Ad_Id }; var result = from grp in groupedKac group grp by grp.Key.Category_Id into final join c in categoryQuery on final.Key equals c.Id select new { Id = final.Key, Name = c.Name, SearchCount = final.Count() }; // here goes result.ToList() or similar 

kac没有过滤单词…所以kac,kac1和kac2的这个连接将返回3行,因为这是此广告的关键字数量

你应该删除它..

试试这个:

 SELECT DISTINCT c.Id, c.Name /*, COUNT(Number of Ads in the KeywordAdCategories table with those 2 keywords) */ FROM Categories AS c INNER JOIN KeywordAdCategories AS kac1 ON kac1.Keyword_Id = (SELECT Id FROM Keywords WHERE Name = 'ALFA') AND kac1.Category_Id = c.Id INNER JOIN KeywordAdCategories AS kac2 ON kac1.Ad_Id = kac2.Ad_Id AND kac2.Keyword_Id = (SELECT Id FROM Keywords WHERE Name = '147') AND kac2.Category_Id = c.Id 

我做了一个测试……

将环境设置为

  declare @Keywords table(id int,name varchar(max)) insert into @Keywords(id,name) values (1356,'ALFA') ,(1357,'ROMEO') ,(1358,'145') ,(1373,'147') declare @Categories table(id int, name varchar(max)) insert into @Categories(id,name) values (1,'Carros') ,(2,'Motos') declare @KeywordAdCategories table(Keyword_Id int, ad_Id int,Category_Id int) insert into @KeywordAdCategories (Keyword_Id , ad_Id,Category_Id) values (1356, 1017,1) ,(1356, 1018,1) ,(1356, 1019,1) ,(1357, 1017,1) ,(1357, 1018,1) ,(1357, 1019,1) ,(1358, 1017,1) ,(1373, 1019,1) 

我运行这两个查询:

 --query 1 SELECT c.Id, c.Name,COUNT(*) as [count] FROM @Categories AS c INNER JOIN @KeywordAdCategories AS kac1 ON kac1.Keyword_Id = (SELECT Id FROM @Keywords WHERE Name = 'ALFA') AND kac1.Category_Id = c.Id GROUP BY c.Id, c.Name 

我得到这个结果集:

  Id Name count ----------- ---------- ----------- 1 Carros 3 

和两个单词的第二个查询……

 --query 2 SELECT c.Id, c.Name,COUNT(*) as [count] FROM @Categories AS c INNER JOIN @KeywordAdCategories AS kac1 ON kac1.Keyword_Id = (SELECT Id FROM @Keywords WHERE Name = 'ALFA') AND kac1.Category_Id = c.Id INNER JOIN @KeywordAdCategories AS kac2 ON kac1.Ad_Id = kac2.Ad_Id AND kac2.Keyword_Id = (SELECT Id FROM @Keywords WHERE Name = '147') AND kac2.Category_Id = c.Id GROUP BY c.Id, c.Name 

结果集是:

  Id Name count ----------- ---------- ----------- 1 Carros 1 

这是你想要的吗?

您可以使用Distinct()方法。

 var query = ... var query = query.Distinct(); 

请参阅此代码返回不同的值。 但是,我想要的是返回强类型集合而不是匿名类型以获取更多详细信息。

将查询字符串拆分为数组,并通过查询每个关键字的数据库并使用联合连接结果集进行迭代。 结果集将是匹配任何给定关键字的每个不同记录。

也许这很接近? 至少子查询打开了一点供你使用。

 var query = from c in categoryQuery let keywords = ( from k in keywordQuery where splitKeywords.Contains(k.Name) join kac in keywordAdCategoryQuery on k.Id equals kac.Keyword_Id where kac.Category_Id == c.Id join a in adQuery on kac.Ad_Id equals a.Id select k.Id ).Distinct() where keywords.Any() select new CategoryListByKeywordsDetailDto { Id = c.Id, Name = c.Name, SearchCount = ( from kac in keywordAdCategoryQuery where kac.Category_Id == c.Id join kId in keywords on kac.Keyword_Id equals kId select kac.Id ).Distinct().Count(), ListController = c.ListController, ListAction = c.ListAction }; 

linq的一个漂亮function是,您可以在更小更简单的步骤中构建复杂的查询,并让linq弄清楚如何将它们连接在一起。

以下是获取此信息的一种方法。 我不确定这是否是最好的,当你选择多个关键字时,你需要检查它是否表现良好。

假设关键字的定义类似于

 var keywords = "Alfa 147"; var splitKeywords = keywords.Split(new char[] {' '}); 

阶段1

获取按广告和类别分组的关键字列表

 var subQuery = (from kac in keywordAdCategoryQuery join k in keywordQuery on kac.Keyword_Id equals k.Id select new { kac.Ad_Id, kac.Category_Id, KeyWord = k.Name, }); var grouped = (from r in subQuery group r by new { r.Ad_Id, r.Category_Id} into results select new { results.Key.Ad_Id , results.Key.Category_Id , keywords = (from r in results select r.KeyWord) }); 

请注意,您发布的类将表明您的数据库没有在表之间定义外键关系。 如果他们这样做,那么这个阶段写起来会稍微简单一些。

第二阶段

过滤掉没有每个关键字的任何组

 foreach(var keyword in splitKeywords) { var copyOfKeyword = keyword ; // Take copy of keyword to avoid closing over loop grouped = (from r in grouped where r.keywords.Contains(copyOfKeyword) select r) ; } 

第3阶段

按类别分组并计算每个类别的结果

 var groupedByCategories = (from r in grouped group r by r.Category_Id into results join c in categoryQuery on results.Key equals c.Id select new { c.Id , c.Name , Count = results.Count() }); 

第四阶段

现在从sql中检索信息。 这应该在一个查询中完成。

 var finalResults = groupedByCategories.ToList(); 

所以,如果我理解正确的需要,你想要在文本中匹配所有单词的子集而不是你现在得到的OR匹配吗? 我看到至少两个选项,第一个可能不会将拆分转换为SQL:

 var query = from k in keywordQuery where !splitKeywords.Except(k.Name.split(' ')).Any() 

这做出以下假设:

  1. 您在关键字中的单词是以空格分隔的。
  2. 您正在寻找完全匹配而不是部分匹配。 (即测试与TestTest不匹配)。

另一种选择是使用谓词构建器动态生成谓词(暂时没有这样做,我的实现可能需要调整 – 但这更可能(并且在我看来更好)解决方案):

 var predicate = PredicateBuilder.True(); foreach (string s in splitKeywords) { predicate.AND(s.Contains(k.Name)); } query.Where(predicate); 

如果有人可以评论我的某些语法是否已关闭,我将不胜感激。 编辑 :包括对谓词构建器的良好引用的链接: http : //www.albahari.com/nutshell/predicatebuilder.aspx

UPDATE

跨多个表的谓词构建器,如果有人到这里寻找如何做到这一点。 PredicateBuilder可以生成跨多个表的谓词吗?

应该可以查询每个关键字然后联合结果集。 重复值将从联合中删除,您可以计算出所需的聚合。

选择时尝试删除课程

  var query = (from k in keywordQuery where splitKeywords.Contains(k.Name) join kac in keywordAdCategoryQuery on k.Id equals kac.Keyword_Id join c in categoryQuery on kac.Category_Id equals c.Id join a in adQuery on kac.Ad_Id equals a.Id select new { Id = c.Id, Name = c.Name, SearchCount = keywordAdCategoryQuery.Where(s => s.Category_Id == c.Id).Where(s => s.Keyword_Id == k.Id).Distinct().Count(), ListController = c.ListController, ListAction = c.ListAction }).Distinct().ToList(); var searchResults = new CategoryListByBeywordsListDto(); searchResults.CategoryListByKeywordsDetails = (from q in query select new CategoryListByKeywordsDetailDto { Id = q.Id, Name = q.Name, SearchCount = q.SearchCount, ListController = q.ListController, ListAction = q.ListAction }).ToList(); 

您正在对CategoryListByKeywordsDetailDto列表执行select distinct。 Distinct仅适用于POCO和匿名对象。 在您的情况下,您需要实现IEqualitycomparer以使select distinct工作。

我在内存集合中使用LINQ直接尝试了这一点(例如,而不是通过SQL) – 似乎对我有用(我认为主要的一点是你要搜索适用于所有指定关键字的广告,而不是任何,正确?无论如何,下面的一些示例代码(一点点评论,并不一定是最有效的,但希望说明一点……)

使用以下“数据集”:

 private List AdCars = new List(); private List KeywordAdCategories = new List(); private List Categories = new List(); private List Keywords = new List(); 

使用您提供的数据在测试方法中填充…

搜索方法看起来有点像这样:

 var splitKeywords = keywords.Split(' '); var validKeywords = Keywords.Join(splitKeywords, kwd => kwd.Name.ToLower(), spl => spl.ToLower(), (kwd, spl) => kwd.Id).ToList(); var groupedAdIds = KeywordAdCategories .GroupBy(kac => kac.Ad_Id) .Where(grp => validKeywords.Except(grp.Select(kac => kac.Keyword_Id)).Any() == false) .Select(grp => grp.Key) .ToList(); var foundKacs = KeywordAdCategories .Where(kac => groupedAdIds.Contains(kac.Ad_Id)) .GroupBy(kbc => kbc.Category_Id, kac => kac.Ad_Id); //Results count by category var catCounts = Categories .Join(foundKacs, cat => cat.Id, kacGrp => kacGrp.Key, (cat, kacGrp) => new { CategoryName = cat.Name, AdCount = kacGrp.Distinct().Count() }) .ToList(); //Actual results set var ads = AdCars.Join(groupedAdIds, ad => ad.Id, grpAdId => grpAdId, (ad, grpAdId) => ad); 

正如我所说,这更多是为了说明,请不要过分关注Joins&GroupBy等的使用(不确定它的确切,呃,“最优”)

所以,使用上面的内容,如果我搜索“Alfa”,我会得到3个广告结果,如果我搜索“Alfa 147”,我只得到1个结果。

编辑:我已经改变代码来代表两种可能的结果(因为我不确定你的问题需要哪些)

ads会为您提供搜索返回的实际广告

catCounts将提供一个匿名类型列表,每个类型代表查找结果,作为按类别划分的广告计数

这有帮助吗?

嗨,如果我正确理解你的问题

“问题在于,因为我有3辆”阿尔法“汽车,它会返回1 + 3条记录(阿尔法似乎为1,结果为147,阿尔法结果为3)

并且Linq并不是真的需要我可能有你需要的只是测试它作为新项目

  public Linqfilter() { //as Note: I modified a few classes from you because i doesn'T have your Member, Operation, Make,... classes #region declaration var originalAdCarList = new List() { new AdCar(){Id=1017, Title= "Alfa Romeo 145 1.6TDI 2013", Category= new Category(){Id =12}} , new AdCar(){Id=1018, Title= "Alfa Romeo 146 1.6TDI 2013", Category= new Category(){Id =11}} , new AdCar(){Id=1019, Title= "Alfa Romeo 147 1.6TDI 2013", Category= new Category(){Id =12}} }; var originalKeywordAdCategoryList = new List() { new KeywordAdCategory() { Keyword_Id=1356, Ad_Id=1017,Category_Id=1}, new KeywordAdCategory() { Keyword_Id=1356, Ad_Id=1018,Category_Id=1}, new KeywordAdCategory() { Keyword_Id=1356, Ad_Id=1019,Category_Id=1}, new KeywordAdCategory() { Keyword_Id=1357, Ad_Id=1017,Category_Id=1}, new KeywordAdCategory() { Keyword_Id=1357, Ad_Id=1018,Category_Id=1}, new KeywordAdCategory() { Keyword_Id=1357, Ad_Id=1019,Category_Id=1}, new KeywordAdCategory() { Keyword_Id=1358, Ad_Id=1017,Category_Id=1}, new KeywordAdCategory() { Keyword_Id=1373, Ad_Id=1019,Category_Id=1} }; var originalCategoryList = new List() { new Category(){Id=1, Name="NULL 1 Carros"}, new Category(){Id=2, Name="NULL 1 Motos"}, new Category(){Id=3, Name="NULL 2 Oficinas"}, new Category(){Id=4 , Name="NULL 2 Stands"}, new Category(){Id=5 , Name="NULL 1 Comerciais"}, new Category(){Id=8, Name="NULL 1 Barcos"}, new Category(){Id=9 , Name="NULL 1 Máquinas"}, new Category(){Id=10 , Name="NULL 1 Caravanas e Autocaravanas"}, new Category(){Id=11 , Name="NULL 1 Peças e Acessórios"}, new Category(){Id=12 , Name="1 1 Citadino"}, new Category(){Id=13 , Name="1 1 Utilitário"}, new Category(){Id=14 , Name="1 1 Monovolume"} }; var originalKeywordList = new List() { new Keyword(){Id=1356 ,Name="ALFA"}, new Keyword(){Id=1357 ,Name="ROMEO"}, new Keyword(){Id=1358 ,Name="145"}, new Keyword(){Id=1373 ,Name="147"} }; #endregion declaration string searchText = "ALFA"; // split the string searchText in an Array of substrings var splitSearch = searchText.Split(' '); var searchKeyList =new List(); // generate a list of Keyword based on splitSearch foreach (string part in splitSearch) if(originalKeywordList.Any(key => key.Name == part)) searchKeyList.Add(originalKeywordList.First(key => key.Name == part)); // generate a list of KeywordAdCategory based on searchKList var searchKACList = new List(); foreach(Keyword key in searchKeyList) foreach (KeywordAdCategory kAC in originalKeywordAdCategoryList.Where(kac => kac.Keyword_Id == key.Id)) searchKACList.Add(kAC); var groupedsearchKAClist = from kac in searchKACList group kac by kac.Keyword_Id; var listFiltered = new List(originalAdCarList); //here starts the real search part foreach (IGrouping kacGroup in groupedsearchKAClist) { var listSingleFiltered = new List(); // generate a list of AdCar that matched the current KeywordAdCategory filter foreach (KeywordAdCategory kac in kacGroup) foreach (AdCar aCar in originalAdCarList.Where(car => car.Id == kac.Ad_Id)) listSingleFiltered.Add(aCar); var tempList = new List(listFiltered); // iterrates over a temporary copie of listFiltered and removes items which don't match to the current listSingleFiltered foreach (AdCar aC in tempList) if (!listSingleFiltered.Any(car => car.Id == aC.Id)) listFiltered.Remove(aC); } var AdCarCount = listFiltered.Count; // is the count of the AdCar who match var CatDic =new Dictionary(); // will contain the Counts foreach Categorie > 0 foreach(AdCar aCar in listFiltered) if(originalCategoryList.Any(cat => cat.Id ==aCar.Category.Id)) { var selectedCat = originalCategoryList.First(cat => cat.Id == aCar.Category.Id); if (!CatDic.ContainsKey(selectedCat)) { CatDic.Add(selectedCat, 1);//new Category Countvalue } else { CatDic[selectedCat]++; //Category Countvalue +1 } } } } public class Keyword { // Primary properties public int Id { get; set; } public string Name { get; set; } } public class Category { // Primary properties public int Id { get; set; } public string Name { get; set; } } public class KeywordAdCategory { //[Key] //[Column("Keyword_Id", Order = 0)] public int Keyword_Id { get; set; } //[Key] //[Column("Ad_Id", Order = 1)] public int Ad_Id { get; set; } //[Key] //[Column("Category_Id", Order = 2)] public int Category_Id { get; set; } } public class Ad { // Primary properties public int Id { get; set; } public string Title { get; set; } public string TitleStandard { get; set; } public string Version { get; set; } public int Year { get; set; } public decimal Price { get; set; } // Navigation properties public string Member { get; set; } public Category Category { get; set; } public IList Features { get; set; } public IList Pictures { get; set; } public IList Operations { get; set; } } public class AdCar : Ad { public int Kms { get; set; } public string Make { get; set; } public int Model { get; set; } public int Fuel { get; set; } public int Color { get; set; } } 

希望它能帮助你或其他人

编辑:

扩展了我的Methode Linqfilter()以回答请求

EDIT2:

我认为这应该是你正在寻找的

  var selectedKWLinq = from kw in originalKeywordList where splitSearch.Contains(kw.Name) select kw; var selectedKACLinq = from kac in originalKeywordAdCategoryList where selectedKWLinq.Any(item => item.Id == kac.Keyword_Id) group kac by kac.Keyword_Id into selectedKAC select selectedKAC; var selectedAdCar = from adC in originalAdCarList where (from skAC in selectedKACLinq where skAC.Any(kac => kac.Ad_Id == adC.Id) select skAC).Count() == selectedKACLinq.Count() select adC; var selectedCategorys = from cat in originalCategoryList join item in selectedAdCar on cat.Id equals item.Category.Id group cat by cat.Id into g select g; //result part var AdCarCount = selectedAdCar.Count(); List> list = selectedCategorys.ToList(); var firstCategoryCount = list[0].Count(); var secoundCategoryCount = list[1].Count();