Skip to content

Commit

Permalink
Merge Separate Action for Autocomplete #41
Browse files Browse the repository at this point in the history
  • Loading branch information
patrickackermann authored Aug 17, 2022
2 parents 22c868d + f901c49 commit 99c1015
Show file tree
Hide file tree
Showing 4 changed files with 108 additions and 22 deletions.
2 changes: 1 addition & 1 deletion src/ClientApp/src/components/Home.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ export function Home() {
if (searchString.length < 3) {
setSearchOptions([]);
} else {
const response = await fetch("/search?query=" + searchString);
const response = await fetch("/search/suggest/" + searchString);
if (response.ok && response.status !== 204 /* No Content */) {
const repositoryTree = await response.json();
setSearchOptions([...new Set(getAllModels(repositoryTree).map((m) => m.name))]);
Expand Down
71 changes: 50 additions & 21 deletions src/Controllers/SearchController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,53 @@ public SearchController(ILogger<SearchController> logger, RepoBrowserContext con
return null;
}

var searchPattern = $"%{EscapeLikePattern(trimmedQuery)}%";
var repositories = await SearchRepositories(trimmedQuery).ConfigureAwait(false);

// Create repository tree
foreach (var repository in repositories.Values)
{
repository.SubsidiarySites = repository.SubsidiarySites.Select(c => repositories[c.HostNameId]).ToHashSet();
}

var root = repositories.Values.SingleOrDefault(r => !r.ParentSites.Any());
if (root != null && PruneEmptyRepositories(root))
{
return root;
}
else
{
return null;
}
}

/// <summary>
/// Get search query suggestions based on <paramref name="query"/>.
/// </summary>
/// <param name="query">The query string to search for.</param>
/// <returns>A sequence of <see cref="Model.Name"/> related to <paramref name="query"/>.</returns>
[HttpGet("suggest/{query}")]
public async Task<IEnumerable<string>> GetSearchSuggestions(string query)
{
logger.LogDebug("Get search options for <{SearchQuery}>", query);

var trimmedQuery = query?.Trim();
if (string.IsNullOrEmpty(trimmedQuery))
{
return Enumerable.Empty<string>();
}

var repositories = await SearchRepositories(trimmedQuery).ConfigureAwait(false);

return repositories
.Values
.SelectMany(r => r.Models)
.Select(m => m.Name)
.ToList();
}

private Task<Dictionary<string, Repository>> SearchRepositories(string query)
{
var searchPattern = $"%{EscapeLikePattern(query)}%";

var modelsNamesFoundFromCatalogs = context.Catalogs
.Where(c => EF.Functions.ILike(c.Identifier, searchPattern, @"\"))
Expand All @@ -59,7 +105,7 @@ public SearchController(ILogger<SearchController> logger, RepoBrowserContext con
.Distinct()
.ToList();

var repositories = await context.Repositories
return context.Repositories
.Include(r => r.SubsidiarySites)
.Include(r => r.ParentSites)
.Include(r => r.Models
Expand All @@ -69,26 +115,9 @@ public SearchController(ILogger<SearchController> logger, RepoBrowserContext con
|| EF.Functions.ILike(m.Version, searchPattern, @"\")
|| EF.Functions.ILike(m.File, searchPattern, @"\")
|| modelsNamesFoundFromCatalogs.Contains(m.Name)
|| m.Tags.Contains(trimmedQuery)))
|| m.Tags.Contains(query)))
.AsNoTracking()
.ToDictionaryAsync(r => r.HostNameId)
.ConfigureAwait(false);

// Create repository tree
foreach (var repository in repositories.Values)
{
repository.SubsidiarySites = repository.SubsidiarySites.Select(c => repositories[c.HostNameId]).ToHashSet();
}

var root = repositories.Values.SingleOrDefault(r => !r.ParentSites.Any());
if (root != null && PruneEmptyRepositories(root))
{
return root;
}
else
{
return null;
}
.ToDictionaryAsync(r => r.HostNameId);
}

/// <summary>
Expand Down
4 changes: 4 additions & 0 deletions src/Program.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
using Microsoft.EntityFrameworkCore;
using ModelRepoBrowser;
using ModelRepoBrowser.Crawler;
using Npgsql.Logging;

NpgsqlLogManager.Provider = new ConsoleLoggingProvider(NpgsqlLogLevel.Debug, true, false);
NpgsqlLogManager.IsParameterLoggingEnabled = true;

var builder = WebApplication.CreateBuilder(args);

Expand Down
53 changes: 53 additions & 0 deletions tests/SearchControllerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -184,4 +184,57 @@ public async Task SearchRepositoryTreeResult()
.AssertSingleItem("jaquelin.com", 1)
.AssertSingleItem("kelsi.biz", 2));
}

[TestMethod]
public async Task GetSearchSuggestions()
{
const string query = "and";
var suggestions = await controller.GetSearchSuggestions(query);
var search = await controller.Search(query);
Assert.IsNotNull(search);

CollectionAssert.AreEquivalent(new[]
{
"Handcrafted Rubber Tuna_Sudanese Pound_syndicate",
"Engineer_Hill_Solutions_Practical_Michigan",
"Iceland Krona_New Israeli Sheqel_matrix_Oklahoma",
"Handcrafted Granite Ball_Associate_haptic_Money Market Account_Beauty",
"deposit",
"Virgin Islands, U.S._withdrawal_CFA Franc BCEAO_THX",
"indigo_Sleek Granite Salad_Practical Concrete Ball_moderator_interface",
"back-end_benchmark_Legacy_Future_Crescent",
"capability_Unbranded Granite Table_Intelligent Cotton Table_static",
"Global_Licensed",
"bandwidth_Refined Fresh Shoes",
"back-end_grey_JBOD",
"bandwidth_auxiliary_Incredible",
"Handcrafted Fresh Hat_metrics_invoice",
"Iowa_Junctions",
"Via_West Virginia_withdrawal",
},
suggestions.ToArray());

CollectionAssert.AreEquivalent(search.GetAllModels().Select(x => x.Name).ToList(), suggestions.ToList());
}

[TestMethod]
public async Task GetSearchSuggestionsNull()
{
var suggestions = await controller.GetSearchSuggestions(null);
suggestions.AssertCount(0);
}

[TestMethod]
public async Task GetSearchSuggestionsEmptyString()
{
var suggestions = await controller.GetSearchSuggestions(string.Empty);
suggestions.AssertCount(0);
}

[TestMethod]
public async Task GetSearchSuggestionsWhitespace()
{
var suggestions = await controller.GetSearchSuggestions(" ");
suggestions.AssertCount(0);
}
}

0 comments on commit 99c1015

Please sign in to comment.