поиск на основе результата предыдущего поиска
прошу помочь с реализацией на C# алгоритм поиска словосочетания следующий
- нашли все совпадения по первому слову из базы данных
- по второму == ищем по результатам Первого (Предыдущего) поиска
- по третьему == ищем по результатам Второго (Предыдущего) поиска
- по четвертому == ищем по результатам Третьего (Предыдущего) поиска
код страницы Поиска проект MVC5
public ActionResult Asearch(int adStatusId = (int)AdStatuses.Published, bool? isBuy = null, string selectedCategoryIds = null,
string selectedTownIds = null, string selectedRegionIds = null, string productName = null, int? page = null)
{
#region Получение List<int> categoryIds, townIds, regionIds из строк selectedCategoryIds, selectedTownIds, selectedRegionIds, productName
List<int> townIds = null;
if (!string.IsNullOrWhiteSpace(selectedTownIds))
{
townIds = new List<int>();
var townIdsParts = selectedTownIds.Split(',');
foreach (var townIdStr in townIdsParts)
{
townIds.Add(Convert.ToInt32(townIdStr));
}
}
var allTowns = TownsDAL.GetTowns();
var allRegions = RegionsDAL.GetRegions();
if (!string.IsNullOrWhiteSpace(selectedRegionIds))
{
if (townIds == null)
townIds = new List<int>();
var regionIdsParts = selectedRegionIds.Split(',');
foreach (var regionIdStr in regionIdsParts)
{
int regionId = Convert.ToInt32(regionIdStr);
var region = allRegions.FirstOrDefault(r => r.Id == regionId);
if (region != null)
{
var townsInRegion = allTowns.Where(t => t.RegionName == region.Name).ToList();
var townIdsForRegion = townsInRegion.Select(t => t.Id).ToList();
townIds.AddRange(townIdsForRegion);
}
}
}
var allCategories = ProductCategoriesDAL.GetCategories();
List<int> categoryIds = null;
if (!string.IsNullOrWhiteSpace(selectedCategoryIds))
{
categoryIds = new List<int>();
var categoryIdsParts = selectedCategoryIds.Split(',');
foreach (var categoryIdStr in categoryIdsParts)
{
categoryIds.Add(Convert.ToInt32(categoryIdStr));
}
}
#region поиск по дочерним категориям от выбранных в фильтре категорий (например выбрали Лом и отходы черных металлов, должны найти объявления со "Стальные")
AdSearchHelper.ChildCategoriesToAdd(categoryIds, allCategories);
#endregion
#endregion
///объединенный List<Ad>
List<Ad> ads = null;// объединенный List<Ad>
List<Ad> adsAdCategory = null; // список объявлений отобранных на основе поиска по категориям
string[] wordsProductName; // массив слов [отходы, цветных] из словосочетания (-отходы-цветных-) для поиска
string adProductName = productName; // переменная для поиска по таблице с продуктами (AdsProduct) по полю полное наименование (Name)
List<Ad> adsAdProductName = null; // список объявлений отобранных на основе поиска по введенному значению слово или словосочетание для поиска по таблице с продуктами (AdsProduct) по полю полное наименование (Name)
string[] wordsAdProductName;// массив слов [отходы, цветных] из словосочетания (-отходы-цветных-) для поиска по таблице с продуктами (AdsProduct) по полю полное наименование (Name)
if (!string.IsNullOrWhiteSpace(productName))
{
if (productName.Contains('-'))
{
List<int> categoryIdsFromProductName = new List<int>();
List<int> categoryIdsFromProductNameChild = new List<int>();
List<int> categoryIdsFromProductNameNext = new List<int>();
List<int> categoryIdsFromProductCategoryNameNext = new List<int>();
List<ProductCategory> allCategoriesNext = new List<ProductCategory>();
// лом-отходы-медных
wordsProductName = productName.Replace('-', ' ').TrimAndReduce().Split(' '); //Итого получили словосочетание == отходы-цветных Если алгоритм сначала отобрал варианты по первому слову,а потом сортирует этот отбор ещё и по всем остальным словам в словосочетании - это как раз вариант с дефисом.
int wordCount = wordsProductName.Length;
IEnumerable<List<int>> enumerable = AdSearchHelper.SearshCategoriesId(wordsProductName, allCategories).ToList();
#region Search в словосочетании с дефисом То есть он выдаст только те варианты где словосочетание указано целиком в названии Категорий товара лом-отходы-медных
for (var i = 0; i < wordsProductName.Length - 1; i++)
{
try
{
//string previous = i > 0 ? wordsProductName[i - 1] : null; https:// stackoverflow.com/questions/8175684/get-to-next-element-in-list
string currentWord = wordsProductName[i];
string nextWord = i < wordsProductName.Count() ? wordsProductName[i + 1] : null;
categoryIdsFromProductName = allCategories.Where(c => c.Name.Contains(currentWord)).Select(c => c.Id).ToList(); // отобрали основные категории
categoryIdsFromProductNameChild = AdSearchHelper.ChildCategoriesToAdd(categoryIdsFromProductName, allCategories);//поиск по дочерним категориям от выбранных в фильтре категорий (например выбрали Лом и отходы черных металлов, должны найти объявления со "Стальные")
if (nextWord != null)
{
allCategoriesNext = ProductCategoriesDAL.CategoryTermsSearch(string.Join(" ,", categoryIdsFromProductNameChild)).ToList(); // создали список Категорий Продуктов
categoryIdsFromProductNameNext = allCategoriesNext.Where(c => c.Name.Contains(nextWord)).Select(c => c.Id).Distinct().ToList();// поиск по следующему слову == итог == Id Категорий Продуктов
categoryIdsFromProductNameNext = AdSearchHelper.ChildCategoriesToAdd(categoryIdsFromProductNameNext, allCategoriesNext); // поиск по дочерним категориям от выбранных в фильтре категорий (например выбрали Лом и отходы черных металлов, должны найти объявления со "Стальные")
adsAdCategory = AdsDAL.GetAdsForSearchPage(adStatusId: null, categoriesId: categoryIdsFromProductNameNext, townIds, isBuy, productName: null, (int)ModerateResults.Accepted).ToList(); // List<Ad> == вытаскиваем все по категориям
}
else
{
if (categoryIdsFromProductNameChild.Any())
{
if (categoryIds == null)
categoryIds = new List<int>();
categoryIds.AddRange(categoryIdsFromProductNameChild);
}
//#region поиск по дочерним категориям от выбранных в фильтре категорий (например выбрали Лом и отходы черных металлов, должны найти объявления со "Стальные")
//categoryIds = AdSearchHelper.ChildCategoriesToAdd(categoryIds, allCategories);
//#endregion
adsAdCategory = AdsDAL.GetAdsForSearchPage(adStatusId: null, categoriesId: categoryIds, townIds, isBuy, productName: null, (int)ModerateResults.Accepted).ToList(); // List<Ad> == вытаскиваем все по категориям
}
}
catch (Exception ex)
{
string expp = ex.Message;
}
}
#endregion
#region Search в словосочетании с дефисом То есть он выдаст только те варианты где словосочетание указано целиком в Полном наименовании товара == то есть по таблице AdProduct поле Name
List<Ad> wordAdsAdProductName = new List<Ad>(); // список объявлений с учетом поиска по словам из словосочетания
for (var i = 0; i < wordsProductName.Length - 1; i++)
{
try
{
// лом-отходы-медных
//string previous = i > 0 ? wordsProductName[i - 1] : null; https:// stackoverflow.com/questions/8175684/get-to-next-element-in-list
string current = wordsProductName[i];
string next = i < wordsProductName.Count() ? wordsProductName[i + 1] : null;
wordAdsAdProductName = AdsDAL.GetAdsForSearchPage(adStatusId: null, categoriesId: null, townIds, isBuy, current.ToLower(), (int)ModerateResults.Accepted).ToList(); // List<Ad> == вытаскиваем все по Наименованию
if (next != null)
{
List<int> idSAd = wordAdsAdProductName.Select(w => w.Id).ToList();
List<int> adidS = new List<int>();
foreach (int idAd in idSAd)
{
int adid = AdProductsDAL.GetAdIds(idAd, next);
if (adid > 0)
adidS.Add(adid);
}
List<int> _adidS = adidS.Distinct().ToList();
adsAdProductName = AdsDAL.GetAds(null, null, null, null, ids: _adidS, null, null, null, null);
LogsDAL.AddMessage(Convert.ToString(string.Join(" ,adid=", _adidS)));
}
else
adsAdProductName = wordAdsAdProductName;
}
catch (Exception ex)
{
string expp = ex.Message;
}
}
#endregion
}
else
{
#region Search !productName.Contains('-') А для случая когда дефиса нет
wordsProductName = productName.Split(' ');//А для случая когда дефиса нет, алгоритм будет выдавать все подряд, где есть хотя бы любое слово из словосочетания По словосочетаниям:- если дефиса нет, то поиск работает по каждому слову в отдельности
if (wordsProductName.Length > 1)
{
foreach (var wordProductName in wordsProductName)
{
try
{
var categoryIdsFromProductName = allCategories.Where(c => c.Name.Contains(wordProductName.TrimAndReduce())).Select(c => c.Id).ToList();
if (categoryIdsFromProductName.Any())
{
if (categoryIds == null)
categoryIds = new List<int>();
categoryIds.AddRange(categoryIdsFromProductName);
}
}
catch (Exception ex)
{
string expp = ex.Message;
}
}
}
else
{
if (productName.EndsWith("."))// По одному слову: - если точка, то да слово ищется целиком.
{
int productNameLastIndexOfCharX2E = productName.LastIndexOf('.');//определить находиться ли точка (char '\x2E') в конце слова ==> отходы.;
if (productNameLastIndexOfCharX2E == productName.Length - 1)
productName = productName.TrimEnd(new char[] { '.' });// и если есть ==> то удалить её ==> отходы
try
{
var categoryIdsFromProductName = allCategories.Where(c => c.Name.Contains(productName.TrimAndReduce())).Select(c => c.Id).ToList();
if (categoryIdsFromProductName.Any())
{
if (categoryIds == null)
categoryIds = new List<int>();
categoryIds.AddRange(categoryIdsFromProductName);
}
adsAdCategory = AdsDAL.GetAdsForSearchPage(adStatusId: null, categoriesId: categoryIds, townIds, isBuy, productName, (int)ModerateResults.Accepted).ToList(); // List<Ad> == вытаскиваем все по категориям
}
catch (Exception ex)
{
string expp = ex.Message;
}
}
else
{
//Запрос в DAL по входным параметрам при необходимости разбираем СТРОКУ на СЛОВА и делаем запрос по ОТдельности для каждого слова
adsAdCategory = AdsDAL.GetAdsForSearchPage(adStatusId: null, categoriesId: categoryIds, townIds, isBuy, productName, (int)ModerateResults.Accepted).ToList(); // List<Ad> == вытаскиваем все по категориям
if (adProductName.Contains('-'))
adsAdProductName = AdsDAL.GetAdsForSearchPage(adStatusId: null, categoriesId: null, townIds, isBuy, adProductName.Replace('-', ' ').ToLower(), (int)ModerateResults.Accepted).ToList(); // List<Ad> == вытаскиваем все по Наименованию
else
{
wordsAdProductName = adProductName.Split(' ');
if (wordsProductName.Length > 1)
{
foreach (var wordAdProductName in wordsAdProductName)
{
var wordAdsAdProductName = AdsDAL.GetAdsForSearchPage(adStatusId: null, categoriesId: null, townIds, isBuy, wordAdProductName.ToLower(), (int)ModerateResults.Accepted).ToList(); // List<Ad> == вытаскиваем все по Наименованию
try
{
if (wordAdsAdProductName.Count != 0)
{
adsAdProductName = adsAdProductName == null ? wordAdsAdProductName : adsAdProductName.Concat(wordAdsAdProductName).ToList();
}
}
catch (Exception ex)
{
string expp = ex.Message;
}
}
}
else
{
if (productName.EndsWith(".")) // По одному слову == если точка, то да слово ищется целиком.
{
try
{
int productNameLastIndexOfCharX2E = productName.LastIndexOf('.');//определить находиться ли точка (char '\x2E') в конце слова ==> отходы.;
if (productNameLastIndexOfCharX2E == productName.Length - 1)
productName = productName.TrimEnd(new char[] { '.' });// и если есть ==> то удалить её ==> отходы
var wordAdsAdProductName = AdsDAL.GetAdsForSearchPage(adStatusId: null, categoriesId: null, townIds, isBuy, productName.ToLower(), (int)ModerateResults.Accepted).ToList(); // List<Ad> == вытаскиваем все по Наименованию
adsAdProductName = adsAdProductName == null ? wordAdsAdProductName : adsAdProductName.Concat(wordAdsAdProductName).ToList();
}
catch (Exception ex)
{
string expp = ex.Message;
}
}
else // По одному слову - если точки нет, то да поиск работает по корню
{
try
{
string vowel = "аеёиоуыьэюя";
string consonant = "бвгджзйклмнпрстфхцчшщ";
string[] vowels = { "а", "е", "ё", "и", "о", "у", "ы", "ь", "э", "ю", "я" };
string[] consonants = { "б", "в", "г", "д", "ж", "з", "й", "к", "л", "м", "н", "п", "р", "с", "т", "ф", "х", "ц", "ч", "ш", "щ" };
string[] nounsEndings = { "а", "е", "ё", "и", "о", "у", "ы", "ь", "э", "ю", "я" };
string[] adjectivesEndings = { "ой", " ий", " ый", " ая", " яя", " ое", " ее", " ого", " его", " ой", " ей", " ому", " ему", " ой", " ей", " ой", " ий", " ый", " ую", " юю", " ое", " ее", " ого", " его", "ой", "ей" };
string[] lastThreeCharactersEndings = { "ого", "его", "ому", "ему" };
var countLettrWord = productName.Length;// длинна слова
char lastCharacter = productName[productName.Length - 1];//Получение последнего символа
char secondToLastCharacter = productName[productName.Length - 2];//Получение предпоследнего символа
var lastThreeCharacters = System.Text.RegularExpressions.Regex.Match(productName, @"(.{3})\s*$"); // //Получение трёх последних символов
bool _productNameLastThreeCharacters = false; // bool переменная наличия окончания "ого", "его", "ому", "ему"
string productNameLastThreeCharacter = ""; // новое значение слова поиска Без окончания "ого", "его", "ому", "ему"
foreach (string lastThreeCharactersEnding in lastThreeCharactersEndings)
{
if (productName.EndsWith(Convert.ToString(lastThreeCharactersEnding)))
{
_productNameLastThreeCharacters = true;
productNameLastThreeCharacter = WordHelper.CutTextByWord(productName, Convert.ToString(lastThreeCharactersEnding));
}
}
if (vowel.Contains(lastCharacter) && consonant.Contains(secondToLastCharacter) && !_productNameLastThreeCharacters)
{
productName = productName.Substring(0, productName.Length - 1);
}
else if (_productNameLastThreeCharacters)
{
productName = productNameLastThreeCharacter;
}
else
{
foreach (var adjectivesEnding in adjectivesEndings)
{
productName = WordHelper.CutTextByWord(productName, adjectivesEnding);
}
}
}
catch (Exception ex)
{
string expp = ex.Message;
}
}
}
}
}
}
#endregion
}
if (adsAdCategory == null)
ads = adsAdProductName;
if (adsAdProductName == null)
ads = adsAdCategory;
if (adsAdCategory != null && adsAdProductName != null)
ads = adsAdCategory.Concat(adsAdProductName).ToList();
}
else
ads = AdsDAL.GetAdsForSearchPage(adStatusId: null, categoriesId: categoryIds, townIds, isBuy, productName: null, (int)ModerateResults.Accepted).ToList(); // List<Ad> == вытаскиваем все по категориям
var adIds = ads.Select(a => a.Id).Distinct().ToList();
ads = ads
.GroupBy(a => a.Id).Select(g => g.FirstOrDefault())
.OrderByDescending(a => a.DateOfPosting).ToList();
var adsToShow = ads.Skip(((page ?? 1) - 1) * 1000/*C.ItemsPerPageAds// 20221130 krakoss*/).Take(1000/*C.ItemsPerPageAds// 20221130 krakoss*/).ToList();
viewModel.Ads = AdHelper.GetAdsForView(currentUserId, ads: adsToShow, needInvitedUserIds: true, needSender: true);
viewModel.AdsCount = ads.Count();
//Заполнение UrlForAdsIndex, OnClickJsScript
AdHelper.FillUrlForAdsIndexAndOnClickJsScript(viewModel.Ads);
#region Нумерация страниц
var url = Urls.Ads;
if (isBuy.HasValue)
url += "?isBuy=" + isBuy.ToString().ToLower();
if (selectedCategoryIds != null)
url += (url.Contains("?") ? "&" : "?") + "selectedCategoryIds=" + selectedCategoryIds;
if (selectedTownIds != null)
url += (url.Contains("?") ? "&" : "?") + "selectedTownIds=" + selectedTownIds;
if (selectedRegionIds != null)
url += (url.Contains("?") ? "&" : "?") + "selectedRegionIds=" + selectedRegionIds;
if (ViewBag.L.ProductName != null)
url += (url.Contains("?") ? "&" : "?") + "productName=" + ViewBag.L.ProductName;
viewModel.PaginationViewModel = new PaginationViewModel(page, ads.Count(), url, 1000/*C.ItemsPerPageAds// 20221130 krakoss*/);
#endregion
#region Заполнение заголовка
var heading = "";
var headingSearchTerms = "";
if (isBuy == true)
heading += "Закупки";
else if (isBuy == false)
heading += "Продажи";
else
heading += "Все объявления";
viewModel.Heading = heading;
if (!string.IsNullOrWhiteSpace(selectedRegionIds))
{
var _region = selectedRegionIds.Split(',').Count() > 1 ? "Регионы: " : "Регион: ";
headingSearchTerms += _region;
var selectedRegion = RegionsDAL.RegionTermsSearch(selectedRegionIds);
foreach (var region in selectedRegion)
{
headingSearchTerms += region.Name + "; ";
}
}
else
{
headingSearchTerms += "Регион: Россия";
}
if (!string.IsNullOrWhiteSpace(selectedTownIds))
{
var _town = selectedTownIds.Split(',').Count() > 1 ? "Города: " : "Город: ";
headingSearchTerms += "<br>" + _town;
var selectedTown = TownsDAL.TownTermsSearch(selectedTownIds);
foreach (var town in selectedTown)
{
headingSearchTerms += town.Name + "; ";
}
}
if (!string.IsNullOrWhiteSpace(selectedCategoryIds))
{
var _categoriy = selectedCategoryIds.Split(',').Count() > 1 ? "Категории: " : "Категория: ";
headingSearchTerms += "<br>" + _categoriy;
var selectedCategory = ProductCategoriesDAL.CategoryTermsSearch(selectedCategoryIds);
foreach (var product in selectedCategory)
{
headingSearchTerms += product.Name + "; ";
}
}
viewModel.HeadingSearchTerms = headingSearchTerms;
#endregion
return View(viewModel);
}
как вот в этой строке кода
for (var i = 0; i < wordsProductName.Length - 1; i++)
реализовать поиск на основе уже имеющего результата для реализации такого поиска надо использовать следующие знания из этого источника Пересечение двух списков в C#
в итоге вот небольшой фрагмент кода
#region Search в Названии Категорий товара по таблице ProductCategory поле Name => лом-отходы-медных
try
{
bool allWord = true;
IEnumerable<int> resСategoryNameIds = null;
foreach (string word in wordsProductName)
{
var categoryNameIds = ProductCategoriesDAL.SearchCategoriesRegex(word, allWord: allWord).Select(c => c.Id).ToList();
if (categoryNameIds.Any() && categoryNameIds.Count() > 0)
{
if (resСategoryNameIds == null)
resСategoryNameIds = categoryNameIds;
else
resСategoryNameIds = categoryNameIds.AsQueryable().Intersect(resСategoryNameIds); //IEnumerable<int> res = val1.AsQueryable().Intersect(val2);
}
}
int countRes = resСategoryNameIds.Count();
adsAdCategory = AdsDAL.GetAdsForSearchPageWordAndIdS(categoriesId: resСategoryNameIds.ToList(), adStatusId: null, townIds, isBuy, (int)ModerateResults.Accepted).ToList(); // List<Ad> == вытаскиваем все по категориям
}
catch (Exception ex)
{
string expp = ex.Message;
LogsDAL.AddError(expp);
}
#endregion
Ответы (1 шт):
Автор решения: krakoss
→ Ссылка
знания полученные из этого источника Пересечение двух списков в C#
помогли решить эту задачу
Теперь используйте метод Intersect(), чтобы получить пересечение двух списков.
IEnumerable res = val1.AsQueryable().Intersect(val2);