using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Timeline.Entities; namespace Timeline.Services { public class SearchResultItem { public SearchResultItem(TItem item, int score) { Item = item; Score = score; } public TItem Item { get; set; } = default!; /// /// Bigger is better. /// public int Score { get; set; } } public class SearchResult { #pragma warning disable CA2227 // Collection properties should be read only public List> Items { get; set; } = new(); #pragma warning restore CA2227 // Collection properties should be read only } public interface ISearchService { /// /// Search timelines whose name or title contains query string. /// /// String to contain. /// Search results. /// Thrown when is null. /// Thrown when is empty. /// /// Implementation should promise high score is at first. /// Task> SearchTimeline(string query); /// /// Search users whose username or nickname contains query string. /// /// String to contain. /// Search results. /// Thrown when is null. /// Thrown when is empty. /// /// Implementation should promise high score is at first. /// Task> SearchUser(string query); } public class SearchService : ISearchService { private readonly DatabaseContext _database; public SearchService(DatabaseContext database) { _database = database; } public async Task> SearchTimeline(string query) { if (query is null) throw new ArgumentNullException(nameof(query)); if (query.Length == 0) throw new ArgumentException("Query string can't be empty.", nameof(query)); var nameLikeTimelines = await _database.Timelines.Include(t => t.Owner).Where(t => t.Name == null ? t.Owner.Username.Contains(query) : t.Name.Contains(query)).ToListAsync(); var titleLikeTimelines = await _database.Timelines.Where(t => t.Title != null && t.Title.Contains(query)).ToListAsync(); var searchResult = new SearchResult(); searchResult.Items.AddRange(nameLikeTimelines.Select(t => new SearchResultItem(t, 2))); searchResult.Items.AddRange(titleLikeTimelines.Select(t => new SearchResultItem(t, 1))); return searchResult; } public async Task> SearchUser(string query) { if (query is null) throw new ArgumentNullException(nameof(query)); if (query.Length == 0) throw new ArgumentException("Query string can't be empty.", nameof(query)); var usernameLikeUsers = await _database.Users.Where(u => u.Username.Contains(query)).ToListAsync(); var nicknameLikeUsers = await _database.Users.Where(u => u.Nickname != null && u.Nickname.Contains(query)).ToListAsync(); var searchResult = new SearchResult(); searchResult.Items.AddRange(usernameLikeUsers.Select(u => new SearchResultItem(u, 2))); searchResult.Items.AddRange(nicknameLikeUsers.Select(u => new SearchResultItem(u, 1))); return searchResult; } } }