diff options
Diffstat (limited to 'Services/FeedService.cs')
| -rw-r--r-- | Services/FeedService.cs | 155 |
1 files changed, 155 insertions, 0 deletions
diff --git a/Services/FeedService.cs b/Services/FeedService.cs new file mode 100644 index 0000000..864a751 --- /dev/null +++ b/Services/FeedService.cs @@ -0,0 +1,155 @@ +using Microsoft.EntityFrameworkCore; + +namespace HyperBooru.Services; + +public interface IFeedService { + public Media[] LoadChunk( + bool selectIngest, + bool includeNsfw, + Media? key = null, + int count = 50); + + public Media[] LoadChunk( + bool selectIngest, + bool includeNsfw, + string query, + Media? key = null, + int count = 50); + + public Media[] LoadChunk( + bool selectIngest, + bool includeNsfw, + Guid tagId, + Media? key = null, + int count = 50); +} + +public class FeedService : IFeedService { + private IDbContextFactory<HBContext> dbFactory; + + public FeedService(IDbContextFactory<HBContext> dbFactory) => + this.dbFactory = dbFactory; + + public Media[] LoadChunk( + bool selectIngest, + bool includeNsfw, + Media? key, + int count) => LoadChunkInternal(selectIngest, includeNsfw, null, null, key, count); + + public Media[] LoadChunk( + bool selectIngest, + bool includeNsfw, + string query, + Media? key, + int count) => LoadChunkInternal(selectIngest, includeNsfw, query, null, key, count); + + public Media[] LoadChunk( + bool selectIngest, + bool includeNsfw, + Guid tagId, + Media? key, + int count) => LoadChunkInternal(selectIngest, includeNsfw, null, tagId, key, count); + + private Media[] LoadChunkInternal( + bool selectIngest, + bool includeNsfw, + string? query, + Guid? tagId, + Media? key, + int count) { + + if(selectIngest && !includeNsfw) + return Array.Empty<Media>(); + + using var db = dbFactory.CreateDbContext(); + + IQueryable<Media> media = db.Media + .AsSingleQuery() + .AsNoTracking() + .Include(m => m.Tags) + .Include(m => m.CurrentUploadedFile); + + if(!includeNsfw) + media = media + .Where(m => !TagsThatImply(db, HBContext.NsfwTag) + .Intersect(m.Tags.Select(t => t.TagDefinitionId)) + .Any()); + + if(selectIngest) { + media = media + .Where(m => m.Tags + .Select(t => t.TagDefinitionId) + .Contains((int) HBObjectId.IngestTag)); + } else { + media = media + .Where(m => !m.Tags + .Select(t => t.TagDefinitionId) + .Contains((int) HBObjectId.IngestTag)); + } + + if(query is not null) { + media = Search(media, query); + } else if(tagId is not null) { + media = media + .Where(m => TagsThatImply(db, (Guid) tagId) + .Intersect(m.Tags.Select(t => t.TagDefinitionId)) + .Any()); + } + + if(key is not null) + media = media.Where(m => m.ObjectId > key.ObjectId); + + return media + .OrderBy(m => m.ObjectId) + .Take(count) + .ToArray(); + } + + private static IQueryable<Media> Search(IQueryable<Media> media, string query) { + // TODO: search implicit tags as well + + query = query.ToLower().Trim(); + + return media + .Where(m => + (m.ShortDescription != null && m.ShortDescription.ToLower().Contains(query)) || + (m.LongDescription != null && m.LongDescription.ToLower().Contains(query)) || + (m.UploadedFiles.Any(uf => uf.Filename != null && uf.Filename.ToLower().Contains(query))) || + (m.OcrData != null && m.OcrData.SearchableText.ToLower().Contains(query)) || + (m.Tags.Any(t => t.TagDefinition.Name.ToLower().Contains(query)))); + } + + private static IQueryable<int> TagsThatImply(HBContext db, Guid tagId) => + db.Database.SqlQueryRaw<int>(""" + WITH RECURSIVE basetag AS ( + SELECT "ObjectId" FROM "Objects" WHERE "Guid" = {0} + ), + impliedtags AS ( + SELECT + "TagDefinitionObjectId" + FROM + "TagDefinitionTagDefinition" + INNER JOIN + basetag + ON + "ImplicitTagsObjectId" = basetag."ObjectId" + UNION + SELECT + "TagDefinitionTagDefinition"."TagDefinitionObjectId" + FROM + "TagDefinitionTagDefinition" + INNER JOIN + impliedtags + ON + impliedtags."TagDefinitionObjectId" = "TagDefinitionTagDefinition"."ImplicitTagsObjectId" + ) + SELECT DISTINCT + "TagDefinitionObjectId" AS "Value" + FROM impliedtags + UNION + SELECT + "ObjectId" AS "Value" + FROM + basetag + """, tagId); +} |
