Ответ 1
Если вы попытаетесь создать свою собственную поисковую систему, вы, вероятно, потерпите неудачу. Почему бы вам не попробовать Lucene. Здесь ссылка http://lucenenet.apache.org/. Приветствия
Мне нужно сделать поиск на основе набора ключевых слов, которые возвращают все объявления, связанные с этими ключевыми словами. Тогда результатом будет список категорий с количеством объявлений для каждой категории.
Поиск выполняется в таблице ключевых слов:
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; }
}
Пример:
Объявления: Автомобиль - Mercedes-Benz GLK Грузовик - Mercedes-Benz Citan
Если я ищу "Мерседес-Бенц", я получаю:
Если я ищу "Mercedes-Benz GLK", я получаю:
Если я обыщу "Mercedes Citan", я получаю:
То, что я получаю до сих пор:
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 для "Мерседес-Бенц"
Любая помощь?
Если вы попытаетесь создать свою собственную поисковую систему, вы, вероятно, потерпите неудачу. Почему бы вам не попробовать Lucene. Здесь ссылка http://lucenenet.apache.org/. Приветствия
Я не компилировал-проверил это или что-то еще, поэтому может потребоваться некоторая настройка, но вы ищете что-то в этом направлении.
var matchingKac = keywordIds.Distinct().ToList()
.Aggregate(
keywordAdCategoryQuery.AsQueryable(),
(q, id) => q.Where(kac => kac.Keyword_Id == id));
Вы эффективно говорите: "Начните с keywordAdCategoryQuery
, и для каждого ключевого слова добавьте условие .Where()
, говорящее, что в нем должно быть это ключевое слово. Вы можете сделать то же самое с циклом for
, если вы найти Aggregate
трудно читаемый.
Думаю, теперь у меня есть решение. Это основано на вашем предыдущем вопросе и нескольких предположениях:
С учетом этих трех предположений я могу сказать, что
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.
это не может быть прямым ответом, но в таких ситуациях "поиска нескольких параметров" я просто забываю о чем угодно и делаю простую вещь, например: "Поиск автопроизводителем", "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
, и поэтому он больше не участвует в поиске
Я предлагаю вам добавить регулярное выражение и опустить специальные символы, а затем использовать Linq для этого
Таким образом, Мерседес-Бенц может стать Мерседесом и бензином
Я рекомендую НЕ определять ключевые слова для объектов таким образом, потому что вы можете искать и находить слишком много объектов, иначе вы ничего не найдете. Вы всегда будете испортить свое время при поиске. Классифицируйте свои объекты таким образом, чтобы пользователи фокусировались на НАЙТИ, а не на поиск.
Я отправил свой ответ на: 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;
}
Он делает именно то, что вы хотите, но я нахожу что-то подозрительное в отношении ключевых слов, которые делятся на суб-ключевые слова. Чем снова, может быть, это просто именование.