using Microsoft.EntityFrameworkCore;
using RMuseum.Models.Divan;
using System;
using System.Data;
using System.Linq;
using RMuseum.DbContext;
using System.Collections.Generic;
using RSecurityBackend.Services.Implementation;
using System.Threading.Tasks;
using RSecurityBackend.Models.Generic.Db;
using RSecurityBackend.Models.Generic;
using RMuseum.Models.Divan.ViewModels;
namespace RMuseum.Services.Implementation
{
///
/// IDivanService implementation
///
public partial class DivanService : IDivanService
{
///
/// category word counts
///
///
///
///
///
public async Task> GetCategoryWordCountsAsync(int catId, string term, PagingParameterModel paging)
{
try
{
var source =
from cwd in _context.CategoryWordCounts
where cwd.CatId == catId && (string.IsNullOrEmpty(term) || cwd.Word.Contains(term))
orderby cwd.RowNmbrInCat
select
cwd;
(PaginationMetadata PagingMeta, CategoryWordCount[] Items) paginatedResult =
await QueryablePaginator.Paginate(source, paging);
return new RServiceResult<(PaginationMetadata PagingMeta, CategoryWordCount[] Items)>(paginatedResult);
}
catch (Exception exp)
{
return new RServiceResult<(PaginationMetadata PagingMeta, CategoryWordCount[] Items)>((null, null), exp.ToString());
}
}
///
/// category words summary
///
///
///
public async Task> GetCategoryWordCountSummaryAsync(int catId)
{
try
{
var res = await _context.CategoryWordCountSummaries.AsNoTracking().Where(c => c.CatId == catId).SingleOrDefaultAsync();
if(res == null)
return new RServiceResult(new CategoryWordCountSummary() { CatId = catId, UniqueWordCount = 0, TotalWordCount = 0 });
return new RServiceResult(res);
}
catch (Exception exp)
{
return new RServiceResult(null, exp.ToString());
}
}
///
/// comparison of word counts for poets ot subcats
///
///
///
///
///
///
public async Task> GetCategoryWordCountsBySubCatsAsync(string term, PagingParameterModel paging, int? parentCatId, int? poetId)
{
try
{
if(poetId != null && parentCatId == null)
{
var cat = await _context.DivanCategories.AsNoTracking().Where(p => p.PoetId == poetId && p.ParentId == null).SingleAsync();
parentCatId = cat.Id;
}
var source =
from cwd in _context.CategoryWordCounts
join catsum in _context.CategoryWordCountSummaries on cwd.CatId equals catsum.CatId
join cat in _context.DivanCategories on cwd.CatId equals cat.Id
where cat.ParentId == parentCatId && cwd.Word == term
orderby cwd.Count descending
select
new PoetOrCatWordStat()
{
CatId = cat.Id,
PoetId = cat.PoetId,
Name = cat.Title,
Count = cwd.Count,
RowNmbrInCat = cwd.RowNmbrInCat,
TotalWordCount = catsum.TotalWordCount,
};
(PaginationMetadata PagingMeta, PoetOrCatWordStat[] Items) paginatedResult =
await QueryablePaginator.Paginate(source, paging);
return new RServiceResult<(PaginationMetadata PagingMeta, PoetOrCatWordStat[] Items)>(paginatedResult);
}
catch (Exception exp)
{
return new RServiceResult<(PaginationMetadata PagingMeta, PoetOrCatWordStat[] Items)>((null, null), exp.ToString());
}
}
///
/// CategoryWordCount for a specific term in a category
///
///
///
///
///
public async Task> GetCategoryWordCountByTermAsync(string term, int? catId, int? poetId)
{
try
{
if (poetId != null && catId == null)
{
var cat = await _context.DivanCategories.AsNoTracking().Where(p => p.PoetId == poetId && p.ParentId == null).SingleAsync();
catId = cat.Id;
}
if (catId == null)
{
catId = 0;
}
return new RServiceResult(await _context.CategoryWordCounts.AsNoTracking().Where(s => s.CatId == catId && s.Word == term).OrderBy(s => s.RowNmbrInCat).FirstOrDefaultAsync());
}
catch (Exception exp)
{
return new RServiceResult(null, exp.ToString());
}
}
///
/// build word counts
///
///
///
public async Task BuildCategoryWordCountsAsync(bool reset, int poetId)
{
int lastFinishedPoetId = 0;
if (!reset)
{
var resOption = await _optionsService.GetValueAsync("CategoryWordCountsLastPoetId", null, null);
if (!string.IsNullOrEmpty(resOption.Result))
{
lastFinishedPoetId = int.Parse(resOption.Result);
}
}
_backgroundTaskQueue.QueueBackgroundWorkItem
(
async token =>
{
using (RMuseumDbContext context = new RMuseumDbContext(new DbContextOptions())) //this is long running job, so _context might be already been freed/collected by GC
{
LongRunningJobProgressServiceEF jobProgressServiceEF = new LongRunningJobProgressServiceEF(context);
var job = (await jobProgressServiceEF.NewJob($"BuildCategoryWordCounts", "Query data")).Result;
try
{
var poets = poetId == 0 ? await context.DivanPoets.AsNoTracking().Where(p => p.Published && p.Id > lastFinishedPoetId).OrderBy(p => p.Id).ToListAsync()
: await context.DivanPoets.AsNoTracking().Where(p => p.Published && p.Id == poetId).OrderBy(p => p.Id).ToListAsync();
foreach (var poet in poets)
{
var poetCat = await context.DivanCategories.AsNoTracking().Where(c => c.PoetId == poet.Id && c.ParentId == null).SingleAsync();
await jobProgressServiceEF.UpdateJob(job.Id, poet.Id, poet.Nickname);
var wordCounts = await _BuildCategoryWordStatsAsync(context, poetCat, jobProgressServiceEF, job);
if(true == await context.CategoryWordCounts.Where(c => c.CatId == poetCat.Id).AnyAsync()
||
true == await context.CategoryWordCountSummaries.Where(c => c.CatId == poetCat.Id).AnyAsync()
)
{
List catIdList = new List
{
poetCat.Id
};
await _populateCategoryChildren(context, poetCat.Id, catIdList);
await jobProgressServiceEF.UpdateJob(job.Id, poet.Id, poet.Nickname + $": Removing old data");
foreach (var catId in catIdList)
{
var oldData1 = await context.CategoryWordCounts.Where(c => c.CatId == catId).ToListAsync();
context.RemoveRange(oldData1);
var oldData2 = await context.CategoryWordCountSummaries.Where(c => c.CatId == catId).ToListAsync();
context.RemoveRange(oldData2);
await jobProgressServiceEF.UpdateJob(job.Id, poet.Id, poet.Nickname + $": Removing old data {catId}");
}
}
await jobProgressServiceEF.UpdateJob(job.Id, poet.Id, poet.Nickname + $": In Memory => DbContext - {wordCounts.Count} - Sorting:");
if (wordCounts.Any())
{
List catIdList = new List
{
poetCat.Id
};
await _populateCategoryChildren(context, poetCat.Id, catIdList);
foreach (var catId in catIdList)
{
var catWordCounts = wordCounts.Where(w => w.CatId == catId).ToList();
if (catWordCounts.Any())
{
await jobProgressServiceEF.UpdateJob(job.Id, poet.Id, poet.Nickname + $": In Memory => DbContext CatId: {catId} - {catWordCounts.Count} - Sorting Started.");
catWordCounts.Sort((a, b) => b.Count.CompareTo(a.Count));
await jobProgressServiceEF.UpdateJob(job.Id, poet.Id, poet.Nickname + $": In Memory => DbContext CatId: {catId} - {catWordCounts.Count} - Sorting Finished.");
for (int i = 0; i < catWordCounts.Count; i++)
{
catWordCounts[i].RowNmbrInCat = i + 1;
}
}
context.Add
(
new CategoryWordCountSummary()
{
CatId = catId,
UniqueWordCount = catWordCounts.Count,
TotalWordCount = catWordCounts.Sum(w => w.Count)
}
);
}
for (int i = 0; i < wordCounts.Count; i++)
{
context.Add(wordCounts[i]);
await jobProgressServiceEF.UpdateJob(job.Id, poet.Id, poet.Nickname + $": Saving {i} of {wordCounts.Count}, {wordCounts[i].Word}");
}
}
if(poetId == 0)
{
RGenericOption option = await context.Options.Where(o => o.Name == "CategoryWordCountsLastPoetId" && o.RAppUserId == null).SingleOrDefaultAsync();
if (option != null)
{
option.Value = poetCat.PoetId.ToString();
context.Options.Update(option);
await context.SaveChangesAsync();
}
else
{
RGenericOption newOption = new RGenericOption
{
Name = "CategoryWordCountsLastPoetId",
Value = poetCat.PoetId.ToString(),
RAppUserId = null
};
context.Options.Add(newOption);
await context.SaveChangesAsync();
}
}
}
await jobProgressServiceEF.UpdateJob(job.Id, 99, "Raw SQL statements");
await context.Database.ExecuteSqlRawAsync("DELETE FROM CategoryWordCounts WHERE CatId = 0");
await context.Database.ExecuteSqlRawAsync("DELETE FROM CategoryWordCountSummaries WHERE CatId = 0");
await context.Database.ExecuteSqlRawAsync(@"
INSERT INTO CategoryWordCounts (Id, CatId, Word, Count, RowNmbrInCat)
SELECT NEWID(), 0, Word, SUM(Count) c, ROW_NUMBER() OVER(ORDER BY SUM(COUNT) DESC)
FROM CategoryWordCounts
WHERE CatId IN (SELECT Id FROM DivanCategories WHERE ParentId IS NULL)
GROUP BY Word
ORDER BY c DESC");
await context.Database.ExecuteSqlRawAsync(@"
INSERT INTO CategoryWordCountSummaries (Id, CatId, UniqueWordCount, TotalWordCount)
SELECT NEWID(), 0,
(SELECT COUNT(*) FROM CategoryWordCounts WHERE CatId = 0),
(SELECT SUM(Count) FROM CategoryWordCounts WHERE CatId = 0)");
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", true);
}
catch (Exception exp)
{
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", false, exp.ToString());
}
}
});
}
private async Task> _BuildCategoryWordStatsAsync(RMuseumDbContext context, DivanCat cat, LongRunningJobProgressServiceEF jobProgressServiceEF, RLongRunningJobStatus job)
{
List counts = new List();
var children = await context.DivanCategories.AsNoTracking().Where(c => c.ParentId == cat.Id).ToListAsync();
foreach (var child in children)
{
var subcatCounts = await _BuildCategoryWordStatsAsync(context, child, jobProgressServiceEF, job);
if (subcatCounts.Any())
{
foreach (var subcatCount in subcatCounts.Where(c => c.CatId == child.Id))
{
var wordCount = counts.Where(c => c.CatId == cat.Id && c.Word == subcatCount.Word).SingleOrDefault();
if (wordCount != null)
{
wordCount.Count += subcatCount.Count;
}
else
{
counts.Add(new CategoryWordCount { CatId = cat.Id, Word = subcatCount.Word, Count = subcatCount.Count });
}
}
counts.AddRange(subcatCounts);
}
}
var poems = await context.DivanPoems.AsNoTracking().Where(p => p.CatId == cat.Id).ToListAsync();
foreach (var poem in poems)
{
var verses = await context.DivanVerses.AsNoTracking().Where(v => v.PoemId == poem.Id && v.VersePosition != VersePosition.Comment).OrderBy(v => v.VOrder).ToListAsync();
foreach (var verse in verses)
{
string[] words = LanguageUtils.MakeTextSearchable(verse.Text).Split([' ', '']);
foreach (var word in words)
{
if(string.IsNullOrEmpty(word)) continue;
var wordCount = counts.Where(c => c.CatId == cat.Id && c.Word == word).SingleOrDefault();
if (wordCount != null)
{
wordCount.Count++;
}
else
{
counts.Add(new CategoryWordCount { CatId = cat.Id, Word = word, Count = 1 });
}
}
}
await jobProgressServiceEF.UpdateJob(job.Id, poem.Id);
}
return counts;
}
///
/// fill CategoryWordCounts.RowNmbrInCat
///
public void FillCategoryWordCountsRowNmbrInCat()
{
_backgroundTaskQueue.QueueBackgroundWorkItem
(
async token =>
{
using (RMuseumDbContext context = new RMuseumDbContext(new DbContextOptions())) //this is long running job, so _context might be already been freed/collected by GC
{
LongRunningJobProgressServiceEF jobProgressServiceEF = new LongRunningJobProgressServiceEF(context);
var job = (await jobProgressServiceEF.NewJob($"FillCategoryWordCountsRowNmbrInCat", "Query data")).Result;
try
{
var poets = await context.DivanPoets.AsNoTracking().Where(p => p.Published).OrderBy(p => p.Id).ToListAsync();
foreach (var poet in poets)
{
var poetCat = await context.DivanCategories.AsNoTracking().Where(c => c.PoetId == poet.Id && c.ParentId == null).SingleAsync();
await jobProgressServiceEF.UpdateJob(job.Id, poet.Id, poet.Nickname);
if (true == await context.CategoryWordCounts.Where(c => c.CatId == poetCat.Id).AnyAsync())
{
List catIdList = new List
{
poetCat.Id
};
await _populateCategoryChildren(context, poetCat.Id, catIdList);
foreach (var catId in catIdList)
{
var catWordCounts = await context.CategoryWordCounts.Where(w => w.CatId == catId).ToListAsync();
if (catWordCounts.Any())
{
catWordCounts.Sort((a, b) => b.Count.CompareTo(a.Count));
for (int i = 0; i < catWordCounts.Count; i++)
{
catWordCounts[i].RowNmbrInCat = i + 1;
context.Update(catWordCounts[i]);
await jobProgressServiceEF.UpdateJob(job.Id, poet.Id, poet.Nickname + $": Saving {i} of {catWordCounts.Count}");
}
}
}
}
}
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", true);
}
catch (Exception exp)
{
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", false, exp.ToString());
}
}
});
}
///
/// fill CategoryWordCountSummaries
///
public void FillCategoryWordCountSummaries()
{
_backgroundTaskQueue.QueueBackgroundWorkItem
(
async token =>
{
using (RMuseumDbContext context = new RMuseumDbContext(new DbContextOptions())) //this is long running job, so _context might be already been freed/collected by GC
{
LongRunningJobProgressServiceEF jobProgressServiceEF = new LongRunningJobProgressServiceEF(context);
var job = (await jobProgressServiceEF.NewJob($"FillCategoryWordCountSummaries", "Query data")).Result;
try
{
var poets = await context.DivanPoets.AsNoTracking().Where(p => p.Published).OrderBy(p => p.Id).ToListAsync();
foreach (var poet in poets)
{
var poetCat = await context.DivanCategories.AsNoTracking().Where(c => c.PoetId == poet.Id && c.ParentId == null).SingleAsync();
await jobProgressServiceEF.UpdateJob(job.Id, poet.Id, poet.Nickname);
if (true == await context.CategoryWordCounts.Where(c => c.CatId == poetCat.Id).AnyAsync())
{
List catIdList = new List
{
poetCat.Id
};
await _populateCategoryChildren(context, poetCat.Id, catIdList);
foreach (var catId in catIdList)
{
var uniqueCount = await context.CategoryWordCounts.Where(w => w.CatId == catId).CountAsync();
if (uniqueCount > 0)
{
var totalCount = await context.CategoryWordCounts.Where(w => w.CatId == catId).SumAsync(w => w.Count);
context.Add
(
new CategoryWordCountSummary()
{
CatId = catId,
UniqueWordCount = uniqueCount,
TotalWordCount = totalCount
}
);
await jobProgressServiceEF.UpdateJob(job.Id, poet.Id, poet.Nickname + $": Saving {catId}");
}
}
}
}
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", true);
}
catch (Exception exp)
{
await jobProgressServiceEF.UpdateJob(job.Id, 100, "", false, exp.ToString());
}
}
});
}
}
}