diff --git a/CHANGELOG.md b/CHANGELOG.md
index b81dbf8c4e..6b5c3b9fac 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,9 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
- Moved `OpenSearch.Client` request classes into their respective namespaces to match those in `OpenSearch.Net` ([#200](https://github.com/opensearch-project/opensearch-net/pull/200), [#202](https://github.com/opensearch-project/opensearch-net/pull/202), [#203](https://github.com/opensearch-project/opensearch-net/pull/203), [#205](https://github.com/opensearch-project/opensearch-net/pull/205), [#206](https://github.com/opensearch-project/opensearch-net/pull/206), [#207](https://github.com/opensearch-project/opensearch-net/pull/207), [#208](https://github.com/opensearch-project/opensearch-net/pull/208), [#209](https://github.com/opensearch-project/opensearch-net/pull/209))
- Removed support for the `net461` target ([#256](https://github.com/opensearch-project/opensearch-net/pull/256))
+### Fixed
+- Fix highlight max_analyzer_offset field name to match with the one introduced in OpenSearch 2.2.0 ([#322](https://github.com/opensearch-project/opensearch-net/pull/322))
+
### Dependencies
- Bumps `Microsoft.CodeAnalysis.CSharp` from 4.2.0 to 4.6.0
- Bumps `Microsoft.TestPlatform.ObjectModel` from 17.5.0 to 17.6.3
@@ -88,4 +91,4 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
[Unreleased]: https://github.com/opensearch-project/opensearch-net/compare/v1.4.0...main
[1.4.0]: https://github.com/opensearch-project/opensearch-net/compare/v1.3.0...v1.4.0
-[1.3.0]: https://github.com/opensearch-project/opensearch-net/compare/v1.2.0...v1.3.0
\ No newline at end of file
+[1.3.0]: https://github.com/opensearch-project/opensearch-net/compare/v1.2.0...v1.3.0
diff --git a/src/OpenSearch.Client/Search/Search/Highlighting/Highlight.cs b/src/OpenSearch.Client/Search/Search/Highlighting/Highlight.cs
index f164dd6b3a..94b93bbb04 100644
--- a/src/OpenSearch.Client/Search/Search/Highlighting/Highlight.cs
+++ b/src/OpenSearch.Client/Search/Search/Highlighting/Highlight.cs
@@ -107,8 +107,9 @@ public interface IHighlight
/// If this setting is set to a non-negative value, the highlighting stops at this defined maximum limit, and the
/// rest of the text is not processed, thus not highlighted and no error is returned.
///
- [DataMember(Name ="max_analyzed_offset")]
- int? MaxAnalyzedOffset { get; set; }
+ /// Introduced in OpenSearch 2.2
+ [DataMember(Name ="max_analyzer_offset")]
+ int? MaxAnalyzerOffset { get; set; }
[DataMember(Name ="max_fragment_length")]
int? MaxFragmentLength { get; set; }
@@ -210,7 +211,7 @@ public class Highlight : IHighlight
public QueryContainer HighlightQuery { get; set; }
///
- public int? MaxAnalyzedOffset { get; set; }
+ public int? MaxAnalyzerOffset { get; set; }
///
public int? MaxFragmentLength { get; set; }
@@ -258,7 +259,7 @@ public class HighlightDescriptor : DescriptorBase, IHi
int? IHighlight.FragmentOffset { get; set; }
int? IHighlight.FragmentSize { get; set; }
QueryContainer IHighlight.HighlightQuery { get; set; }
- int? IHighlight.MaxAnalyzedOffset { get; set; }
+ int? IHighlight.MaxAnalyzerOffset { get; set; }
int? IHighlight.MaxFragmentLength { get; set; }
int? IHighlight.NoMatchSize { get; set; }
int? IHighlight.NumberOfFragments { get; set; }
@@ -323,8 +324,8 @@ public HighlightDescriptor HighlightQuery(Func, Q
///
public HighlightDescriptor BoundaryMaxScan(int? boundaryMaxScan) => Assign(boundaryMaxScan, (a, v) => a.BoundaryMaxScan = v);
- ///
- public HighlightDescriptor MaxAnalyzedOffset(int? maxAnalyzedOffset) => Assign(maxAnalyzedOffset, (a, v) => a.MaxAnalyzedOffset = v);
+ ///
+ public HighlightDescriptor MaxAnalyzerOffset(int? maxAnalyzerOffset) => Assign(maxAnalyzerOffset, (a, v) => a.MaxAnalyzerOffset = v);
///
public HighlightDescriptor MaxFragmentLength(int? maxFragmentLength) => Assign(maxFragmentLength, (a, v) => a.MaxFragmentLength = v);
diff --git a/src/OpenSearch.Client/Search/Search/Highlighting/HighlightField.cs b/src/OpenSearch.Client/Search/Search/Highlighting/HighlightField.cs
index 4e78137ed6..8702926752 100644
--- a/src/OpenSearch.Client/Search/Search/Highlighting/HighlightField.cs
+++ b/src/OpenSearch.Client/Search/Search/Highlighting/HighlightField.cs
@@ -116,6 +116,15 @@ public interface IHighlightField
[DataMember(Name = "matched_fields")]
Fields MatchedFields { get; set; }
+
+ ///
+ /// If this setting is set to a non-negative value, the highlighting stops at this defined maximum limit, and the
+ /// rest of the text is not processed, thus not highlighted and no error is returned.
+ ///
+ /// Introduced in OpenSearch 2.2
+ [DataMember(Name = "max_analyzer_offset")]
+ int? MaxAnalyzerOffset { get; set; }
+
[DataMember(Name = "max_fragment_length")]
int? MaxFragmentLength { get; set; }
@@ -191,6 +200,7 @@ public interface IHighlightField
///
[DataMember(Name = "type")]
Union Type { get; set; }
+
}
public class HighlightField : IHighlightField
@@ -228,6 +238,9 @@ public class HighlightField : IHighlightField
///
public Fields MatchedFields { get; set; }
+ ///
+ public int? MaxAnalyzerOffset { get; set; }
+
///
public int? MaxFragmentLength { get; set; }
@@ -273,6 +286,9 @@ public class HighlightFieldDescriptor : DescriptorBase MatchedFields(Func, IProm
public HighlightFieldDescriptor HighlightQuery(Func, QueryContainer> querySelector) =>
Assign(querySelector, (a, v) => a.HighlightQuery = v?.Invoke(new QueryContainerDescriptor()));
+ ///
+ public HighlightFieldDescriptor MaxAnalyzerOffset(int? maxAnalyzerOffset) => Assign(maxAnalyzerOffset, (a, v) => a.MaxAnalyzerOffset = v);
+
///
public HighlightFieldDescriptor MaxFragmentLength(int? maxFragmentLength) => Assign(maxFragmentLength, (a, v) => a.MaxFragmentLength = v);
diff --git a/tests/Tests/Search/Request/HighlightingUsageTestsWithMaxAnalyzerOffset.cs b/tests/Tests/Search/Request/HighlightingUsageTestsWithMaxAnalyzerOffset.cs
new file mode 100644
index 0000000000..63faf46347
--- /dev/null
+++ b/tests/Tests/Search/Request/HighlightingUsageTestsWithMaxAnalyzerOffset.cs
@@ -0,0 +1,316 @@
+/* SPDX-License-Identifier: Apache-2.0
+*
+* The OpenSearch Contributors require contributions made to
+* this file be licensed under the Apache-2.0 license or a
+* compatible open source license.
+*/
+/*
+* Modifications Copyright OpenSearch Contributors. See
+* GitHub history for details.
+*
+* Licensed to Elasticsearch B.V. under one or more contributor
+* license agreements. See the NOTICE file distributed with
+* this work for additional information regarding copyright
+* ownership. Elasticsearch B.V. licenses this file to you under
+* the Apache License, Version 2.0 (the "License"); you may
+* not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing,
+* software distributed under the License is distributed on an
+* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+* KIND, either express or implied. See the License for the
+* specific language governing permissions and limitations
+* under the License.
+*/
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using FluentAssertions;
+using OpenSearch.Client;
+using OpenSearch.OpenSearch.Xunit.XunitPlumbing;
+using Tests.Core.Extensions;
+using Tests.Core.ManagedOpenSearch.Clusters;
+using Tests.Domain;
+using Tests.Framework.EndpointTests;
+using Tests.Framework.EndpointTests.TestState;
+using Xunit;
+
+namespace Tests.Search.Request
+{
+ /**
+ * Allows to highlight search results on one or more fields.
+ * The implementation uses either the lucene `highlighter` or `fast-vector-highlighter`.
+ *
+ * See the OpenSearch documentation on {ref_current}/search-request-body.html#request-body-search-highlighting[highlighting] for more detail.
+ */
+ [SkipVersion("<2.2.0", "MaxAnalyzerOffset field was introduced in 2.2.0")]
+ public class HighlightingUsageTestsWithMaxAnalyzerOffset : HighlightingUsageTests
+ {
+ public HighlightingUsageTestsWithMaxAnalyzerOffset(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
+
+ protected override object ExpectJson =>
+ new
+ {
+ query = new
+ {
+ match = new Dictionary
+ {
+ {
+ "name.standard", new Dictionary
+ {
+ { "query", "Upton Sons Shield Rice Rowe Roberts" }
+ }
+ }
+ }
+ },
+ highlight = new
+ {
+ pre_tags = new[] { "" },
+ post_tags = new[] { "" },
+ encoder = "html",
+ highlight_query = new
+ {
+ match = new Dictionary
+ {
+ {
+ "name.standard", new Dictionary
+ {
+ { "query", "Upton Sons Shield Rice Rowe Roberts" }
+ }
+ }
+ }
+ },
+ fields = new Dictionary
+ {
+ {
+ "name.standard", new Dictionary
+ {
+ { "type", "plain" },
+ { "force_source", true },
+ { "fragment_size", 150 },
+ { "fragmenter", "span" },
+ { "number_of_fragments", 3 },
+ { "no_match_size", 150 },
+ { "max_analyzer_offset", 500 }
+ }
+ },
+ {
+ "leadDeveloper.firstName", new Dictionary
+ {
+ { "type", "fvh" },
+ { "phrase_limit", 10 },
+ { "boundary_max_scan", 50 },
+ { "pre_tags", new[] { "" } },
+ { "post_tags", new[] { "" } },
+ {
+ "highlight_query", new Dictionary
+ {
+ {
+ "match", new Dictionary
+ {
+ {
+ "leadDeveloper.firstName", new Dictionary
+ {
+ { "query", "Kurt Edgardo Naomi Dariana Justice Felton" }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ "leadDeveloper.lastName", new Dictionary
+ {
+ { "type", "unified" },
+ { "pre_tags", new[] { "" } },
+ { "post_tags", new[] { "" } },
+ {
+ "highlight_query", new Dictionary
+ {
+ {
+ "match", new Dictionary
+ {
+ {
+ "leadDeveloper.lastName", new Dictionary
+ {
+ { "query", LastNameSearch }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ },
+ max_analyzer_offset = 1_000_000
+ }
+ };
+
+ protected override Func, ISearchRequest> Fluent => s => s
+
+
+ .Query(q => q
+ .Match(m => m
+ .Field(f => f.Name.Suffix("standard"))
+ .Query("Upton Sons Shield Rice Rowe Roberts")
+ )
+ )
+ .Highlight(h => h
+ .PreTags("")
+ .PostTags("")
+ .Encoder(HighlighterEncoder.Html)
+ .HighlightQuery(q => q
+ .Match(m => m
+ .Field(f => f.Name.Suffix("standard"))
+ .Query("Upton Sons Shield Rice Rowe Roberts")
+ )
+ )
+ .Fields(
+ fs => fs
+ .Field(p => p.Name.Suffix("standard"))
+ .Type("plain")
+ .ForceSource()
+ .FragmentSize(150)
+ .Fragmenter(HighlighterFragmenter.Span)
+ .NumberOfFragments(3)
+ .NoMatchSize(150)
+ .MaxAnalyzerOffset(500),
+ fs => fs
+ .Field(p => p.LeadDeveloper.FirstName)
+ .Type(HighlighterType.Fvh)
+ .PreTags("")
+ .PostTags("")
+ .BoundaryMaxScan(50)
+ .PhraseLimit(10)
+ .HighlightQuery(q => q
+ .Match(m => m
+ .Field(p => p.LeadDeveloper.FirstName)
+ .Query("Kurt Edgardo Naomi Dariana Justice Felton")
+ )
+ ),
+ fs => fs
+ .Field(p => p.LeadDeveloper.LastName)
+ .Type(HighlighterType.Unified)
+ .PreTags("")
+ .PostTags("")
+ .HighlightQuery(q => q
+ .Match(m => m
+ .Field(p => p.LeadDeveloper.LastName)
+ .Query(LastNameSearch)
+ )
+ )
+ )
+ .MaxAnalyzerOffset(1_000_000) //the default value
+ );
+
+ protected override SearchRequest Initializer =>
+ new SearchRequest
+ {
+ Query = new MatchQuery
+ {
+ Query = "Upton Sons Shield Rice Rowe Roberts",
+ Field = "name.standard"
+ },
+ Highlight = new Highlight
+ {
+ PreTags = new[] { "" },
+ PostTags = new[] { "" },
+ Encoder = HighlighterEncoder.Html,
+ HighlightQuery = new MatchQuery
+ {
+ Query = "Upton Sons Shield Rice Rowe Roberts",
+ Field = "name.standard"
+ },
+ Fields = new Dictionary
+ {
+ {
+ "name.standard", new HighlightField
+ {
+ Type = HighlighterType.Plain,
+ ForceSource = true,
+ FragmentSize = 150,
+ Fragmenter = HighlighterFragmenter.Span,
+ NumberOfFragments = 3,
+ NoMatchSize = 150,
+ MaxAnalyzerOffset = 500
+ }
+ },
+ {
+ "leadDeveloper.firstName", new HighlightField
+ {
+ Type = "fvh",
+ PhraseLimit = 10,
+ BoundaryMaxScan = 50,
+ PreTags = new[] { "" },
+ PostTags = new[] { "" },
+ HighlightQuery = new MatchQuery
+ {
+ Field = "leadDeveloper.firstName",
+ Query = "Kurt Edgardo Naomi Dariana Justice Felton"
+ }
+ }
+ },
+ {
+ "leadDeveloper.lastName", new HighlightField
+ {
+ Type = HighlighterType.Unified,
+ PreTags = new[] { "" },
+ PostTags = new[] { "" },
+ HighlightQuery = new MatchQuery
+ {
+ Field = "leadDeveloper.lastName",
+ Query = LastNameSearch
+ }
+ }
+ }
+ },
+ MaxAnalyzerOffset = 1_000_000
+ }
+ };
+
+ protected override void ExpectResponse(ISearchResponse response)
+ {
+ response.ShouldBeValid();
+
+ foreach (var highlightsInEachHit in response.Hits.Select(d => d.Highlight))
+ {
+ foreach (var highlightField in highlightsInEachHit)
+ {
+ if (highlightField.Key == "name.standard")
+ {
+ foreach (var highlight in highlightField.Value)
+ {
+ highlight.Should().Contain("");
+ highlight.Should().Contain("");
+ }
+ }
+ else if (highlightField.Key == "leadDeveloper.firstName")
+ {
+ foreach (var highlight in highlightField.Value)
+ {
+ highlight.Should().Contain("");
+ highlight.Should().Contain("");
+ }
+ }
+ else if (highlightField.Key == "leadDeveloper.lastName")
+ {
+ foreach (var highlight in highlightField.Value)
+ {
+ highlight.Should().Contain("");
+ highlight.Should().Contain("");
+ }
+ }
+ else
+ Assert.True(false, $"highlights contains unexpected key {highlightField.Key}");
+ }
+ }
+ }
+ }
+}