From 78748cc1045a98aabbac7ad65e2e180fbdff1b16 Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Sat, 31 Aug 2024 21:08:33 -0500 Subject: [PATCH] feat: work on performing searches --- .../Interfaces/IRepository.cs | 2 +- .../Interfaces/ISearchService.cs | 6 ++++++ src/SanctionsSearch.Worker/Models/Address.cs | 5 +++++ src/SanctionsSearch.Worker/Models/Sdn.cs | 11 +++++++++++ .../Models/SearchRequest.cs | 5 +++++ .../Models/SearchResult.cs | 17 ++++++++++++++++- .../Persistence/EfRepository.cs | 11 +++++++++-- .../Services/SearchService.cs | 15 +++++++++++++++ 8 files changed, 68 insertions(+), 4 deletions(-) create mode 100644 src/SanctionsSearch.Worker/Interfaces/ISearchService.cs create mode 100644 src/SanctionsSearch.Worker/Services/SearchService.cs diff --git a/src/SanctionsSearch.Worker/Interfaces/IRepository.cs b/src/SanctionsSearch.Worker/Interfaces/IRepository.cs index 83a1c3c..8048e38 100644 --- a/src/SanctionsSearch.Worker/Interfaces/IRepository.cs +++ b/src/SanctionsSearch.Worker/Interfaces/IRepository.cs @@ -3,5 +3,5 @@ namespace SanctionsSearch.Worker.Interfaces; interface IRepository where T : Entity { Task Upsert(T entity); - Task> Find(Expression> predicate); + Task> Find(Expression> predicate, params Expression>[]? includes); } \ No newline at end of file diff --git a/src/SanctionsSearch.Worker/Interfaces/ISearchService.cs b/src/SanctionsSearch.Worker/Interfaces/ISearchService.cs new file mode 100644 index 0000000..b3d2984 --- /dev/null +++ b/src/SanctionsSearch.Worker/Interfaces/ISearchService.cs @@ -0,0 +1,6 @@ +namespace SanctionsSearch.Worker.Interfaces; + +interface ISearchService +{ + Task PerformSearchAsync(SearchRequest request); +} \ No newline at end of file diff --git a/src/SanctionsSearch.Worker/Models/Address.cs b/src/SanctionsSearch.Worker/Models/Address.cs index b7f3f0c..8c54f9d 100644 --- a/src/SanctionsSearch.Worker/Models/Address.cs +++ b/src/SanctionsSearch.Worker/Models/Address.cs @@ -9,4 +9,9 @@ class Address : Entity public string Remarks { get; set; } = string.Empty; public virtual Sdn Sdn { get; set; } = default!; + + public override string ToString() + { + return $"{StreetAddress}, {CityProvincePostal}, {Country}"; + } } \ No newline at end of file diff --git a/src/SanctionsSearch.Worker/Models/Sdn.cs b/src/SanctionsSearch.Worker/Models/Sdn.cs index f3f5f02..b14dd72 100644 --- a/src/SanctionsSearch.Worker/Models/Sdn.cs +++ b/src/SanctionsSearch.Worker/Models/Sdn.cs @@ -17,4 +17,15 @@ class Sdn : Entity public virtual ICollection
Addresses { get; set; } = []; public virtual ICollection Aliases { get; set; } = []; public virtual ICollection Comments { get; set; } = []; + + public Hit ToHit() + { + return new Hit + { + Name = Name, + Address = Addresses.FirstOrDefault()?.ToString() ?? string.Empty, + Type = Type, + Programs = [.. Program.Split("] [")] + }; + } } \ No newline at end of file diff --git a/src/SanctionsSearch.Worker/Models/SearchRequest.cs b/src/SanctionsSearch.Worker/Models/SearchRequest.cs index f4bb54f..7458d94 100644 --- a/src/SanctionsSearch.Worker/Models/SearchRequest.cs +++ b/src/SanctionsSearch.Worker/Models/SearchRequest.cs @@ -9,4 +9,9 @@ class SearchRequest public string State { get; set; } = string.Empty; public string Zip { get; set; } = string.Empty; public string Country { get; set; } = string.Empty; + + public Expression> ToSdnFilter() + { + return sdn => EF.Functions.Like(sdn.Name, $"%{Name}%"); + } } \ No newline at end of file diff --git a/src/SanctionsSearch.Worker/Models/SearchResult.cs b/src/SanctionsSearch.Worker/Models/SearchResult.cs index 4ea39d5..27d6635 100644 --- a/src/SanctionsSearch.Worker/Models/SearchResult.cs +++ b/src/SanctionsSearch.Worker/Models/SearchResult.cs @@ -2,5 +2,20 @@ namespace SanctionsSearch.Worker.Models; class SearchResult { - // TODO: Add properties to represent the search result + public int SearchRequestId { get; init; } + public List Hits { get; init; } = []; + + public SearchResult(int searchRequestId, List hits) + { + SearchRequestId = searchRequestId; + Hits = hits; + } +} + +class Hit +{ + public string Name { get; init; } = string.Empty; + public string Address { get; init; } = string.Empty; + public string Type { get; init; } = string.Empty; + public List Programs { get; init; } = []; } \ No newline at end of file diff --git a/src/SanctionsSearch.Worker/Persistence/EfRepository.cs b/src/SanctionsSearch.Worker/Persistence/EfRepository.cs index 83062ba..fcb03f6 100644 --- a/src/SanctionsSearch.Worker/Persistence/EfRepository.cs +++ b/src/SanctionsSearch.Worker/Persistence/EfRepository.cs @@ -11,11 +11,18 @@ class EfRepository : IRepository where T : Entity _logger = logger; } - public async Task> Find(Expression> predicate) + public async Task> Find(Expression> predicate, params Expression>[]? includes) { try { - return await _context.Set().Where(predicate).ToListAsync(); + var query = _context.Set().AsQueryable(); + + if (includes is not null) + { + query = includes.Aggregate(query, (current, include) => current.Include(include)); + } + + return await query.Where(predicate).ToListAsync(); } catch (Exception ex) { diff --git a/src/SanctionsSearch.Worker/Services/SearchService.cs b/src/SanctionsSearch.Worker/Services/SearchService.cs new file mode 100644 index 0000000..3d49c8e --- /dev/null +++ b/src/SanctionsSearch.Worker/Services/SearchService.cs @@ -0,0 +1,15 @@ + + +namespace SanctionsSearch.Worker.Services; + +class SearchService(IUnitOfWork uow) : ISearchService +{ + private readonly IUnitOfWork _uow = uow; + + public async Task PerformSearchAsync(SearchRequest request) + { + var results = await _uow.Sdns.Find(request.ToSdnFilter(), sdn => sdn.Addresses); + var hits = results.Select(sdn => sdn.ToHit()).ToList(); + return new(request.Id, hits); + } +} \ No newline at end of file