diff --git a/.circleci/config.yml b/.circleci/config.yml index 5a077e4..5c538d8 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -13,7 +13,7 @@ executors: auth: username: $DOCKER_LOGIN password: $DOCKER_ACCESSTOKEN - - image: typesense/typesense:0.22.2 # For integration testing + - image: typesense/typesense:0.23.0 # For integration testing auth: username: $DOCKER_LOGIN password: $DOCKER_ACCESSTOKEN diff --git a/README.md b/README.md index 736f426..39ed946 100644 --- a/README.md +++ b/README.md @@ -297,7 +297,7 @@ var deleteSynonym = await typesenseClient.DeleteSynonym("Addresses", "Address_Sy ### Typesense API Errors -Typesense API exceptions in the [Typesense-api-errors](https://typesense.org/docs/0.22.2/api/api-errors.html) spec. +Typesense API exceptions in the [Typesense-api-errors](https://typesense.org/docs/0.23.0/api/api-errors.html) spec. | Type | Description | |:-------------------------------------------|:---------------------------------------------------------------------------| @@ -331,5 +331,5 @@ dotnet test --filter Category=Integration To enable running integration tests you can run Typesense in a docker container using the command below. ```sh -docker run -p 8108:8108 -v/tmp/data:/data typesense/typesense:0.22.2 --data-dir /data --api-key=key +docker run -p 8108:8108 -v/tmp/data:/data typesense/typesense:0.23.0 --data-dir /data --api-key=key ``` diff --git a/src/Typesense/Field.cs b/src/Typesense/Field.cs index d469f45..4ccff96 100644 --- a/src/Typesense/Field.cs +++ b/src/Typesense/Field.cs @@ -17,6 +17,8 @@ public record Field public bool? Optional { get; init; } [JsonPropertyName("index")] public bool? Index { get; init; } + [JsonPropertyName("sort")] + public bool? Sort { get; init; } public Field(string name, FieldType type) { @@ -39,7 +41,6 @@ public Field(string name, FieldType type, bool facet, bool? optional) Optional = optional; } - [JsonConstructor] public Field(string name, FieldType type, bool facet, bool? optional, bool? index) { Name = name; @@ -49,6 +50,22 @@ public Field(string name, FieldType type, bool facet, bool? optional, bool? inde Index = index; } + [JsonConstructor] + public Field(string name, + FieldType type, + bool facet, + bool? optional, + bool? index, + bool? sort) + { + Name = name; + Type = type; + Facet = facet; + Optional = optional; + Index = index; + Sort = sort; + } + [Obsolete("A better choice going forward is using the constructor with 'FieldType' enum instead.")] public Field(string name, string type, bool facet, bool optional = false, bool index = true) { diff --git a/src/Typesense/ImportType.cs b/src/Typesense/ImportType.cs index 3649def..8d315e8 100644 --- a/src/Typesense/ImportType.cs +++ b/src/Typesense/ImportType.cs @@ -4,5 +4,6 @@ public enum ImportType { Create, Upsert, - Update + Update, + Emplace } diff --git a/src/Typesense/SearchParameters.cs b/src/Typesense/SearchParameters.cs index d76d49c..38f4a05 100644 --- a/src/Typesense/SearchParameters.cs +++ b/src/Typesense/SearchParameters.cs @@ -190,12 +190,28 @@ public record SearchParameters /// public bool? PreSegmentedQuery { get; set; } + /// + /// Treat space as typo: search for q=basket ball if q=basketball is not found or vice-versa. + /// + public bool? SplitJoinTokens { get; set; } + /// /// If you have some overrides defined but want to disable all of them during /// query time, you can do that by setting this parameter to false /// public bool? EnableOverrides { get; set; } + /// + /// Control the number of words that Typesense considers for typo and prefix searching. + /// Default: 4 (or 10000 if exhaustive_search is enabled). + /// + public int? MaxCandiates { get; set; } + + /// + /// Controls the fuzziness of the facet query filter. + /// + public int? FacetQueryNumberTypos { get; set; } + [Obsolete("Use multi-arity constructor instead.")] public SearchParameters() { diff --git a/src/Typesense/TypesenseClient.cs b/src/Typesense/TypesenseClient.cs index afb7a78..e05ffff 100644 --- a/src/Typesense/TypesenseClient.cs +++ b/src/Typesense/TypesenseClient.cs @@ -170,6 +170,9 @@ public async Task> ImportDocuments( case ImportType.Upsert: path += "&action=upsert"; break; + case ImportType.Emplace: + path += "&action=emplace"; + break; default: throw new ArgumentException($"Could not handle {nameof(ImportType)} with name '{Enum.GetName(importType)}'", nameof(importType)); } @@ -442,6 +445,12 @@ private static string CreateUrlSearchParameters(SearchParameters searchParameter urlParameters += $"&pre_segmented_query={searchParameters.PreSegmentedQuery.Value.ToString().ToLowerInvariant()}"; if (searchParameters.EnableOverrides is not null) urlParameters += $"&enable_overrides={searchParameters.EnableOverrides.Value.ToString().ToLowerInvariant()}"; + if (searchParameters.SplitJoinTokens is not null) + urlParameters += $"&split_join_tokens={searchParameters.SplitJoinTokens.Value.ToString().ToLowerInvariant()}"; + if (searchParameters.MaxCandiates is not null) + urlParameters += $"&max_candidates={searchParameters.MaxCandiates.Value.ToString().ToLowerInvariant()}"; + if (searchParameters.FacetQueryNumberTypos is not null) + urlParameters += $"&facet_query_num_typos={searchParameters.FacetQueryNumberTypos.Value.ToString().ToLowerInvariant()}"; return urlParameters; } diff --git a/test/Typesense.Tests/TypesenseClientTests.cs b/test/Typesense.Tests/TypesenseClientTests.cs index fd0cf32..8c8ce82 100644 --- a/test/Typesense.Tests/TypesenseClientTests.cs +++ b/test/Typesense.Tests/TypesenseClientTests.cs @@ -39,9 +39,9 @@ public async Task Create_schema() 0, new List { - new Field("company_name", FieldType.String, false, false, true), - new Field("num_employees", FieldType.Int32, false, false, true), - new Field("country", FieldType.String, true, false, true), + new Field("company_name", FieldType.String, false, false, true, false), + new Field("num_employees", FieldType.Int32, false, false, true, true), + new Field("country", FieldType.String, true, false, true, false), }, "num_employees"); @@ -70,7 +70,7 @@ public async Task Create_schema_with_wildcard_field() 0, new List { - new Field(".*", FieldType.String, false, true, true), + new Field(".*", FieldType.String, false, true, true, false), }, ""); @@ -97,9 +97,9 @@ public async Task Retrieve_collection() 0, new List { - new Field("company_name", FieldType.String, false, false, true), - new Field("num_employees", FieldType.Int32, false, false, true), - new Field("country", FieldType.String, true, false, true), + new Field("company_name", FieldType.String, false, false, true, false), + new Field("num_employees", FieldType.Int32, false, false, true, true), + new Field("country", FieldType.String, true, false, true, false), }, "num_employees"); @@ -118,9 +118,9 @@ public async Task Retrieve_collections() 0, new List { - new Field("company_name", FieldType.String, false, false, true), - new Field("num_employees", FieldType.Int32, false, false, true), - new Field("country", FieldType.String, true, false, true), + new Field("company_name", FieldType.String, false, false, true, false), + new Field("num_employees", FieldType.Int32, false, false, true, true), + new Field("country", FieldType.String, true, false, true, false), }, "num_employees") }; @@ -137,9 +137,9 @@ public async Task Delete_collection() 0, new List { - new Field("company_name", FieldType.String, false, false, true), - new Field("num_employees", FieldType.Int32, false, false, true), - new Field("country", FieldType.String, true, false, true), + new Field("company_name", FieldType.String, false, false, true, false), + new Field("num_employees", FieldType.Int32, false, false, true, true), + new Field("country", FieldType.String, true, false, true, false), }, "num_employees"); @@ -226,7 +226,8 @@ public async Task Import_documents_create() } }; - var response = await _client.ImportDocuments("companies", companies, 40, ImportType.Create); + var response = await _client.ImportDocuments( + "companies", companies, 40, ImportType.Create); response.Should().BeEquivalentTo(expected); } @@ -296,6 +297,39 @@ public async Task Import_documents_upsert() response.Should().BeEquivalentTo(expected); } + [Fact, TestPriority(7)] + public async Task Import_documents_emplace() + { + var expected = new List + { + new ImportResponse(true), + new ImportResponse(true), + }; + + var companies = new List + { + new Company + { + Id = "125", + CompanyName = "Future Technology", + NumEmployees = 1232, + Country = "UK", + }, + new Company + { + Id = "126", + CompanyName = "Random Corp.", + NumEmployees = 531, + Country = "AU", + } + }; + + var response = await _client.ImportDocuments( + "companies", companies, 40, ImportType.Emplace); + + response.Should().BeEquivalentTo(expected); + } + [Fact, TestPriority(8)] public async Task Export_documents() {