Поиск по набору ключевых слов

Мне нужно сделать поиск на основе набора ключевых слов, которые возвращают все объявления, связанные с этими ключевыми словами. Тогда результатом будет список категорий с количеством объявлений для каждой категории.

Поиск выполняется в таблице ключевых слов:

public class KeywordSearch
{
    public int Id { get; set; }
    public string Name { get; set; }
    public Keyword Keyword { get; set; }
}

Если таблица ключевых слов:

public class Keyword
{
    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; }
}

Наконец, таблица Category:

public class Category
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Пример:

  • Ключевые слова: "Мерседес-Бенц" и "GLK"
  • Ключевое словоПоиск: "Мерседес" и "Бенц" для ключевого слова "Мерседес-Бенц"              "GLK" для ключевого слова "GLK"
  • Категория: "Автомобили" и "Грузовики"
  • Объявления: Автомобиль - Mercedes-Benz GLK   Грузовик - Mercedes-Benz Citan

    Если я ищу "Мерседес-Бенц", я получаю:

    • Автомобили: 1
    • Грузовики: 1

    Если я ищу "Mercedes-Benz GLK", я получаю:

    • Автомобили: 1

    Если я обыщу "Mercedes Citan", я получаю:

    • Грузовики: 1

То, что я получаю до сих пор:

var keywordIds = from k in keywordSearchQuery
                    where splitKeywords.Contains(k.Name)
                    select k.Keyword.Id;

var matchingKac = from kac in keywordAdCategoryQuery
                    where keywordIds.Distinct().Contains(kac.Keyword_Id)
                    select kac;

var addIDs = from kac in matchingKac
             group kac by kac.Ad_Id into d
             where d.Count() == splitKeywords.Count()
             select d.Key;

var groupedKac = from kac in keywordAdCategoryQuery
                    where addIDs.Contains(kac.Ad_Id)               <--- EDIT2
                    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 CategoryGetAllBySearchDto
                {
                    Id = final.Key,
                    Name = c.Name,
                    ListController = c.ListController,
                    ListAction = c.ListAction,
                    SearchCount = final.Count()
                };

Проблема заключается в том, что я не могу получить только те объявления, которые соответствуют всем ключевым словам.

ИЗМЕНИТЬ:

Если ключевое слово составлено из 2 или более ключевых слов, таких как "Mercedes-Benz", строка "где d.Count() == splitKeywords.Count()" терпит неудачу, поскольку d.count = 1 и splitkeywords.Count = 2 для "Мерседес-Бенц"

Любая помощь?

Ответы

Ответ 1

Если вы попытаетесь создать свою собственную поисковую систему, вы, вероятно, потерпите неудачу. Почему бы вам не попробовать Lucene. Здесь ссылка http://lucenenet.apache.org/. Приветствия

Ответ 2

Я не компилировал-проверил это или что-то еще, поэтому может потребоваться некоторая настройка, но вы ищете что-то в этом направлении.

var matchingKac = keywordIds.Distinct().ToList()
    .Aggregate(
        keywordAdCategoryQuery.AsQueryable(),
        (q, id) => q.Where(kac => kac.Keyword_Id == id));

Вы эффективно говорите: "Начните с keywordAdCategoryQuery, и для каждого ключевого слова добавьте условие .Where(), говорящее, что в нем должно быть это ключевое слово. Вы можете сделать то же самое с циклом for, если вы найти Aggregate трудно читаемый.

Ответ 3

Думаю, теперь у меня есть решение. Это основано на вашем предыдущем вопросе и нескольких предположениях:

  • Ключевые слова - это полные имена, такие как "Mercedes-Benz GLK", "Mercedes-Benz Citan" .
  • Ключевыми словами являются "Mercedes", "Benz" и "GLK" для "Mercedes-Benz GLK" и "Mercedes", "Benz" и "Citan" для "Mercedes-Benz Citan" .
  • "Mercedes-Benz GLK" - это "автомобиль", "Mercedes-Benz Citan" - это "грузовик".

С учетом этих трех предположений я могу сказать, что

var keywordIds = from k in keywordSearchQuery
                 where splitKeywords.Contains(k.Name)
                 select k.Keyword.Id;

является виновником, и все запросы ниже полагаются на него. Этот запрос найдет все ключевые слова, содержащие любые слова в вашей строке поиска.

Пример: данная поисковая строка "Мерседес-Бенц GLK" будет разделена на "Мерседес", "Бенц" и "GLK". Ваш запрос теперь находит "Мерседес" и "Бенц" в "Мерседес-Бенц GLK" и "Mercedes-Benz Citan" .
Я думаю, что очевидно, что вы не хотите, чтобы "Mercedes-Benz GLK" соответствовал "Mercedes-Benz Citan" .

Решение состоит в том, чтобы указать, чтобы запрос соответствовал каждому splitKeywords с любым поиском Keywordsearch и возвращал соответствующее ключевое слово:

var keywordIds = keywordSearchQuery
                 .GroupBy(k => k.Keyword.Id)
                 .Where(g => splitKeywords.All(w => 
                                               g.Any(k => k.Name.Contains(w))))
                 .Select(g => g.Key);

Как для addIds, меняющих его на var addIDs = matchingKac.Select(ad => ad.Ad_Id).Distinct();, нужно сделать трюк. Или, если matchKac требуется только в addIds, вы можете изменить его на

var matchingKac = (from kac in keywordAdCategoryQuery
                   where keywordIds.Distinct().Contains(kac.Keyword_Id)
                   select kac.Ad_Id).Distinct();

и удалите addIds.

Ответ 4

это не может быть прямым ответом, но в таких ситуациях "поиска нескольких параметров" я просто забываю о чем угодно и делаю простую вещь, например: "Поиск автопроизводителем", "CategoryId", "MillageMax", "Цена":

var searchResults = from c in carDb.Cars
where (c.Manufacturer.Contains(Manufacturer) || Manufacturer == null) &&
                 (c.CategoryId == CategoryId || CategoryId == null) &&
                    (c.Millage <= MillageMax || MillageMax== null) &&
                          (c.Price <= Price  || Price == null) 
select c

теперь, если любой из параметров null, он отменяет содержащую строку, делая все выражение в скобках True, и поэтому он больше не участвует в поиске

Ответ 5

Я предлагаю вам добавить регулярное выражение и опустить специальные символы, а затем использовать Linq для этого

Таким образом, Мерседес-Бенц может стать Мерседесом и бензином

Ответ 6

Я рекомендую НЕ определять ключевые слова для объектов таким образом, потому что вы можете искать и находить слишком много объектов, иначе вы ничего не найдете. Вы всегда будете испортить свое время при поиске. Классифицируйте свои объекты таким образом, чтобы пользователи фокусировались на НАЙТИ, а не на поиск.

Ответ 7

Я отправил свой ответ на: https://github.com/n074v41l4bl34u/StackOverflow19796132 Не стесняйтесь просмотреть его.

Вот самый важный фрагмент.


с:

internal class SearchDomain
{
  public List<Keyword> Keywords { get; set; }
  public List<Category> Categories { get; set; }
  public List<KeywordAdCategory> KeywordAdCategories { get; set; }
}

то

private static char[] keywordPartsSplitter = new char[] { ' ', '-' };

internal static Dictionary<Category, Dictionary<int, List<KeywordAdCategory>>> FromStringInput(string searchPhrase, SearchDomain searchDomain)
{
  var identifiedKeywords = searchPhrase
    .Split(keywordPartsSplitter);

  var knownKeywordParts = identifiedKeywords
    .Where
    (ik =>
      searchDomain
      .Keywords
      .SelectMany(x => x.GetKeywordParts())
      .Any(kp => kp.Equals(ik, StringComparison.InvariantCultureIgnoreCase))
    );

  var keywordkSearches = knownKeywordParts
    .Select((kkp, n) => new KeywordSearch()
    {
      Id = n,
      Name = kkp,
      Keyword = searchDomain
        .Keywords
        .Single
        (k =>
          k.GetKeywordParts()
            .Any(kp => kp.Equals(kkp, StringComparison.InvariantCultureIgnoreCase))
        )
    });

  var relevantKeywords = keywordkSearches
    .Select(ks => ks.Keyword)
    .Distinct();

  var keywordAdCategoriesByCategory = searchDomain.Categories
    .GroupJoin
    (
      searchDomain.KeywordAdCategories,
      c => c.Id,
      kac => kac.Category_Id,
      (c, kac) => new { Category = c, AdKeywordsForCategory = kac }
    );

  var relevantKeywordAdCategories = keywordAdCategoriesByCategory
    .Where
    (kacbk =>
      relevantKeywords
        .All
        (rk =>
          kacbk
            .AdKeywordsForCategory
            .Any(kac => kac.Keyword_Id == rk.Id)
        )
    );

  var foundAdsInCategories = relevantKeywordAdCategories
    .ToDictionary
    (rkac =>
      rkac.Category,
      rkac => rkac.AdKeywordsForCategory
        .GroupBy(g => g.Ad_Id)
        .ToDictionary(x => x.Key, x => x.ToList())
    );

  return foundAdsInCategories;
}

Он делает именно то, что вы хотите, но я нахожу что-то подозрительное в отношении ключевых слов, которые делятся на суб-ключевые слова. Чем снова, может быть, это просто именование.