diff --git a/T120B165-ImgBoard/Controllers/PostController.cs b/T120B165-ImgBoard/Controllers/PostController.cs index 1987f0d..28c4ff8 100644 --- a/T120B165-ImgBoard/Controllers/PostController.cs +++ b/T120B165-ImgBoard/Controllers/PostController.cs @@ -259,9 +259,9 @@ public class PostController( [HttpGet] [ProducesResponseType(StatusCodes.Status200OK)] [ProducesResponseType(StatusCodes.Status400BadRequest)] - public async Task>> GetAll([Range(1, int.MaxValue)] int pageNumber = 1) + public async Task>> GetAll(string? query, [Range(1, int.MaxValue)] int pageNumber = 1) { - var list = await postService.GetAll(pageNumber); + var list = await postService.FindAll(query, pageNumber); var newItems = list.Items.Select(i => { var fileUrl = Url.Action(nameof(PatchFileContent), "Post", diff --git a/T120B165-ImgBoard/Services/PostService.cs b/T120B165-ImgBoard/Services/PostService.cs index 5d428b9..ecc5f36 100644 --- a/T120B165-ImgBoard/Services/PostService.cs +++ b/T120B165-ImgBoard/Services/PostService.cs @@ -1,3 +1,4 @@ +using System.Text.RegularExpressions; using Microsoft.EntityFrameworkCore; using T120B165_ImgBoard.Data; using T120B165_ImgBoard.Models; @@ -13,6 +14,7 @@ public interface IPostService string fileName, string fileContentType, long fileSize); Task GetById(int postId, bool includeUnfinished = false); Task> GetAll(int pageNumber = 1); + public Task> FindAll(string? query, int pageNumber = 1); Task Delete(Post post); Task Update(Post post); } @@ -83,6 +85,70 @@ public class PostService(ImgBoardContext context): IPostService .ToListAsync(); return new PagedList(items, pageNumber, PageSize, totalCount); } + + public async Task> FindAll(string? query, int pageNumber = 1) + { + var postsQuery = context.Posts + .Where(p => p.File.FinishedDate != null); + + // Apply query filtering if the query string is not null or empty + if (!string.IsNullOrWhiteSpace(query)) + { + // Parse the query string into required and excluded tags + var (requiredTags, excludedTags) = ParseTagQuery(query); + + // Filter for required tags (Post must have ALL of these tags) + foreach (var requiredTag in requiredTags) + { + // Where the post's Tags collection contains ANY tag whose name matches the requiredTag. + postsQuery = postsQuery.Where(p => p.Tags.Any(t => t.Name == requiredTag)); + } + + // Filter out excluded tags (Post must NOT have ANY of these tags) + foreach (var excludedTag in excludedTags) + { + // Where the post's Tags collection does NOT contain ANY tag whose name matches the excludedTag. + postsQuery = postsQuery.Where(p => p.Tags.All(t => t.Name != excludedTag)); + } + } + + var totalCount = await postsQuery.CountAsync(); + var items = await postsQuery + .Skip((pageNumber - 1) * PageSize) + .Take(PageSize) + .Include(b => b.Author) + .Include(b => b.Tags) + .Include(b => b.File) + .ToListAsync(); + return new PagedList(items, pageNumber, PageSize, totalCount); + } + + private static (List requiredTags, List excludedTags) ParseTagQuery(string query) + { + var requiredTags = new List(); + var excludedTags = new List(); + + // Regex to find all "+tag" or "-tag" segments + var matches = Regex.Matches(query, @"([+-])([\w-]+)"); + + foreach (Match match in matches) + { + var type = match.Groups[1].Value; // "+" or "-" + var tag = match.Groups[2].Value; // The tag name + + switch (type) + { + case "+": + requiredTags.Add(tag); + break; + case "-": + excludedTags.Add(tag); + break; + } + } + + return (requiredTags, excludedTags); + } public async Task Delete(Post post) {