diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d6d01078a..876ec464c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ### Added - Added support for the `Cat.PitSegments` and `Cat.SegmentReplication` APIs ([#527](https://github.com/opensearch-project/opensearch-net/pull/527)) +- Added `.Strict(...)`, `.Verbatim(...)`, `.Name(...)` methods on `QueryContainer` to help modify contained query attributes ([#509](https://github.com/opensearch-project/opensearch-net/pull/509)) ### Removed - Removed the `Features` API which is not supported by OpenSearch from the low-level client ([#331](https://github.com/opensearch-project/opensearch-net/pull/331)) diff --git a/src/OpenSearch.Client/QueryDsl/Abstractions/Container/QueryContainer-Dsl.cs b/src/OpenSearch.Client/QueryDsl/Abstractions/Container/QueryContainer-Dsl.cs index 2d4221d6e4..e7504c8591 100644 --- a/src/OpenSearch.Client/QueryDsl/Abstractions/Container/QueryContainer-Dsl.cs +++ b/src/OpenSearch.Client/QueryDsl/Abstractions/Container/QueryContainer-Dsl.cs @@ -131,5 +131,58 @@ out QueryContainer queryContainer // ReSharper disable once UnusedMember.Global internal bool ShouldSerialize(IJsonFormatterResolver formatterResolver) => IsWritable; + + /// + /// Assigns a name to the contained query. + /// + /// + /// + public QueryContainer Name(string name) + { + ContainedQuery.Name = name; + return this; + } + + /// + /// Applies or removes the `strict` attribute to the contained query and optionally to all child sub-queries. + /// + /// + /// When true, it applies the attribute to all child sub-queries. + /// + public QueryContainer Strict(bool strict = true, bool recurse = false) + { + if (recurse) + { + var visitor = new StrictnessPropagatingVisitor(strict); + Accept(visitor); + } + else + { + ContainedQuery.IsStrict = strict; + } + + return this; + } + + /// + /// Applies or removes the `verbatim` attribute to the contained query and optionally to all child sub-queries. + /// + /// + /// When true, it applies the attribute to all child sub-queries. + /// + public QueryContainer Verbatim(bool verbatim = true, bool recurse = false) + { + if (recurse) + { + var visitor = new VerbatimPropagatingVisitor(verbatim); + Accept(visitor); + } + else + { + ContainedQuery.IsVerbatim = verbatim; + } + + return this; + } } } diff --git a/src/OpenSearch.Client/QueryDsl/Visitor/StrictnessPropagatingVisitor.cs b/src/OpenSearch.Client/QueryDsl/Visitor/StrictnessPropagatingVisitor.cs new file mode 100644 index 0000000000..c97703c6d4 --- /dev/null +++ b/src/OpenSearch.Client/QueryDsl/Visitor/StrictnessPropagatingVisitor.cs @@ -0,0 +1,24 @@ +/* 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. +*/ + +using System; + +namespace OpenSearch.Client +{ + public class StrictnessPropagatingVisitor : QueryVisitor + { + private readonly bool _strict; + + public StrictnessPropagatingVisitor(bool strict) => _strict = strict; + + public override void Visit(IQuery query) + { + query.IsStrict = _strict; + base.Visit(query); + } + } +} diff --git a/src/OpenSearch.Client/QueryDsl/Visitor/VerbatimPropagatingVisitor.cs b/src/OpenSearch.Client/QueryDsl/Visitor/VerbatimPropagatingVisitor.cs new file mode 100644 index 0000000000..c8daa65d21 --- /dev/null +++ b/src/OpenSearch.Client/QueryDsl/Visitor/VerbatimPropagatingVisitor.cs @@ -0,0 +1,22 @@ +/* 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. +*/ + +namespace OpenSearch.Client +{ + public class VerbatimPropagatingVisitor : QueryVisitor + { + private readonly bool _verbatim; + + public VerbatimPropagatingVisitor(bool verbatim) => _verbatim = verbatim; + + public override void Visit(IQuery query) + { + query.IsVerbatim = _verbatim; + base.Visit(query); + } + } +} diff --git a/tests/Tests/QueryDsl/Container/QueryContainerTests.cs b/tests/Tests/QueryDsl/Container/QueryContainerTests.cs new file mode 100644 index 0000000000..918e4afe46 --- /dev/null +++ b/tests/Tests/QueryDsl/Container/QueryContainerTests.cs @@ -0,0 +1,102 @@ +/* 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. +*/ + +using FluentAssertions; +using OpenSearch.Client; +using OpenSearch.OpenSearch.Xunit.XunitPlumbing; +using Xunit; + +namespace Tests.QueryDsl.Container +{ + public class QueryContainerTests + { + [TU] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void StrictAndVerbatimAttributesAreRecursivelySetCorrectly(bool targetStrict, bool targetVerbatim) + { + // Arrange + var query0 = new TermQuery { Field = "field", Value = 1, IsStrict = !targetStrict, IsVerbatim = !targetVerbatim }; + var query1 = new BoolQuery { MustNot = new QueryContainer[] { query0 }, IsStrict = !targetStrict, IsVerbatim = !targetVerbatim }; + var query2 = new TermQuery { Field = "field2", Value = 7, IsStrict = !targetStrict, IsVerbatim = !targetVerbatim }; + var query3 = new BoolQuery { Must = new QueryContainer[] { query1, query2 }, IsStrict = !targetStrict, IsVerbatim = !targetVerbatim }; + var queryContainer = new QueryContainer(query3); + + // Act + queryContainer.Strict(targetStrict, recurse: true); + queryContainer.Verbatim(targetVerbatim, recurse: true); + + // Assert + query0.IsStrict.Should().Be(targetStrict); + query0.IsVerbatim.Should().Be(targetVerbatim); + query1.IsStrict.Should().Be(targetStrict); + query1.IsVerbatim.Should().Be(targetVerbatim); + query2.IsStrict.Should().Be(targetStrict); + query2.IsVerbatim.Should().Be(targetVerbatim); + query3.IsStrict.Should().Be(targetStrict); + query3.IsVerbatim.Should().Be(targetVerbatim); + } + + [TU] + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void StrictAndVerbatimAttributesAreSetCorrectly(bool targetStrict, bool targetVerbatim) + { + // Arrange + var query0 = new TermQuery { Field = "field", Value = 1, IsStrict = !targetStrict, IsVerbatim = !targetVerbatim }; + var query1 = new BoolQuery { MustNot = new QueryContainer[] { query0 }, IsStrict = !targetStrict, IsVerbatim = !targetVerbatim }; + var query2 = new TermQuery { Field = "field2", Value = 7, IsStrict = !targetStrict, IsVerbatim = !targetVerbatim }; + var query3 = new BoolQuery { Must = new QueryContainer[] { query1, query2 }, IsStrict = !targetStrict, IsVerbatim = !targetVerbatim }; + var queryContainer = new QueryContainer(query3); + + // Act + queryContainer.Strict(targetStrict, recurse: false); + queryContainer.Verbatim(targetVerbatim, recurse: false); + + // Assert + query0.IsStrict.Should().Be(!targetStrict); + query0.IsVerbatim.Should().Be(!targetVerbatim); + query1.IsStrict.Should().Be(!targetStrict); + query1.IsVerbatim.Should().Be(!targetVerbatim); + query2.IsStrict.Should().Be(!targetStrict); + query2.IsVerbatim.Should().Be(!targetVerbatim); + + query3.IsStrict.Should().Be(targetStrict); + query3.IsVerbatim.Should().Be(targetVerbatim); + } + + [TU] + [InlineData("name1")] + [InlineData("a name")] + [InlineData(null)] + public void SettingTheNameOnTheQueryContainerSetTheNameOnTheContainedQuery(string name) + { + // Arrange + var query0 = new TermQuery { Name = "a", Field = "field", Value = 1 }; + var query1 = new BoolQuery { Name = "b", MustNot = new QueryContainer[] { query0 } }; + var query2 = new TermQuery { Name = "c", Field = "field2", Value = 7 }; + var query3 = new BoolQuery { Name = "d", Must = new QueryContainer[] { query1, query2 } }; + var queryContainer = new QueryContainer(query3); + + // Act + queryContainer.Name(name); + + // Assert + query3.Name.Should().Be(name); + queryContainer.ContainedQuery.Name.Should().Be(name); + query0.Name.Should().Be("a"); + query1.Name.Should().Be("b"); + query2.Name.Should().Be("c"); + } + + + } +}