Linq distinct record containing keywords

前端 未结 12 2080
醉话见心
醉话见心 2021-02-19 05:09

I need to return a distinct list of records based on a car keywords search like: \"Alfa 147\"

The problem is that, as I have 3 \"Alfa\" cars, it returns 1 + 3 records (i

相关标签:
12条回答
  • 2021-02-19 05:46

    Try removing the class while select

     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();
    
    0 讨论(0)
  • 2021-02-19 05:48

    hi if i understand your problem correctly

    "The problem is that, as I have 3 "Alfa" cars, it returns 1 + 3 records (it seems 1 for the Alfa and 147 result, and 3 for the Alfa result)"

    and Linq isn't really required i maybe have what you need just test it as new project

        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<AdCar>() 
            {
                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<KeywordAdCategory>() 
            {
                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<Category>()
            {
                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<Keyword>() 
            {
                 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<Keyword>();
    
            // 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<KeywordAdCategory>();
            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<AdCar>(originalAdCarList);
    
            //here starts the real search part
            foreach (IGrouping<int, KeywordAdCategory> kacGroup in groupedsearchKAClist)
            {
    
                var listSingleFiltered = new List<AdCar>();
                //  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<AdCar>(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<Category, int>(); // 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<string> Features { get; set; }
        public IList<int> Pictures { get; set; }
        public IList<string> 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; }
    }
    

    hopefully it will help you or someone else

    Edit:

    extended my Methode Linqfilter() to answer the request

    Edit2:

    i think that should be exactly what you are looking for

            var selectedKWLinq = from kw in originalKeywordList
                                 where splitSearch.Contains(kw.Name) 
                                 select kw;
    
            var selectedKACLinq = from kac in originalKeywordAdCategoryList
                                  where selectedKWLinq.Any<Keyword>(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<IGrouping<int, Category>> list = selectedCategorys.ToList(); 
            var firstCategoryCount = list[0].Count();
            var secoundCategoryCount = list[1].Count();
    
    0 讨论(0)
  • 2021-02-19 05:48

    The kac is not filtering words... so this joins of kac, kac1 and kac2 will return 3 lines, cause this is the numbers of keywords for this ad

    You should remove it..

    Try this:

    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
    

    I did a test...

    Setting the ambient as

        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)
    

    I run these two queries:

    --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
    

    I get this result set:

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

    and the second query for two words...

    --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
    

    Result set is:

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

    Is this what you want?

    0 讨论(0)
  • 2021-02-19 05:50

    You can use the Distinct() method.

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

    See This code returns distinct values. However, what I want is to return a strongly typed collection as opposed to an anonymous type for more details.

    0 讨论(0)
  • 2021-02-19 05:53

    Fiuu, this was brain-wreck. I splited query in several pieces, but it's executed as a whole at the end (var result). And I returned anonymous class, but intention is clear.

    Here is the solution:

    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
    
    0 讨论(0)
  • 2021-02-19 05:55

    So, if I understand the need correctly, you want all of the subset of words to be matched in the text and not the OR matching you are getting right now? I see at least two options, the first of which may not translate the split to SQL:

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

    This makes the following assumptions:

    1. Your words in the Keywords are space delimited.
    2. You are looking for exact matches and not partial matches. (I.e. Test will not match TestTest).

    The other option being to dynamically generate a predicate using predicate builder (haven't done this in a while, my implementation might need tweaking - but this is the more likely (and better in my mind) solution):

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

    If someone can comment if some of my syntax is off I would appreciate it. EDIT: Including link to a good reference on predicate builder: http://www.albahari.com/nutshell/predicatebuilder.aspx

    UPDATE

    Predicate builder across multiple tables, if anyone gets here looking for how to do that. Can PredicateBuilder generate predicates that span multiple tables?

    0 讨论(0)
提交回复
热议问题