blog3000/Blog3000/Shared/BlogPostSearcher.cs

165 lines
5.3 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
namespace Blog3000.Shared
{
public class BlogPostSearch
{
private readonly Func<IAsyncEnumerable<BlogPost>> postSource;
private readonly Func<IAsyncEnumerable<BlogPostHeader>> headerSource;
private readonly string searchTerm;
private readonly List<string> tokens = new List<string>();
private bool invalidTermsRemoved;
/// <summary>
///
/// </summary>
/// <param name="searchTerm">searchTerm or NULL to return the latest items only</param>
public BlogPostSearch(
Func<IAsyncEnumerable<BlogPost>> postSource,
Func<IAsyncEnumerable<BlogPostHeader>> headerSource,
string searchTerm)
{
this.postSource = postSource;
this.headerSource = headerSource;
this.searchTerm = searchTerm;
TokenizeTerm();
}
public bool IsNonEmptyTerm
{
get { return tokens.Count > 0; }
}
public bool InvalidTermsRemoved
{
get { return invalidTermsRemoved; }
}
public bool RequiresFullText
{
get { return tokens.Where(c => !c.StartsWith("#")).FirstOrDefault() != null; }
}
/// <summary>
///
/// </summary>
/// <returns>Search results or latest items when searchterm is null</returns>
public async Task<List<BlogPostHeader>> ExecAsync(
int startIdx, int pageMaxSize, bool findLastPage)
{
IAsyncEnumerable<BlogPostHeader> fetch;
if (!IsNonEmptyTerm)
{
fetch = headerSource()
.Where(p => (p.StickyMenuPos ?? -1) < 0)
.OrderByDescending(p => p.Revisions?.Latest()?.ChangedAt ?? DateTime.MinValue)
.ThenByDescending(p => p.Title);
}
else
{
if (!RequiresFullText)
{
var efetch = headerSource();
foreach (var tok in tokens)
{
if (tok.StartsWith("#"))
{
efetch = efetch
.Where(
p => (
((p.Topics != null) && (p.Topics.Find(t => t.ToLowerInvariant().Contains(tok)) != null))
)
);
}
}
fetch = efetch;
}
else
{
var efetch = postSource();
foreach (var tok in tokens)
{
System.Diagnostics.Debug.WriteLine($"Tok: {tok}");
if (tok.StartsWith("#"))
{
efetch = efetch
.Where(
p => (
((p.Topics != null) && (p.Topics.Find(t => t.ToLowerInvariant().Contains(tok)) != null))
)
);
}
else
{
efetch = ((IAsyncEnumerable<BlogPost>)efetch)
.Where(
p => (
((p.Title?.ToLowerInvariant() ?? "").Contains(tok)) ||
((p.Text?.ToLowerInvariant() ?? "").Contains(tok)) ||
((p.Abstract?.ToLowerInvariant() ?? "").Contains(tok)) ||
((p.Topics != null) && (p.Topics.Find(t => t.ToLowerInvariant().Contains(tok)) != null))
)
);
}
}
fetch = efetch;
}
fetch = fetch
.OrderByDescending(p => p.Revisions?.Latest()?.ChangedAt ?? DateTime.MinValue)
.OrderByDescending(p => p.Title);
}
List<BlogPostHeader> res;
if (!findLastPage)
{
res = await fetch
.Skip(startIdx)
.Take(pageMaxSize)
.ToListAsync();
}
else
{
var count = await fetch.CountAsync();
var sidx = count - pageMaxSize;
if (sidx < 0) sidx = 0;
res = await fetch
.Skip(sidx)
.Take(pageMaxSize)
.ToListAsync();
}
return res;
}
private void TokenizeTerm()
{
if (searchTerm != null)
{
foreach (var t in searchTerm.ToLowerInvariant().Split(" ")) // Improve
{
if (!String.IsNullOrWhiteSpace(t) && (t.Length >= 2))
{
tokens.Add(t);
}
else invalidTermsRemoved = true;
}
}
}
}
}