diff --git a/.env b/.env index 767307bf..e1107443 100644 --- a/.env +++ b/.env @@ -1,3 +1,3 @@ -MEILISEARCH_VERSION=v1.9.0 +MEILISEARCH_VERSION=v1.10.0 PROXIED_MEILISEARCH=http://nginx/api/ MEILISEARCH_URL=http://meilisearch:7700 diff --git a/src/Meilisearch/Constants.cs b/src/Meilisearch/Constants.cs index 5aa0a1b3..9410ea12 100644 --- a/src/Meilisearch/Constants.cs +++ b/src/Meilisearch/Constants.cs @@ -1,6 +1,8 @@ using System.Text.Json; using System.Text.Json.Serialization; +using Meilisearch.Converters; + namespace Meilisearch { /// diff --git a/src/Meilisearch/Converters/AlwaysIncludeEmptyObjectConverter.cs b/src/Meilisearch/Converters/AlwaysIncludeEmptyObjectConverter.cs new file mode 100644 index 00000000..6c2342a4 --- /dev/null +++ b/src/Meilisearch/Converters/AlwaysIncludeEmptyObjectConverter.cs @@ -0,0 +1,72 @@ +using System; +using System.Linq; +using System.Reflection; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Meilisearch.Converters +{ + /// + /// Always include property in json. MultiSearchFederationOptions will be serialized as "{}" + /// + public class MultiSearchFederationOptionsConverter : JsonConverter + { + /// + /// Would override the default read logic, but here we use the default + /// + /// + /// + /// + /// + public override MultiSearchFederationOptions Read(ref Utf8JsonReader reader, Type typeToConvert, + JsonSerializerOptions options) + { + return JsonSerializer.Deserialize(ref reader, options); + } + + /// + /// Write json for MultiSearchFederationOptions and include it always as empty object + /// + /// + /// + /// + public override void Write(Utf8JsonWriter writer, MultiSearchFederationOptions value, + JsonSerializerOptions options) + { + if (value == null || !HasAnyValueSet(value)) + { + WriteEmptyObject(writer); + } + else + { + JsonSerializer.Serialize(writer, value); + } + } + private static void WriteEmptyObject(Utf8JsonWriter writer) + { + writer.WriteStartObject(); + writer.WriteEndObject(); + } + + private bool HasAnyValueSet(MultiSearchFederationOptions value) + { + foreach (var property in + value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + var propertyValue = property.GetValue(value); + var defaultValue = GetDefaultValue(property.PropertyType); + + if (!Equals(propertyValue, defaultValue)) + { + return true; + } + } + return false; + } + + private object GetDefaultValue(Type type) + { + return type.IsValueType ? Activator.CreateInstance(type) : null; + } + } +} diff --git a/src/Meilisearch/FederatedMultiSearchQuery.cs b/src/Meilisearch/FederatedMultiSearchQuery.cs new file mode 100644 index 00000000..35e57af3 --- /dev/null +++ b/src/Meilisearch/FederatedMultiSearchQuery.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +using Meilisearch.Converters; + +namespace Meilisearch +{ + /// + /// Search query used in federated multi-index search + /// + public class FederatedMultiSearchQuery + { + /// + /// Default constructor that ensures FederationOptions are always set + /// + public FederatedMultiSearchQuery() + { + FederationOptions = new MultiSearchFederationOptions(); + } + + /// + /// The queries + /// + [JsonPropertyName("queries")] + public List Queries { get; set; } + + /// + /// The federated search query options + /// + [JsonInclude] + [JsonPropertyName("federation")] + [JsonConverter(typeof(MultiSearchFederationOptionsConverter))] + public MultiSearchFederationOptions FederationOptions { get; set; } + } +} diff --git a/src/Meilisearch/FederatedSearchQuery.cs b/src/Meilisearch/FederatedSearchQuery.cs new file mode 100644 index 00000000..45a24fbd --- /dev/null +++ b/src/Meilisearch/FederatedSearchQuery.cs @@ -0,0 +1,16 @@ +using System.Text.Json.Serialization; + +namespace Meilisearch +{ + /// + /// Search query for federated multi-index search + /// + public class FederatedSearchQuery : SearchQueryBase + { + /// + /// Federated search options + /// + [JsonPropertyName("federationOptions")] + public MultiSearchFederationOptions FederationOptions { get; set; } + } +} diff --git a/src/Meilisearch/Meilisearch.csproj b/src/Meilisearch/Meilisearch.csproj index e13645d9..d15f2245 100644 --- a/src/Meilisearch/Meilisearch.csproj +++ b/src/Meilisearch/Meilisearch.csproj @@ -13,7 +13,6 @@ logo.png README.md MIT - True diff --git a/src/Meilisearch/MeilisearchClient.cs b/src/Meilisearch/MeilisearchClient.cs index c926da86..c4be414b 100644 --- a/src/Meilisearch/MeilisearchClient.cs +++ b/src/Meilisearch/MeilisearchClient.cs @@ -13,7 +13,6 @@ namespace Meilisearch { - /// /// Typed client for Meilisearch. /// @@ -29,7 +28,9 @@ public class MeilisearchClient /// /// URL corresponding to Meilisearch server. /// API Key to connect to the Meilisearch server. - public MeilisearchClient(string url, string apiKey = default) : this(new HttpClient(new MeilisearchMessageHandler(new HttpClientHandler())) { BaseAddress = url.ToSafeUri() }, apiKey) + public MeilisearchClient(string url, string apiKey = default) : this( + new HttpClient(new MeilisearchMessageHandler(new HttpClientHandler())) { BaseAddress = url.ToSafeUri() }, + apiKey) { } @@ -58,7 +59,8 @@ public async Task GetVersionAsync(CancellationToken cancella { var response = await _http.GetAsync("version", cancellationToken).ConfigureAwait(false); - return await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + return await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); } /// @@ -75,23 +77,47 @@ public Index Index(string uid) } /// - /// Searches multiple indexes at once + /// Searches multiple indexes at once but gets an aggregated list of results. + /// + /// The query to be executed (must have at least one query inside) + /// + /// Aggregated results. + /// + public async Task> FederatedMultiSearchAsync(FederatedMultiSearchQuery query, + CancellationToken cancellationToken = default) + { + if (!query.Queries.TrueForAll(x => x.IndexUid != null)) + { + throw new ArgumentNullException("IndexUid", "IndexUid should be provided for all search queries"); + } + + var responseMessage = await _http.PostAsJsonAsync("multi-search", query, + Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken); + return await responseMessage.Content + .ReadFromJsonAsync>(cancellationToken: cancellationToken) + .ConfigureAwait(false); + } + + /// + /// Searches multiple indexes at once. /// /// The queries to be executed (must have IndexUid set) /// /// /// - public async Task MultiSearchAsync(MultiSearchQuery query, CancellationToken cancellationToken = default) + public async Task MultiSearchAsync(MultiSearchQuery query, + CancellationToken cancellationToken = default) { if (!query.Queries.TrueForAll(x => x.IndexUid != null)) { throw new ArgumentNullException("IndexUid", "IndexUid should be provided for all search queries"); } - var responseMessage = await _http.PostAsJsonAsync("multi-search", query, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken); + var responseMessage = await _http.PostAsJsonAsync("multi-search", query, + Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken); return await responseMessage.Content - .ReadFromJsonAsync(cancellationToken: cancellationToken) - .ConfigureAwait(false); + .ReadFromJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); } /// @@ -101,13 +127,16 @@ public async Task MultiSearchAsync(MultiSearchQuery query, Ca /// Primary key for documents. /// The cancellation token for this call. /// Returns the associated task. - public async Task CreateIndexAsync(string uid, string primaryKey = default, CancellationToken cancellationToken = default) + public async Task CreateIndexAsync(string uid, string primaryKey = default, + CancellationToken cancellationToken = default) { var index = new Index(uid, primaryKey); - var responseMessage = await _http.PostJsonCustomAsync("indexes", index, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken) + var responseMessage = await _http.PostJsonCustomAsync("indexes", index, + Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken) .ConfigureAwait(false); - return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); } /// @@ -117,7 +146,8 @@ public async Task CreateIndexAsync(string uid, string primaryKey = def /// Primary key set. /// The cancellation token for this call. /// Returns the associated task. - public async Task UpdateIndexAsync(string uid, string primarykeytoChange, CancellationToken cancellationToken = default) + public async Task UpdateIndexAsync(string uid, string primarykeytoChange, + CancellationToken cancellationToken = default) { return await Index(uid).UpdateAsync(primarykeytoChange, cancellationToken).ConfigureAwait(false); } @@ -140,7 +170,8 @@ public async Task DeleteIndexAsync(string uid, CancellationToken cance /// Query parameters. Supports limit and offset. /// The cancellation token for this call. /// An IEnumerable of indexes in JsonElement format. - public async Task GetAllRawIndexesAsync(IndexesQuery query = default, CancellationToken cancellationToken = default) + public async Task GetAllRawIndexesAsync(IndexesQuery query = default, + CancellationToken cancellationToken = default) { var uri = query.ToQueryString(uri: "indexes"); var response = await _http.GetAsync(uri, cancellationToken).ConfigureAwait(false); @@ -155,12 +186,15 @@ public async Task GetAllRawIndexesAsync(IndexesQuery query = defau /// Query parameters. Supports limit and offset. /// The cancellation token for this call. /// Return Enumerable of Index. - public async Task>> GetAllIndexesAsync(IndexesQuery query = default, CancellationToken cancellationToken = default) + public async Task>> GetAllIndexesAsync(IndexesQuery query = default, + CancellationToken cancellationToken = default) { var uri = query.ToQueryString(uri: "indexes"); var response = await _http.GetAsync(uri, cancellationToken).ConfigureAwait(false); - var content = await response.Content.ReadFromJsonAsync>>(cancellationToken: cancellationToken).ConfigureAwait(false); + var content = await response.Content + .ReadFromJsonAsync>>(cancellationToken: cancellationToken) + .ConfigureAwait(false); content.Results .Select(p => p.WithHttpClient(_http)) .ToList(); @@ -188,7 +222,7 @@ public async Task GetIndexAsync(string uid, CancellationToken cancellatio public async Task GetRawIndexAsync(string uid, CancellationToken cancellationToken = default) { var json = await ( - await Meilisearch.Index.GetRawAsync(_http, uid, cancellationToken).ConfigureAwait(false)) + await Meilisearch.Index.GetRawAsync(_http, uid, cancellationToken).ConfigureAwait(false)) .Content.ReadAsStringAsync().ConfigureAwait(false); return JsonDocument.Parse(json).RootElement; } @@ -199,7 +233,8 @@ await Meilisearch.Index.GetRawAsync(_http, uid, cancellationToken).ConfigureAwai /// Query parameters supports by the method. /// The cancellation token for this call. /// Returns a list of tasks. - public async Task>> GetTasksAsync(TasksQuery query = default, CancellationToken cancellationToken = default) + public async Task>> GetTasksAsync(TasksQuery query = default, + CancellationToken cancellationToken = default) { return await TaskEndpoint().GetTasksAsync(query, cancellationToken).ConfigureAwait(false); } @@ -229,7 +264,8 @@ public async Task WaitForTaskAsync( int intervalMs = 50, CancellationToken cancellationToken = default) { - return await TaskEndpoint().WaitForTaskAsync(taskUid, timeoutMs, intervalMs, cancellationToken).ConfigureAwait(false); + return await TaskEndpoint().WaitForTaskAsync(taskUid, timeoutMs, intervalMs, cancellationToken) + .ConfigureAwait(false); } /// @@ -239,7 +275,8 @@ public async Task WaitForTaskAsync( /// Returns stats of all indexes. public async Task GetStatsAsync(CancellationToken cancellationToken = default) { - return await _http.GetFromJsonAsync("stats", cancellationToken: cancellationToken).ConfigureAwait(false); + return await _http.GetFromJsonAsync("stats", cancellationToken: cancellationToken) + .ConfigureAwait(false); } /// @@ -281,7 +318,8 @@ public async Task CreateDumpAsync(CancellationToken cancellationToken { var response = await _http.PostAsync("dumps", default, cancellationToken).ConfigureAwait(false); - return await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + return await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); } /// @@ -301,10 +339,12 @@ public async Task CreateSnapshotAsync(CancellationToken cancellationTo /// Query parameters supports by the method. /// The cancellation token for this call. /// Returns a list of the API keys. - public async Task>> GetKeysAsync(KeysQuery query = default, CancellationToken cancellationToken = default) + public async Task>> GetKeysAsync(KeysQuery query = default, + CancellationToken cancellationToken = default) { var uri = query.ToQueryString(uri: "keys"); - return await _http.GetFromJsonAsync>>(uri, cancellationToken: cancellationToken) + return await _http + .GetFromJsonAsync>>(uri, cancellationToken: cancellationToken) .ConfigureAwait(false); } @@ -329,10 +369,12 @@ public async Task GetKeyAsync(string keyOrUid, CancellationToken cancellati public async Task CreateKeyAsync(Key keyOptions, CancellationToken cancellationToken = default) { var responseMessage = - await _http.PostAsJsonAsync("keys", keyOptions, Constants.JsonSerializerOptionsWriteNulls, cancellationToken: cancellationToken) + await _http.PostAsJsonAsync("keys", keyOptions, Constants.JsonSerializerOptionsWriteNulls, + cancellationToken: cancellationToken) .ConfigureAwait(false); - return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); } /// @@ -341,7 +383,8 @@ await _http.PostAsJsonAsync("keys", keyOptions, Constants.JsonSerializerOptionsW /// Query parameters supports by the method. /// The cancellation token for this call. /// Returns the task info of finished task. - public async Task CancelTasksAsync(CancelTasksQuery query, CancellationToken cancellationToken = default) + public async Task CancelTasksAsync(CancelTasksQuery query, + CancellationToken cancellationToken = default) { return await TaskEndpoint().CancelTasksAsync(query, cancellationToken).ConfigureAwait(false); } @@ -352,7 +395,8 @@ public async Task CancelTasksAsync(CancelTasksQuery query, Cancellatio /// Query parameters supports by the method. /// The cancellation token for this call. /// Returns the task info of finished task. - public async Task DeleteTasksAsync(DeleteTasksQuery query, CancellationToken cancellationToken = default) + public async Task DeleteTasksAsync(DeleteTasksQuery query, + CancellationToken cancellationToken = default) { return await TaskEndpoint().DeleteTasksAsync(query, cancellationToken).ConfigureAwait(false); } @@ -365,19 +409,18 @@ public async Task DeleteTasksAsync(DeleteTasksQuery query, Cancellatio /// A name to label the key internally. /// The cancellation token for this call. /// Returns the updated API key. - public async Task UpdateKeyAsync(string keyOrUid, string description = null, string name = null, CancellationToken cancellationToken = default) + public async Task UpdateKeyAsync(string keyOrUid, string description = null, string name = null, + CancellationToken cancellationToken = default) { - var key = new Key - { - Name = name, - Description = description - }; + var key = new Key { Name = name, Description = description }; var responseMessage = - await _http.PatchAsJsonAsync($"keys/{keyOrUid}", key, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken) + await _http.PatchAsJsonAsync($"keys/{keyOrUid}", key, Constants.JsonSerializerOptionsRemoveNulls, + cancellationToken: cancellationToken) .ConfigureAwait(false); - return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); } /// @@ -399,12 +442,15 @@ public async Task DeleteKeyAsync(string keyOrUid, CancellationToken cancel /// List of IndexSwap objects. /// The cancellation token for this call. /// Returns the task info of finished task. - public async Task SwapIndexesAsync(List indexes, CancellationToken cancellationToken = default) + public async Task SwapIndexesAsync(List indexes, + CancellationToken cancellationToken = default) { - var response = await _http.PostAsJsonAsync("swap-indexes", indexes, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken) - .ConfigureAwait(false); + var response = await _http.PostAsJsonAsync("swap-indexes", indexes, + Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken) + .ConfigureAwait(false); - return await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false); + return await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken) + .ConfigureAwait(false); } /// @@ -417,7 +463,8 @@ public async Task SwapIndexesAsync(List indexes, Cancellati /// When there is no defined in the client or as argument. /// When the sent param is in the past /// Returns a generated tenant token. - public string GenerateTenantToken(string apiKeyUid, TenantTokenRules searchRules, string apiKey = null, DateTime? expiresAt = null) + public string GenerateTenantToken(string apiKeyUid, TenantTokenRules searchRules, string apiKey = null, + DateTime? expiresAt = null) { return TenantToken.GenerateToken(apiKeyUid, searchRules, apiKey ?? ApiKey, expiresAt); } @@ -436,6 +483,5 @@ private TaskEndpoint TaskEndpoint() return _taskEndpoint; } - } } diff --git a/src/Meilisearch/MultiSearchFederationOptions.cs b/src/Meilisearch/MultiSearchFederationOptions.cs new file mode 100644 index 00000000..e07a70d1 --- /dev/null +++ b/src/Meilisearch/MultiSearchFederationOptions.cs @@ -0,0 +1,25 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +using Meilisearch.Converters; + +namespace Meilisearch +{ + /// + /// Federation options in federated multi-index search + /// + public class MultiSearchFederationOptions + { + /// + /// Number of documents to skip + /// + [JsonPropertyName("offset")] + public int Offset { get; set; } + + /// + /// Maximum number of documents returned + /// + [JsonPropertyName("limit")] + public int Limit { get; set; } + } +} diff --git a/src/Meilisearch/SearchQuery.cs b/src/Meilisearch/SearchQuery.cs index fcfcbf19..a4de0e48 100644 --- a/src/Meilisearch/SearchQuery.cs +++ b/src/Meilisearch/SearchQuery.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Text.Json.Serialization; namespace Meilisearch @@ -6,110 +5,8 @@ namespace Meilisearch /// /// Search Query for Meilisearch class. /// - public class SearchQuery + public class SearchQuery : SearchQueryBase { - /// - /// The id of the index - /// - [JsonPropertyName("indexUid")] - public string IndexUid { get; set; } - - /// - /// Gets or sets query string. - /// - [JsonPropertyName("q")] - public string Q { get; set; } - - /// - /// Gets or sets the filter to apply to the query. - /// - [JsonPropertyName("filter")] - public dynamic Filter { get; set; } - - /// - /// Gets or sets attributes to retrieve. - /// - [JsonPropertyName("attributesToRetrieve")] - public IEnumerable AttributesToRetrieve { get; set; } - - /// - /// Gets or sets attributes to crop. - /// - [JsonPropertyName("attributesToCrop")] - public IEnumerable AttributesToCrop { get; set; } - - /// - /// Gets or sets attributes to search on. - /// - [JsonPropertyName("attributesToSearchOn")] - public IEnumerable AttributesToSearchOn { get; set; } - - /// - /// Gets or sets length used to crop field values. - /// - [JsonPropertyName("cropLength")] - public int? CropLength { get; set; } - - /// - /// Gets or sets attributes to highlight. - /// - [JsonPropertyName("attributesToHighlight")] - public IEnumerable AttributesToHighlight { get; set; } - - /// - /// Gets or sets the crop marker to apply before and/or after cropped part selected within an attribute defined in `attributesToCrop` parameter. - /// - [JsonPropertyName("cropMarker")] - public string CropMarker { get; set; } - - /// - /// Gets or sets the tag to put before the highlighted query terms. - /// - [JsonPropertyName("highlightPreTag")] - public string HighlightPreTag { get; set; } - - /// - /// Gets or sets the tag to put after the highlighted query terms. - /// - [JsonPropertyName("highlightPostTag")] - public string HighlightPostTag { get; set; } - - /// - /// Gets or sets the facets for the query. - /// - [JsonPropertyName("facets")] - public IEnumerable Facets { get; set; } - - /// - /// Gets or sets showMatchesPosition. It defines whether an object that contains information about the matches should be returned or not. - /// - [JsonPropertyName("showMatchesPosition")] - public bool? ShowMatchesPosition { get; set; } - - /// - /// Gets or sets the sorted attributes. - /// - [JsonPropertyName("sort")] - public IEnumerable Sort { get; set; } - - /// - /// Gets or sets the words matching strategy. - /// - [JsonPropertyName("matchingStrategy")] - public string MatchingStrategy { get; set; } - - /// - /// Gets or sets showRankingScore parameter. It defines wheter the global ranking score of a document (between 0 and 1) is returned or not. - /// - [JsonPropertyName("showRankingScore")] - public bool? ShowRankingScore { get; set; } - - /// - /// Gets or sets showRankingScoreDetails parameter. It defines whether details on how the ranking score was computed are returned or not. - /// - [JsonPropertyName("showRankingScoreDetails")] - public bool? ShowRankingScoreDetails { get; set; } - // pagination: /// diff --git a/src/Meilisearch/SearchQueryBase.cs b/src/Meilisearch/SearchQueryBase.cs new file mode 100644 index 00000000..ca2ce0c1 --- /dev/null +++ b/src/Meilisearch/SearchQueryBase.cs @@ -0,0 +1,113 @@ +using System.Collections.Generic; +using System.Text.Json.Serialization; + +namespace Meilisearch +{ + /// + /// Base properties of search query + /// + public class SearchQueryBase + { + /// + /// The id of the index + /// + [JsonPropertyName("indexUid")] + public string IndexUid { get; set; } + + /// + /// Gets or sets query string. + /// + [JsonPropertyName("q")] + public string Q { get; set; } + + /// + /// Gets or sets the filter to apply to the query. + /// + [JsonPropertyName("filter")] + public dynamic Filter { get; set; } + + /// + /// Gets or sets attributes to retrieve. + /// + [JsonPropertyName("attributesToRetrieve")] + public IEnumerable AttributesToRetrieve { get; set; } + + /// + /// Gets or sets attributes to crop. + /// + [JsonPropertyName("attributesToCrop")] + public IEnumerable AttributesToCrop { get; set; } + + /// + /// Gets or sets attributes to search on. + /// + [JsonPropertyName("attributesToSearchOn")] + public IEnumerable AttributesToSearchOn { get; set; } + + /// + /// Gets or sets length used to crop field values. + /// + [JsonPropertyName("cropLength")] + public int? CropLength { get; set; } + + /// + /// Gets or sets attributes to highlight. + /// + [JsonPropertyName("attributesToHighlight")] + public IEnumerable AttributesToHighlight { get; set; } + + /// + /// Gets or sets the crop marker to apply before and/or after cropped part selected within an attribute defined in `attributesToCrop` parameter. + /// + [JsonPropertyName("cropMarker")] + public string CropMarker { get; set; } + + /// + /// Gets or sets the tag to put before the highlighted query terms. + /// + [JsonPropertyName("highlightPreTag")] + public string HighlightPreTag { get; set; } + + /// + /// Gets or sets the tag to put after the highlighted query terms. + /// + [JsonPropertyName("highlightPostTag")] + public string HighlightPostTag { get; set; } + + /// + /// Gets or sets the facets for the query. + /// + [JsonPropertyName("facets")] + public IEnumerable Facets { get; set; } + + /// + /// Gets or sets showMatchesPosition. It defines whether an object that contains information about the matches should be returned or not. + /// + [JsonPropertyName("showMatchesPosition")] + public bool? ShowMatchesPosition { get; set; } + + /// + /// Gets or sets the sorted attributes. + /// + [JsonPropertyName("sort")] + public IEnumerable Sort { get; set; } + + /// + /// Gets or sets the words matching strategy. + /// + [JsonPropertyName("matchingStrategy")] + public string MatchingStrategy { get; set; } + + /// + /// Gets or sets showRankingScore parameter. It defines whether the global ranking score of a document (between 0 and 1) is returned or not. + /// + [JsonPropertyName("showRankingScore")] + public bool? ShowRankingScore { get; set; } + + /// + /// Gets or sets showRankingScoreDetails parameter. It defines whether details on how the ranking score was computed are returned or not. + /// + [JsonPropertyName("showRankingScoreDetails")] + public bool? ShowRankingScoreDetails { get; set; } + } +} diff --git a/tests/Meilisearch.Tests/Datasets.cs b/tests/Meilisearch.Tests/Datasets.cs index 610454a2..d4ae2155 100644 --- a/tests/Meilisearch.Tests/Datasets.cs +++ b/tests/Meilisearch.Tests/Datasets.cs @@ -55,7 +55,6 @@ sealed class UnixEpochDateTimeConverter : JsonConverter public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - var unixTime = reader.GetInt64(); return s_epoch.AddMilliseconds(unixTime); } diff --git a/tests/Meilisearch.Tests/MultiIndexSearchTests.cs b/tests/Meilisearch.Tests/MultiIndexSearchTests.cs index e5101ab0..9daeb4b6 100644 --- a/tests/Meilisearch.Tests/MultiIndexSearchTests.cs +++ b/tests/Meilisearch.Tests/MultiIndexSearchTests.cs @@ -31,7 +31,8 @@ public async Task InitializeAsync() _index2 = await _fixture.SetUpBasicIndex("BasicIndex-MultiSearch-Index2"); var t1 = _index1.UpdateFilterableAttributesAsync(new[] { "genre" }); var t2 = _index2.UpdateFilterableAttributesAsync(new[] { "genre" }); - await Task.WhenAll((await Task.WhenAll(t1, t2)).Select(x => _fixture.DefaultClient.WaitForTaskAsync(x.TaskUid))); + await Task.WhenAll( + (await Task.WhenAll(t1, t2)).Select(x => _fixture.DefaultClient.WaitForTaskAsync(x.TaskUid))); } [Fact] @@ -40,7 +41,7 @@ public async Task BasicSearch() var result = await _fixture.DefaultClient.MultiSearchAsync(new MultiSearchQuery() { Queries = new System.Collections.Generic.List() - { + { new SearchQuery() { IndexUid = _index1.Uid, Q = "", Filter = "genre = 'SF'" }, new SearchQuery() { IndexUid = _index2.Uid, Q = "", Filter = "genre = 'Action'" } } @@ -77,5 +78,65 @@ Movie GetMovie(IEnumerable movies, string id) return og.Name == x.Name && og.Genre == x.Genre; }).Should().BeTrue(); } + + + [Fact] + public async Task FederatedSearchWithNoFederationOptions() + { + var result = await _fixture.DefaultClient.FederatedMultiSearchAsync( + new FederatedMultiSearchQuery() + { + Queries = new List() + { + new FederatedSearchQuery() { IndexUid = _index1.Uid, Q = "", Filter = "genre = 'SF'" }, + new FederatedSearchQuery() + { + IndexUid = _index2.Uid, Q = "", Filter = "genre = 'Action'" + } + }, + }); + + result.Hits.Should().HaveCount(4); + } + + [Fact] + public async Task FederatedSearchWithEmptyOptions() + { + var result = await _fixture.DefaultClient.FederatedMultiSearchAsync( + new FederatedMultiSearchQuery() + { + Queries = new List() + { + new FederatedSearchQuery() { IndexUid = _index1.Uid, Q = "", Filter = "genre = 'SF'" }, + new FederatedSearchQuery() + { + IndexUid = _index2.Uid, Q = "", Filter = "genre = 'Action'" + } + }, + FederationOptions = new MultiSearchFederationOptions() { } + }); + + result.Hits.Should().HaveCount(4); + } + + [Fact] + public async Task FederatedSearchWithLimitAndOffset() + { + var federatedquer = new FederatedMultiSearchQuery() + { + Queries = new List() + { + new FederatedSearchQuery() { IndexUid = _index1.Uid, Q = "", Filter = "genre = 'SF'" }, + new FederatedSearchQuery() { IndexUid = _index2.Uid, Q = "", Filter = "genre = 'Action'" } + }, + FederationOptions = new MultiSearchFederationOptions() { Limit = 2, Offset = 0 } + }; + var result = await _fixture.DefaultClient.FederatedMultiSearchAsync(federatedquer + ); + + var testJson = JsonSerializer.Serialize(federatedquer); + + result.Hits.Should().HaveCount(2); + } } }