Skip to content

Commit

Permalink
Merge #357
Browse files Browse the repository at this point in the history
357: Changes related to the next Meilisearch release (v0.30.0) r=brunoocasali a=meili-bot

Related to this issue: meilisearch/integration-guides#221

This PR:
- gathers the changes related to the next Meilisearch release (v0.30.0) so that this package is ready when the official release is out.
- should pass the tests against the [latest pre-release of Meilisearch](https://github.com/meilisearch/meilisearch/releases).
- might eventually contain test failures until the Meilisearch v0.30.0 is out.

⚠️ This PR should NOT be merged until the next release of Meilisearch (v0.30.0) is out.

_This PR is auto-generated for the [pre-release week](https://github.com/meilisearch/integration-guides/blob/main/resources/pre-release-week.md) purpose._


Co-authored-by: meili-bot <74670311+meili-bot@users.noreply.github.com>
Co-authored-by: Bruno Casali <brunoocasali@gmail.com>
  • Loading branch information
3 people authored Dec 5, 2022
2 parents 4f8c1bc + bfaa5c6 commit 24d5c87
Show file tree
Hide file tree
Showing 22 changed files with 642 additions and 52 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -235,7 +235,7 @@ JSON Output:

## 🤖 Compatibility with Meilisearch

This package only guarantees the compatibility with the [version v0.29.0 of Meilisearch](https://github.com/meilisearch/meilisearch/releases/tag/v0.29.0).
This package only guarantees the compatibility with the [version v0.30.0 of Meilisearch](https://github.com/meilisearch/meilisearch/releases/tag/v0.30.0).

## 🎬 Examples

Expand Down
30 changes: 24 additions & 6 deletions src/Meilisearch/Extensions/ObjectExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Reflection;

Expand All @@ -24,7 +25,7 @@ internal static IDictionary<string, string> AsDictionary(this object source, Bin
}

/// <summary>
/// Transforms a Meilisearch object containing Lists into an URL encoded query string.
/// Transforms any object fields into an URL encoded query string.
/// </summary>
/// <param name="source">Object to transform.</param>
/// <param name="bindingAttr">Binding flags.</param>
Expand All @@ -34,19 +35,36 @@ internal static string ToQueryString(this object source, BindingFlags bindingAtt
var values = new List<string>();
foreach (var field in source.GetType().GetProperties(bindingAttr))
{
if (field.GetValue(source, null) != null)
var value = field.GetValue(source, null);
var key = Uri.EscapeDataString(char.ToLowerInvariant(field.Name[0]) + field.Name.Substring(1));

if (value != null)
{
var isList = field.GetValue(source, null).GetType().IsGenericType && field.GetValue(source, null).GetType().GetGenericTypeDefinition() == typeof(List<>);
if (isList)
var type = value.GetType();

if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>))
{
values.Add(Uri.EscapeDataString(char.ToLowerInvariant(field.Name[0]) + field.Name.Substring(1)) + "=" + string.Join(",", (List<string>)field.GetValue(source, null)));
Type itemType = type.GetGenericArguments()[0];
if (itemType == typeof(string))
{
values.Add(key + "=" + string.Join(",", (List<string>)value));
}
else if (itemType == typeof(int))
{
values.Add(key + "=" + string.Join(",", (List<int>)value));
}
}
else if (value is DateTime)
{
values.Add(key + "=" + Uri.EscapeDataString(((DateTime)value).ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz")));
}
else
{
values.Add(Uri.EscapeDataString(char.ToLowerInvariant(field.Name[0]) + field.Name.Substring(1)) + "=" + Uri.EscapeDataString(field.GetValue(source, null).ToString()));
values.Add(key + "=" + Uri.EscapeDataString(value.ToString()));
}
}
}

return string.Join("&", values);
}
}
Expand Down
42 changes: 42 additions & 0 deletions src/Meilisearch/ISearchable.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace Meilisearch
{
/// <summary>
/// Wrapper for Search Results.
/// </summary>
/// <typeparam name="T">Hit type.</typeparam>
public interface ISearchable<T>
{
/// <summary>
/// Results of the query.
/// </summary>
[JsonPropertyName("hits")]
IReadOnlyCollection<T> Hits { get; }

/// <summary>
/// Returns the number of documents matching the current search query for each given facet.
/// </summary>
[JsonPropertyName("facetDistribution")]
IReadOnlyDictionary<string, IReadOnlyDictionary<string, int>> FacetDistribution { get; }

/// <summary>
/// Processing time of the query.
/// </summary>
[JsonPropertyName("processingTimeMs")]
int ProcessingTimeMs { get; }

/// <summary>
/// Query originating the response.
/// </summary>
[JsonPropertyName("query")]
string Query { get; }

/// <summary>
/// Contains the location of each occurrence of queried terms across all fields.
/// </summary>
[JsonPropertyName("_matchesPosition")]
IReadOnlyDictionary<string, IReadOnlyCollection<MatchPosition>> MatchesPostion { get; }
}
}
17 changes: 14 additions & 3 deletions src/Meilisearch/Index.Documents.cs
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ public async Task<TaskInfo> DeleteAllDocumentsAsync(CancellationToken cancellati
/// <param name="cancellationToken">The cancellation token for this call.</param>
/// <typeparam name="T">Type parameter to return.</typeparam>
/// <returns>Returns Enumerable of items.</returns>
public async Task<SearchResult<T>> SearchAsync<T>(string query,
public async Task<ISearchable<T>> SearchAsync<T>(string query,
SearchQuery searchAttributes = default(SearchQuery), CancellationToken cancellationToken = default)
{
SearchQuery body;
Expand All @@ -470,8 +470,19 @@ public async Task<SearchResult<T>> SearchAsync<T>(string query,
var responseMessage = await _http.PostAsJsonAsync($"indexes/{Uid}/search", body,
Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken)
.ConfigureAwait(false);
return await responseMessage.Content
.ReadFromJsonAsync<SearchResult<T>>(cancellationToken: cancellationToken).ConfigureAwait(false);

if (body.Page != null || body.HitsPerPage != null)
{
return await responseMessage.Content
.ReadFromJsonAsync<PaginatedSearchResult<T>>(cancellationToken: cancellationToken)
.ConfigureAwait(false);
}
else
{
return await responseMessage.Content
.ReadFromJsonAsync<SearchResult<T>>(cancellationToken: cancellationToken)
.ConfigureAwait(false);
}
}
}
}
2 changes: 1 addition & 1 deletion src/Meilisearch/Index.Tasks.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public async Task<TasksResults<IEnumerable<TaskResource>>> GetTasksAsync(TasksQu
{
if (query == null)
{
query = new TasksQuery { IndexUid = new List<string> { this.Uid } };
query = new TasksQuery { IndexUids = new List<string> { this.Uid } };
}

return await TaskEndpoint().GetTasksAsync(query, cancellationToken).ConfigureAwait(false);
Expand Down
19 changes: 19 additions & 0 deletions src/Meilisearch/IndexSwap.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace Meilisearch
{
/// <summary>
/// Model for index swaps requests.
/// </summary>
public class IndexSwap
{
[JsonPropertyName("indexes")]
public List<string> Indexes { get; private set; }

public IndexSwap(string indexA, string indexB)
{
this.Indexes = new List<string> { indexA, indexB };
}
}
}
8 changes: 8 additions & 0 deletions src/Meilisearch/Key.cs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,14 @@ public enum KeyAction
/// </summary>
TasksGet,
/// <summary>
/// Allows canceling tasks in the tasks endpoint.
/// </summary>
TasksCancel,
/// <summary>
/// Allows deleting tasks in the tasks endpoint.
/// </summary>
TasksDelete,
/// <summary>
/// Provides access to all settings endpoints and equivalents for all subroutes.
/// </summary>
SettingsAll,
Expand Down
27 changes: 27 additions & 0 deletions src/Meilisearch/MatchPosition.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System.Text.Json.Serialization;

namespace Meilisearch
{
public class MatchPosition
{
public MatchPosition(int start, int length)
{
Start = start;
Length = length;
}

/// <summary>
/// The beginning of a matching term within a field.
/// WARNING: This value is in bytes and not the number of characters. For example, ü represents two bytes but one character.
/// </summary>
[JsonPropertyName("start")]
public int Start { get; }

/// <summary>
/// The length of a matching term within a field.
/// WARNING: This value is in bytes and not the number of characters. For example, ü represents two bytes but one character.
/// </summary>
[JsonPropertyName("length")]
public int Length { get; }
}
}
36 changes: 36 additions & 0 deletions src/Meilisearch/MeilisearchClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,28 @@ await _http.PostAsJsonAsync("keys", keyOptions, Constants.JsonSerializerOptionsW
return await responseMessage.Content.ReadFromJsonAsync<Key>(cancellationToken: cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Cancel tasks given a specific query.
/// </summary>
/// <param name="query">Query parameters supports by the method.</param>
/// <param name="cancellationToken">The cancellation token for this call.</param>
/// <returns>Returns the task info of finished task.</returns>
public async Task<TaskInfo> CancelTasksAsync(CancelTasksQuery query, CancellationToken cancellationToken = default)
{
return await TaskEndpoint().CancelTasksAsync(query, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Delete tasks given a specific query.
/// </summary>
/// <param name="query">Query parameters supports by the method.</param>
/// <param name="cancellationToken">The cancellation token for this call.</param>
/// <returns>Returns the task info of finished task.</returns>
public async Task<TaskInfo> DeleteTasksAsync(DeleteTasksQuery query, CancellationToken cancellationToken = default)
{
return await TaskEndpoint().DeleteTasksAsync(query, cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Updates an API key for the Meilisearch server.
/// </summary>
Expand Down Expand Up @@ -354,6 +376,20 @@ public async Task<bool> DeleteKeyAsync(string keyOrUid, CancellationToken cancel
return responseMessage.StatusCode == HttpStatusCode.NoContent;
}

/// <summary>
/// Swaps indexes unique identifiers.
/// </summary>
/// <param name="indexes">List of IndexSwap objects.</param>
/// <param name="cancellationToken">The cancellation token for this call.</param>
/// <returns>Returns the task info of finished task.</returns>
public async Task<TaskInfo> SwapIndexesAsync(List<IndexSwap> indexes, CancellationToken cancellationToken = default)
{
var response = await _http.PostAsJsonAsync("swap-indexes", indexes, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken)
.ConfigureAwait(false);

return await response.Content.ReadFromJsonAsync<TaskInfo>(cancellationToken: cancellationToken).ConfigureAwait(false);
}

/// <summary>
/// Generate a tenant token string to be used during search.
/// </summary>
Expand Down
75 changes: 75 additions & 0 deletions src/Meilisearch/PaginatedSearchResult.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
using System.Collections.Generic;
using System.Text.Json.Serialization;

namespace Meilisearch
{
/// <summary>
/// Wrapper for Search Results with finite pagination.
/// </summary>
/// <typeparam name="T">Hit type.</typeparam>
public class PaginatedSearchResult<T> : ISearchable<T>
{
public PaginatedSearchResult(IReadOnlyCollection<T> hits, int hitsPerPage, int page, int total,
IReadOnlyDictionary<string, IReadOnlyDictionary<string, int>> facetDistribution,
int processingTimeMs, string query,
IReadOnlyDictionary<string, IReadOnlyCollection<MatchPosition>> matchesPostion)
{
Hits = hits;
HitsPerPage = hitsPerPage;
Page = page;
Total = total;
FacetDistribution = facetDistribution;
ProcessingTimeMs = processingTimeMs;
Query = query;
MatchesPostion = matchesPostion;
}

/// <summary>
/// Number of documents each page.
/// </summary>
[JsonPropertyName("hitsPerPage")]
public int HitsPerPage { get; }

/// <summary>
/// Number of documents to take.
/// </summary>
[JsonPropertyName("page")]
public int Page { get; }

/// <summary>
/// Results of the query.
/// </summary>
[JsonPropertyName("hits")]
public IReadOnlyCollection<T> Hits { get; }

/// <summary>
/// Gets the estimated total number of hits returned by the search.
/// </summary>
[JsonPropertyName("total")]
public int Total { get; }

/// <summary>
/// Returns the number of documents matching the current search query for each given facet.
/// </summary>
[JsonPropertyName("facetDistribution")]
public IReadOnlyDictionary<string, IReadOnlyDictionary<string, int>> FacetDistribution { get; }

/// <summary>
/// Processing time of the query.
/// </summary>
[JsonPropertyName("processingTimeMs")]
public int ProcessingTimeMs { get; }

/// <summary>
/// Query originating the response.
/// </summary>
[JsonPropertyName("query")]
public string Query { get; }

/// <summary>
/// Contains the location of each occurrence of queried terms across all fields.
/// </summary>
[JsonPropertyName("_matchesPosition")]
public IReadOnlyDictionary<string, IReadOnlyCollection<MatchPosition>> MatchesPostion { get; }
}
}
Loading

0 comments on commit 24d5c87

Please sign in to comment.