using Microsoft.EntityFrameworkCore; namespace HyperBooru.Services; public interface ISearchService { public Media[] Search(string query); } public class SearchService : ISearchService { private ITagService tagService; private IDbContextFactory dbFactory; public SearchService( IDbContextFactory dbFactory, ITagService tagService) { this.tagService = tagService; this.dbFactory = dbFactory; } public Media[] Search(string query) { var db = dbFactory.CreateDbContext(); query = query.ToLower(); int[] descriptionResults = SearchDescription(query); int[] ocrResults = SearchOcr(query); var matchedTag = db.TagDefinitions .FirstOrDefault(td => td.Name.ToLower() == query); int[] tags; if(matchedTag is not null) { tags = tagService .TagsThatImply(matchedTag) .Select(td => td.ObjectId) .ToArray(); } else { // TODO: Expand scope to all tags that imply tags = db.TagDefinitions .Where(td => td.Name.ToLower().Contains(query)) .Select(td => td.ObjectId) .ToArray(); } int[] tagResults = SearchTags(tags); int[] mediaIds = descriptionResults .Union(ocrResults) .Union(tagResults) .OrderDescending() .ToArray(); return db.Media .Include(m => m.Tags) .Where(m => mediaIds.Contains(m.ObjectId)) .ToArray(); } // TODO: Make asynchronous private int[] SearchTags(int[] tags) { return Task.Run(() => { using var db = dbFactory.CreateDbContext(); return db.Media .Include(m => m.Tags) .AsEnumerable() .Where(m => m.Tags.IntersectBy(tags, t => t.TagDefinitionId).Any()) .Select(m => m.ObjectId) .ToArray(); }).GetAwaiter().GetResult(); } // TODO: Make asynchronous private int[] SearchDescription(string query) { return Task.Run(() => { using var db = dbFactory.CreateDbContext(); query = query.ToLower(); return db.Media .Where(m => (m.ShortDescription != null && m.ShortDescription.ToLower().Contains(query)) || (m.LongDescription != null && m.LongDescription.ToLower().Contains(query))) .Select(m => m.ObjectId) .ToArray(); }).GetAwaiter().GetResult(); } // TODO: Make asynchronous private int[] SearchOcr(string query) { return Task.Run(() => { using var db = dbFactory.CreateDbContext(); query = query.ToLower(); return db.OcrData .Include(o => o.Media) .Where(o => o.SearchableText.Contains(query)) .Select(o => o.Media.ObjectId) .ToArray(); }).GetAwaiter().GetResult(); } }