├── assets
└── logo.png
├── .env
├── Directory.Build.props
├── .gitignore
├── src
└── Meilisearch
│ ├── runtimeconfig.json
│ ├── QueryParameters
│ ├── DeleteDocumentsQuery.cs
│ ├── KeysQuery.cs
│ ├── IndexesQuery.cs
│ ├── DocumentsQuery.cs
│ ├── CancelTasksQuery.cs
│ ├── DeleteTasksQuery.cs
│ └── TasksQuery.cs
│ ├── ContentType.cs
│ ├── Pagination.cs
│ ├── Client
│ ├── MeiliSearchHealth.cs
│ └── MeiliSearchVersion.cs
│ ├── MultiSearchQuery.cs
│ ├── FederatedSearchQuery.cs
│ ├── MultiSearchResult.cs
│ ├── HybridSearch.cs
│ ├── Errors
│ ├── MeilisearchTenantTokenExpired.cs
│ ├── MeilisearchTimeoutError.cs
│ ├── MeilisearchTenantTokenApiKeyUidInvalid.cs
│ ├── MeilisearchTenantTokenApiKeyInvalid.cs
│ ├── MeilisearchCommunicationError.cs
│ ├── MeilisearchApiErrorContent.cs
│ └── MeilisearchApiError.cs
│ ├── FacetStat.cs
│ ├── IndexSwap.cs
│ ├── MultiSearchFederationOptions.cs
│ ├── EmbedderSource.cs
│ ├── Version.cs
│ ├── Result.cs
│ ├── MatchPosition.cs
│ ├── ResourceResults.cs
│ ├── TasksResults.cs
│ ├── Faceting.cs
│ ├── Converters
│ ├── TaskInfoTypeConverter.cs
│ ├── SortFacetValuesConverter.cs
│ ├── EmbedderSourceConverter.cs
│ └── AlwaysIncludeEmptyObjectConverter.cs
│ ├── FederatedMultiSearchQuery.cs
│ ├── Index.SimilarDocuments.cs
│ ├── Stats.cs
│ ├── FacetSearchResult.cs
│ ├── Constants.cs
│ ├── Extensions
│ ├── EnumerableExtensions.cs
│ ├── ObjectExtensions.cs
│ ├── StringExtensions.cs
│ └── HttpExtensions.cs
│ ├── TenantTokenRules.cs
│ ├── TypoTolerance.cs
│ ├── ISearchable.cs
│ ├── FacetSearchQuery.cs
│ ├── EmbedderDistribution.cs
│ ├── SearchQuery.cs
│ ├── TenantToken.cs
│ ├── SimilarDocumentsResult.cs
│ ├── Index.Dictionary.cs
│ ├── Index.Faceting.cs
│ ├── Index.SearchCutoffMs.cs
│ ├── Index.Pagination.cs
│ ├── Index.Settings.cs
│ ├── Index.StopWords.cs
│ ├── MeilisearchMessageHandler.cs
│ ├── Index.RankingRules.cs
│ ├── Index.Synonyms.cs
│ ├── Index.TypoTolerance.cs
│ ├── Index.Tasks.cs
│ ├── Index.SeparatorTokens.cs
│ ├── Index.NonSeparatorTokens.cs
│ ├── IndexStats.cs
│ ├── Index.ProximityPrecision.cs
│ ├── ISearchableJsonConverter.cs
│ ├── Embedder.cs
│ ├── Index.Embedders.cs
│ ├── SimilarDocumentsQuery.cs
│ ├── TaskResource.cs
│ ├── SearchResult.cs
│ ├── PaginatedSearchResult.cs
│ ├── Settings.cs
│ ├── Meilisearch.csproj
│ ├── TaskInfo.cs
│ ├── SearchQueryBase.cs
│ └── TaskEndpoint.cs
├── .yamllint.yml
├── .github
├── ISSUE_TEMPLATE
│ ├── config.yml
│ ├── feature_request.md
│ └── bug_report.md
├── workflows
│ ├── release-drafter.yml
│ ├── publish.yml
│ ├── pre-release-tests.yml
│ └── tests.yml
├── dependabot.yml
├── scripts
│ └── check-release.sh
└── release-draft-template.yml
├── .cursor
└── rules
│ └── general.mdc
├── nginx.conf
├── tests
└── Meilisearch.Tests
│ ├── Properties
│ └── launchSettings.json
│ ├── Utils.cs
│ ├── Models
│ └── VectorMovie.cs
│ ├── Product.cs
│ ├── Datasets
│ ├── movies_with_int_id.json
│ ├── movies_with_string_id.json
│ ├── movies_for_vector.json
│ ├── movies_for_faceting.json
│ ├── movies_with_info.json
│ ├── products_for_distinct_search.json
│ └── songs_custom_delimiter.csv
│ ├── StringExtensionsTests.cs
│ ├── IndexSwapTest.cs
│ ├── VersionTests.cs
│ ├── Meilisearch.Tests.csproj
│ ├── Movie.cs
│ ├── Datasets.cs
│ ├── ServerConfigs
│ ├── BaseUriServer.cs
│ └── ProxiedUriServer.cs
│ ├── TaskInfoTests.cs
│ └── MultiIndexSearchTests.cs
├── docker-compose.yml
├── LICENSE
└── Meilisearch.sln
/assets/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/meilisearch/meilisearch-dotnet/HEAD/assets/logo.png
--------------------------------------------------------------------------------
/.env:
--------------------------------------------------------------------------------
1 | MEILISEARCH_VERSION=v1.16
2 | PROXIED_MEILISEARCH=http://nginx/api/
3 | MEILISEARCH_URL=http://meilisearch:7700
4 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 7.3
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | bin/
2 | obj/
3 | /packages/
4 | riderModule.iml
5 | /_ReSharper.Caches/
6 | .idea
7 | /.idea/
8 | .vscode
9 | /.vs
10 | *.user
11 |
--------------------------------------------------------------------------------
/src/Meilisearch/runtimeconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "runtimeOptions": {
3 | "configProperties": {
4 | "System.Globalization.Invariant": true
5 | }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/.yamllint.yml:
--------------------------------------------------------------------------------
1 | extends: default
2 | ignore: |
3 | node_modules
4 | rules:
5 | comments-indentation: disable
6 | line-length: disable
7 | document-start: disable
8 | brackets: disable
9 | truthy: disable
10 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Support questions & other
4 | url: https://discord.meilisearch.com/
5 | about: Support is not handled here but on our Discord
6 |
--------------------------------------------------------------------------------
/.cursor/rules/general.mdc:
--------------------------------------------------------------------------------
1 | ---
2 | alwaysApply: true
3 | ---
4 |
5 | - Use Docker for local development
6 | - Run tests with `docker-compose run --rm package bash -c "dotnet test && dotnet format --verbosity normal --verify-no-changes"`
7 |
--------------------------------------------------------------------------------
/src/Meilisearch/QueryParameters/DeleteDocumentsQuery.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Meilisearch.QueryParameters
4 | {
5 | public class DeleteDocumentsQuery
6 | {
7 | [JsonPropertyName("filter")]
8 | public object Filter { get; set; }
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/src/Meilisearch/ContentType.cs:
--------------------------------------------------------------------------------
1 | namespace Meilisearch
2 | {
3 | internal static class ContentType
4 | {
5 | internal const string Json = "application/json";
6 | internal const string Ndjson = "application/x-ndjson";
7 | internal const string Csv = "text/csv";
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/nginx.conf:
--------------------------------------------------------------------------------
1 | events {
2 | }
3 |
4 | http {
5 | server {
6 | server_name _;
7 | # server_name your.server.url;
8 | error_log /etc/nginx/error.log debug;
9 |
10 | location /api/ {
11 | proxy_pass http://meilisearchproxy:7700/;
12 | }
13 |
14 | client_max_body_size 100M;
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "Meilisearch.Tests": {
4 | "commandName": "Project",
5 | "launchBrowser": true,
6 | "environmentVariables": {
7 | "ASPNETCORE_ENVIRONMENT": "Development"
8 | },
9 | "applicationUrl": "https://localhost:52605;http://localhost:52606"
10 | }
11 | }
12 | }
--------------------------------------------------------------------------------
/.github/workflows/release-drafter.yml:
--------------------------------------------------------------------------------
1 | name: Release Drafter
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 |
8 | jobs:
9 | update_release_draft:
10 | runs-on: ubuntu-latest
11 | steps:
12 | - uses: release-drafter/release-drafter@v6
13 | with:
14 | config-name: release-draft-template.yml
15 | env:
16 | GITHUB_TOKEN: ${{ secrets.RELEASE_DRAFTER_TOKEN }}
17 |
--------------------------------------------------------------------------------
/src/Meilisearch/Pagination.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Pagination configuration.
7 | ///
8 | public class Pagination
9 | {
10 | ///
11 | /// Max total hits in each page
12 | ///
13 | [JsonPropertyName("maxTotalHits")]
14 | public int MaxTotalHits { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "github-actions"
4 | directory: "/"
5 | schedule:
6 | interval: "monthly"
7 | labels:
8 | - 'dependencies'
9 | rebase-strategy: disabled
10 |
11 | - package-ecosystem: nuget
12 | directory: "/"
13 | schedule:
14 | interval: "monthly"
15 | time: "04:00"
16 | open-pull-requests-limit: 10
17 | labels:
18 | - dependencies
19 | rebase-strategy: disabled
20 |
--------------------------------------------------------------------------------
/src/Meilisearch/Client/MeiliSearchHealth.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Deserialized response of the Meilisearch health.
7 | ///
8 | public class MeiliSearchHealth
9 | {
10 | ///
11 | /// Gets or sets health of Meilisearch server.
12 | ///
13 | [JsonPropertyName("status")]
14 | public string Status { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Meilisearch/MultiSearchQuery.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Search query used in multi-index search
8 | ///
9 | public class MultiSearchQuery
10 | {
11 | ///
12 | /// The queries
13 | ///
14 | [JsonPropertyName("queries")]
15 | public List Queries { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Utils.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text.Json;
3 | using System.Threading.Tasks;
4 |
5 | namespace Meilisearch.Tests
6 | {
7 | public static class JsonFileReader
8 | {
9 | public static async Task ReadAsync(string filePath)
10 | {
11 | using (var stream = File.OpenRead(filePath))
12 | {
13 | return await JsonSerializer.DeserializeAsync(stream);
14 | }
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/.github/scripts/check-release.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | # Checking if current tag matches the package version
4 | current_tag=$(echo $GITHUB_REF | cut -d '/' -f 3 | sed -r 's/^v//')
5 | file_tag=$(grep '' src/Meilisearch/Meilisearch.csproj | cut -d '>' -f 2 | cut -d '<' -f 1 | tr -d ' ')
6 | if [ "$current_tag" != "$file_tag" ]; then
7 | echo "Error: the current tag does not match the version in package file(s)."
8 | echo "$current_tag vs $file_tag"
9 | exit 1
10 | fi
11 |
12 | echo 'OK'
13 | exit 0
14 |
--------------------------------------------------------------------------------
/src/Meilisearch/FederatedSearchQuery.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Search query for federated multi-index search
7 | ///
8 | public class FederatedSearchQuery : SearchQueryBase
9 | {
10 | ///
11 | /// Federated search options
12 | ///
13 | [JsonPropertyName("federationOptions")]
14 | public MultiSearchFederationOptions FederationOptions { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/src/Meilisearch/MultiSearchResult.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json;
3 | using System.Text.Json.Serialization;
4 |
5 | namespace Meilisearch
6 | {
7 | ///
8 | /// Search result used in multi-index search
9 | ///
10 | public class MultiSearchResult
11 | {
12 | ///
13 | /// The search results
14 | ///
15 | [JsonPropertyName("results")]
16 | public List> Results { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Meilisearch/QueryParameters/KeysQuery.cs:
--------------------------------------------------------------------------------
1 | namespace Meilisearch.QueryParameters
2 | {
3 | ///
4 | /// A class that handles the creation of a query string for Keys.
5 | ///
6 | public class KeysQuery
7 | {
8 | ///
9 | /// Gets or sets the limit.
10 | ///
11 | public int? Limit { get; set; }
12 |
13 | ///
14 | /// Gets or sets the offset.
15 | ///
16 | public int? Offset { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature Request & Enhancement 💡
3 | about: Suggest a new idea for the project.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 |
10 |
11 | **Description**
12 | Brief explanation of the feature.
13 |
14 | **Basic example**
15 | If the proposal involves something new or a change, include a basic example. How would you use the feature? In which context?
16 |
17 | **Other**
18 | Any other things you want to add.
19 |
--------------------------------------------------------------------------------
/src/Meilisearch/QueryParameters/IndexesQuery.cs:
--------------------------------------------------------------------------------
1 | namespace Meilisearch.QueryParameters
2 | {
3 | ///
4 | /// A class that handles the creation of a query string for Indexes.
5 | ///
6 | public class IndexesQuery
7 | {
8 | ///
9 | /// Gets or sets the limit.
10 | ///
11 | public int? Limit { get; set; }
12 |
13 | ///
14 | /// Gets or sets the offset.
15 | ///
16 | public int? Offset { get; set; }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Meilisearch/HybridSearch.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Meilisearch
4 | {
5 | public class HybridSearch
6 | {
7 | ///
8 | /// Gets or sets the embedder.
9 | ///
10 | [JsonPropertyName("embedder")]
11 | public string Embedder { get; set; }
12 |
13 | ///
14 | /// Gets or sets the semantic ratio.
15 | ///
16 | [JsonPropertyName("semanticRatio")]
17 | public double SemanticRatio { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Models/VectorMovie.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch.Tests.Models
5 | {
6 | public class VectorMovie
7 | {
8 | [JsonPropertyName("id")]
9 | public string Id { get; set; }
10 |
11 | [JsonPropertyName("title")]
12 | public string Title { get; set; }
13 |
14 | [JsonPropertyName("release_year")]
15 | public int ReleaseYear { get; set; }
16 |
17 | [JsonPropertyName("_vectors")]
18 | public Dictionary Vectors { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Product.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Meilisearch.Tests
4 | {
5 | public class Product
6 | {
7 | [JsonPropertyName("id")]
8 | public int Id { get; set; }
9 |
10 | [JsonPropertyName("description")]
11 | public string Description { get; set; }
12 |
13 | [JsonPropertyName("brand")]
14 | public string Brand { get; set; }
15 |
16 | [JsonPropertyName("product_id")]
17 | public string ProductId { get; set; }
18 |
19 | [JsonPropertyName("color")]
20 | public string Color { get; set; }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/Meilisearch/Errors/MeilisearchTenantTokenExpired.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Represents an exception thrown when the provided expiration date is invalid or in the past.
7 | ///
8 | public class MeilisearchTenantTokenExpired : Exception
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | ///
13 | public MeilisearchTenantTokenExpired()
14 | : base("Provide a valid UTC DateTime in the future.")
15 | {
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/src/Meilisearch/FacetStat.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Wrapper for Facet Stats.
8 | ///
9 | public class FacetStat
10 | {
11 | ///
12 | /// Minimum value returned by FacetDistribution per facet
13 | ///
14 | [JsonPropertyName("min")]
15 | public float Min { get; set; }
16 |
17 | ///
18 | /// Maximum value returned by FacetDistribution per facet
19 | ///
20 | [JsonPropertyName("max")]
21 | public float Max { get; set; }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/src/Meilisearch/Errors/MeilisearchTimeoutError.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Error sent when request not processed in expected time.
7 | ///
8 | public class MeilisearchTimeoutError : Exception
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | /// Handler Exception for MeilisearchTimeoutError with message.
13 | ///
14 | /// Custom error message.
15 | public MeilisearchTimeoutError(string message)
16 | : base(message)
17 | {
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/src/Meilisearch/IndexSwap.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Model for index swaps requests.
8 | ///
9 | public class IndexSwap
10 | {
11 | [JsonPropertyName("indexes")]
12 | public List Indexes { get; private set; }
13 |
14 | [JsonPropertyName("rename")]
15 | public bool Rename { get; set; } = false;
16 |
17 | public IndexSwap(string indexA, string indexB, bool rename = false)
18 | {
19 | this.Indexes = new List { indexA, indexB };
20 | this.Rename = rename;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug Report 🐞
3 | about: Create a report to help us improve.
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 | ---
8 |
9 |
10 |
11 | **Description**
12 | Description of what the bug is about.
13 |
14 | **Expected behavior**
15 | What you expected to happen.
16 |
17 | **Current behavior**
18 | What happened.
19 |
20 | **Screenshots or Logs**
21 | If applicable, add screenshots or logs to help explain your problem.
22 |
23 | **Environment (please complete the following information):**
24 | - OS: [e.g. Debian GNU/Linux]
25 | - Meilisearch version: [e.g. v.0.20.0]
26 | - meilisearch-dotnet version: [e.g v0.6.0]
27 |
--------------------------------------------------------------------------------
/src/Meilisearch/Errors/MeilisearchTenantTokenApiKeyUidInvalid.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Represents an exception thrown when `apiKey` is not present
7 | /// to sign correctly the Tenant Token generation.
8 | ///
9 | public class MeilisearchTenantTokenApiKeyUidInvalid : Exception
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | public MeilisearchTenantTokenApiKeyUidInvalid()
15 | : base("Cannot generate a signed token without a valid apiKeyUid. Provide one in the method params.")
16 | {
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/Meilisearch/MultiSearchFederationOptions.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | using Meilisearch.Converters;
5 |
6 | namespace Meilisearch
7 | {
8 | ///
9 | /// Federation options in federated multi-index search
10 | ///
11 | public class MultiSearchFederationOptions
12 | {
13 | ///
14 | /// Number of documents to skip
15 | ///
16 | [JsonPropertyName("offset")]
17 | public int Offset { get; set; }
18 |
19 | ///
20 | /// Maximum number of documents returned
21 | ///
22 | [JsonPropertyName("limit")]
23 | public int Limit { get; set; }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Datasets/movies_with_int_id.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Id": 10,
4 | "Name": "Gladiator",
5 | "Genre": null
6 | },
7 | {
8 | "Id": 11,
9 | "Name": "Interstellar",
10 | "Genre": null
11 | },
12 | {
13 | "Id": 12,
14 | "Name": "Star Wars",
15 | "Genre": "SF"
16 | },
17 | {
18 | "Id": 13,
19 | "Name": "Harry Potter",
20 | "Genre": "SF"
21 | },
22 | {
23 | "Id": 14,
24 | "Name": "Iron Man",
25 | "Genre": "Action"
26 | },
27 | {
28 | "Id": 15,
29 | "Name": "Spider-Man",
30 | "Genre": "Action"
31 | },
32 | {
33 | "Id": 16,
34 | "Name": "Am\u00E9lie Poulain",
35 | "Genre": "French movie"
36 | }
37 | ]
--------------------------------------------------------------------------------
/src/Meilisearch/Errors/MeilisearchTenantTokenApiKeyInvalid.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Represents an exception thrown when `apiKey` is not present
7 | /// to sign correctly the Tenant Token generation.
8 | ///
9 | public class MeilisearchTenantTokenApiKeyInvalid : Exception
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | ///
14 | public MeilisearchTenantTokenApiKeyInvalid()
15 | : base("Cannot generate a signed token without a valid apiKey. Provide one in the MeilisearchClient instance or in the method params. The key MUST be at least 16 characters, or 128 bits")
16 | {
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Datasets/movies_with_string_id.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Id": "10",
4 | "Name": "Gladiator",
5 | "Genre": null
6 | },
7 | {
8 | "Id": "11",
9 | "Name": "Interstellar",
10 | "Genre": null
11 | },
12 | {
13 | "Id": "12",
14 | "Name": "Star Wars",
15 | "Genre": "SF"
16 | },
17 | {
18 | "Id": "13",
19 | "Name": "Harry Potter",
20 | "Genre": "SF"
21 | },
22 | {
23 | "Id": "14",
24 | "Name": "Iron Man",
25 | "Genre": "Action"
26 | },
27 | {
28 | "Id": "15",
29 | "Name": "Spider-Man",
30 | "Genre": "Action"
31 | },
32 | {
33 | "Id": "16",
34 | "Name": "Am\u00E9lie Poulain",
35 | "Genre": "French movie"
36 | }
37 | ]
--------------------------------------------------------------------------------
/.github/workflows/publish.yml:
--------------------------------------------------------------------------------
1 | name: Publish to Nuget
2 |
3 | on:
4 | push:
5 | tags:
6 | - v*
7 |
8 | jobs:
9 | publish:
10 | name: Build, pack & publish
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/checkout@v5
14 | - name: Install dependencies
15 | run: |
16 | sudo apt-get update
17 | - name: Check release validity
18 | run: sh .github/scripts/check-release.sh
19 | - name: Setup dotnet
20 | uses: actions/setup-dotnet@v5
21 | with:
22 | dotnet-version: 8.x
23 | - name: Pack
24 | run: dotnet pack src/Meilisearch/Meilisearch.csproj -c Release -o src/Meilisearch/bin/Release
25 | - name: Publish
26 | run: dotnet nuget push src/Meilisearch/bin/Release/*.nupkg -k ${{secrets.NUGET_API_KEY}} -s https://api.nuget.org/v3/index.json
27 |
--------------------------------------------------------------------------------
/src/Meilisearch/Errors/MeilisearchCommunicationError.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Error sent when trying to connecting to Meilisearch.
7 | ///
8 | public class MeilisearchCommunicationError : Exception
9 | {
10 | ///
11 | /// Initializes a new instance of the class.
12 | /// Handler Exception for MeilisearchCommunicationError with message and inner exception.
13 | ///
14 | /// Custom error message.
15 | /// Inner exception.
16 | public MeilisearchCommunicationError(string message, Exception innerException)
17 | : base(message, innerException)
18 | {
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/Meilisearch/EmbedderSource.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | using Meilisearch.Converters;
4 |
5 | namespace Meilisearch
6 | {
7 | ///
8 | /// Embedder source.
9 | ///
10 | [JsonConverter(typeof(EmbedderSourceConverter))]
11 | public enum EmbedderSource
12 | {
13 | ///
14 | /// OpenAI
15 | ///
16 | OpenAi,
17 |
18 | ///
19 | /// Hugging Face
20 | ///
21 | HuggingFace,
22 |
23 | ///
24 | /// Ollama
25 | ///
26 | Ollama,
27 |
28 | ///
29 | /// REST
30 | ///
31 | Rest,
32 |
33 | ///
34 | /// User-provided
35 | ///
36 | UserProvided,
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Datasets/movies_for_vector.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "title": "Shazam!",
4 | "release_year": 2019,
5 | "id": "287947",
6 | "_vectors": { "manual": [0.8, 0.4, -0.5]}
7 | },
8 | {
9 | "title": "Captain Marvel",
10 | "release_year": 2019,
11 | "id": "299537",
12 | "_vectors": { "manual": [0.6, 0.8, -0.2] }
13 | },
14 | {
15 | "title": "Escape Room",
16 | "release_year": 2019,
17 | "id": "522681",
18 | "_vectors": { "manual": [0.1, 0.6, 0.8] }
19 | },
20 | {
21 | "title": "How to Train Your Dragon: The Hidden World",
22 | "release_year": 2019,
23 | "id": "166428",
24 | "_vectors": { "manual": [0.7, 0.7, -0.4] }
25 | },
26 | {
27 | "title": "All Quiet on the Western Front",
28 | "release_year": 1930,
29 | "id": "143",
30 | "_vectors": { "manual": [-0.5, 0.3, 0.85] }
31 | }
32 | ]
33 |
--------------------------------------------------------------------------------
/.github/release-draft-template.yml:
--------------------------------------------------------------------------------
1 | name-template: 'v$RESOLVED_VERSION'
2 | tag-template: 'v$RESOLVED_VERSION'
3 | exclude-labels:
4 | - 'skip-changelog'
5 | version-resolver:
6 | minor:
7 | labels:
8 | - 'breaking-change'
9 | default: patch
10 | categories:
11 | - title: '⚠️ Breaking changes'
12 | label: 'breaking-change'
13 | - title: '🚀 Enhancements'
14 | label: 'enhancement'
15 | - title: '🐛 Bug Fixes'
16 | label: 'bug'
17 | - title: '🔒 Security'
18 | label: 'security'
19 | - title: '⚙️ Maintenance/misc'
20 | label:
21 | - 'dependencies'
22 | - 'maintenance'
23 | - 'documentation'
24 | template: |
25 | $CHANGES
26 |
27 | Thanks again to $CONTRIBUTORS! 🎉
28 | no-changes-template: 'Changes are coming soon 😎'
29 | sort-direction: 'ascending'
30 | replacers:
31 | - search: '/(?:and )?@meili-bot,?/g'
32 | replace: ''
33 |
--------------------------------------------------------------------------------
/src/Meilisearch/Version.cs:
--------------------------------------------------------------------------------
1 | namespace Meilisearch
2 | {
3 | ///
4 | /// Information regarding an API key for the Meilisearch server.
5 | ///
6 | public class Version
7 | {
8 | ///
9 | /// Extracts version from Meilisearch.csproj.
10 | ///
11 | /// Returns a formatted version.
12 | public string GetQualifiedVersion()
13 | {
14 | return $"Meilisearch .NET (v{GetVersion()})";
15 | }
16 |
17 | ///
18 | /// Extracts the "major.minor.build" version from Meilisearch.csproj.
19 | ///
20 | /// Returns a version from the GetType as String.
21 | public string GetVersion()
22 | {
23 | return GetType().Assembly.GetName().Version.ToString(3);
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/Meilisearch/Client/MeiliSearchVersion.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Deserialized response of the Meilisearch version.
7 | ///
8 | public class MeiliSearchVersion
9 | {
10 | ///
11 | /// Gets or sets commit SHA for Meilisearch.
12 | ///
13 | [JsonPropertyName("commitSha")]
14 | public string CommitSha { get; set; }
15 |
16 | ///
17 | /// Gets or sets build date of the current version.
18 | ///
19 | [JsonPropertyName("commitDate")]
20 | public string CommitDate { get; set; }
21 |
22 | ///
23 | /// Gets or sets information about Meilisearch version.
24 | ///
25 | [JsonPropertyName("pkgVersion")]
26 | public string Version { get; set; }
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/Meilisearch/Result.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Generic result class.
7 | /// When returning a list, Meilisearch stores the data in the "results" field, to allow better pagination.
8 | ///
9 | /// Type of the Meilisearch server object. Ex: keys, tasks, ...
10 | public class Result
11 | {
12 | public Result(T results, int? limit)
13 | {
14 | Results = results;
15 | Limit = limit;
16 | }
17 |
18 | ///
19 | /// Gets the "results" field.
20 | ///
21 | [JsonPropertyName("results")]
22 | public T Results { get; }
23 |
24 | ///
25 | /// Gets limit size.
26 | ///
27 | [JsonPropertyName("limit")]
28 | public int? Limit { get; }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/src/Meilisearch/MatchPosition.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Meilisearch
4 | {
5 | public class MatchPosition
6 | {
7 | public MatchPosition(int start, int length)
8 | {
9 | Start = start;
10 | Length = length;
11 | }
12 |
13 | ///
14 | /// The beginning of a matching term within a field.
15 | /// WARNING: This value is in bytes and not the number of characters. For example, ü represents two bytes but one character.
16 | ///
17 | [JsonPropertyName("start")]
18 | public int Start { get; }
19 |
20 | ///
21 | /// The length of a matching term within a field.
22 | /// WARNING: This value is in bytes and not the number of characters. For example, ü represents two bytes but one character.
23 | ///
24 | [JsonPropertyName("length")]
25 | public int Length { get; }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/Meilisearch/ResourceResults.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Generic result class for resources.
7 | /// When returning a list, Meilisearch stores the data in the "results" field, to allow better pagination.
8 | ///
9 | /// Type of the Meilisearch server object. Ex: keys, indexes, ...
10 | public class ResourceResults : Result
11 | {
12 | public ResourceResults(T results, int? limit, int offset, int total)
13 | : base(results, limit)
14 | {
15 | Offset = offset;
16 | Total = total;
17 | }
18 |
19 | ///
20 | /// Gets offset size.
21 | ///
22 | [JsonPropertyName("offset")]
23 | public int Offset { get; }
24 |
25 | ///
26 | /// Gets total size.
27 | ///
28 | [JsonPropertyName("total")]
29 | public int Total { get; }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/StringExtensionsTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using Meilisearch.Extensions;
4 |
5 | using Xunit;
6 |
7 | namespace Meilisearch.Tests
8 | {
9 | public class StringExtensionsTests
10 | {
11 | [Theory]
12 | [InlineData("http://localhost:7700", "http://localhost:7700/")]
13 | [InlineData("http://localhost:7700/", "http://localhost:7700/")]
14 | [InlineData("http://localhost:7700/api", "http://localhost:7700/api/")]
15 | [InlineData("http://localhost:7700/api/", "http://localhost:7700/api/")]
16 | public void CheckUrisEndWithSlash(string actual, string expected)
17 | {
18 | Assert.Equal(expected, actual.ToSafeUri().AbsoluteUri);
19 | }
20 |
21 | [Theory]
22 | [InlineData("")]
23 | [InlineData(" ")]
24 | [InlineData(null)]
25 | public void ToSafeUriShouldThrowsArgumentException(string badUri)
26 | {
27 | Assert.Throws(badUri.ToSafeUri);
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Datasets/movies_for_faceting.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Id": "10",
4 | "Name": "Gladiator",
5 | "Genre": null
6 | },
7 | {
8 | "Id": "11",
9 | "Name": "Interstellar",
10 | "Genre": null
11 | },
12 | {
13 | "Id": "12",
14 | "Name": "Star Wars",
15 | "Genre": "SF"
16 | },
17 | {
18 | "Id": "13",
19 | "Name": "Harry Potter",
20 | "Genre": "SF"
21 | },
22 | {
23 | "Id": "14",
24 | "Name": "Iron Man",
25 | "Genre": "Action"
26 | },
27 | {
28 | "Id": "15",
29 | "Name": "Spider-Man",
30 | "Genre": "Action"
31 | },
32 | {
33 | "Id": "16",
34 | "Name": "Am\u00E9lie Poulain",
35 | "Genre": "French movie"
36 | },
37 | {
38 | "Id": "17",
39 | "Name": "Mission Impossible",
40 | "Genre": "Action"
41 | },
42 | {
43 | "Id": "1344",
44 | "Name": "The Hobbit",
45 | "Genre": "sci fi"
46 | }
47 | ]
--------------------------------------------------------------------------------
/src/Meilisearch/TasksResults.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Generic result class for resources.
7 | /// When returning a list, Meilisearch stores the data in the "results" field, to allow better pagination.
8 | ///
9 | /// Type of the Meilisearch server object. Ex: keys, indexes, ...
10 | public class TasksResults : Result
11 | {
12 | public TasksResults(T results, int? limit, int? from, int? next, int? total)
13 | : base(results, limit)
14 | {
15 | From = from;
16 | Next = next;
17 | Total = total;
18 | }
19 |
20 | ///
21 | /// Gets from size.
22 | ///
23 | public int? From { get; }
24 |
25 | ///
26 | /// Gets next size.
27 | ///
28 | public int? Next { get; }
29 |
30 | ///
31 | /// Gets total number of tasks.
32 | ///
33 | public int? Total { get; }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Meilisearch/Faceting.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | using Meilisearch.Converters;
5 |
6 | namespace Meilisearch
7 | {
8 | ///
9 | /// Faceting configuration.
10 | ///
11 | public class Faceting
12 | {
13 | ///
14 | /// Gets or sets maxValuesPerFacet.
15 | ///
16 | [JsonPropertyName("maxValuesPerFacet")]
17 | public int MaxValuesPerFacet { get; set; }
18 |
19 | ///
20 | /// Gets or sets sortFacetValuesBy.
21 | ///
22 | [JsonPropertyName("sortFacetValuesBy")]
23 | public Dictionary SortFacetValuesBy { get; set; }
24 | }
25 |
26 | [JsonConverter(typeof(SortFacetValuesConverter))]
27 | public enum SortFacetValuesByType
28 | {
29 | ///
30 | /// Sort by alphanumerical value.
31 | ///
32 | Alpha,
33 |
34 | ///
35 | /// Sort by count value.
36 | ///
37 | Count
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "3.7"
2 |
3 | volumes:
4 | nuget:
5 |
6 | services:
7 | package:
8 | image: mcr.microsoft.com/dotnet/sdk:8.0
9 | tty: true
10 | stdin_open: true
11 | working_dir: /home/package
12 | env_file: .env
13 | depends_on:
14 | - meilisearch
15 | - nginx
16 | links:
17 | - meilisearch
18 | - nginx
19 | volumes:
20 | - ./:/home/package
21 | - nuget:/root/.nuget/packages/
22 |
23 | meilisearch:
24 | image: getmeili/meilisearch-enterprise:${MEILISEARCH_VERSION}
25 | ports:
26 | - "7700:7700"
27 | environment:
28 | - MEILI_MASTER_KEY=masterKey
29 | - MEILI_NO_ANALYTICS=true
30 |
31 | meilisearchproxy:
32 | image: getmeili/meilisearch-enterprise:${MEILISEARCH_VERSION}
33 | ports:
34 | - "7700"
35 | environment:
36 | - MEILI_MASTER_KEY=masterKey
37 | - MEILI_NO_ANALYTICS=true
38 |
39 | nginx:
40 | image: nginx:latest
41 | container_name: production_nginx
42 | volumes:
43 | - ./nginx.conf:/etc/nginx/nginx.conf
44 | ports:
45 | - 8080:80
46 | depends_on:
47 | - meilisearchproxy
48 |
--------------------------------------------------------------------------------
/src/Meilisearch/Converters/TaskInfoTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 |
7 | namespace Meilisearch.Converters
8 | {
9 | public class TaskInfoTypeConverter : JsonConverter
10 | {
11 | public override TaskInfoType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
12 | {
13 | if (reader.TokenType == JsonTokenType.String)
14 | {
15 | var enumValue = reader.GetString();
16 | if (Enum.TryParse(enumValue, true, out var taskInfoType))
17 | {
18 | return taskInfoType;
19 | }
20 | }
21 |
22 | // If we reach here, it means we encountered an unknown value
23 | return TaskInfoType.Unknown;
24 | }
25 |
26 | public override void Write(Utf8JsonWriter writer, TaskInfoType value, JsonSerializerOptions options)
27 | {
28 | writer.WriteStringValue(value.ToString());
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 sa
4 | Copyright (c) 2020-2025 Meili SAS
5 |
6 | Permission is hereby granted, free of charge, to any person obtaining a copy
7 | of this software and associated documentation files (the "Software"), to deal
8 | in the Software without restriction, including without limitation the rights
9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 | copies of the Software, and to permit persons to whom the Software is
11 | furnished to do so, subject to the following conditions:
12 |
13 | The above copyright notice and this permission notice shall be included in all
14 | copies or substantial portions of the Software.
15 |
16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 | SOFTWARE.
23 |
--------------------------------------------------------------------------------
/src/Meilisearch/FederatedMultiSearchQuery.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | using Meilisearch.Converters;
5 |
6 | namespace Meilisearch
7 | {
8 | ///
9 | /// Search query used in federated multi-index search
10 | ///
11 | public class FederatedMultiSearchQuery
12 | {
13 | ///
14 | /// Default constructor that ensures FederationOptions are always set
15 | ///
16 | public FederatedMultiSearchQuery()
17 | {
18 | FederationOptions = new MultiSearchFederationOptions();
19 | }
20 |
21 | ///
22 | /// The queries
23 | ///
24 | [JsonPropertyName("queries")]
25 | public List Queries { get; set; }
26 |
27 | ///
28 | /// The federated search query options
29 | ///
30 | [JsonInclude]
31 | [JsonPropertyName("federation")]
32 | [JsonConverter(typeof(MultiSearchFederationOptionsConverter))]
33 | public MultiSearchFederationOptions FederationOptions { get; set; }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/Meilisearch/Converters/SortFacetValuesConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.Json;
3 | using System.Text.Json.Serialization;
4 |
5 | namespace Meilisearch.Converters
6 | {
7 | public class SortFacetValuesConverter : JsonConverter
8 | {
9 | public override SortFacetValuesByType Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
10 | {
11 | if (reader.TokenType == JsonTokenType.String)
12 | {
13 | var enumValue = reader.GetString();
14 | if (Enum.TryParse(enumValue, true, out var sortFacetValues))
15 | {
16 | return sortFacetValues;
17 | }
18 | }
19 |
20 | // If we reach here, it means we encountered an unknown value, so we'll use meilisearch default of Alpha
21 | return SortFacetValuesByType.Alpha;
22 | }
23 |
24 | public override void Write(Utf8JsonWriter writer, SortFacetValuesByType value, JsonSerializerOptions options)
25 | {
26 | writer.WriteStringValue(value.ToString().ToLower());
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/src/Meilisearch/Errors/MeilisearchApiErrorContent.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Error sent by Meilisearch API.
7 | ///
8 | public class MeilisearchApiErrorContent
9 | {
10 | public MeilisearchApiErrorContent(string message, string code, string type, string link)
11 | {
12 | Message = message;
13 | Code = code;
14 | Type = type;
15 | Link = link;
16 | }
17 |
18 | ///
19 | /// Gets the message.
20 | ///
21 | [JsonPropertyName("message")]
22 | public string Message { get; }
23 |
24 | ///
25 | /// Gets the code.
26 | ///
27 | [JsonPropertyName("code")]
28 | public string Code { get; }
29 |
30 | ///
31 | /// Gets the type.
32 | ///
33 | [JsonPropertyName("type")]
34 | public string Type { get; }
35 |
36 | ///
37 | /// Gets the link.
38 | ///
39 | [JsonPropertyName("link")]
40 | public string Link { get; }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/.github/workflows/pre-release-tests.yml:
--------------------------------------------------------------------------------
1 | # Testing the code base against the Meilisearch pre-releases
2 | name: Pre-Release Tests
3 |
4 | # Will only run for PRs and pushes to bump-meilisearch-v*
5 | on:
6 | push:
7 | branches: bump-meilisearch-v*
8 | pull_request:
9 | branches: bump-meilisearch-v*
10 |
11 | jobs:
12 | integration_tests:
13 | name: integration-tests-against-rc
14 | runs-on: ubuntu-latest
15 | steps:
16 | - uses: actions/checkout@v5
17 | - name: Setup .NET Core
18 | uses: actions/setup-dotnet@v5
19 | with:
20 | dotnet-version: 8.x
21 | - name: Get the latest Meilisearch RC
22 | run: echo "MEILISEARCH_VERSION=$(curl https://raw.githubusercontent.com/meilisearch/integration-guides/main/scripts/get-latest-meilisearch-rc.sh | bash)" >> $GITHUB_ENV
23 | - name: Meilisearch (${{ env.MEILISEARCH_VERSION }}) setup with Docker
24 | run: MEILISEARCH_VERSION=${{ env.MEILISEARCH_VERSION }} docker compose up -d
25 | - name: Install dependencies
26 | run: dotnet restore
27 | - name: Build
28 | run: dotnet build --configuration Release --no-restore
29 | - name: Run tests
30 | run: dotnet test --no-restore --verbosity normal
31 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/IndexSwapTest.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json;
3 |
4 | using Xunit;
5 |
6 | namespace Meilisearch.Tests
7 | {
8 | public class IndexSwapTests
9 | {
10 | [Fact]
11 | public void PreventMoreThanTwoIndexesPerObject()
12 | {
13 | var swap = new IndexSwap("indexA", "indexB");
14 |
15 | Assert.Equal(new List { "indexA", "indexB" }, swap.Indexes);
16 | }
17 |
18 | [Fact]
19 | public void CreateExpectedJSONFormat()
20 | {
21 | var swap = new IndexSwap("indexA", "indexB");
22 |
23 | var json = JsonSerializer.Serialize(swap);
24 | Assert.Contains("\"indexes\":[\"indexA\",\"indexB\"]", json);
25 | Assert.Contains("\"rename\":false", json);
26 | }
27 |
28 | [Fact]
29 | public void CreateExpectedJSONFormatWithRenameTrue()
30 | {
31 | var swap = new IndexSwap("indexA", "indexB", rename: true);
32 |
33 | var json = JsonSerializer.Serialize(swap);
34 | Assert.Contains("\"indexes\":[\"indexA\",\"indexB\"]", json);
35 | Assert.Contains("\"rename\":true", json);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Meilisearch/QueryParameters/DocumentsQuery.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch.QueryParameters
5 | {
6 | ///
7 | /// A class that handles the creation of a query string for Documents.
8 | ///
9 | public class DocumentsQuery
10 | {
11 | ///
12 | /// Gets or sets the limit.
13 | ///
14 | [JsonPropertyName("limit")]
15 | public int? Limit { get; set; }
16 |
17 | ///
18 | /// Gets or sets the offset.
19 | ///
20 | [JsonPropertyName("offset")]
21 | public int? Offset { get; set; }
22 |
23 | ///
24 | /// Gets or sets the attributes to retrieve.
25 | ///
26 | [JsonPropertyName("fields")]
27 | public List Fields { get; set; }
28 |
29 | ///
30 | /// An optional filter to apply
31 | ///
32 | [JsonPropertyName("filter")]
33 | public object Filter { get; set; }
34 |
35 | ///
36 | /// An optional sort to apply
37 | ///
38 | [JsonPropertyName("sort")]
39 | public List Sort { get; set; }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/Meilisearch.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meilisearch", "src\Meilisearch\Meilisearch.csproj", "{1876F5BB-672A-4440-88ED-D5E14DC507AC}"
4 | EndProject
5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Meilisearch.Tests", "tests\Meilisearch.Tests\Meilisearch.Tests.csproj", "{CDAACB2F-0197-4266-B40C-D51F61136ABF}"
6 | EndProject
7 | Global
8 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
9 | Debug|Any CPU = Debug|Any CPU
10 | Release|Any CPU = Release|Any CPU
11 | EndGlobalSection
12 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
13 | {1876F5BB-672A-4440-88ED-D5E14DC507AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
14 | {1876F5BB-672A-4440-88ED-D5E14DC507AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
15 | {1876F5BB-672A-4440-88ED-D5E14DC507AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
16 | {1876F5BB-672A-4440-88ED-D5E14DC507AC}.Release|Any CPU.Build.0 = Release|Any CPU
17 | {CDAACB2F-0197-4266-B40C-D51F61136ABF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
18 | {CDAACB2F-0197-4266-B40C-D51F61136ABF}.Debug|Any CPU.Build.0 = Debug|Any CPU
19 | {CDAACB2F-0197-4266-B40C-D51F61136ABF}.Release|Any CPU.ActiveCfg = Release|Any CPU
20 | {CDAACB2F-0197-4266-B40C-D51F61136ABF}.Release|Any CPU.Build.0 = Release|Any CPU
21 | EndGlobalSection
22 | EndGlobal
23 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.SimilarDocuments.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http.Json;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace Meilisearch
6 | {
7 | public partial class Index
8 | {
9 | ///
10 | /// Search for similar documents.
11 | ///
12 | /// The query to search for similar documents.
13 | /// The cancellation token for this call.
14 | /// The type of the documents to return.
15 | /// Returns the similar documents.
16 | public async Task> SearchSimilarDocumentsAsync(
17 | SimilarDocumentsQuery query,
18 | CancellationToken cancellationToken = default)
19 | {
20 | var responseMessage = await _http
21 | .PostAsJsonAsync(
22 | $"indexes/{Uid}/similar",
23 | query,
24 | Constants.JsonSerializerOptionsRemoveNulls,
25 | cancellationToken: cancellationToken)
26 | .ConfigureAwait(false);
27 |
28 | return await responseMessage.Content
29 | .ReadFromJsonAsync>(cancellationToken: cancellationToken)
30 | .ConfigureAwait(false);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/Meilisearch/Stats.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text.Json.Serialization;
4 |
5 | namespace Meilisearch
6 | {
7 | ///
8 | /// Wrapper for index stats.
9 | ///
10 | public class Stats
11 | {
12 | public Stats(long databaseSize, DateTime? lastUpdate, IReadOnlyDictionary indexes, long usedDatabaseSize)
13 | {
14 | DatabaseSize = databaseSize;
15 | LastUpdate = lastUpdate;
16 | Indexes = indexes;
17 | UsedDatabaseSize = usedDatabaseSize;
18 | }
19 |
20 | ///
21 | /// Gets database size.
22 | ///
23 | [JsonPropertyName("databaseSize")]
24 | public long DatabaseSize { get; }
25 |
26 | ///
27 | /// Gets the total space used by the data stored in Meilisearch.
28 | ///
29 | [JsonPropertyName("usedDatabaseSize")]
30 | public long UsedDatabaseSize { get; }
31 |
32 | ///
33 | /// Gets last update timestamp.
34 | ///
35 | [JsonPropertyName("lastUpdate")]
36 | public DateTime? LastUpdate { get; }
37 |
38 | ///
39 | /// Gets index stats.
40 | ///
41 | [JsonPropertyName("indexes")]
42 | public IReadOnlyDictionary Indexes { get; }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Datasets/movies_with_info.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "Id": "10",
4 | "Name": "Gladiator",
5 | "Info": {
6 | "Comment": "a movie about old times",
7 | "ReviewNb": 700
8 | }
9 | },
10 | {
11 | "Id": "11",
12 | "Name": "Interstellar",
13 | "Info": {
14 | "Comment": "the best movie",
15 | "ReviewNb": 1000
16 | }
17 | },
18 | {
19 | "Id": "12",
20 | "Name": "Star Wars",
21 | "Info": {
22 | "Comment": "a lot of wars in the stars",
23 | "ReviewNb": 900
24 | }
25 | },
26 | {
27 | "Id": "13",
28 | "Name": "Harry Potter",
29 | "Info": {
30 | "Comment": "a movie about a wizard boy",
31 | "ReviewNb": 900
32 | }
33 | },
34 | {
35 | "Id": "14",
36 | "Name": "Iron Man",
37 | "Info": {
38 | "Comment": "a movie about a rich man",
39 | "ReviewNb": 800
40 | }
41 | },
42 | {
43 | "Id": "15",
44 | "Name": "Spider-Man",
45 | "Info": {
46 | "Comment": "the spider bit the boy",
47 | "ReviewNb": 900
48 | }
49 | },
50 | {
51 | "Id": "16",
52 | "Name": "Am\u00E9lie Poulain",
53 | "Info": {
54 | "Comment": "talks about hapiness",
55 | "ReviewNb": 800
56 | }
57 | }
58 | ]
--------------------------------------------------------------------------------
/src/Meilisearch/FacetSearchResult.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Wrapper for FacetSearchResponse
8 | ///
9 | public class FacetSearchResult
10 | {
11 | ///
12 | /// Gets or sets the facetHits property
13 | ///
14 | [JsonPropertyName("facetHits")]
15 | public IEnumerable FacetHits { get; set; }
16 |
17 | ///
18 | /// Gets or sets the facet query
19 | ///
20 | [JsonPropertyName("facetQuery")]
21 | public string FacetQuery { get; set; }
22 |
23 | ///
24 | /// Gets or sets the processingTimeMs property
25 | ///
26 | [JsonPropertyName("processingTimeMs")]
27 | public int ProcessingTimeMs { get; set; }
28 |
29 | ///
30 | /// Wrapper for Facet Hit
31 | ///
32 | public class FacetHit
33 | {
34 | ///
35 | /// Gets or sets the value property
36 | ///
37 | [JsonPropertyName("value")]
38 | public string Value { get; set; }
39 |
40 | ///
41 | /// Gets or sets the count property
42 | ///
43 | [JsonPropertyName("count")]
44 | public int Count { get; set; }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Meilisearch/Constants.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Serialization;
3 |
4 | using Meilisearch.Converters;
5 |
6 | namespace Meilisearch
7 | {
8 | ///
9 | /// This class adds some defaults to work with Meilisearch client.
10 | ///
11 | internal static class Constants
12 | {
13 | ///
14 | /// JsonSerializer options used when serializing objects that needs to remove null values.
15 | ///
16 | internal static readonly JsonSerializerOptions JsonSerializerOptionsRemoveNulls = new JsonSerializerOptions
17 | {
18 | DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
19 | PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
20 | };
21 |
22 | ///
23 | /// JsonSerializer options used when serializing objects that keeps null values.
24 | ///
25 | internal static readonly JsonSerializerOptions JsonSerializerOptionsWriteNulls = new JsonSerializerOptions
26 | {
27 | DefaultIgnoreCondition = JsonIgnoreCondition.Never,
28 | PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
29 | };
30 |
31 | internal static string VersionErrorHintMessage(string message, string method)
32 | {
33 | return
34 | $"{message}\nHint: It might not be working because maybe you're not up to date with the Meilisearch version that ${method} call requires.";
35 | }
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/Meilisearch/Extensions/EnumerableExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace Meilisearch.Extensions
6 | {
7 | ///
8 | /// Extensions methods for IEnumerable.
9 | ///
10 | internal static class EnumerableExtensions
11 | {
12 | ///
13 | /// Returns chunks of a list.
14 | ///
15 | /// The list to split.
16 | /// Size of the chunks.
17 | /// Type of objects in the list.
18 | /// List of chunks.
19 | /// Thrown if fullList is null.
20 | /// Throw if chunkSize is lower than 1.
21 | internal static IEnumerable> GetChunks(this IEnumerable fullList, int chunkSize)
22 | {
23 | if (fullList is null)
24 | {
25 | throw new ArgumentNullException(nameof(fullList));
26 | }
27 |
28 | if (chunkSize < 1)
29 | {
30 | throw new ArgumentException("chunkSize value must be greater than 0", nameof(chunkSize));
31 | }
32 |
33 | var total = fullList.Count();
34 | var sent = 0;
35 | while (sent < total)
36 | {
37 | yield return fullList.Skip(sent).Take(chunkSize);
38 | sent += chunkSize;
39 | }
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/Meilisearch/TenantTokenRules.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Wrapper class used to map all the supported types to be used in
7 | /// the `searchRules` claim in the Tenant Tokens.
8 | ///
9 | public class TenantTokenRules
10 | {
11 | private readonly object _rules;
12 |
13 | ///
14 | /// Initializes a new instance of the class based on a rules json object.
15 | ///
16 | ///
17 | ///
18 | /// example:
19 | ///
20 | /// {'*': {"filter": 'tag = Tale'}}
21 | ///
22 | ///
23 | public TenantTokenRules(IReadOnlyDictionary rules)
24 | {
25 | _rules = rules;
26 | }
27 |
28 | ///
29 | /// Initializes a new instance of the class based on a rules string array.
30 | ///
31 | ///
32 | ///
33 | /// example:
34 | ///
35 | /// ['books']
36 | ///
37 | ///
38 | public TenantTokenRules(string[] rules)
39 | {
40 | _rules = rules;
41 | }
42 |
43 | ///
44 | /// Accessor method used to retrieve the searchRules claim.
45 | ///
46 | /// A object with the supported type representing the `searchRules`.
47 | public object ToClaim()
48 | {
49 | return _rules;
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/Meilisearch/Errors/MeilisearchApiError.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Error sent by Meilisearch API.
8 | ///
9 | public class MeilisearchApiError : Exception
10 | {
11 | ///
12 | /// Initializes a new instance of the class.
13 | /// Handler Exception received from Meilisearch API.
14 | ///
15 | /// Specific error message from Meilisearch Api.
16 | public MeilisearchApiError(MeilisearchApiErrorContent apiError)
17 | : base(string.Format("MeilisearchApiError, Message: {0}, Code: {1}, Type: {2}, Link: {3}", apiError.Message, apiError.Code, apiError.Type, apiError.Link))
18 | {
19 | Code = apiError.Code;
20 | }
21 |
22 | ///
23 | /// Initializes a new instance of the class.
24 | /// Handler Exception when Meilisearch API doesn't send a response message.
25 | ///
26 | /// Status code from http response message.
27 | /// Reason Phrase from http response message.
28 | public MeilisearchApiError(HttpStatusCode statusCode, string reasonPhrase)
29 | : base(string.Format("MeilisearchApiError, Message: {0}, Code: {1}", reasonPhrase, (int)statusCode))
30 | {
31 | }
32 |
33 | ///
34 | /// Gets or sets the code return by MeilisearchApi.
35 | ///
36 | public string Code { get; set; }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/src/Meilisearch/TypoTolerance.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Typo Tolerance configuration.
8 | ///
9 | public class TypoTolerance
10 | {
11 | ///
12 | /// Whether the typo tolerance feature is enabled
13 | ///
14 | [JsonPropertyName("enabled")]
15 | public bool? Enabled { get; set; }
16 |
17 | ///
18 | /// Disable the typo tolerance feature on the specified document attributes.
19 | ///
20 | [JsonPropertyName("disableOnAttributes")]
21 | public IEnumerable DisableOnAttributes { get; set; }
22 |
23 | ///
24 | /// Disable the typo tolerance feature for a given set of terms in a search query.
25 | ///
26 | [JsonPropertyName("disableOnWords")]
27 | public IEnumerable DisableOnWords { get; set; }
28 |
29 | ///
30 | /// Customize the minimum word size to tolerate typos.
31 | ///
32 | [JsonPropertyName("minWordSizeForTypos")]
33 | public TypoSize MinWordSizeForTypos { get; set; }
34 |
35 | public class TypoSize
36 | {
37 | ///
38 | /// Customize the minimum word size to tolerate 1 typo.
39 | ///
40 | [JsonPropertyName("oneTypo")]
41 | public int? OneTypo { get; set; }
42 |
43 | ///
44 | /// Customize the minimum word size to tolerate 2 typos.
45 | ///
46 | [JsonPropertyName("twoTypos")]
47 | public int? TwoTypos { get; set; }
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/.github/workflows/tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 |
3 | on:
4 | pull_request:
5 | push:
6 | branches:
7 | - main
8 | merge_group:
9 |
10 | jobs:
11 | integration_tests:
12 | # Will not run if the event is a PR to bump-meilisearch-v* (so a pre-release PR)
13 | # Will still run for each push to bump-meilisearch-v*
14 | if: github.event_name != 'pull_request' || !startsWith(github.base_ref, 'bump-meilisearch-v')
15 | name: integration-tests
16 | runs-on: ubuntu-latest
17 | steps:
18 | - uses: actions/checkout@v5
19 | - name: Setup .NET Core
20 | uses: actions/setup-dotnet@v5
21 | with:
22 | dotnet-version: "8.0.x"
23 | - name: Install dependencies
24 | run: dotnet restore
25 | - name: Build
26 | run: dotnet build --configuration Release --no-restore
27 | - name: Meilisearch (latest version) setup with Docker
28 | env:
29 | # Any docker tag is actually accepted as a valid version
30 | MEILISEARCH_VERSION: latest
31 | run: docker compose up -d
32 | - name: Run tests
33 | run: dotnet test --no-restore --verbosity normal
34 |
35 | format:
36 | name: format-check
37 | runs-on: ubuntu-latest
38 | steps:
39 | - uses: actions/checkout@v5
40 | - name: Setup .NET Core
41 | uses: actions/setup-dotnet@v5
42 | with:
43 | dotnet-version: "8.0.x"
44 | - name: Check with dotnet-format
45 | run: dotnet format --version
46 | - name: Check with dotnet-format
47 | run: dotnet format --verbosity normal --verify-no-changes
48 |
49 | yaml-lint:
50 | name: Yaml linting check
51 | runs-on: ubuntu-latest
52 | steps:
53 | - uses: actions/checkout@v5
54 | - name: Yaml lint check
55 | uses: ibiqlik/action-yamllint@v3
56 | with:
57 | config_file: .yamllint.yml
58 |
--------------------------------------------------------------------------------
/src/Meilisearch/ISearchable.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Wrapper for Search Results.
8 | ///
9 | /// Hit type.
10 | [JsonConverter(typeof(ISearchableJsonConverterFactory))]
11 | public interface ISearchable
12 | {
13 | ///
14 | /// The uid of the index
15 | ///
16 | [JsonPropertyName("indexUid")]
17 | string IndexUid { get; }
18 |
19 | ///
20 | /// Results of the query.
21 | ///
22 | [JsonPropertyName("hits")]
23 | IReadOnlyCollection Hits { get; }
24 |
25 | ///
26 | /// Returns the number of documents matching the current search query for each given facet.
27 | ///
28 | [JsonPropertyName("facetDistribution")]
29 | IReadOnlyDictionary> FacetDistribution { get; }
30 |
31 | ///
32 | /// Processing time of the query.
33 | ///
34 | [JsonPropertyName("processingTimeMs")]
35 | int ProcessingTimeMs { get; }
36 |
37 | ///
38 | /// Query originating the response.
39 | ///
40 | [JsonPropertyName("query")]
41 | string Query { get; }
42 |
43 | ///
44 | /// Contains the location of each occurrence of queried terms across all fields.
45 | ///
46 | [JsonPropertyName("_matchesPosition")]
47 | IReadOnlyDictionary> MatchesPosition { get; }
48 |
49 | ///
50 | /// Returns the numeric min and max values per facet of the hits returned by the search query.
51 | ///
52 | [JsonPropertyName("facetStats")]
53 | IReadOnlyDictionary FacetStats { get; }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Meilisearch/Converters/EmbedderSourceConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.Json;
3 | using System.Text.Json.Serialization;
4 |
5 | namespace Meilisearch.Converters
6 | {
7 | internal class EmbedderSourceConverter : JsonConverter
8 | {
9 | public override EmbedderSource Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
10 | {
11 | if (reader.TokenType == JsonTokenType.String)
12 | {
13 | var enumValue = reader.GetString();
14 | if (Enum.TryParse(enumValue, true, out var embedderSource))
15 | {
16 | return embedderSource;
17 | }
18 |
19 | throw new JsonException($"Invalid EmbedderSource value: '{enumValue}'.");
20 | }
21 |
22 | throw new JsonException($"Expected string for EmbedderSource, but found {reader.TokenType}.");
23 | }
24 |
25 | public override void Write(Utf8JsonWriter writer, EmbedderSource value, JsonSerializerOptions options)
26 | {
27 | string source;
28 | switch (value)
29 | {
30 | case EmbedderSource.OpenAi:
31 | source = "openAi";
32 | break;
33 | case EmbedderSource.HuggingFace:
34 | source = "huggingFace";
35 | break;
36 | case EmbedderSource.Ollama:
37 | source = "ollama";
38 | break;
39 | case EmbedderSource.Rest:
40 | source = "rest";
41 | break;
42 | case EmbedderSource.UserProvided:
43 | source = "userProvided";
44 | break;
45 | default:
46 | throw new ArgumentOutOfRangeException(nameof(value), value, null);
47 | }
48 |
49 | writer.WriteStringValue(source);
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/VersionTests.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Net.Http;
3 | using System.Xml;
4 |
5 | using Meilisearch.Extensions;
6 |
7 | using Xunit;
8 |
9 | namespace Meilisearch.Tests
10 | {
11 | public class VersionTests
12 | {
13 | private readonly Version _version;
14 |
15 | public VersionTests()
16 | {
17 | _version = new Version();
18 | }
19 |
20 | [Fact]
21 | public void GetQualifiedVersion()
22 | {
23 | var qualifiedVersion = _version.GetQualifiedVersion();
24 | var version = _version.GetVersion();
25 |
26 | Assert.Equal(qualifiedVersion, $"Meilisearch .NET (v{version})");
27 | }
28 |
29 | [Fact]
30 | public void GetSimpleVersionFromCsprojFile()
31 | {
32 | // get the current version defined in the csproj file
33 | var xmldoc = new XmlDocument();
34 | var currentDir = Directory.GetParent(Directory.GetCurrentDirectory()).FullName;
35 | var path = Path.Combine(currentDir, @"../../../../src/Meilisearch/Meilisearch.csproj");
36 | xmldoc.Load(path);
37 | var mgr = new XmlNamespaceManager(xmldoc.NameTable);
38 | mgr.AddNamespace("x", "http://schemas.microsoft.com/developer/msbuild/2003");
39 | var versionFromCsproj = xmldoc.FirstChild.FirstChild.SelectSingleNode("Version").InnerText;
40 |
41 | var value = _version.GetVersion();
42 |
43 | Assert.NotNull(value);
44 | Assert.Equal(versionFromCsproj, value);
45 | }
46 |
47 | [Fact]
48 | public void GetDefaultUserAgentHeader()
49 | {
50 | var httpClient = new HttpClient();
51 | httpClient.AddDefaultUserAgent();
52 | var userAgent = string.Join(' ', httpClient.DefaultRequestHeaders.GetValues("User-Agent"));
53 | var version = new Version();
54 |
55 | Assert.Equal(version.GetQualifiedVersion(), userAgent);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/src/Meilisearch/FacetSearchQuery.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Wrapper for facet search query
8 | ///
9 | public class FacetSearchQuery
10 | {
11 | ///
12 | /// Gets or sets the facetName property
13 | ///
14 | [JsonPropertyName("facetName")]
15 | public string FacetName { get; set; }
16 |
17 | ///
18 | /// Gets or sets the facetQuery property
19 | ///
20 | [JsonPropertyName("facetQuery")]
21 | public string FacetQuery { get; set; }
22 |
23 | ///
24 | /// Gets or sets the q property
25 | ///
26 | [JsonPropertyName("q")]
27 | public string Query { get; set; }
28 |
29 | ///
30 | /// Gets or sets the filter property
31 | ///
32 | [JsonPropertyName("filter")]
33 | public dynamic Filter { get; set; }
34 |
35 | ///
36 | /// Gets or sets the matchingStrategy property, can be last, all or frequency.
37 | ///
38 | [JsonPropertyName("matchingStrategy")]
39 | public string MatchingStrategy { get; set; }
40 |
41 | ///
42 | /// Gets or sets the attributesToSearchOn property
43 | ///
44 | [JsonPropertyName("attributesToSearchOn")]
45 | public IEnumerable AttributesToSearchOn { get; set; }
46 |
47 | ///
48 | /// When true, returns an exhaustive (exact) count for facet values during facet search.
49 | /// This may increase response time on large datasets. Omit or set to false to favor performance.
50 | /// Default (when null/omitted): server defaults apply.
51 | ///
52 | [JsonPropertyName("exhaustiveFacetCount")]
53 | public bool? ExhaustiveFacetCount { get; set; }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Meilisearch.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | false
6 |
7 |
8 |
9 | 1701;1702;CA1861
10 |
11 |
12 |
13 | 1701;1702;CA1861
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/Meilisearch/EmbedderDistribution.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Embedder distribution.
8 | ///
9 | public class EmbedderDistribution
10 | {
11 | private double _mean;
12 | private double _sigma;
13 |
14 | ///
15 | /// Creates a new instance of .
16 | ///
17 | /// Mean value between 0 and 1.
18 | /// Sigma value between 0 and 1.
19 | public EmbedderDistribution(double mean, double sigma)
20 | {
21 | Mean = mean;
22 | Sigma = sigma;
23 | }
24 |
25 | ///
26 | /// Gets or sets the mean.
27 | ///
28 | [JsonPropertyName("mean")]
29 | public double Mean
30 | {
31 | get => _mean;
32 | set
33 | {
34 | if (value < 0 || value > 1)
35 | {
36 | throw new ArgumentOutOfRangeException(nameof(Mean), "Mean must be between 0 and 1.");
37 | }
38 |
39 | _mean = value;
40 | }
41 | }
42 |
43 | ///
44 | /// Gets or sets the sigma.
45 | ///
46 | [JsonPropertyName("sigma")]
47 | public double Sigma
48 | {
49 | get => _sigma;
50 | set
51 | {
52 | if (value < 0 || value > 1)
53 | {
54 | throw new ArgumentOutOfRangeException(nameof(Sigma), "Sigma must be between 0 and 1.");
55 | }
56 |
57 | _sigma = value;
58 | }
59 | }
60 |
61 | ///
62 | /// Creates a new instance of with a uniform distribution.
63 | ///
64 | ///
65 | public static EmbedderDistribution Uniform() => new EmbedderDistribution(0.5, 0.5);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/Meilisearch/SearchQuery.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 |
3 | namespace Meilisearch
4 | {
5 | ///
6 | /// Search Query for Meilisearch class.
7 | ///
8 | public class SearchQuery : SearchQueryBase
9 | {
10 | // pagination:
11 |
12 | ///
13 | /// Gets or sets offset for the Query.
14 | ///
15 | [JsonPropertyName("offset")]
16 | public int? Offset { get; set; }
17 |
18 | ///
19 | /// Gets or sets limits the number of results.
20 | ///
21 | [JsonPropertyName("limit")]
22 | public int? Limit { get; set; }
23 |
24 | ///
25 | /// Gets or sets hitsPerPage.
26 | ///
27 | [JsonPropertyName("hitsPerPage")]
28 | public int? HitsPerPage { get; set; }
29 |
30 | ///
31 | /// Gets or sets page.
32 | ///
33 | [JsonPropertyName("page")]
34 | public int? Page { get; set; }
35 |
36 | ///
37 | /// Sets distinct attribute at search time.
38 | ///
39 | [JsonPropertyName("distinct")]
40 | public string Distinct { get; set; }
41 |
42 | ///
43 | /// Gets or sets rankingScoreThreshold, a number between 0.0 and 1.0.
44 | ///
45 | [JsonPropertyName("rankingScoreThreshold")]
46 | public decimal? RankingScoreThreshold { get; set; }
47 |
48 | ///
49 | /// Gets or sets the hybrid search settings.
50 | ///
51 | [JsonPropertyName("hybrid")]
52 | public HybridSearch Hybrid { get; set; }
53 |
54 | ///
55 | /// Gets or sets the vector.
56 | ///
57 | [JsonPropertyName("vector")]
58 | public double[] Vector { get; set; }
59 |
60 | ///
61 | /// Gets or sets whether to retrieve vectors.
62 | ///
63 | [JsonPropertyName("retrieveVectors")]
64 | public bool RetrieveVectors { get; set; }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Meilisearch/TenantToken.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IdentityModel.Tokens.Jwt;
3 | using System.Security.Claims;
4 | using System.Text;
5 | using System.Text.Json;
6 |
7 | using Microsoft.IdentityModel.Tokens;
8 |
9 | namespace Meilisearch
10 | {
11 | public class TenantToken
12 | {
13 | ///
14 | /// Generates a Tenant Token in a JWT string format.
15 | ///
16 | /// JWT string
17 | public static string GenerateToken(string apiKeyUid, TenantTokenRules searchRules, string apiKey, DateTime? expiresAt)
18 | {
19 | if (string.IsNullOrEmpty(apiKeyUid))
20 | {
21 | throw new MeilisearchTenantTokenApiKeyUidInvalid();
22 | }
23 |
24 | if (string.IsNullOrEmpty(apiKey) || apiKey.Length < 16)
25 | {
26 | throw new MeilisearchTenantTokenApiKeyInvalid();
27 | }
28 |
29 | if (expiresAt.HasValue && DateTime.Compare(DateTime.UtcNow, (DateTime)expiresAt) > 0)
30 | {
31 | throw new MeilisearchTenantTokenExpired();
32 | }
33 |
34 | var rules = searchRules.ToClaim();
35 | var isArray = rules is string[];
36 | var valueType = isArray ? JsonClaimValueTypes.JsonArray : JsonClaimValueTypes.Json;
37 |
38 | var identity = new ClaimsIdentity();
39 | identity.AddClaim(new Claim("apiKeyUid", apiKeyUid));
40 | identity.AddClaim(new Claim("searchRules", JsonSerializer.Serialize(searchRules.ToClaim()), valueType));
41 |
42 | var signingKey = Encoding.ASCII.GetBytes(apiKey);
43 |
44 | var tokenDescriptor = new SecurityTokenDescriptor
45 | {
46 | Subject = identity,
47 | Expires = expiresAt,
48 | SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(signingKey), SecurityAlgorithms.HmacSha256Signature)
49 | };
50 |
51 | var tokenHandler = new JwtSecurityTokenHandler
52 | {
53 | SetDefaultTimesOnTokenCreation = false
54 | };
55 |
56 | var token = tokenHandler.CreateToken(tokenDescriptor);
57 | return tokenHandler.WriteToken(token);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/src/Meilisearch/SimilarDocumentsResult.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Search result for similar documents.
8 | ///
9 | public class SimilarDocumentsResult
10 | {
11 | ///
12 | /// Creates a new instance of the class.
13 | ///
14 | ///
15 | ///
16 | ///
17 | ///
18 | ///
19 | ///
20 | public SimilarDocumentsResult(
21 | IReadOnlyCollection hits,
22 | string id,
23 | int processingTimeMs,
24 | int offset,
25 | int limit,
26 | int estimatedTotalHits)
27 | {
28 | Hits = hits;
29 | Id = id;
30 | ProcessingTimeMs = processingTimeMs;
31 | Offset = offset;
32 | Limit = limit;
33 | EstimatedTotalHits = estimatedTotalHits;
34 | }
35 |
36 | ///
37 | /// Gets the hits.
38 | ///
39 | [JsonPropertyName("hits")]
40 | public IReadOnlyCollection Hits { get; }
41 |
42 | ///
43 | /// Gets the id.
44 | ///
45 | [JsonPropertyName("id")]
46 | public string Id { get; }
47 |
48 | ///
49 | /// Gets the processing time in milliseconds.
50 | ///
51 | [JsonPropertyName("processingTimeMs")]
52 | public int ProcessingTimeMs { get; }
53 |
54 | ///
55 | /// Gets the offset.
56 | ///
57 | [JsonPropertyName("offset")]
58 | public int Offset { get; }
59 |
60 | ///
61 | /// Gets the limit.
62 | ///
63 | [JsonPropertyName("limit")]
64 | public int Limit { get; }
65 |
66 | ///
67 | /// Gets the estimated total hits.
68 | ///
69 | [JsonPropertyName("estimatedTotalHits")]
70 | public int EstimatedTotalHits { get; }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.Dictionary.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Net.Http.Json;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace Meilisearch
7 | {
8 | public partial class Index
9 | {
10 | ///
11 | /// Gets the dictionary of an index.
12 | ///
13 | /// The cancellation token for this call.
14 | /// Returns the dictionary.
15 | public async Task> GetDictionaryAsync(CancellationToken cancellationToken = default)
16 | {
17 | return await _http.GetFromJsonAsync($"indexes/{Uid}/settings/dictionary", cancellationToken: cancellationToken)
18 | .ConfigureAwait(false);
19 | }
20 |
21 | ///
22 | /// Updates the dictionary of an index.
23 | ///
24 | /// Dictionary object.
25 | /// The cancellation token for this call.
26 | /// Returns the task info of the asynchronous task.
27 | public async Task UpdateDictionaryAsync(IEnumerable dictionary, CancellationToken cancellationToken = default)
28 | {
29 | var responseMessage =
30 | await _http.PutAsJsonAsync($"indexes/{Uid}/settings/dictionary", dictionary, cancellationToken: cancellationToken)
31 | .ConfigureAwait(false);
32 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
33 | }
34 |
35 | ///
36 | /// Resets the dictionary to their default values.
37 | ///
38 | /// The cancellation token for this call.
39 | /// Returns the task info of the asynchronous task.
40 | public async Task ResetDictionaryAsync(CancellationToken cancellationToken = default)
41 | {
42 | var httpResponse = await _http.DeleteAsync($"indexes/{Uid}/settings/dictionary", cancellationToken).ConfigureAwait(false);
43 | return await httpResponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Movie.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json;
3 |
4 | namespace Meilisearch.Tests
5 | {
6 | public class Movie
7 | {
8 | public string Id { get; set; }
9 |
10 | public string Name { get; set; }
11 |
12 | public string Genre { get; set; }
13 | }
14 |
15 | public struct MovieStruct
16 | {
17 | public string Id { get; set; }
18 |
19 | public string Name { get; set; }
20 |
21 | public string Genre { get; set; }
22 | }
23 | public class MovieInfo
24 | {
25 | public string Comment { get; set; }
26 |
27 | public int ReviewNb { get; set; }
28 | }
29 |
30 | public class MovieWithInfo
31 | {
32 | public string Id { get; set; }
33 |
34 | public string Name { get; set; }
35 |
36 | public MovieInfo Info { get; set; }
37 | }
38 |
39 |
40 | public class MovieWithIntId
41 | {
42 | public int Id { get; set; }
43 |
44 | public string Name { get; set; }
45 |
46 | public string Genre { get; set; }
47 | }
48 |
49 | public class FormattedMovie
50 | {
51 | public string Id { get; set; }
52 |
53 | public string Name { get; set; }
54 |
55 | public string Genre { get; set; }
56 |
57 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Naming convention used to match meilisearch.")]
58 | public Movie _Formatted { get; set; }
59 | }
60 |
61 | public class MovieWithRankingScore
62 | {
63 | public string Id { get; set; }
64 |
65 | public string Name { get; set; }
66 |
67 | public string Genre { get; set; }
68 |
69 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Naming convention used to match meilisearch.")]
70 | public double? _RankingScore { get; set; }
71 | }
72 |
73 | public class MovieWithRankingScoreDetails
74 | {
75 | public string Id { get; set; }
76 |
77 | public string Name { get; set; }
78 |
79 | public string Genre { get; set; }
80 |
81 | [System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE1006:Naming Styles", Justification = "Naming convention used to match meilisearch.")]
82 | public IDictionary _RankingScoreDetails { get; set; }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.Faceting.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http.Json;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | using Meilisearch.Extensions;
6 | namespace Meilisearch
7 | {
8 | public partial class Index
9 | {
10 | ///
11 | /// Gets the faceting setting.
12 | ///
13 | /// The cancellation token for this call.
14 | /// Returns the faceting setting.
15 | public async Task GetFacetingAsync(CancellationToken cancellationToken = default)
16 | {
17 | return await _http.GetFromJsonAsync($"indexes/{Uid}/settings/faceting", cancellationToken: cancellationToken)
18 | .ConfigureAwait(false);
19 | }
20 |
21 | ///
22 | /// Updates the faceting setting.
23 | ///
24 | /// Faceting instance
25 | /// The cancellation token for this call.
26 | /// Returns the task info of the asynchronous task.
27 | public async Task UpdateFacetingAsync(Faceting faceting, CancellationToken cancellationToken = default)
28 | {
29 | var responseMessage =
30 | await _http.PatchAsJsonAsync($"indexes/{Uid}/settings/faceting", faceting, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken)
31 | .ConfigureAwait(false);
32 |
33 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken)
34 | .ConfigureAwait(false);
35 | }
36 |
37 | ///
38 | /// Resets the faceting setting.
39 | ///
40 | /// The cancellation token for this call.
41 | /// Returns the task info of the asynchronous task.
42 | public async Task ResetFacetingAsync(CancellationToken cancellationToken = default)
43 | {
44 | var response = await _http.DeleteAsync($"indexes/{Uid}/settings/faceting", cancellationToken)
45 | .ConfigureAwait(false);
46 |
47 | return await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Meilisearch/QueryParameters/CancelTasksQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Meilisearch.QueryParameters
5 | {
6 | ///
7 | /// A class that handles the creation of a query string when cancelling tasks.
8 | ///
9 | public class CancelTasksQuery
10 | {
11 | ///
12 | /// Gets or sets the lists of indexUid to filter on. Case-sensitive.
13 | ///
14 | public List IndexUids { get; set; }
15 |
16 | ///
17 | /// Gets or sets the list of statuses to filter on.
18 | ///
19 | public List Statuses { get; set; }
20 |
21 | ///
22 | /// Gets or sets the list of types to filter on.
23 | ///
24 | public List Types { get; set; }
25 |
26 | ///
27 | /// Gets or sets the list of uids to filter on. Case-sensitive.
28 | ///
29 | public List Uids { get; set; }
30 |
31 | ///
32 | /// Gets or sets the list of canceledBy uids to filter on. Case-sensitive.
33 | ///
34 | public List CanceledBy { get; set; }
35 |
36 | ///
37 | /// Gets or sets the date before the task is enqueued to filter.
38 | ///
39 | public DateTime? BeforeEnqueuedAt { get; set; }
40 |
41 | ///
42 | /// Gets or sets the date after the task is enqueued to filter.
43 | ///
44 | public DateTime? AfterEnqueuedAt { get; set; }
45 |
46 | ///
47 | /// Gets or sets the date before the task was started to filter.
48 | ///
49 | public DateTime? BeforeStartedAt { get; set; }
50 |
51 | ///
52 | /// Gets or sets the date after the task was started to filter.
53 | ///
54 | public DateTime? AfterStartedAt { get; set; }
55 |
56 | ///
57 | /// Gets or sets the date before the task was finished to filter.
58 | ///
59 | public DateTime? BeforeFinishedAt { get; set; }
60 |
61 | ///
62 | /// Gets or sets the date after the task was finished to filter.
63 | ///
64 | public DateTime? AfterFinishedAt { get; set; }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Meilisearch/QueryParameters/DeleteTasksQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Meilisearch.QueryParameters
5 | {
6 | ///
7 | /// A class that handles the creation of a query string when deleting tasks.
8 | ///
9 | public class DeleteTasksQuery
10 | {
11 | ///
12 | /// Gets or sets the lists of indexUid to filter on. Case-sensitive.
13 | ///
14 | public List IndexUids { get; set; }
15 |
16 | ///
17 | /// Gets or sets the list of statuses to filter on.
18 | ///
19 | public List Statuses { get; set; }
20 |
21 | ///
22 | /// Gets or sets the list of types to filter on.
23 | ///
24 | public List Types { get; set; }
25 |
26 | ///
27 | /// Gets or sets the list of uids to filter on. Case-sensitive.
28 | ///
29 | public List Uids { get; set; }
30 |
31 | ///
32 | /// Gets or sets the list of canceledBy uids to filter on. Case-sensitive.
33 | ///
34 | public List CanceledBy { get; set; }
35 |
36 | ///
37 | /// Gets or sets the date before the task is enqueued to filter.
38 | ///
39 | public DateTime? BeforeEnqueuedAt { get; set; }
40 |
41 | ///
42 | /// Gets or sets the date after the task is enqueued to filter.
43 | ///
44 | public DateTime? AfterEnqueuedAt { get; set; }
45 |
46 | ///
47 | /// Gets or sets the date before the task was started to filter.
48 | ///
49 | public DateTime? BeforeStartedAt { get; set; }
50 |
51 | ///
52 | /// Gets or sets the date after the task was started to filter.
53 | ///
54 | public DateTime? AfterStartedAt { get; set; }
55 |
56 | ///
57 | /// Gets or sets the date before the task was finished to filter.
58 | ///
59 | public DateTime? BeforeFinishedAt { get; set; }
60 |
61 | ///
62 | /// Gets or sets the date after the task was finished to filter.
63 | ///
64 | public DateTime? AfterFinishedAt { get; set; }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.SearchCutoffMs.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http.Json;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | namespace Meilisearch
6 | {
7 | public partial class Index
8 | {
9 |
10 | ///
11 | /// Gets the search cutoff in milliseconds.
12 | ///
13 | /// Returns the search cutoff in milliseconds.
14 | public async Task GetSearchCutoffMsAsync(CancellationToken cancellationToken = default)
15 | {
16 | return await _http.GetFromJsonAsync($"indexes/{Uid}/settings/search-cutoff-ms", cancellationToken: cancellationToken)
17 | .ConfigureAwait(false);
18 | }
19 | ///
20 | /// Sets the search cutoff in milliseconds.
21 | ///
22 | /// The search cutoff in milliseconds.
23 | /// The cancellation token for this call.
24 | /// Returns the task info of the asynchronous task.
25 | public async Task UpdateSearchCutoffMsAsync(int searchCutoffMs, CancellationToken cancellationToken = default)
26 | {
27 | var responseMessage =
28 | await _http.PutAsJsonAsync($"indexes/{Uid}/settings/search-cutoff-ms", searchCutoffMs, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken)
29 | .ConfigureAwait(false);
30 |
31 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken)
32 | .ConfigureAwait(false);
33 | }
34 |
35 | ///
36 | /// Resets the search cutoff in milliseconds. (default: 1500)
37 | ///
38 | /// The cancellation token for this call.
39 | /// Returns the task info of the asynchronous task.
40 | public async Task ResetSearchCutoffMsAsync(CancellationToken cancellationToken = default)
41 | {
42 | var responseMessage = await _http.DeleteAsync($"indexes/{Uid}/settings/search-cutoff-ms", cancellationToken)
43 | .ConfigureAwait(false);
44 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.Pagination.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http.Json;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | using Meilisearch.Extensions;
6 | namespace Meilisearch
7 | {
8 | public partial class Index
9 | {
10 | ///
11 | /// Gets the pagination setting.
12 | ///
13 | /// The cancellation token for this call.
14 | /// Returns the pagination setting.
15 | public async Task GetPaginationAsync(CancellationToken cancellationToken = default)
16 | {
17 | return await _http.GetFromJsonAsync($"indexes/{Uid}/settings/pagination", cancellationToken: cancellationToken)
18 | .ConfigureAwait(false);
19 | }
20 |
21 | ///
22 | /// Updates the pagination setting.
23 | ///
24 | /// Pagination instance
25 | /// The cancellation token for this call.
26 | /// Returns the task info of the asynchronous task.
27 | public async Task UpdatePaginationAsync(Pagination pagination, CancellationToken cancellationToken = default)
28 | {
29 | var responseMessage =
30 | await _http.PatchAsJsonAsync($"indexes/{Uid}/settings/pagination", pagination, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken)
31 | .ConfigureAwait(false);
32 |
33 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken)
34 | .ConfigureAwait(false);
35 | }
36 |
37 | ///
38 | /// Resets the pagination setting.
39 | ///
40 | /// The cancellation token for this call.
41 | /// Returns the task info of the asynchronous task.
42 | public async Task ResetPaginationAsync(CancellationToken cancellationToken = default)
43 | {
44 | var response = await _http.DeleteAsync($"indexes/{Uid}/settings/pagination", cancellationToken)
45 | .ConfigureAwait(false);
46 |
47 | return await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.Settings.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http.Json;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | using Meilisearch.Extensions;
6 | namespace Meilisearch
7 | {
8 | public partial class Index
9 | {
10 | ///
11 | /// Gets all the settings of an index.
12 | ///
13 | /// The cancellation token for this call.
14 | /// Returns all the settings.
15 | public async Task GetSettingsAsync(CancellationToken cancellationToken = default)
16 | {
17 | return await _http.GetFromJsonAsync($"indexes/{Uid}/settings", cancellationToken: cancellationToken)
18 | .ConfigureAwait(false);
19 | }
20 |
21 | ///
22 | /// Updates all the settings of an index.
23 | /// The settings that are not passed in parameter are not overwritten.
24 | ///
25 | /// Settings object.
26 | /// The cancellation token for this call.
27 | /// Returns the task info of the asynchronous task.
28 | public async Task UpdateSettingsAsync(Settings settings, CancellationToken cancellationToken = default)
29 | {
30 | var responseMessage =
31 | await _http.PatchAsJsonAsync($"indexes/{Uid}/settings", settings, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken)
32 | .ConfigureAwait(false);
33 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
34 | }
35 |
36 | ///
37 | /// Resets all the settings to their default values.
38 | ///
39 | /// The cancellation token for this call.
40 | /// Returns the task info of the asynchronous task.
41 | public async Task ResetSettingsAsync(CancellationToken cancellationToken = default)
42 | {
43 | var httpResponse = await _http.DeleteAsync($"indexes/{Uid}/settings", cancellationToken).ConfigureAwait(false);
44 | return await httpResponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.StopWords.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Net.Http.Json;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace Meilisearch
7 | {
8 | public partial class Index
9 | {
10 | ///
11 | /// Gets the stop words setting.
12 | ///
13 | /// The cancellation token for this call.
14 | /// Returns the stop words setting.
15 | public async Task> GetStopWordsAsync(CancellationToken cancellationToken = default)
16 | {
17 | return await _http.GetFromJsonAsync>($"indexes/{Uid}/settings/stop-words", cancellationToken: cancellationToken)
18 | .ConfigureAwait(false);
19 | }
20 |
21 | ///
22 | /// Updates the stop words setting.
23 | ///
24 | /// Collection of stop words.
25 | /// The cancellation token for this call.
26 | /// Returns the task info of the asynchronous task.
27 | public async Task UpdateStopWordsAsync(IEnumerable stopWords, CancellationToken cancellationToken = default)
28 | {
29 | var responseMessage =
30 | await _http.PutAsJsonAsync($"indexes/{Uid}/settings/stop-words", stopWords, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken)
31 | .ConfigureAwait(false);
32 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
33 | }
34 |
35 | ///
36 | /// Resets the stop words setting.
37 | ///
38 | /// The cancellation token for this call.
39 | /// Returns the task info of the asynchronous task.
40 | public async Task ResetStopWordsAsync(CancellationToken cancellationToken = default)
41 | {
42 | var httpresponse = await _http.DeleteAsync($"indexes/{Uid}/settings/stop-words", cancellationToken)
43 | .ConfigureAwait(false);
44 | return await httpresponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Meilisearch/MeilisearchMessageHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http;
2 | using System.Net.Http.Json;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace Meilisearch
7 | {
8 | ///
9 | /// Typed http request for Meilisearch.
10 | ///
11 | public class MeilisearchMessageHandler : DelegatingHandler
12 | {
13 |
14 | ///
15 | /// Initializes a new instance of the class.
16 | /// Default message handler for Meilisearch API.
17 | ///
18 | public MeilisearchMessageHandler()
19 | {
20 | }
21 |
22 | ///
23 | /// Initializes a new instance of the class.
24 | /// Default message handler for Meilisearch API.
25 | ///
26 | /// InnerHandler.
27 | public MeilisearchMessageHandler(HttpMessageHandler innerHandler)
28 | : base(innerHandler)
29 | {
30 | }
31 |
32 | ///
33 | /// Override SendAsync to handle errors.
34 | ///
35 | /// Request.
36 | /// Cancellation Token.
37 | /// Return HttpResponseMessage.
38 | protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
39 | {
40 | try
41 | {
42 | var response = await base.SendAsync(request, cancellationToken);
43 | if (!response.IsSuccessStatusCode)
44 | {
45 | if (response.Content.Headers.ContentLength != 0)
46 | {
47 | var content = await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
48 | throw new MeilisearchApiError(content);
49 | }
50 |
51 | throw new MeilisearchApiError(response.StatusCode, response.ReasonPhrase);
52 | }
53 |
54 | return response;
55 | }
56 | catch (HttpRequestException ex)
57 | {
58 | throw new MeilisearchCommunicationError("CommunicationError", ex);
59 | }
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.RankingRules.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Net.Http.Json;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace Meilisearch
7 | {
8 | public partial class Index
9 | {
10 | ///
11 | /// Gets the ranking rules setting.
12 | ///
13 | /// The cancellation token for this call.
14 | /// Returns the ranking rules setting.
15 | public async Task> GetRankingRulesAsync(CancellationToken cancellationToken = default)
16 | {
17 | return await _http.GetFromJsonAsync>($"indexes/{Uid}/settings/ranking-rules", cancellationToken: cancellationToken)
18 | .ConfigureAwait(false);
19 | }
20 |
21 | ///
22 | /// Updates the ranking rules setting.
23 | ///
24 | /// Collection of ranking rules.
25 | /// The cancellation token for this call.
26 | /// Returns the task info of the asynchronous task.
27 | public async Task UpdateRankingRulesAsync(IEnumerable rankingRules, CancellationToken cancellationToken = default)
28 | {
29 | var responseMessage =
30 | await _http.PutAsJsonAsync($"indexes/{Uid}/settings/ranking-rules", rankingRules, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken)
31 | .ConfigureAwait(false);
32 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
33 | }
34 |
35 | ///
36 | /// Resets the ranking rules setting.
37 | ///
38 | /// The cancellation token for this call.
39 | /// Returns the task info of the asynchronous task.
40 | public async Task ResetRankingRulesAsync(CancellationToken cancellationToken = default)
41 | {
42 | var httpresponse = await _http.DeleteAsync($"indexes/{Uid}/settings/ranking-rules", cancellationToken)
43 | .ConfigureAwait(false);
44 | return await httpresponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.Synonyms.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Net.Http.Json;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace Meilisearch
7 | {
8 | public partial class Index
9 | {
10 | ///
11 | /// Gets the synonyms setting.
12 | ///
13 | /// The cancellation token for this call.
14 | /// Returns the synonyms setting.
15 | public async Task>> GetSynonymsAsync(CancellationToken cancellationToken = default)
16 | {
17 | return await _http.GetFromJsonAsync>>($"indexes/{Uid}/settings/synonyms", cancellationToken: cancellationToken)
18 | .ConfigureAwait(false);
19 | }
20 |
21 | ///
22 | /// Updates the synonyms setting.
23 | ///
24 | /// Collection of synonyms.
25 | /// The cancellation token for this call.
26 | /// Returns the task info of the asynchronous task.
27 | public async Task UpdateSynonymsAsync(Dictionary> synonyms, CancellationToken cancellationToken = default)
28 | {
29 | var responseMessage =
30 | await _http.PutAsJsonAsync($"indexes/{Uid}/settings/synonyms", synonyms, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken)
31 | .ConfigureAwait(false);
32 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken)
33 | .ConfigureAwait(false);
34 | }
35 |
36 | ///
37 | /// Resets the synonyms setting.
38 | ///
39 | /// The cancellation token for this call.
40 | /// Returns the task info of the asynchronous task.
41 | public async Task ResetSynonymsAsync(CancellationToken cancellationToken = default)
42 | {
43 | var httpresponse = await _http.DeleteAsync($"indexes/{Uid}/settings/synonyms", cancellationToken)
44 | .ConfigureAwait(false);
45 | return await httpresponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.TypoTolerance.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Http.Json;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | using Meilisearch.Extensions;
6 | namespace Meilisearch
7 | {
8 | public partial class Index
9 | {
10 | ///
11 | /// Gets the typo tolerance setting.
12 | ///
13 | /// The cancellation token for this call.
14 | /// Returns the typo tolerance setting.
15 | public async Task GetTypoToleranceAsync(CancellationToken cancellationToken = default)
16 | {
17 | return await _http.GetFromJsonAsync($"indexes/{Uid}/settings/typo-tolerance", cancellationToken: cancellationToken)
18 | .ConfigureAwait(false);
19 | }
20 |
21 | ///
22 | /// Updates the typo tolerance setting.
23 | ///
24 | /// TypoTolerance instance
25 | /// The cancellation token for this call.
26 | /// Returns the task info of the asynchronous task.
27 | public async Task UpdateTypoToleranceAsync(TypoTolerance typoTolerance, CancellationToken cancellationToken = default)
28 | {
29 | var responseMessage =
30 | await _http.PatchAsJsonAsync($"indexes/{Uid}/settings/typo-tolerance", typoTolerance, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken)
31 | .ConfigureAwait(false);
32 |
33 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken)
34 | .ConfigureAwait(false);
35 | }
36 |
37 | ///
38 | /// Resets the typo tolerance setting.
39 | ///
40 | /// The cancellation token for this call.
41 | /// Returns the task info of the asynchronous task.
42 | public async Task ResetTypoToleranceAsync(CancellationToken cancellationToken = default)
43 | {
44 | var response = await _http.DeleteAsync($"indexes/{Uid}/settings/typo-tolerance", cancellationToken)
45 | .ConfigureAwait(false);
46 |
47 | return await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.Tasks.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Threading;
3 | using System.Threading.Tasks;
4 |
5 | using Meilisearch.QueryParameters;
6 |
7 | namespace Meilisearch
8 | {
9 | public partial class Index
10 | {
11 | ///
12 | /// Gets the tasks.
13 | ///
14 | /// Query parameters supports by the method.
15 | /// The cancellation token for this call.
16 | /// Returns a list of the operations status.
17 | public async Task>> GetTasksAsync(TasksQuery query = null, CancellationToken cancellationToken = default)
18 | {
19 | if (query == null)
20 | {
21 | query = new TasksQuery { IndexUids = new List { this.Uid } };
22 | }
23 |
24 | return await TaskEndpoint().GetTasksAsync(query, cancellationToken).ConfigureAwait(false);
25 | }
26 |
27 | ///
28 | /// Get on task.
29 | ///
30 | /// Uid of the task.
31 | /// The cancellation token for this call.
32 | /// Returns the tasks.
33 | public async Task GetTaskAsync(int taskUid, CancellationToken cancellationToken = default)
34 | {
35 | return await TaskEndpoint().GetTaskAsync(taskUid, cancellationToken).ConfigureAwait(false);
36 | }
37 |
38 | ///
39 | /// Waits until the asynchronous task was done.
40 | ///
41 | /// Unique identifier of the asynchronous task.
42 | /// Timeout in millisecond.
43 | /// Interval in millisecond between each check.
44 | /// The cancellation token for this call.
45 | /// Returns the task info of finished task.
46 | public async Task WaitForTaskAsync(
47 | int taskUid,
48 | double timeoutMs = 5000.0,
49 | int intervalMs = 50,
50 | CancellationToken cancellationToken = default)
51 | {
52 | return await TaskEndpoint().WaitForTaskAsync(taskUid, timeoutMs, intervalMs, cancellationToken).ConfigureAwait(false);
53 | }
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.SeparatorTokens.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Net.Http.Json;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace Meilisearch
7 | {
8 | public partial class Index
9 | {
10 | ///
11 | /// Gets all the separator tokens settings.
12 | ///
13 | /// The cancellation token for this call.
14 | /// Returns all the configured separator tokens.
15 | public async Task> GetSeparatorTokensAsync(CancellationToken cancellationToken = default)
16 | {
17 | return await _http.GetFromJsonAsync>($"indexes/{Uid}/settings/separator-tokens", cancellationToken: cancellationToken)
18 | .ConfigureAwait(false);
19 | }
20 |
21 | ///
22 | /// Updates all the separator tokens settings.
23 | ///
24 | /// Collection of separator tokens.
25 | /// The cancellation token for this call.
26 | /// Returns the task info of the asynchronous task.
27 | public async Task UpdateSeparatorTokensAsync(IEnumerable separatorTokens, CancellationToken cancellationToken = default)
28 | {
29 | var responseMessage =
30 | await _http.PutAsJsonAsync($"indexes/{Uid}/settings/separator-tokens", separatorTokens, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken)
31 | .ConfigureAwait(false);
32 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
33 | }
34 |
35 | ///
36 | /// Resets all the separator tokens settings.
37 | ///
38 | /// The cancellation token for this call.
39 | /// Returns the task info of the asynchronous task.
40 | public async Task ResetSeparatorTokensAsync(CancellationToken cancellationToken = default)
41 | {
42 | var httpresponse = await _http.DeleteAsync($"indexes/{Uid}/settings/separator-tokens", cancellationToken)
43 | .ConfigureAwait(false);
44 | return await httpresponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.NonSeparatorTokens.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Net.Http.Json;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | namespace Meilisearch
7 | {
8 | public partial class Index
9 | {
10 | ///
11 | /// Gets all the non separator tokens settings.
12 | ///
13 | /// The cancellation token for this call.
14 | /// Returns all the configured non separator tokens.
15 | public async Task> GetNonSeparatorTokensAsync(CancellationToken cancellationToken = default)
16 | {
17 | return await _http.GetFromJsonAsync>($"indexes/{Uid}/settings/non-separator-tokens", cancellationToken: cancellationToken)
18 | .ConfigureAwait(false);
19 | }
20 |
21 | ///
22 | /// Updates all the non separator tokens settings.
23 | ///
24 | /// Collection of separator tokens.
25 | /// The cancellation token for this call.
26 | /// Returns the task info of the asynchronous task.
27 | public async Task UpdateNonSeparatorTokensAsync(IEnumerable nonSeparatorTokens, CancellationToken cancellationToken = default)
28 | {
29 | var responseMessage =
30 | await _http.PutAsJsonAsync($"indexes/{Uid}/settings/non-separator-tokens", nonSeparatorTokens, Constants.JsonSerializerOptionsRemoveNulls, cancellationToken: cancellationToken)
31 | .ConfigureAwait(false);
32 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
33 | }
34 |
35 | ///
36 | /// Resets all the non separator tokens settings.
37 | ///
38 | /// The cancellation token for this call.
39 | /// Returns the task info of the asynchronous task.
40 | public async Task ResetNonSeparatorTokensAsync(CancellationToken cancellationToken = default)
41 | {
42 | var httpresponse = await _http.DeleteAsync($"indexes/{Uid}/settings/non-separator-tokens", cancellationToken)
43 | .ConfigureAwait(false);
44 | return await httpresponse.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/Meilisearch/IndexStats.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Wrapper for index stats.
8 | ///
9 | public class IndexStats
10 | {
11 | public IndexStats(int numberOfDocuments, bool isIndexing, IReadOnlyDictionary fieldDistribution, long rawDocumentDbSize, long avgDocumentSize, int numberOfEmbeddedDocuments, int numberOfEmbeddings)
12 | {
13 | NumberOfDocuments = numberOfDocuments;
14 | IsIndexing = isIndexing;
15 | FieldDistribution = fieldDistribution;
16 | RawDocumentDbSize = rawDocumentDbSize;
17 | AvgDocumentSize = avgDocumentSize;
18 | NumberOfEmbeddedDocuments = numberOfEmbeddedDocuments;
19 | NumberOfEmbeddings = numberOfEmbeddings;
20 | }
21 |
22 | ///
23 | /// Gets the total number of documents.
24 | ///
25 | [JsonPropertyName("numberOfDocuments")]
26 | public int NumberOfDocuments { get; }
27 |
28 | ///
29 | /// Gets a value indicating whether the index is currently indexing.
30 | ///
31 | [JsonPropertyName("isIndexing")]
32 | public bool IsIndexing { get; }
33 |
34 | ///
35 | /// Gets field distribution.
36 | ///
37 | [JsonPropertyName("fieldDistribution")]
38 | public IReadOnlyDictionary FieldDistribution { get; }
39 |
40 | ///
41 | /// Get the total size of the documents stored in Meilisearch
42 | ///
43 | [JsonPropertyName("rawDocumentDbSize")]
44 | public long RawDocumentDbSize { get; }
45 |
46 | ///
47 | /// Get the total size of the documents stored in Meilisearch divided by the number of documents
48 | ///
49 | [JsonPropertyName("avgDocumentSize")]
50 | public long AvgDocumentSize { get; }
51 |
52 | ///
53 | /// Get the number of document in index that contains at least one embedded representation
54 | ///
55 | [JsonPropertyName("numberOfEmbeddedDocuments")]
56 | public int NumberOfEmbeddedDocuments { get; }
57 |
58 |
59 | ///
60 | /// Get the total number of embeddings representation that exists in that indexes
61 | ///
62 | [JsonPropertyName("numberOfEmbeddings")]
63 | public int NumberOfEmbeddings { get; }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.ProximityPrecision.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net.Http.Json;
5 | using System.Threading;
6 | using System.Threading.Tasks;
7 |
8 | using Meilisearch.Extensions;
9 |
10 | namespace Meilisearch
11 | {
12 | public partial class Index
13 | {
14 | ///
15 | /// Gets the proximity precision setting.
16 | ///
17 | /// The cancellation token for this call.
18 | /// Returns the proximity precision setting.
19 | public async Task GetProximityPrecisionAsync(CancellationToken cancellationToken = default)
20 | {
21 | return await _http.GetFromJsonAsync($"indexes/{Uid}/settings/proximity-precision", cancellationToken: cancellationToken)
22 | .ConfigureAwait(false);
23 | }
24 |
25 | ///
26 | /// Updates the proximity precision setting.
27 | ///
28 | /// The new proximity precision setting.
29 | /// The cancellation token for this call.
30 | /// Returns the task info of the asynchronous task.
31 | public async Task UpdateProximityPrecisionAsync(string proximityPrecision, CancellationToken cancellationToken = default)
32 | {
33 | var responseMessage =
34 | await _http.PutAsJsonAsync($"indexes/{Uid}/settings/proximity-precision", proximityPrecision, cancellationToken: cancellationToken)
35 | .ConfigureAwait(false);
36 |
37 | return await responseMessage.Content.ReadFromJsonAsync(cancellationToken: cancellationToken)
38 | .ConfigureAwait(false);
39 | }
40 |
41 | ///
42 | /// Resets proximity precision setting.
43 | ///
44 | /// The cancellation token for this call.
45 | /// Returns the task info of the asynchronous task.
46 | public async Task ResetProximityPrecisionAsync(CancellationToken cancellationToken = default)
47 | {
48 | var response = await _http.DeleteAsync($"indexes/{Uid}/settings/proximity-precision", cancellationToken)
49 | .ConfigureAwait(false);
50 |
51 | return await response.Content.ReadFromJsonAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/Meilisearch/ISearchableJsonConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.Json;
3 | using System.Text.Json.Serialization;
4 |
5 | namespace Meilisearch
6 | {
7 | ///
8 | /// The json converter factory for
9 | ///
10 | public class ISearchableJsonConverterFactory : JsonConverterFactory
11 | {
12 | ///
13 | public override bool CanConvert(Type typeToConvert)
14 | {
15 | return typeToConvert.IsInterface
16 | && typeToConvert.IsGenericType
17 | && (typeToConvert.GetGenericTypeDefinition() == typeof(ISearchable<>));
18 | }
19 |
20 | ///
21 | public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
22 | {
23 | var genericArgs = typeToConvert.GetGenericArguments();
24 | var converterType = typeof(ISearchableJsonConverter<>).MakeGenericType(
25 | genericArgs[0]
26 | );
27 | var converter = (JsonConverter)Activator.CreateInstance(converterType);
28 | return converter;
29 | }
30 | }
31 |
32 | ///
33 | /// The json converter for
34 | ///
35 | ///
36 | public class ISearchableJsonConverter : JsonConverter>
37 | {
38 | ///
39 | public override ISearchable Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
40 | {
41 | var document = JsonSerializer.Deserialize(ref reader, options);
42 | return document.TryGetProperty("page", out _) || document.TryGetProperty("hitsPerPage", out _)
43 | ? document.Deserialize>(options)
44 | : (ISearchable)document.Deserialize>(options);
45 | }
46 |
47 | ///
48 | public override void Write(Utf8JsonWriter writer, ISearchable value, JsonSerializerOptions options)
49 | {
50 | if (value is PaginatedSearchResult paginated)
51 | {
52 | JsonSerializer.Serialize(writer, paginated, options);
53 | }
54 | else if (value is SearchResult normal)
55 | {
56 | JsonSerializer.Serialize(writer, normal, options);
57 | }
58 | else
59 | {
60 | JsonSerializer.Serialize(writer, (object)value, options);
61 | }
62 | }
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Datasets/products_for_distinct_search.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 1,
4 | "description": "Leather Jacket",
5 | "brand": "Lee Jeans",
6 | "product_id": "123456",
7 | "color": "Brown"
8 | },
9 | {
10 | "id": 2,
11 | "description": "Leather Jacket",
12 | "brand": "Lee Jeans",
13 | "product_id": "123456",
14 | "color": "Black"
15 | },
16 | {
17 | "id": 3,
18 | "description": "Leather Jacket",
19 | "brand": "Lee Jeans",
20 | "product_id": "123456",
21 | "color": "Blue"
22 | },
23 | {
24 | "id": 4,
25 | "description": "T-Shirt",
26 | "brand": "Nike",
27 | "product_id": "789012",
28 | "color": "Red"
29 | },
30 | {
31 | "id": 5,
32 | "description": "T-Shirt",
33 | "brand": "Nike",
34 | "product_id": "789012",
35 | "color": "Blue"
36 | },
37 | {
38 | "id": 6,
39 | "description": "Running Shoes",
40 | "brand": "Adidas",
41 | "product_id": "456789",
42 | "color": "Black"
43 | },
44 | {
45 | "id": 7,
46 | "description": "Running Shoes",
47 | "brand": "Adidas",
48 | "product_id": "456789",
49 | "color": "White"
50 | },
51 | {
52 | "id": 8,
53 | "description": "Hoodie",
54 | "brand": "Puma",
55 | "product_id": "987654",
56 | "color": "Gray"
57 | },
58 | {
59 | "id": 9,
60 | "description": "Sweater",
61 | "brand": "Gap",
62 | "product_id": "234567",
63 | "color": "Green"
64 | },
65 | {
66 | "id": 10,
67 | "description": "Sweater",
68 | "brand": "Gap",
69 | "product_id": "234567",
70 | "color": "Red"
71 | },
72 | {
73 | "id": 11,
74 | "description": "Sweater",
75 | "brand": "Gap",
76 | "product_id": "234567",
77 | "color": "Blue"
78 | },
79 | {
80 | "id": 12,
81 | "description": "Jeans",
82 | "brand": "Levi's",
83 | "product_id": "345678",
84 | "color": "Indigo"
85 | },
86 | {
87 | "id": 13,
88 | "description": "Jeans",
89 | "brand": "Levi's",
90 | "product_id": "345678",
91 | "color": "Black"
92 | },
93 | {
94 | "id": 14,
95 | "description": "Jeans",
96 | "brand": "Levi's",
97 | "product_id": "345678",
98 | "color": "Stone Wash"
99 | }
100 | ]
101 |
--------------------------------------------------------------------------------
/src/Meilisearch/Embedder.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Embedder configuration.
8 | ///
9 | public class Embedder
10 | {
11 | ///
12 | /// Gets or sets the source.
13 | ///
14 | [JsonPropertyName("source")]
15 | public EmbedderSource Source { get; set; }
16 |
17 | ///
18 | /// Gets or sets the URL.
19 | ///
20 | [JsonPropertyName("url")]
21 | public string Url { get; set; }
22 |
23 | ///
24 | /// Gets or sets the API key.
25 | ///
26 | [JsonPropertyName("apiKey")]
27 | public string ApiKey { get; set; }
28 |
29 | ///
30 | /// Gets or sets the model.
31 | ///
32 | [JsonPropertyName("model")]
33 | public string Model { get; set; }
34 |
35 | ///
36 | /// Gets or sets the document template.
37 | ///
38 | [JsonPropertyName("documentTemplate")]
39 | public string DocumentTemplate { get; set; }
40 |
41 | ///
42 | /// Gets or sets the document template max bytes.
43 | ///
44 | [JsonPropertyName("documentTemplateMaxBytes")]
45 | public int? DocumentTemplateMaxBytes { get; set; }
46 |
47 | ///
48 | /// Gets or sets the dimensions.
49 | ///
50 | [JsonPropertyName("dimensions")]
51 | public int? Dimensions { get; set; }
52 |
53 | ///
54 | /// Gets or sets the revision.
55 | ///
56 | [JsonPropertyName("revision")]
57 | public string Revision { get; set; }
58 |
59 | ///
60 | /// Gets or sets the distribution.
61 | ///
62 | [JsonPropertyName("distribution")]
63 | public EmbedderDistribution Distribution { get; set; }
64 |
65 | ///
66 | /// Gets or sets the request.
67 | ///
68 | [JsonPropertyName("request")]
69 | public Dictionary Request { get; set; }
70 |
71 | ///
72 | /// Gets or sets the response.
73 | ///
74 | [JsonPropertyName("response")]
75 | public Dictionary Response { get; set; }
76 |
77 | ///
78 | /// Gets or sets whether the vectors should be compressed.
79 | ///
80 | [JsonPropertyName("binaryQuantized")]
81 | public bool? BinaryQuantized { get; set; }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/Meilisearch/QueryParameters/TasksQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace Meilisearch.QueryParameters
5 | {
6 | ///
7 | /// A class that handles the creation of a query string for Tasks.
8 | ///
9 | public class TasksQuery
10 | {
11 | ///
12 | /// Gets or sets the Number of tasks to return.
13 | ///
14 | public int? Limit { get; set; }
15 |
16 | ///
17 | /// Gets or sets the uid of the first task returned.
18 | ///
19 | public int? From { get; set; }
20 |
21 | ///
22 | /// Gets or sets the lists of indexUid to filter on. Case-sensitive.
23 | ///
24 | public List IndexUids { get; set; }
25 |
26 | ///
27 | /// Gets or sets the lists of uid to filter on. Case-sensitive.
28 | ///
29 | public List Uids { get; set; }
30 |
31 | ///
32 | /// Gets or sets the list of statuses to filter on.
33 | ///
34 | public List Statuses { get; set; }
35 |
36 | ///
37 | /// Gets or sets the list of types to filter on.
38 | ///
39 | public List Types { get; set; }
40 |
41 | ///
42 | /// Gets or sets the list of canceledBy uids to filter on. Case-sensitive.
43 | ///
44 | public List CanceledBy { get; set; }
45 |
46 | ///
47 | /// Gets or sets the date before the task is enqueued to filter.
48 | ///
49 | public DateTime? BeforeEnqueuedAt { get; set; }
50 |
51 | ///
52 | /// Gets or sets the date after the task is enqueued to filter.
53 | ///
54 | public DateTime? AfterEnqueuedAt { get; set; }
55 |
56 | ///
57 | /// Gets or sets the date before the task was started to filter.
58 | ///
59 | public DateTime? BeforeStartedAt { get; set; }
60 |
61 | ///
62 | /// Gets or sets the date after the task was started to filter.
63 | ///
64 | public DateTime? AfterStartedAt { get; set; }
65 |
66 | ///
67 | /// Gets or sets the date before the task was finished to filter.
68 | ///
69 | public DateTime? BeforeFinishedAt { get; set; }
70 |
71 | ///
72 | /// Gets or sets the date after the task was finished to filter.
73 | ///
74 | public DateTime? AfterFinishedAt { get; set; }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/Meilisearch/Converters/AlwaysIncludeEmptyObjectConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Reflection;
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 |
7 | namespace Meilisearch.Converters
8 | {
9 | ///
10 | /// Always include property in json. MultiSearchFederationOptions will be serialized as "{}"
11 | ///
12 | public class MultiSearchFederationOptionsConverter : JsonConverter
13 | {
14 | ///
15 | /// Would override the default read logic, but here we use the default
16 | ///
17 | ///
18 | ///
19 | ///
20 | ///
21 | public override MultiSearchFederationOptions Read(ref Utf8JsonReader reader, Type typeToConvert,
22 | JsonSerializerOptions options)
23 | {
24 | return JsonSerializer.Deserialize(ref reader, options);
25 | }
26 |
27 | ///
28 | /// Write json for MultiSearchFederationOptions and include it always as empty object
29 | ///
30 | ///
31 | ///
32 | ///
33 | public override void Write(Utf8JsonWriter writer, MultiSearchFederationOptions value,
34 | JsonSerializerOptions options)
35 | {
36 | if (value == null || !HasAnyValueSet(value))
37 | {
38 | WriteEmptyObject(writer);
39 | }
40 | else
41 | {
42 | JsonSerializer.Serialize(writer, value);
43 | }
44 | }
45 | private static void WriteEmptyObject(Utf8JsonWriter writer)
46 | {
47 | writer.WriteStartObject();
48 | writer.WriteEndObject();
49 | }
50 |
51 | private bool HasAnyValueSet(MultiSearchFederationOptions value)
52 | {
53 | foreach (var property in
54 | value.GetType().GetProperties(BindingFlags.Public | BindingFlags.Instance))
55 | {
56 | var propertyValue = property.GetValue(value);
57 | var defaultValue = GetDefaultValue(property.PropertyType);
58 |
59 | if (!Equals(propertyValue, defaultValue))
60 | {
61 | return true;
62 | }
63 | }
64 | return false;
65 | }
66 |
67 | private object GetDefaultValue(Type type)
68 | {
69 | return type.IsValueType ? Activator.CreateInstance(type) : null;
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/src/Meilisearch/Index.Embedders.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Net.Http.Json;
3 | using System.Threading;
4 | using System.Threading.Tasks;
5 |
6 | using Meilisearch.Extensions;
7 |
8 | namespace Meilisearch
9 | {
10 | public partial class Index
11 | {
12 | ///
13 | /// Gets the embedders setting.
14 | ///
15 | /// The cancellation token for this call.
16 | /// Returns the embedders setting.
17 | public async Task> GetEmbeddersAsync(CancellationToken cancellationToken = default)
18 | {
19 | return await _http
20 | .GetFromJsonAsync>(
21 | $"indexes/{Uid}/settings/embedders",
22 | cancellationToken: cancellationToken)
23 | .ConfigureAwait(false);
24 | }
25 |
26 | ///
27 | /// Updates the embedders setting.
28 | ///
29 | /// Collection of embedders.
30 | /// The cancellation token for this call.
31 | /// Returns the task info of the asynchronous task.
32 | public async Task UpdateEmbeddersAsync(Dictionary embedders, CancellationToken cancellationToken = default)
33 | {
34 | var responseMessage =
35 | await _http.PatchAsJsonAsync(
36 | $"indexes/{Uid}/settings/embedders",
37 | embedders,
38 | Constants.JsonSerializerOptionsRemoveNulls,
39 | cancellationToken: cancellationToken)
40 | .ConfigureAwait(false);
41 |
42 | return await responseMessage.Content
43 | .ReadFromJsonAsync(cancellationToken: cancellationToken)
44 | .ConfigureAwait(false);
45 | }
46 |
47 | ///
48 | /// Resets the embedders setting.
49 | ///
50 | /// The cancellation token for this call.
51 | /// Returns the task info of the asynchronous task.
52 | public async Task ResetEmbeddersAsync(CancellationToken cancellationToken = default)
53 | {
54 | var response = await _http
55 | .DeleteAsync($"indexes/{Uid}/settings/embedders", cancellationToken)
56 | .ConfigureAwait(false);
57 |
58 | return await response.Content
59 | .ReadFromJsonAsync(cancellationToken: cancellationToken)
60 | .ConfigureAwait(false);
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/Meilisearch/SimilarDocumentsQuery.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Search query for similar documents.
8 | ///
9 | public class SimilarDocumentsQuery
10 | {
11 | ///
12 | /// Creates a new instance of the class.
13 | ///
14 | ///
15 | public SimilarDocumentsQuery(string id)
16 | {
17 | if (string.IsNullOrEmpty(id))
18 | {
19 | throw new ArgumentNullException(nameof(id));
20 | }
21 |
22 | Id = id;
23 | }
24 |
25 | ///
26 | /// Gets the document id.
27 | ///
28 | [JsonPropertyName("id")]
29 | public string Id { get; }
30 |
31 | ///
32 | /// Gets or sets the embedder.
33 | ///
34 | [JsonPropertyName("embedder")]
35 | public string Embedder { get; set; }
36 |
37 | ///
38 | /// Gets or sets the attributes to retrieve.
39 | ///
40 | [JsonPropertyName("attributesToRetrieve")]
41 | public string[] AttributesToRetrieve { get; set; } = new[] { "*" };
42 |
43 | ///
44 | /// Gets or sets the offset.
45 | ///
46 | [JsonPropertyName("offset")]
47 | public int Offset { get; set; } = 0;
48 |
49 | ///
50 | /// Gets or sets the limit.
51 | ///
52 | [JsonPropertyName("limit")]
53 | public int Limit { get; set; } = 20;
54 |
55 | ///
56 | /// Gets or sets the filter.
57 | ///
58 | [JsonPropertyName("filter")]
59 | public dynamic Filter { get; set; }
60 |
61 | ///
62 | /// Gets or sets whether to show the ranking score.
63 | ///
64 | [JsonPropertyName("showRankingScore")]
65 | public bool ShowRankingScore { get; set; }
66 |
67 | ///
68 | /// Gets or sets whether to show the ranking score details.
69 | ///
70 | [JsonPropertyName("showRankingScoreDetails")]
71 | public bool ShowRankingScoreDetails { get; set; }
72 |
73 | ///
74 | /// Gets or sets the ranking score threshold.
75 | ///
76 | [JsonPropertyName("rankingScoreThreshold")]
77 | public decimal? RankingScoreThreshold { get; set; }
78 |
79 | ///
80 | /// Gets or sets whether to retrieve the vectors.
81 | ///
82 | [JsonPropertyName("retrieveVectors")]
83 | public bool RetrieveVectors { get; set; }
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/Meilisearch/TaskResource.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text.Json.Serialization;
4 |
5 | namespace Meilisearch
6 | {
7 | ///
8 | /// Information of the regarding a task.
9 | ///
10 | public class TaskResource
11 | {
12 | public TaskResource(int uid, string indexUid, TaskInfoStatus status, TaskInfoType type,
13 | IReadOnlyDictionary details, IReadOnlyDictionary error, string duration, DateTime enqueuedAt,
14 | DateTime? startedAt, DateTime? finishedAt)
15 | {
16 | Uid = uid;
17 | IndexUid = indexUid;
18 | Status = status;
19 | Type = type;
20 | Details = details;
21 | Error = error;
22 | Duration = duration;
23 | EnqueuedAt = enqueuedAt;
24 | StartedAt = startedAt;
25 | FinishedAt = finishedAt;
26 | }
27 |
28 | ///
29 | /// The unique sequential identifier of the task.
30 | ///
31 | [JsonPropertyName("uid")]
32 | public int Uid { get; }
33 |
34 | ///
35 | /// The unique index identifier.
36 | ///
37 | [JsonPropertyName("indexUid")]
38 | public string IndexUid { get; }
39 |
40 | ///
41 | /// The status of the task. Possible values are enqueued, processing, succeeded, failed.
42 | ///
43 | [JsonPropertyName("status")]
44 | public TaskInfoStatus Status { get; }
45 |
46 | ///
47 | /// The type of task.
48 | ///
49 | [JsonPropertyName("type")]
50 | public TaskInfoType Type { get; }
51 |
52 | ///
53 | /// Detailed information on the task payload.
54 | ///
55 | [JsonPropertyName("details")]
56 | public IReadOnlyDictionary Details { get; }
57 |
58 | ///
59 | /// Error details and context. Only present when a task has the failed status.
60 | ///
61 | [JsonPropertyName("error")]
62 | public IReadOnlyDictionary Error { get; }
63 |
64 | ///
65 | /// The total elapsed time the task spent in the processing state, in ISO 8601 format.
66 | ///
67 | [JsonPropertyName("duration")]
68 | public string Duration { get; }
69 |
70 | ///
71 | /// The date and time when the task was first enqueued, in RFC 3339 format.
72 | ///
73 | [JsonPropertyName("enqueuedAt")]
74 | public DateTime EnqueuedAt { get; }
75 |
76 | ///
77 | /// The date and time when the task began processing, in RFC 3339 format.
78 | ///
79 | [JsonPropertyName("startedAt")]
80 | public DateTime? StartedAt { get; }
81 |
82 | ///
83 | /// The date and time when the task finished processing, whether failed or succeeded, in RFC 3339 format.
84 | ///
85 | [JsonPropertyName("finishedAt")]
86 | public DateTime? FinishedAt { get; }
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Datasets/songs_custom_delimiter.csv:
--------------------------------------------------------------------------------
1 | id;title;album;artist;genre;country;released;duration;released-timestamp;duration-float
2 | 702481615;Armatage Shanks;Dookie: The Ultimate Critical Review;Green Day;Rock;Europe;2005;;1104537600;
3 | 888221515;Old Folks;Six Classic Albums Plus Bonus Tracks;Harold Land;Jazz;Europe;2013;6:36;1356998400;6.36
4 | 1382413601;คำขอร้อง;สำเนียงคนจันทร์ / เอาเถอะถ้าเห็นเขาดีกว่า;อิทธิพล บำรุงกุล;"Folk; World; & Country";Thailand;;;;
5 | 190889300;Track 1;Summer Breeze;Dreas;Funk / Soul;US;2008;18:56;1199145600;18.56
6 | 813645611;Slave (Alternative Version);Honky Château;Elton John;Rock;Europe;;2:53;;2.5300000000000002
7 | 394018506;Sex & Geld;Trackz Für Den Index;Mafia Clikk;Hip Hop;Germany;2006;5:02;1136073600;5.02
8 | 1522481803;Pisciaunella;Don Pepp U Pacce;Giovanni Russo (2);"Folk; World; & Country";Italy;1980;;315532800;
9 | 862296713;不知;Kiss 2001 Hong Kong Live Concert;Various;Electronic;Hong Kong;2002-04-13;;1018656000;
10 | 467946423;Rot;Be Quick Or Be Dead Vol. 3;Various;Electronic;Serbia;2013-06-20;1:00;1371686400;1
11 | 1323854803;"Simulation Project 1; ツキハナ「Moonflower」";Unlimited Dream Company;Amun Dragoon;Electronic;US;2018-04-10;2:44;1523318400;2.44
12 | 235115704;Doctor Vine;The Big F;The Big F;Rock;US;1989;5:29;599616000;5.29
13 | 249025232;"Ringel; Ringel; Reihe";Kinderlieder ABC - Der Bielefelder Kinderchor Singt 42 Lieder Von A-Z;Der Bielefelder Kinderchor;Children's;Germany;1971;;31536000;
14 | 710094000;Happy Safari = Safari Feliz;Safari Swings Again = El Safari Sigue En Su Swing;Bert Kaempfert & His Orchestra;Jazz;Argentina;1977;2:45;220924800;2.45
15 | 538632700;Take Me Up;Spring;Various;Electronic;US;2000;3:06;946684800;3.06
16 | 1556505508;Doin To Me ( Radio Version );Say My Name;Netta Dogg;Hip Hop;US;2005;;1104537600;
17 | 1067031900;Concerto For Balloon & Orchestra / Concerto For Synthesizer & Orchestra;Concerto For Balloon & Orchestra And Three Overtures;Stanyan String & Wind Ensemble;Classical;US;1977;;220924800;
18 | 137251914;"I Love The Nightlife (Disco 'Round) (Real Rapino 7"" Mix)";The Adventures Of Priscilla: Queen Of The Desert - Original Motion Picture Soundtrack;Various;Stage & Screen;US;1994;3:31;757382400;3.31
19 | 554983904;Walking On The Moon;Certifiable (Live In Buenos Aires);The Police;Rock;Malaysia;2008-11-00;;1225497600;
20 | 557616002;Two Soldiers;Jerry Garcia / David Grisman;David Grisman;"Folk; World; & Country";US;2014-04-00;4:24;1396310400;4.24
21 | 878936809;When You Gonna Learn;Live At Firenze 93;Jamiroquai;Funk / Soul;France;2004;13:01;1072915200;13.01
22 | 368960707;Sardo D.O.C.;Sardinia Pride Compilation Vol.2;Various;Hip Hop;Italy;2012-06-22;4:41;1340323200;4.41
23 | 1416041312;Sympathy For The Devil;Under Cover;Ozzy Osbourne;Rock;Russia;2005;7:11;1104537600;7.11
24 | 1260509200;Grosse Overturen;noxious effects garanty;Nocif (3);Rock;France;1990;;631152000;
25 | 1466381324;Πρινιώτης;Μουσικά Πατήματα Της Κρήτης;Αντώνης Μαρτσάκης;"Folk; World; & Country";Greece;2019;;1546300800;
26 | 256009724;Here I Stand And Face The Rain (Demo);Hunting High And Low;a-ha;Electronic;UK & Europe;2010-07-23;;1279843200;
27 | 565253008;Born Free;At His Best Goldfinger;The Royal Philharmonic Orchestra;Blues;Japan;1976;;189302400;
28 | 492519701;Others;Where Did She Go;Stephan Sulke;Rock;US;1965;2:43;-157766400;2.43
29 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/Datasets.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text.Json;
4 | using System.Text.Json.Serialization;
5 |
6 | namespace Meilisearch.Tests
7 | {
8 | internal static class Datasets
9 | {
10 | private static readonly string BasePath = Path.Combine(Directory.GetCurrentDirectory(), "Datasets");
11 | public static readonly string SmallMoviesJsonPath = Path.Combine(BasePath, "small_movies.json");
12 | public static readonly string SongsCsvPath = Path.Combine(BasePath, "songs.csv");
13 | public static readonly string SongsCsvCustomDelimiterPath = Path.Combine(BasePath, "songs_custom_delimiter.csv");
14 | public static readonly string SongsNdjsonPath = Path.Combine(BasePath, "songs.ndjson");
15 |
16 | public static readonly string MoviesWithStringIdJsonPath = Path.Combine(BasePath, "movies_with_string_id.json");
17 | public static readonly string MoviesForFacetingJsonPath = Path.Combine(BasePath, "movies_for_faceting.json");
18 | public static readonly string MoviesForVectorJsonPath = Path.Combine(BasePath, "movies_for_vector.json");
19 | public static readonly string MoviesWithIntIdJsonPath = Path.Combine(BasePath, "movies_with_int_id.json");
20 | public static readonly string MoviesWithInfoJsonPath = Path.Combine(BasePath, "movies_with_info.json");
21 |
22 | public static readonly string ProductsForDistinctJsonPath = Path.Combine(BasePath, "products_for_distinct_search.json");
23 | }
24 |
25 | public class DatasetSmallMovie
26 | {
27 | public string Id { get; set; }
28 | public string Title { get; set; }
29 | public string Poster { get; set; }
30 | public string Overview { get; set; }
31 | [JsonPropertyName("release_date")]
32 | [JsonConverter(typeof(UnixEpochDateTimeConverter))]
33 | public DateTime ReleaseDate { get; set; }
34 | public string Genre { get; set; }
35 | }
36 |
37 | public class DatasetSong
38 | {
39 | public string Id { get; set; }
40 | public string Title { get; set; }
41 | public string Album { get; set; }
42 | public string Artist { get; set; }
43 | public string Genre { get; set; }
44 | public string Country { get; set; }
45 | public string Released { get; set; }
46 | public string Duration { get; set; }
47 | [JsonPropertyName("released-timestamp")]
48 | public long? ReleasedTimestamp { get; set; }
49 | [JsonPropertyName("duration-float")]
50 | public double? DurationFloat { get; set; }
51 | }
52 |
53 | sealed class UnixEpochDateTimeConverter : JsonConverter
54 | {
55 | static readonly DateTime s_epoch = new DateTime(1970, 1, 1, 0, 0, 0);
56 |
57 | public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
58 | {
59 | var unixTime = reader.GetInt64();
60 | return s_epoch.AddMilliseconds(unixTime);
61 | }
62 |
63 | public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
64 | {
65 | var unixTime = Convert.ToInt64((value - s_epoch).TotalMilliseconds);
66 | writer.WriteNumberValue(unixTime);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/Meilisearch/SearchResult.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Wrapper for Search Results.
8 | ///
9 | /// Hit type.
10 | public class SearchResult : ISearchable
11 | {
12 | ///
13 | /// Create a new search result where the documents are of type
14 | ///
15 | ///
16 | ///
17 | ///
18 | ///
19 | ///
20 | ///
21 | ///
22 | ///
23 | ///
24 | ///
25 | public SearchResult(IReadOnlyCollection hits, int offset, int limit, int estimatedTotalHits,
26 | IReadOnlyDictionary> facetDistribution,
27 | int processingTimeMs, string query,
28 | IReadOnlyDictionary> matchesPosition,
29 | IReadOnlyDictionary facetStats,
30 | string indexUid)
31 | {
32 | Hits = hits;
33 | Offset = offset;
34 | Limit = limit;
35 | EstimatedTotalHits = estimatedTotalHits;
36 | FacetDistribution = facetDistribution;
37 | ProcessingTimeMs = processingTimeMs;
38 | Query = query;
39 | MatchesPosition = matchesPosition;
40 | FacetStats = facetStats;
41 | IndexUid = indexUid;
42 | }
43 |
44 | ///
45 | [JsonPropertyName("hits")]
46 | public IReadOnlyCollection Hits { get; }
47 |
48 | ///
49 | /// Number of documents skipped.
50 | ///
51 | [JsonPropertyName("offset")]
52 | public int Offset { get; }
53 |
54 | ///
55 | /// Number of documents to take.
56 | ///
57 | [JsonPropertyName("limit")]
58 | public int Limit { get; }
59 |
60 | ///
61 | /// Gets the estimated total number of hits returned by the search.
62 | ///
63 | [JsonPropertyName("estimatedTotalHits")]
64 | public int EstimatedTotalHits { get; }
65 |
66 | ///
67 | [JsonPropertyName("facetDistribution")]
68 | public IReadOnlyDictionary> FacetDistribution { get; }
69 |
70 | ///
71 | [JsonPropertyName("processingTimeMs")]
72 | public int ProcessingTimeMs { get; }
73 |
74 | ///
75 | [JsonPropertyName("query")]
76 | public string Query { get; }
77 |
78 | ///
79 | [JsonPropertyName("_matchesPosition")]
80 | public IReadOnlyDictionary> MatchesPosition { get; }
81 |
82 | ///
83 | [JsonPropertyName("facetStats")]
84 | public IReadOnlyDictionary FacetStats { get; }
85 |
86 | ///
87 | [JsonPropertyName("indexUid")]
88 | public string IndexUid { get; }
89 | }
90 | }
91 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/ServerConfigs/BaseUriServer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using Xunit;
4 |
5 | namespace Meilisearch.Tests.ServerConfigs
6 | {
7 | public class BaseUriServer
8 | {
9 | const string CollectionFixtureName = nameof(BaseUriServer);
10 | private const string MeilisearchTestAddress = "http://localhost:7700/";
11 |
12 | public class ConfigFixture : IndexFixture
13 | {
14 | public override string MeilisearchAddress()
15 | {
16 | return Environment.GetEnvironmentVariable("MEILISEARCH_URL") ?? MeilisearchTestAddress;
17 | }
18 | }
19 |
20 | [CollectionDefinition(CollectionFixtureName)]
21 | public class IndexCollection : ICollectionFixture
22 | {
23 | }
24 |
25 | [Collection(CollectionFixtureName)]
26 | public class DocumentTests : DocumentTests
27 | {
28 | public DocumentTests(ConfigFixture fixture) : base(fixture)
29 | {
30 | }
31 | }
32 |
33 | [Collection(CollectionFixtureName)]
34 | public class IndexTests : IndexTests
35 | {
36 | public IndexTests(ConfigFixture fixture) : base(fixture)
37 | {
38 | }
39 | }
40 |
41 | [Collection(CollectionFixtureName)]
42 | public class KeyTests : KeyTests
43 | {
44 | public KeyTests(ConfigFixture fixture) : base(fixture)
45 | {
46 | }
47 | }
48 |
49 | [Collection(CollectionFixtureName)]
50 | public class MeilisearchClientTests : MeilisearchClientTests
51 | {
52 | public MeilisearchClientTests(ConfigFixture fixture) : base(fixture)
53 | {
54 | }
55 | }
56 |
57 | [Collection(CollectionFixtureName)]
58 | public class SearchTests : SearchTests
59 | {
60 | public SearchTests(ConfigFixture fixture) : base(fixture)
61 | {
62 | }
63 | }
64 |
65 | [Collection(CollectionFixtureName)]
66 | public class MultiIndexSearchTests : MultiIndexSearchTests
67 | {
68 | public MultiIndexSearchTests(ConfigFixture fixture) : base(fixture)
69 | {
70 | }
71 | }
72 |
73 | [Collection(CollectionFixtureName)]
74 | public class FacetingSearchTests : FacetSearchTests
75 | {
76 | public FacetingSearchTests(ConfigFixture fixture) : base(fixture)
77 | {
78 | }
79 | }
80 |
81 | [Collection(CollectionFixtureName)]
82 | public class SettingsTests : SettingsTests
83 | {
84 | public SettingsTests(ConfigFixture fixture) : base(fixture)
85 | {
86 | }
87 | }
88 |
89 | [Collection(CollectionFixtureName)]
90 | public class TaskInfoTests : TaskInfoTests
91 | {
92 | public TaskInfoTests(ConfigFixture fixture) : base(fixture)
93 | {
94 | }
95 | }
96 |
97 | [Collection(CollectionFixtureName)]
98 | public class TenantTokenTests : TenantTokenTests
99 | {
100 | public TenantTokenTests(ConfigFixture fixture) : base(fixture)
101 | {
102 | }
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/ServerConfigs/ProxiedUriServer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | using Xunit;
4 |
5 | namespace Meilisearch.Tests.ServerConfigs
6 | {
7 | public class ProxiedUriServer
8 | {
9 | const string CollectionFixtureName = nameof(ProxiedUriServer);
10 | private const string MeilisearchTestAddress = "http://localhost:8080/api/";
11 |
12 | public class ConfigFixture : IndexFixture
13 | {
14 | public override string MeilisearchAddress()
15 | {
16 | return Environment.GetEnvironmentVariable("PROXIED_MEILISEARCH") ?? MeilisearchTestAddress;
17 | }
18 | }
19 |
20 | [CollectionDefinition(CollectionFixtureName)]
21 | public class IndexCollection : ICollectionFixture
22 | {
23 | }
24 |
25 | [Collection(CollectionFixtureName)]
26 | public class DocumentTests : DocumentTests
27 | {
28 | public DocumentTests(ConfigFixture fixture) : base(fixture)
29 | {
30 | }
31 | }
32 |
33 | [Collection(CollectionFixtureName)]
34 | public class IndexTests : IndexTests
35 | {
36 | public IndexTests(ConfigFixture fixture) : base(fixture)
37 | {
38 | }
39 | }
40 |
41 | [Collection(CollectionFixtureName)]
42 | public class KeyTests : KeyTests
43 | {
44 | public KeyTests(ConfigFixture fixture) : base(fixture)
45 | {
46 | }
47 | }
48 |
49 | [Collection(CollectionFixtureName)]
50 | public class MeilisearchClientTests : MeilisearchClientTests
51 | {
52 | public MeilisearchClientTests(ConfigFixture fixture) : base(fixture)
53 | {
54 | }
55 | }
56 |
57 | [Collection(CollectionFixtureName)]
58 | public class SearchTests : SearchTests
59 | {
60 | public SearchTests(ConfigFixture fixture) : base(fixture)
61 | {
62 | }
63 | }
64 |
65 | [Collection(CollectionFixtureName)]
66 | public class MultiIndexSearchTests : MultiIndexSearchTests
67 | {
68 | public MultiIndexSearchTests(ConfigFixture fixture) : base(fixture)
69 | {
70 | }
71 | }
72 |
73 | [Collection(CollectionFixtureName)]
74 | public class FacetingSearchTests : FacetSearchTests
75 | {
76 | public FacetingSearchTests(ConfigFixture fixture) : base(fixture)
77 | {
78 | }
79 | }
80 |
81 | [Collection(CollectionFixtureName)]
82 | public class SettingsTests : SettingsTests
83 | {
84 | public SettingsTests(ConfigFixture fixture) : base(fixture)
85 | {
86 | }
87 | }
88 |
89 | [Collection(CollectionFixtureName)]
90 | public class TaskInfoTests : TaskInfoTests
91 | {
92 | public TaskInfoTests(ConfigFixture fixture) : base(fixture)
93 | {
94 | }
95 | }
96 |
97 | [Collection(CollectionFixtureName)]
98 | public class TenantTokenTests : TenantTokenTests
99 | {
100 | public TenantTokenTests(ConfigFixture fixture) : base(fixture)
101 | {
102 | }
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/Meilisearch/PaginatedSearchResult.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Wrapper for Search Results with finite pagination.
8 | ///
9 | /// Hit type.
10 | public class PaginatedSearchResult : ISearchable
11 | {
12 | ///
13 | /// Creates a new paginated search result of type
14 | ///
15 | ///
16 | ///
17 | ///
18 | ///
19 | ///
20 | ///
21 | ///
22 | ///
23 | ///
24 | ///
25 | ///
26 | public PaginatedSearchResult(
27 | IReadOnlyCollection hits,
28 | int hitsPerPage,
29 | int page,
30 | int totalHits,
31 | int totalPages,
32 | IReadOnlyDictionary> facetDistribution,
33 | int processingTimeMs,
34 | string query,
35 | IReadOnlyDictionary> matchesPosition,
36 | IReadOnlyDictionary facetStats,
37 | string indexUid
38 | )
39 | {
40 | Hits = hits;
41 | HitsPerPage = hitsPerPage;
42 | Page = page;
43 | TotalHits = totalHits;
44 | TotalPages = totalPages;
45 | FacetDistribution = facetDistribution;
46 | ProcessingTimeMs = processingTimeMs;
47 | Query = query;
48 | MatchesPosition = matchesPosition;
49 | FacetStats = facetStats;
50 | IndexUid = indexUid;
51 | }
52 |
53 | ///
54 | /// Number of documents each page.
55 | ///
56 | [JsonPropertyName("hitsPerPage")]
57 | public int HitsPerPage { get; }
58 |
59 | ///
60 | /// Number of documents to take.
61 | ///
62 | [JsonPropertyName("page")]
63 | public int Page { get; }
64 |
65 | ///
66 | /// Total number of documents' pages.
67 | ///
68 | [JsonPropertyName("totalPages")]
69 | public int TotalPages { get; }
70 |
71 | ///
72 | [JsonPropertyName("hits")]
73 | public IReadOnlyCollection Hits { get; }
74 |
75 | ///
76 | /// Gets the total number of hits returned by the search.
77 | ///
78 | [JsonPropertyName("totalHits")]
79 | public int TotalHits { get; }
80 |
81 | ///
82 | [JsonPropertyName("facetDistribution")]
83 | public IReadOnlyDictionary> FacetDistribution { get; }
84 |
85 | ///
86 | [JsonPropertyName("processingTimeMs")]
87 | public int ProcessingTimeMs { get; }
88 |
89 | ///
90 | [JsonPropertyName("query")]
91 | public string Query { get; }
92 |
93 | ///
94 | [JsonPropertyName("_matchesPosition")]
95 | public IReadOnlyDictionary> MatchesPosition { get; }
96 |
97 | ///
98 | [JsonPropertyName("facetStats")]
99 | public IReadOnlyDictionary FacetStats { get; }
100 |
101 | ///
102 | [JsonPropertyName("indexUid")]
103 | public string IndexUid { get; }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/Meilisearch/Extensions/ObjectExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Reflection;
5 |
6 | namespace Meilisearch.Extensions
7 | {
8 | ///
9 | /// Meilisearch objects manipulation.
10 | ///
11 | internal static class ObjectExtensions
12 | {
13 | ///
14 | /// Transforms an Meilisearch object into a dictionary.
15 | ///
16 | /// Object to transform.
17 | /// Binding flags.
18 | /// Returns a dictionary.
19 | internal static IDictionary AsDictionary(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance)
20 | {
21 | return source.GetType().GetProperties(bindingAttr).Where(p => p.GetValue(source, null) != null).ToDictionary(
22 | propInfo => char.ToLowerInvariant(propInfo.Name[0]) + propInfo.Name.Substring(1),
23 | propInfo => propInfo.GetValue(source, null).ToString());
24 | }
25 |
26 | ///
27 | /// Transforms any object fields into an URL encoded query string.
28 | ///
29 | /// Object to transform.
30 | /// Binding flags.
31 | /// Uri to prepend before query string.
32 | /// Returns an url encoded query string.
33 | internal static string ToQueryString(this object source, BindingFlags bindingAttr = BindingFlags.DeclaredOnly | BindingFlags.Public | BindingFlags.Instance, string uri = "")
34 | {
35 | var values = new List();
36 | if (source != null)
37 | {
38 | foreach (var field in source.GetType().GetProperties(bindingAttr))
39 | {
40 | var value = field.GetValue(source, null);
41 | var key = Uri.EscapeDataString(char.ToLowerInvariant(field.Name[0]) + field.Name.Substring(1));
42 |
43 | if (value != null)
44 | {
45 | if (value is List stringValue)
46 | {
47 | values.Add(key + "=" + string.Join(",", stringValue));
48 | }
49 | else if (value is List intValue)
50 | {
51 | values.Add(key + "=" + string.Join(",", intValue));
52 | }
53 | else if (value is List taskInfoStatusValue)
54 | {
55 | values.Add(key + "=" + string.Join(",", taskInfoStatusValue.Select(x => x.ToString())));
56 | }
57 | else if (value is List taskInfoTypeValue)
58 | {
59 | values.Add(key + "=" + string.Join(",", taskInfoTypeValue.Select(x => x.ToString())));
60 | }
61 | else if (value is DateTime datetimeValue)
62 | {
63 | values.Add(key + "=" + Uri.EscapeDataString(datetimeValue.ToString("yyyy-MM-dd'T'HH:mm:ss.fffzzz")));
64 | }
65 | else
66 | {
67 | values.Add(key + "=" + Uri.EscapeDataString(value.ToString()));
68 | }
69 | }
70 | }
71 | }
72 |
73 | var queryString = string.Join("&", values);
74 | if (string.IsNullOrWhiteSpace(uri))
75 | {
76 | return queryString;
77 | }
78 |
79 | if (string.IsNullOrEmpty(queryString))
80 | {
81 | return uri;
82 | }
83 |
84 | return $"{uri}?{queryString}";
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/Meilisearch/Settings.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Setttings of an index.
8 | ///
9 | public class Settings
10 | {
11 | ///
12 | /// Gets or sets the ranking rules.
13 | ///
14 | [JsonPropertyName("rankingRules")]
15 | public IEnumerable RankingRules { get; set; }
16 |
17 | ///
18 | /// Gets or sets the distinct attribute.
19 | ///
20 | [JsonPropertyName("distinctAttribute")]
21 | public string DistinctAttribute { get; set; }
22 |
23 | ///
24 | /// Gets or sets the searchable attributes.
25 | ///
26 | [JsonPropertyName("searchableAttributes")]
27 | public IEnumerable SearchableAttributes { get; set; }
28 |
29 | ///
30 | /// Gets or sets the displayed attributes.
31 | ///
32 | [JsonPropertyName("displayedAttributes")]
33 | public IEnumerable DisplayedAttributes { get; set; }
34 |
35 | ///
36 | /// Gets or sets the stop-words list.
37 | ///
38 | [JsonPropertyName("stopWords")]
39 | public IEnumerable StopWords { get; set; }
40 |
41 | ///
42 | /// Gets or sets the synonyms list.
43 | ///
44 | [JsonPropertyName("synonyms")]
45 | public Dictionary> Synonyms { get; set; }
46 |
47 | ///
48 | /// Gets or sets the non separator tokens list.
49 | ///
50 | [JsonPropertyName("nonSeparatorTokens")]
51 | public List NonSeparatorTokens { get; set; }
52 |
53 | ///
54 | /// Gets or sets the separator tokens list.
55 | ///
56 | [JsonPropertyName("separatorTokens")]
57 | public List SeparatorTokens { get; set; }
58 |
59 | ///
60 | /// Gets or sets the filterable attributes.
61 | ///
62 | [JsonPropertyName("filterableAttributes")]
63 | public IEnumerable FilterableAttributes { get; set; }
64 |
65 | ///
66 | /// Gets or sets the sortable attributes.
67 | ///
68 | [JsonPropertyName("sortableAttributes")]
69 | public IEnumerable SortableAttributes { get; set; }
70 |
71 | ///
72 | /// Gets or sets the typo tolerance attributes.
73 | ///
74 | [JsonPropertyName("typoTolerance")]
75 | public TypoTolerance TypoTolerance { get; set; }
76 |
77 | ///
78 | /// Gets or sets the faceting attributes.
79 | ///
80 | [JsonPropertyName("faceting")]
81 | public Faceting Faceting { get; set; }
82 |
83 | ///
84 | /// Gets or sets the pagination attributes.
85 | ///
86 | [JsonPropertyName("pagination")]
87 | public Pagination Pagination { get; set; }
88 |
89 | ///
90 | /// Gets or sets the dictionary object.
91 | ///
92 | [JsonPropertyName("dictionary")]
93 | public IEnumerable Dictionary { get; set; }
94 |
95 | ///
96 | /// Gets or sets the proximity precision attribute.
97 | ///
98 | [JsonPropertyName("proximityPrecision")]
99 | public string ProximityPrecision { get; set; }
100 |
101 | ///
102 | /// Gets or sets the searchCutoffMs attribute.
103 | ///
104 | [JsonPropertyName("searchCutoffMs")]
105 | public int? SearchCutoffMs { get; set; }
106 |
107 | ///
108 | /// Gets or sets the embeddings attribute.
109 | ///
110 | [JsonPropertyName("embedders")]
111 | public Dictionary Embedders { get; set; }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/Meilisearch/Meilisearch.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | Library
6 | MeiliSearch
7 | 0.18.0
8 | .NET wrapper for Meilisearch, an open-source search engine
9 | https://github.com/meilisearch/meilisearch-dotnet
10 | meilisearch;dotnet;sdk;search-engine;search;instant-search
11 | Meilisearch
12 | https://meilisearch.com
13 | logo.png
14 | README.md
15 | MIT
16 | True
17 |
18 |
19 | $(NoWarn);1591
20 |
21 |
22 | true
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 | <_Parameter1>Meilisearch.Tests
37 |
38 |
39 |
40 |
41 | Index.cs
42 |
43 |
44 | Index.cs
45 |
46 |
47 | Index.cs
48 |
49 |
50 | Index.cs
51 |
52 |
53 | Index.cs
54 |
55 |
56 | Index.cs
57 |
58 |
59 | Index.cs
60 |
61 |
62 | Index.cs
63 |
64 |
65 | Index.cs
66 |
67 |
68 | Index.cs
69 |
70 |
71 | Index.cs
72 |
73 |
74 | Index.cs
75 |
76 |
77 | Index.cs
78 |
79 |
80 | Index.cs
81 |
82 |
83 | Index.cs
84 |
85 |
86 | Index.cs
87 |
88 |
89 | Index.cs
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/src/Meilisearch/TaskInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text.Json.Serialization;
4 |
5 | using Meilisearch.Converters;
6 |
7 | namespace Meilisearch
8 | {
9 | ///
10 | /// Information of the regarding a task.
11 | ///
12 | public class TaskInfo
13 | {
14 | public TaskInfo(int taskUid, string indexUid, TaskInfoStatus status, TaskInfoType type,
15 | IReadOnlyDictionary details, IReadOnlyDictionary error, string duration, DateTime enqueuedAt,
16 | DateTime? startedAt, DateTime? finishedAt)
17 | {
18 | TaskUid = taskUid;
19 | IndexUid = indexUid;
20 | Status = status;
21 | Type = type;
22 | Details = details;
23 | Error = error;
24 | Duration = duration;
25 | EnqueuedAt = enqueuedAt;
26 | StartedAt = startedAt;
27 | FinishedAt = finishedAt;
28 | }
29 |
30 | ///
31 | /// The unique sequential identifier of the task.
32 | ///
33 | [JsonPropertyName("taskUid")]
34 | public int TaskUid { get; }
35 |
36 | ///
37 | /// The unique index identifier.
38 | ///
39 | [JsonPropertyName("indexUid")]
40 | public string IndexUid { get; }
41 |
42 | ///
43 | /// The status of the task. Possible values are enqueued, processing, succeeded, failed.
44 | ///
45 | [JsonPropertyName("status")]
46 | public TaskInfoStatus Status { get; }
47 |
48 | ///
49 | /// The type of task.
50 | ///
51 | [JsonPropertyName("type")]
52 | public TaskInfoType Type { get; }
53 |
54 | ///
55 | /// Detailed information on the task payload.
56 | ///
57 | [JsonPropertyName("details")]
58 | public IReadOnlyDictionary Details { get; }
59 |
60 | ///
61 | /// Error details and context. Only present when a task has the failed status.
62 | ///
63 | [JsonPropertyName("error")]
64 | public IReadOnlyDictionary Error { get; }
65 |
66 | ///
67 | /// The total elapsed time the task spent in the processing state, in ISO 8601 format.
68 | ///
69 | [JsonPropertyName("duration")]
70 | public string Duration { get; }
71 |
72 | ///
73 | /// The date and time when the task was first enqueued, in RFC 3339 format.
74 | ///
75 | [JsonPropertyName("enqueuedAt")]
76 | public DateTime EnqueuedAt { get; }
77 |
78 | ///
79 | /// The date and time when the task began processing, in RFC 3339 format.
80 | ///
81 | [JsonPropertyName("startedAt")]
82 | public DateTime? StartedAt { get; }
83 |
84 | ///
85 | /// The date and time when the task finished processing, whether failed or succeeded, in RFC 3339 format.
86 | ///
87 | [JsonPropertyName("finishedAt")]
88 | public DateTime? FinishedAt { get; }
89 |
90 | ///
91 | /// A taskUid who canceled the current task.
92 | ///
93 | [JsonPropertyName("canceledBy")]
94 | public int? CanceledBy { get; }
95 | }
96 |
97 | [JsonConverter(typeof(JsonStringEnumConverter))]
98 | public enum TaskInfoStatus
99 | {
100 | Enqueued,
101 | Processing,
102 | Succeeded,
103 | Failed,
104 | Canceled
105 | }
106 |
107 | [JsonConverter(typeof(TaskInfoTypeConverter))]
108 | public enum TaskInfoType
109 | {
110 | IndexCreation,
111 | IndexUpdate,
112 | IndexDeletion,
113 | DocumentAdditionOrUpdate,
114 | DocumentDeletion,
115 | SettingsUpdate,
116 | DumpCreation,
117 | TaskCancelation,
118 | SnapshotCreation,
119 | TaskDeletion,
120 | IndexSwap,
121 | Unknown
122 | }
123 | }
124 |
--------------------------------------------------------------------------------
/tests/Meilisearch.Tests/TaskInfoTests.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 |
5 | using FluentAssertions;
6 |
7 | using Meilisearch.QueryParameters;
8 |
9 | using Xunit;
10 |
11 | namespace Meilisearch.Tests
12 | {
13 | public abstract class TaskInfoTests : IAsyncLifetime where TFixture : IndexFixture
14 | {
15 | private readonly MeilisearchClient _client;
16 | private Index _index;
17 | private readonly TFixture _fixture;
18 |
19 | public TaskInfoTests(TFixture fixture)
20 | {
21 | _fixture = fixture;
22 | _client = fixture.DefaultClient;
23 | }
24 |
25 | public async Task InitializeAsync()
26 | {
27 | await _fixture.DeleteAllIndexes(); // Test context cleaned for each [Fact]
28 | _index = await _fixture.SetUpBasicIndex("BasicIndex-TaskInfoTests");
29 | }
30 |
31 | public Task DisposeAsync() => Task.CompletedTask;
32 |
33 | [Fact]
34 | public async Task GetMultipleTaskInfoFromClient()
35 | {
36 | await _index.AddDocumentsAsync(new[] { new Movie { Id = "1" } });
37 | var taskResponse = await _client.GetTasksAsync();
38 | var tasks = taskResponse.Results;
39 | tasks.Count().Should().BeGreaterOrEqualTo(1);
40 | }
41 |
42 | [Fact]
43 | public async Task GetMultipleTaskInfoFromIndex()
44 | {
45 | await _index.AddDocumentsAsync(new[] { new Movie { Id = "1" } });
46 | var taskResponse = await _index.GetTasksAsync();
47 | var tasks = taskResponse.Results.Where(t => t.IndexUid != _index.Uid);
48 |
49 | taskResponse.Results.Count().Should().BeGreaterOrEqualTo(1);
50 | Assert.Empty(tasks);
51 | }
52 |
53 | [Fact]
54 | public async Task GetMultipleTaskInfoWithLimitFromClient()
55 | {
56 | await _index.AddDocumentsAsync(new[] { new Movie { Id = "1" } });
57 | var tasks = await _client.GetTasksAsync(new TasksQuery { Limit = 1 });
58 |
59 | tasks.Results.Count().Should().BeGreaterOrEqualTo(1);
60 | Assert.Equal(1, tasks.Limit);
61 | }
62 |
63 | [Fact]
64 | public async Task GetMultipleTaskInfoWithQueryParameters()
65 | {
66 | await _index.AddDocumentsAsync(new[] { new Movie { Id = "1" } });
67 | var taskResponse = await _index.GetTasksAsync(new TasksQuery { Limit = 1, IndexUids = new List { _index.Uid } });
68 |
69 | taskResponse.Results.Count().Should().BeGreaterOrEqualTo(1);
70 | taskResponse.Total.Should().BeGreaterThan(0);
71 | }
72 |
73 | [Fact]
74 | public async Task GetOneTaskInfo()
75 | {
76 | var task = await _index.AddDocumentsAsync(new[] { new Movie { Id = "2" } });
77 | var fetchedTask = await _index.GetTaskAsync(task.TaskUid);
78 | fetchedTask.Should().NotBeNull();
79 | fetchedTask.Uid.Should().BeGreaterOrEqualTo(0);
80 | }
81 |
82 | [Fact]
83 | public async Task DefaultWaitForTask()
84 | {
85 | var task = await _index.AddDocumentsAsync(new[] { new Movie { Id = "3" } });
86 | var finishedTask = await _index.WaitForTaskAsync(task.TaskUid);
87 | Assert.Equal(finishedTask.Uid, task.TaskUid);
88 | Assert.Equal(TaskInfoStatus.Succeeded, finishedTask.Status);
89 | }
90 |
91 | [Fact]
92 | public async Task CustomWaitForTask()
93 | {
94 | var task = await _index.AddDocumentsAsync(new[] { new Movie { Id = "4" } });
95 | var finishedTask = await _index.WaitForTaskAsync(task.TaskUid, 10000.0, 20);
96 | Assert.Equal(finishedTask.Uid, task.TaskUid);
97 | Assert.Equal(TaskInfoStatus.Succeeded, finishedTask.Status);
98 | }
99 |
100 | [Fact]
101 | public async Task WaitForTaskWithException()
102 | {
103 | var task = await _index.AddDocumentsAsync(new[] { new Movie { Id = "5" } });
104 | await Assert.ThrowsAsync(() => _index.WaitForTaskAsync(task.TaskUid, 0.0, 20));
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/Meilisearch/SearchQueryBase.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Text.Json.Serialization;
3 |
4 | namespace Meilisearch
5 | {
6 | ///
7 | /// Base properties of search query
8 | ///
9 | public class SearchQueryBase
10 | {
11 | ///
12 | /// The id of the index
13 | ///
14 | [JsonPropertyName("indexUid")]
15 | public string IndexUid { get; set; }
16 |
17 | ///
18 | /// Gets or sets query string.
19 | ///
20 | [JsonPropertyName("q")]
21 | public string Q { get; set; }
22 |
23 | ///
24 | /// Gets or sets the filter to apply to the query.
25 | ///
26 | [JsonPropertyName("filter")]
27 | public dynamic Filter { get; set; }
28 |
29 | ///
30 | /// Gets or sets attributes to retrieve.
31 | ///
32 | [JsonPropertyName("attributesToRetrieve")]
33 | public IEnumerable AttributesToRetrieve { get; set; }
34 |
35 | ///
36 | /// Gets or sets attributes to crop.
37 | ///
38 | [JsonPropertyName("attributesToCrop")]
39 | public IEnumerable AttributesToCrop { get; set; }
40 |
41 | ///
42 | /// Gets or sets attributes to search on.
43 | ///
44 | [JsonPropertyName("attributesToSearchOn")]
45 | public IEnumerable AttributesToSearchOn { get; set; }
46 |
47 | ///
48 | /// Gets or sets length used to crop field values.
49 | ///
50 | [JsonPropertyName("cropLength")]
51 | public int? CropLength { get; set; }
52 |
53 | ///
54 | /// Gets or sets attributes to highlight.
55 | ///
56 | [JsonPropertyName("attributesToHighlight")]
57 | public IEnumerable AttributesToHighlight { get; set; }
58 |
59 | ///
60 | /// Gets or sets the crop marker to apply before and/or after cropped part selected within an attribute defined in `attributesToCrop` parameter.
61 | ///
62 | [JsonPropertyName("cropMarker")]
63 | public string CropMarker { get; set; }
64 |
65 | ///
66 | /// Gets or sets the tag to put before the highlighted query terms.
67 | ///
68 | [JsonPropertyName("highlightPreTag")]
69 | public string HighlightPreTag { get; set; }
70 |
71 | ///
72 | /// Gets or sets the tag to put after the highlighted query terms.
73 | ///
74 | [JsonPropertyName("highlightPostTag")]
75 | public string HighlightPostTag { get; set; }
76 |
77 | ///
78 | /// Gets or sets the facets for the query.
79 | ///
80 | [JsonPropertyName("facets")]
81 | public IEnumerable Facets { get; set; }
82 |
83 | ///
84 | /// Gets or sets showMatchesPosition. It defines whether an object that contains information about the matches should be returned or not.
85 | ///
86 | [JsonPropertyName("showMatchesPosition")]
87 | public bool? ShowMatchesPosition { get; set; }
88 |
89 | ///
90 | /// Gets or sets the sorted attributes.
91 | ///
92 | [JsonPropertyName("sort")]
93 | public IEnumerable Sort { get; set; }
94 |
95 | ///
96 | /// Gets or sets the words matching strategy.
97 | ///
98 | [JsonPropertyName("matchingStrategy")]
99 | public string MatchingStrategy { get; set; }
100 |
101 | ///
102 | /// Gets or sets showRankingScore parameter. It defines whether the global ranking score of a document (between 0 and 1) is returned or not.
103 | ///
104 | [JsonPropertyName("showRankingScore")]
105 | public bool? ShowRankingScore { get; set; }
106 |
107 | ///
108 | /// Gets or sets showRankingScoreDetails parameter. It defines whether details on how the ranking score was computed are returned or not.
109 | ///
110 | [JsonPropertyName("showRankingScoreDetails")]
111 | public bool? ShowRankingScoreDetails { get; set; }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/src/Meilisearch/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Text;
5 |
6 | namespace Meilisearch.Extensions
7 | {
8 | internal static class StringExtensions
9 | {
10 | ///
11 | /// This method makes sure that the uri has a trailing slash.
12 | /// If not, it will append one silently.
13 | ///
14 | /// uri to Meilisearch server.
15 | /// A well formatted Uri
16 | /// Thrown when uri is not or whitespace.
17 | internal static Uri ToSafeUri(this string uri)
18 | {
19 | if (string.IsNullOrWhiteSpace(uri))
20 | {
21 | throw new ArgumentNullException(nameof(uri));
22 | }
23 |
24 | var trimmed = uri.Trim();
25 | return trimmed.EndsWith("/") ? new Uri(trimmed) : new Uri($"{trimmed}/");
26 | }
27 |
28 | ///
29 | /// Returns chunks from a CSV string.
30 | ///
31 | /// The CSV string to split.
32 | /// Size of the chunks.
33 | /// List of CSV string.
34 | /// Thrown if csvString is null.
35 | /// Throw if chunkSize is lower than 1.
36 | internal static IEnumerable GetCsvChunks(this string csvString, int chunkSize)
37 | {
38 | if (string.IsNullOrWhiteSpace(csvString))
39 | {
40 | throw new ArgumentNullException(nameof(csvString));
41 | }
42 |
43 | if (chunkSize < 1)
44 | {
45 | throw new ArgumentException("chunkSize value must be greater than 0", nameof(chunkSize));
46 | }
47 |
48 | using (var sr = new StringReader(csvString))
49 | {
50 | // We extract the CSV header on first line
51 | var csvHeader = sr.ReadLine();
52 |
53 | var sb = new StringBuilder();
54 | // We add the CSV header first on our chunck
55 | sb.AppendLine(csvHeader);
56 | var line = "";
57 | var lineNumber = 0;
58 | while ((line = sr.ReadLine()) != null)
59 | {
60 | sb.AppendLine(line);
61 | ++lineNumber;
62 |
63 | if (lineNumber % chunkSize == 0)
64 | {
65 | // We return our chunk, we clear our string builder and add back on first line the CSV header
66 | yield return sb.ToString();
67 | sb.Clear();
68 | sb.AppendLine(csvHeader);
69 | }
70 | }
71 |
72 | // After the last line we check if we have something to send
73 | if (lineNumber % chunkSize != 0)
74 | {
75 | yield return sb.ToString();
76 | }
77 | }
78 | }
79 |
80 | ///
81 | /// Returns chunks from a NDJSON string.
82 | ///
83 | /// The NDJSON string to split.
84 | /// Size of the chunks.
85 | /// List of NDJSON string.
86 | /// Thrown if ndjsonString is null.
87 | /// Throw if chunkSize is lower than 1.
88 | internal static IEnumerable GetNdjsonChunks(this string ndjsonString, int chunkSize)
89 | {
90 | if (string.IsNullOrWhiteSpace(ndjsonString))
91 | {
92 | throw new ArgumentNullException(nameof(ndjsonString));
93 | }
94 |
95 | if (chunkSize < 1)
96 | {
97 | throw new ArgumentException("chunkSize value must be greater than 0", nameof(chunkSize));
98 | }
99 |
100 | using (var sr = new StringReader(ndjsonString))
101 | {
102 | var sb = new StringBuilder();
103 | var line = "";
104 | var lineNumber = 0;
105 | while ((line = sr.ReadLine()) != null)
106 | {
107 | sb.AppendLine(line);
108 | ++lineNumber;
109 |
110 | if (lineNumber % chunkSize == 0)
111 | {
112 | // We return our chunk, we clear our string builder
113 | yield return sb.ToString();
114 | sb.Clear();
115 | }
116 | }
117 |
118 | // After the last line we check if we have something to send
119 | if (lineNumber % chunkSize != 0)
120 | {
121 | yield return sb.ToString();
122 | }
123 | }
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/src/Meilisearch/Extensions/HttpExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Net.Http;
3 | using System.Net.Http.Headers;
4 | using System.Net.Http.Json;
5 | using System.Text;
6 | using System.Text.Json;
7 | using System.Threading;
8 | using System.Threading.Tasks;
9 |
10 | namespace Meilisearch.Extensions
11 | {
12 | ///
13 | /// Class to communicate with the Meilisearch server without charset-utf-8 as Content-Type.
14 | ///
15 | internal static class HttpExtensions
16 | {
17 | ///
18 | /// Sends JSON payload using POST without "charset-utf-8" as Content-Type.
19 | ///
20 | /// HttpClient.
21 | /// Endpoint.
22 | /// Body sent.
23 | /// The cancellation token for this call.
24 | /// Type of the body to send.
25 | /// Returns the HTTP response from the Meilisearch server.
26 | internal static async Task PostJsonCustomAsync(this HttpClient client, string uri, T body, CancellationToken cancellationToken = default)
27 | {
28 | var payload = PrepareJsonPayload(body);
29 |
30 | return await client.PostAsync(uri, payload, cancellationToken).ConfigureAwait(false);
31 | }
32 |
33 | ///
34 | /// Sends JSON payload using POST without "charset-utf-8" as Content-Type and using JSON serializer options.
35 | ///
36 | /// HttpClient.
37 | /// Endpoint.
38 | /// Body sent.
39 | /// Json options for serialization.
40 | /// The cancellation token for this call.
41 | /// Type of the body to send.
42 | /// Returns the HTTP response from the Meilisearch server.
43 | internal static async Task PostJsonCustomAsync(this HttpClient client, string uri, T body, JsonSerializerOptions options, CancellationToken cancellationToken = default)
44 | {
45 | var payload = PrepareJsonPayload(body, options);
46 |
47 | return await client.PostAsync(uri, payload, cancellationToken).ConfigureAwait(false);
48 | }
49 |
50 | ///
51 | /// Sends JSON payload using PUT without "charset-utf-8" as Content-Type.
52 | ///
53 | /// HttpClient.
54 | /// Endpoint.
55 | /// Body sent.
56 | /// Json options for serialization.
57 | /// The cancellation token for this call.
58 | /// Type of the body to send.
59 | /// Returns the HTTP response from the Meilisearch server.
60 | internal static async Task PutJsonCustomAsync