From c751709b1b4fe6f16fd84647e8e071455e7b78d6 Mon Sep 17 00:00:00 2001 From: Jake Mannens Date: Tue, 17 Mar 2026 03:04:36 +1100 Subject: v0.1a --- Services/SearchService.cs | 117 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 117 insertions(+) create mode 100644 Services/SearchService.cs (limited to 'Services/SearchService.cs') diff --git a/Services/SearchService.cs b/Services/SearchService.cs new file mode 100644 index 0000000..5ca12e1 --- /dev/null +++ b/Services/SearchService.cs @@ -0,0 +1,117 @@ +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().Trim(); + + int[] descriptionResults = SearchDescription(query); + int[] filenameResults = SearchFilenames(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(filenameResults) + .Union(ocrResults) + .Union(tagResults) + .OrderDescending() + .ToArray(); + + return db.Media + .Include(m => m.Tags) + .Include(m => m.CurrentUploadedFile) + .Where(m => mediaIds.Contains(m.ObjectId)) + .ToArray(); + } + + // 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[] SearchFilenames(string query) { + return Task.Run(() => { + using var db = dbFactory.CreateDbContext(); + query = query.ToLower(); + return db.UploadedFiles + .Include(uf => uf.Media) + .Where(uf => uf.Filename != null && uf.Filename.ToLower().Contains(query)) + .Select(uf => uf.Media.ObjectId) + .Distinct() + .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(); + } + + // 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(); + } +} -- cgit v1.3