aboutsummaryrefslogtreecommitdiff
path: root/BackEnd/Timeline/Services/SearchService.cs
blob: 680ef9e392520474f342ae7e5936746f523b2ec6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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<TItem>
    {
        public SearchResultItem(TItem item, int score)
        {
            Item = item;
            Score = score;
        }

        public TItem Item { get; set; } = default!;

        /// <summary>
        /// Bigger is better.
        /// </summary>
        public int Score { get; set; }
    }

    public class SearchResult<TItem>
    {
#pragma warning disable CA2227 // Collection properties should be read only
        public List<SearchResultItem<TItem>> Items { get; set; } = new();
#pragma warning restore CA2227 // Collection properties should be read only
    }

    public interface ISearchService
    {
        /// <summary>
        /// Search timelines whose name or title contains query string.
        /// </summary>
        /// <param name="query">String to contain.</param>
        /// <returns>Search results.</returns>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="query"/> is null.</exception>
        /// <exception cref="ArgumentException">Thrown when <paramref name="query"/> is empty.</exception>
        /// <remarks>
        /// Implementation should promise high score is at first.
        /// </remarks>
        Task<SearchResult<TimelineEntity>> SearchTimeline(string query);

        /// <summary>
        /// Search users whose username or nickname contains query string.
        /// </summary>
        /// <param name="query">String to contain.</param>
        /// <returns>Search results.</returns>
        /// <exception cref="ArgumentNullException">Thrown when <paramref name="query"/> is null.</exception>
        /// <exception cref="ArgumentException">Thrown when <paramref name="query"/> is empty.</exception>
        /// <remarks>
        /// Implementation should promise high score is at first.
        /// </remarks>
        Task<SearchResult<UserEntity>> SearchUser(string query);
    }

    public class SearchService : ISearchService
    {
        private readonly DatabaseContext _database;

        public SearchService(DatabaseContext database)
        {
            _database = database;
        }

        public async Task<SearchResult<TimelineEntity>> 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<TimelineEntity>();
            searchResult.Items.AddRange(nameLikeTimelines.Select(t => new SearchResultItem<TimelineEntity>(t, 2)));
            searchResult.Items.AddRange(titleLikeTimelines.Select(t => new SearchResultItem<TimelineEntity>(t, 1)));

            return searchResult;
        }

        public async Task<SearchResult<UserEntity>> 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<UserEntity>();
            searchResult.Items.AddRange(usernameLikeUsers.Select(u => new SearchResultItem<UserEntity>(u, 2)));
            searchResult.Items.AddRange(nicknameLikeUsers.Select(u => new SearchResultItem<UserEntity>(u, 1)));

            return searchResult;

        }
    }
}