-
Notifications
You must be signed in to change notification settings - Fork 58
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
592: Add Facet search support r=curquiza a=danFbach # Pull Request ## Related issue Fixes #461 ## What does this PR do? - implements facet search ## PR checklist Please check if your PR fulfills the following requirements: - [x] Does this PR fix an existing issue, or have you listed the changes applied in the PR description (and why they are needed)? - [x] Have you read the contributing guidelines? - [x] Have you made sure that the title is accurate and descriptive of the changes? Few notes: - Implemented the `FacetSearchAsync` method with a `facetName` parameter seperate from a `FacetSearchQuery` object parameter, similar to how `SearchAsync` separates out the `query` parameter from the `SearchQuery` object, since these parameters are **required**. - Not sure how we want to handle an empty or null `facetName`? This may change tests requirements. See commented out tests - Test `FacetSearchWithFilterFacetIsNull` is "wrong." Perhaps this is just how meilisearch works, but filtering by `genre IS NULL` returns nothing, but 2 entries in the `IndexForFaceting-SearchTests` have a `null` genre. Would like some feedback here - Any additional tests? Co-authored-by: Dan Fehrenbach <[email protected]> Co-authored-by: Ahmed Fwela <[email protected]> Co-authored-by: Clémentine <[email protected]>
- Loading branch information
Showing
7 changed files
with
395 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using System.Collections.Generic; | ||
using System.Text.Json.Serialization; | ||
|
||
namespace Meilisearch | ||
{ | ||
/// <summary> | ||
/// Wrapper for facet search query | ||
/// </summary> | ||
public class FacetSearchQuery | ||
{ | ||
/// <summary> | ||
/// Gets or sets the facetName property | ||
/// </summary> | ||
[JsonPropertyName("facetName")] | ||
public string FacetName { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the facetQuery property | ||
/// </summary> | ||
[JsonPropertyName("facetQuery")] | ||
public string FacetQuery { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the q property | ||
/// </summary> | ||
[JsonPropertyName("q")] | ||
public string Query { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the filter property | ||
/// </summary> | ||
[JsonPropertyName("filter")] | ||
public dynamic Filter { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the matchingStrategy property, can be <c>last</c>, <c>all</c> or <c>frequency</c>. | ||
/// </summary> | ||
[JsonPropertyName("matchingStrategy")] | ||
public string MatchingStrategy { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the attributesToSearchOn property | ||
/// </summary> | ||
[JsonPropertyName("attributesToSearchOn")] | ||
public IEnumerable<string> AttributesToSearchOn { get; set; } | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
using System.Collections.Generic; | ||
using System.Text.Json.Serialization; | ||
|
||
namespace Meilisearch | ||
{ | ||
/// <summary> | ||
/// Wrapper for FacetSearchResponse | ||
/// </summary> | ||
public class FacetSearchResult | ||
{ | ||
/// <summary> | ||
/// Gets or sets the facetHits property | ||
/// </summary> | ||
[JsonPropertyName("facetHits")] | ||
public IEnumerable<FacetHit> FacetHits { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the facet query | ||
/// </summary> | ||
[JsonPropertyName("facetQuery")] | ||
public string FacetQuery { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the processingTimeMs property | ||
/// </summary> | ||
[JsonPropertyName("processingTimeMs")] | ||
public int ProcessingTimeMs { get; set; } | ||
|
||
/// <summary> | ||
/// Wrapper for Facet Hit | ||
/// </summary> | ||
public class FacetHit | ||
{ | ||
/// <summary> | ||
/// Gets or sets the value property | ||
/// </summary> | ||
[JsonPropertyName("value")] | ||
public string Value { get; set; } | ||
|
||
/// <summary> | ||
/// Gets or sets the count property | ||
/// </summary> | ||
[JsonPropertyName("count")] | ||
public int Count { get; set; } | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,242 @@ | ||
using System.Linq; | ||
using System.Threading.Tasks; | ||
|
||
using FluentAssertions; | ||
|
||
using Xunit; | ||
|
||
namespace Meilisearch.Tests | ||
{ | ||
public abstract class FacetSearchTests<TFixture> : IAsyncLifetime where TFixture : IndexFixture | ||
{ | ||
private Index _indexForFaceting; | ||
|
||
private readonly TFixture _fixture; | ||
|
||
public FacetSearchTests(TFixture fixture) | ||
{ | ||
_fixture = fixture; | ||
} | ||
|
||
public async Task InitializeAsync() | ||
{ | ||
await _fixture.DeleteAllIndexes(); // Test context cleaned for each [Fact] | ||
_indexForFaceting = await _fixture.SetUpIndexForFaceting("IndexForFaceting-SearchTests"); | ||
} | ||
|
||
public Task DisposeAsync() => Task.CompletedTask; | ||
|
||
[Fact] | ||
public async Task BasicFacetSearch() | ||
{ | ||
var results = await _indexForFaceting.FacetSearchAsync("genre"); | ||
|
||
Assert.Equal(4, results.FacetHits.Count()); | ||
Assert.Null(results.FacetQuery); | ||
} | ||
|
||
//[Fact] //these may or may not be required. | ||
//public async Task BasicFacetSearchWithNoFacet() | ||
//{ | ||
// var results = await _indexForFaceting.SearchFacetsAsync(null); | ||
|
||
// results.FacetHits.Should().BeEmpty(); | ||
//} | ||
|
||
//[Fact] | ||
//public async Task BasicFacetSearchWithEmptyFacet() | ||
//{ | ||
// var results = await _indexForFaceting.SearchFacetsAsync(string.Empty); | ||
|
||
// results.FacetHits.Should().BeEmpty(); | ||
//} | ||
|
||
[Fact] | ||
public async Task FacetSearchWithFilter() | ||
{ | ||
var query = new FacetSearchQuery() | ||
{ | ||
Filter = "genre = SF" | ||
}; | ||
var results = await _indexForFaceting.FacetSearchAsync("genre", query); | ||
|
||
Assert.Single(results.FacetHits); | ||
Assert.Equal("SF", results.FacetHits.First().Value); | ||
Assert.Equal(2, results.FacetHits.First().Count); | ||
Assert.Null(results.FacetQuery); | ||
} | ||
|
||
[Fact] | ||
public async Task FacetSearchWithFilterWithSpaces() | ||
{ | ||
var query = new FacetSearchQuery() | ||
{ | ||
Filter = "genre = 'sci fi'" | ||
}; | ||
var results = await _indexForFaceting.FacetSearchAsync("genre", query); | ||
|
||
Assert.Single(results.FacetHits); | ||
Assert.Equal("sci fi", results.FacetHits.First().Value); | ||
Assert.Equal(1, results.FacetHits.First().Count); | ||
Assert.Null(results.FacetQuery); | ||
} | ||
|
||
[Fact] | ||
public async Task FacetSearchWithFilterFacetIsNotNull() | ||
{ | ||
var query = new FacetSearchQuery() | ||
{ | ||
Filter = "genre IS NOT NULL" | ||
}; | ||
var results = await _indexForFaceting.FacetSearchAsync("genre", query); | ||
|
||
Assert.Equal(4, results.FacetHits.Count()); | ||
Assert.Equal("Action", results.FacetHits.First().Value); | ||
Assert.Equal(3, results.FacetHits.First().Count); | ||
Assert.Null(results.FacetQuery); | ||
} | ||
|
||
[Fact] | ||
public async Task FacetSearchWithMultipleFilter() | ||
{ | ||
var newFilters = new Settings | ||
{ | ||
FilterableAttributes = new string[] { "genre", "id" }, | ||
}; | ||
var task = await _indexForFaceting.UpdateSettingsAsync(newFilters); | ||
task.TaskUid.Should().BeGreaterOrEqualTo(0); | ||
await _indexForFaceting.WaitForTaskAsync(task.TaskUid); | ||
|
||
var query = new FacetSearchQuery() | ||
{ | ||
Filter = "genre = SF AND id != 13" | ||
}; | ||
var results = await _indexForFaceting.FacetSearchAsync("genre", query); | ||
|
||
Assert.Single(results.FacetHits); | ||
Assert.Equal("SF", results.FacetHits.First().Value); | ||
Assert.Equal(1, results.FacetHits.First().Count); | ||
Assert.Null(results.FacetQuery); | ||
} | ||
|
||
[Fact] | ||
public async Task FacetSearchWithFilterFacetIsNull() | ||
{ | ||
var query = new FacetSearchQuery() | ||
{ | ||
Filter = "genre IS NULL" | ||
}; | ||
var results = await _indexForFaceting.FacetSearchAsync("genre", query); | ||
|
||
Assert.Empty(results.FacetHits); | ||
Assert.Null(results.FacetQuery); | ||
} | ||
|
||
[Fact] | ||
public async Task FacetSearchWithFacetQuery() | ||
{ | ||
var query = new FacetSearchQuery() | ||
{ | ||
FacetQuery = "SF" | ||
}; | ||
var results = await _indexForFaceting.FacetSearchAsync("genre", query); | ||
|
||
Assert.Single(results.FacetHits); | ||
Assert.Equal("SF", results.FacetHits.First().Value); | ||
Assert.Equal(2, results.FacetHits.First().Count); | ||
results.FacetQuery.Should().NotBeNullOrEmpty(); | ||
} | ||
|
||
[Fact] | ||
public async Task FacetSearchWithFacetQueryWithSpaces() | ||
{ | ||
var query = new FacetSearchQuery() | ||
{ | ||
FacetQuery = "sci fi" | ||
}; | ||
var results = await _indexForFaceting.FacetSearchAsync("genre", query); | ||
|
||
Assert.Single(results.FacetHits); | ||
Assert.Equal("sci fi", results.FacetHits.First().Value); | ||
Assert.Equal(1, results.FacetHits.First().Count); | ||
results.FacetQuery.Should().NotBeNullOrEmpty(); | ||
} | ||
|
||
[Fact] | ||
public async Task FacetSearchWithLooseFacetQuery() | ||
{ | ||
var query = new FacetSearchQuery() | ||
{ | ||
FacetQuery = "s" | ||
}; | ||
var results = await _indexForFaceting.FacetSearchAsync("genre", query); | ||
|
||
Assert.Equal(2, results.FacetHits.Count()); | ||
Assert.Equal("sci fi", results.FacetHits.First().Value); | ||
Assert.Equal(1, results.FacetHits.First().Count); | ||
results.FacetQuery.Should().NotBeNullOrEmpty(); | ||
} | ||
|
||
[Fact] | ||
public async Task FacetSearchWithLooseQuery() | ||
{ | ||
var query = new FacetSearchQuery() | ||
{ | ||
Query = "s" | ||
}; | ||
var results = await _indexForFaceting.FacetSearchAsync("genre", query); | ||
|
||
Assert.Equal(3, results.FacetHits.Count()); | ||
Assert.Contains(results.FacetHits, x => x.Value.Equals("Action") && x.Count == 1); | ||
Assert.Contains(results.FacetHits, x => x.Value.Equals("SF") && x.Count == 2); | ||
Assert.Contains(results.FacetHits, x => x.Value.Equals("sci fi") && x.Count == 1); | ||
Assert.Null(results.FacetQuery); | ||
} | ||
|
||
[Fact] | ||
public async Task FacetSearchWithMultipleQueryAndLastMatchingStrategy() | ||
{ | ||
var query = new FacetSearchQuery() | ||
{ | ||
Query = "action spider man", | ||
MatchingStrategy = "last" | ||
}; | ||
var results = await _indexForFaceting.FacetSearchAsync("genre", query); | ||
|
||
Assert.Single(results.FacetHits); | ||
results.FacetHits.First().Count.Should().Be(3); | ||
Assert.Null(results.FacetQuery); | ||
} | ||
|
||
[Fact] | ||
public async Task FacetSearchWithMultipleQueryAndAllMatchingStrategy() | ||
{ | ||
var query = new FacetSearchQuery() | ||
{ | ||
Query = "action spider man", | ||
MatchingStrategy = "all", | ||
}; | ||
var results = await _indexForFaceting.FacetSearchAsync("genre", query); | ||
|
||
Assert.Single(results.FacetHits); | ||
results.FacetHits.First().Count.Should().Be(1); | ||
Assert.Null(results.FacetQuery); | ||
} | ||
|
||
[Fact] | ||
public async Task FacetSearchWithMultipleQueryAndAllMatchingStrategyAndAttributesToSearchOn() | ||
{ | ||
var query = new FacetSearchQuery() | ||
{ | ||
Query = "spider man", | ||
MatchingStrategy = "all", | ||
AttributesToSearchOn = new[] { "name" } | ||
}; | ||
var results = await _indexForFaceting.FacetSearchAsync("genre", query); | ||
|
||
Assert.Single(results.FacetHits); | ||
results.FacetHits.First().Count.Should().Be(1); | ||
Assert.Null(results.FacetQuery); | ||
} | ||
} | ||
} |
Oops, something went wrong.