using full text index for poem search

This commit is contained in:
Hamid Reza Mohammadi 2021-05-07 14:59:23 +04:30
parent a5c826a904
commit 9c3a2beca5
2 changed files with 98 additions and 94 deletions

View File

@ -7876,14 +7876,21 @@
<returns></returns>
</member>
<member name="M:RMuseum.Services.Implementation.GanjoorService.Search(RSecurityBackend.Models.Generic.PagingParameterModel,System.String,System.Nullable{System.Int32},System.Nullable{System.Int32})">
<summary>
Search
</summary>
<param name="paging"></param>
<param name="term"></param>
<param name="poetId"></param>
<param name="catId"></param>
<returns></returns>
<summary>
Search
You need to run this scripts manually on the database before using this method:
CREATE FULLTEXT CATALOG [GanjoorPoemPlainTextCatalog] WITH ACCENT_SENSITIVITY = OFF
ALTER TABLE [dbo].[GanjoorPoems] ADD CONSTRAINT [PK_GanjoorPoems] PRIMARY KEY CLUSTERED
(
[Id] ASC
) WITH(PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON[PRIMARY]
</summary>
<param name="paging"></param>
<param name="term"></param>
<param name="poetId"></param>
<param name="catId"></param>
<returns></returns>
</member>
<member name="M:RMuseum.Services.Implementation.GanjoorService.ModifyPage(System.Int32,System.Guid,RMuseum.Models.Ganjoor.ViewModels.GanjoorModifyPageViewModel)">
<summary>

View File

@ -1785,9 +1785,26 @@ namespace RMuseum.Services.Implementation
return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorPoemCompleteViewModel[] Items)>((null, null), exp.ToString());
}
}
private async Task _populateCategoryChildren(int catId, List<int> catListId)
{
var catRes = await GetCatById(catId, false);
foreach(var c in catRes.Result.Cat.Children)
{
catListId.Add(c.Id);
await _populateCategoryChildren(c.Id, catListId);
}
}
/// <summary>
/// Search
/// You need to run this scripts manually on the database before using this method:
///
/// CREATE FULLTEXT CATALOG [GanjoorPoemPlainTextCatalog] WITH ACCENT_SENSITIVITY = OFF
/// ALTER TABLE [dbo].[GanjoorPoems] ADD CONSTRAINT [PK_GanjoorPoems] PRIMARY KEY CLUSTERED
///(
/// [Id] ASC
///) WITH(PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, SORT_IN_TEMPDB = OFF, IGNORE_DUP_KEY = OFF, ONLINE = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON[PRIMARY]
/// </summary>
/// <param name="paging"></param>
/// <param name="term"></param>
@ -1804,76 +1821,50 @@ namespace RMuseum.Services.Implementation
{
return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorPoemCompleteViewModel[] Items)>((null, null), "خطای جستجوی عبارت خالی");
}
using (SqlConnection sqlConnection = new SqlConnection(Configuration.GetConnectionString("DefaultConnection")))
string freeText;
if(term.IndexOf('"') == 0 && term.LastIndexOf('"') == (term.Length - 1))
{
await sqlConnection.OpenAsync();
freeText = term;
}
else
{
string[] words =term.Split(' ', StringSplitOptions.RemoveEmptyEntries);
IDbConnection dapper = sqlConnection;
var parameters = new DynamicParameters();
string sql = "SELECT* FROM GanjoorPoems ";
string whereOrAnd = " WHERE ";
if (catId != null)
{
parameters.Add("@CatId", catId);
sql += $" {whereOrAnd} FullUrl LIKE (SELECT FullUrl FROM GanjoorCategories WHERE Id = @CatId) + '%' ";
whereOrAnd = " AND ";
}
else
if (poetId != null)
{
parameters.Add("@PoetId", poetId);
sql += $" {whereOrAnd} CatId IN (SELECT Id FROM GanjoorCategories WHERE PoetId = @PoetId ) ";
whereOrAnd = " AND ";
}
string[] words = term.IndexOf('"') == 0 && term.LastIndexOf('"') == (term.Length - 1) ? new string[] { term.Replace("\"", "") } : term.Split(' ', StringSplitOptions.RemoveEmptyEntries);
int wIndex = 0;
freeText = "";
string emptyOrAnd = "";
foreach (string word in words)
{
wIndex++;
parameters.Add($"@Word{wIndex}", word);
sql += $" {whereOrAnd} PlainText LIKE N'%' + @Word{wIndex} + '%' ";
whereOrAnd = " AND ";
freeText += $" {emptyOrAnd} \"{word}\" ";
emptyOrAnd = " AND ";
}
}
if(poetId != null && catId == null)
{
var poetRes = await GetPoetById((int)poetId);
if (!string.IsNullOrEmpty(poetRes.ExceptionString))
return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorPoemCompleteViewModel[] Items)>((null, null), poetRes.ExceptionString);
catId = poetRes.Result.Cat.Id;
}
List<int> catIdList = new List<int>();
if(catId != null)
{
catIdList.Add((int)catId);
await _populateCategoryChildren((int)catId, catIdList);
}
sql += $" ORDER BY CatId, Id OFFSET {(paging.PageNumber - 1) * paging.PageSize} ROWS FETCH NEXT {paging.PageSize + 1} ROWS ONLY ";
List<GanjoorPoem> poems = new List<GanjoorPoem> (await dapper.QueryAsync<GanjoorPoem>(sql, parameters));
int clientPageSize = (poems.Count == paging.PageSize + 1) ? paging.PageSize : poems.Count;
int count = clientPageSize;
int PageSize = paging.PageSize;
int CurrentPage = paging.PageNumber;
int TotalCount = -1;
int TotalPages = -1;
bool previousPage = CurrentPage > 1;
bool nextPage = clientPageSize != poems.Count;
PaginationMetadata paginationMetadata = new PaginationMetadata()
{
totalCount = TotalCount,
pageSize = PageSize,
currentPage = CurrentPage,
totalPages = TotalPages,
hasPreviousPage = previousPage,
hasNextPage = nextPage
};
List<GanjoorPoemCompleteViewModel> items = new List<GanjoorPoemCompleteViewModel>();
Dictionary<int, GanjoorPoetCompleteViewModel> cachedPoetsByCat = new Dictionary<int, GanjoorPoetCompleteViewModel>();
foreach (var poem in poems)
{
var item = new GanjoorPoemCompleteViewModel()
var source =
_context.GanjoorPoems
.Where(p =>
(catId == null || catIdList.Contains(p.CatId))
&&
EF.Functions.FreeText(p.PlainText, freeText)
)
.Include(p => p.Cat)
.OrderBy(p => p.CatId).ThenBy(p => p.Id)
.Select
(
poem =>
new GanjoorPoemCompleteViewModel()
{
Id = poem.Id,
Title = poem.Title,
@ -1884,34 +1875,41 @@ namespace RMuseum.Services.Implementation
PlainText = poem.PlainText,
GanjoorMetre = poem.GanjoorMetre,
RhymeLetters = poem.RhymeLetters,
};
if (cachedPoetsByCat.TryGetValue(poem.CatId, out GanjoorPoetCompleteViewModel poet))
{
item.Category = poet;
Category = new GanjoorPoetCompleteViewModel()
{
Poet = new GanjoorPoetViewModel()
{
Id = poem.Cat.Poet.Id,
}
},
}
else
{
int itemPoetId = await dapper.QuerySingleAsync<int>($"SELECT PoetId FROM GanjoorCategories WHERE Id = {poem.CatId}");
poet = (await GetPoetById(itemPoetId)).Result;
);
cachedPoetsByCat.Add(poem.CatId, poet);
item.Category = poet;
}
items.Add
(
item
);
(PaginationMetadata PagingMeta, GanjoorPoemCompleteViewModel[] Items) paginatedResult =
await QueryablePaginator<GanjoorPoemCompleteViewModel>.Paginate(source, paging);
Dictionary<int, GanjoorPoetCompleteViewModel> cachedPoets = new Dictionary<int, GanjoorPoetCompleteViewModel>();
foreach (var item in paginatedResult.Items)
{
if (cachedPoets.TryGetValue(item.Category.Poet.Id, out GanjoorPoetCompleteViewModel poet))
{
item.Category = poet;
}
else
{
poet = (await GetPoetById(item.Category.Poet.Id)).Result;
cachedPoets.Add(item.Category.Poet.Id, poet);
item.Category = poet;
}
return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorPoemCompleteViewModel[] Items)>(new(paginationMetadata, items.ToArray()));
}
return new RServiceResult<(PaginationMetadata PagingMeta, GanjoorPoemCompleteViewModel[] Items)>(paginatedResult);
}
catch (Exception exp)
{
@ -1919,7 +1917,6 @@ namespace RMuseum.Services.Implementation
}
}
/// <summary>
/// modify page
/// </summary>