diff --git a/src/Nest/QueryDsl/TermLevel/Terms/TermsQuery.cs b/src/Nest/QueryDsl/TermLevel/Terms/TermsQuery.cs
index b608712760f..a65170d717b 100644
--- a/src/Nest/QueryDsl/TermLevel/Terms/TermsQuery.cs
+++ b/src/Nest/QueryDsl/TermLevel/Terms/TermsQuery.cs
@@ -1,4 +1,5 @@
using System;
+using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
@@ -26,14 +27,14 @@ public class TermsQuery : FieldNameQueryBase, ITermsQuery
internal override void WrapInContainer(IQueryContainer c) => c.Terms = this;
internal static bool IsConditionless(ITermsQuery q)
{
- return q.Field.IsConditionless()
+ return q.Field.IsConditionless()
|| (
- (q.Terms == null
- || !q.Terms.HasAny()
- || q.Terms.All(t=>t == null
+ (q.Terms == null
+ || !q.Terms.HasAny()
+ || q.Terms.All(t=>t == null
|| ((t as string)?.IsNullOrEmpty()).GetValueOrDefault(false))
)
- &&
+ &&
(q.TermsLookup == null
|| q.TermsLookup.Id == null
|| q.TermsLookup.Path.IsConditionless()
@@ -44,11 +45,11 @@ internal static bool IsConditionless(ITermsQuery q)
}
///
- /// A query that match on any (configurable) of the provided terms.
+ /// A query that match on any (configurable) of the provided terms.
/// This is a simpler syntax query for using a bool query with several term queries in the should clauses.
///
/// The type that represents the expected hit type
- public class TermsQueryDescriptor
+ public class TermsQueryDescriptor
: FieldNameQueryDescriptorBase, ITermsQuery, T>
, ITermsQuery where T : class
{
@@ -67,7 +68,13 @@ public TermsQueryDescriptor TermsLookup(Func Terms(IEnumerable terms) => Assign(a => a.Terms = terms?.Cast());
- public TermsQueryDescriptor Terms(params TValue[] terms) => Assign(a => a.Terms = terms?.Cast());
+ public TermsQueryDescriptor Terms(params TValue[] terms) => Assign(a => {
+ if(terms?.Length == 1 && typeof(IEnumerable).IsAssignableFrom(typeof(TValue)) && typeof(TValue) != typeof(string))
+ {
+ a.Terms = (terms.First() as IEnumerable)?.Cast();
+ }
+ else a.Terms = terms?.Cast();
+ });
}
}
diff --git a/src/Nest/QueryDsl/TermLevel/Terms/TermsQueryJsonConverter.cs b/src/Nest/QueryDsl/TermLevel/Terms/TermsQueryJsonConverter.cs
index 1c2ff54e267..0b7c495c8b9 100644
--- a/src/Nest/QueryDsl/TermLevel/Terms/TermsQueryJsonConverter.cs
+++ b/src/Nest/QueryDsl/TermLevel/Terms/TermsQueryJsonConverter.cs
@@ -75,7 +75,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist
case "minimum_should_match":
reader.Read();
var min = serializer.Deserialize(reader);
- f.MinimumShouldMatch = min;
+ f.MinimumShouldMatch = min;
break;
case "boost":
reader.Read();
@@ -131,7 +131,7 @@ private void ReadTerms(ITermsQuery termsQuery, JsonReader reader, JsonSerializer
}
else if (reader.TokenType == JsonToken.StartArray)
{
- var values = JArray.Load(reader).Values();
+ var values = JArray.Load(reader).Values();
termsQuery.Terms = values;
}
}
diff --git a/src/Tests/QueryDsl/QueryDslIntegrationTestsBase.cs b/src/Tests/QueryDsl/QueryDslIntegrationTestsBase.cs
new file mode 100644
index 00000000000..ec08ce2e540
--- /dev/null
+++ b/src/Tests/QueryDsl/QueryDslIntegrationTestsBase.cs
@@ -0,0 +1,45 @@
+using System;
+using System.Collections.Generic;
+using Elasticsearch.Net;
+using FluentAssertions;
+using Nest;
+using Tests.Framework;
+using Tests.Framework.Integration;
+using Tests.Framework.MockData;
+using Xunit;
+
+namespace Tests.QueryDsl
+{
+ [Collection(IntegrationContext.ReadOnly)]
+ public abstract class QueryDslIntegrationTestsBase : ApiIntegrationTestBase, ISearchRequest, SearchDescriptor, SearchRequest>
+ {
+ protected QueryDslIntegrationTestsBase(IIntegrationCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
+ protected override LazyResponses ClientUsage() => Calls(
+ fluent: (client, f) => client.Search(f),
+ fluentAsync: (client, f) => client.SearchAsync(f),
+ request: (client, r) => client.Search(r),
+ requestAsync: (client, r) => client.SearchAsync(r)
+ );
+
+ protected override HttpMethod HttpMethod => HttpMethod.POST;
+ protected override string UrlPath => "/project/project/_search";
+ protected override int ExpectStatusCode => 200;
+ protected override bool ExpectIsValid => true;
+
+ protected abstract object QueryJson { get; }
+
+ protected abstract QueryContainer QueryInitializer { get; }
+ protected abstract QueryContainer QueryFluent(QueryContainerDescriptor q);
+
+ protected override object ExpectJson => new { query = this.QueryJson };
+
+ protected override Func, ISearchRequest> Fluent => s => s
+ .Query(this.QueryFluent);
+
+ protected override SearchRequest Initializer =>
+ new SearchRequest
+ {
+ Query = this.QueryInitializer
+ };
+ }
+}
diff --git a/src/Tests/QueryDsl/TermLevel/Terms/TermsListQueryUsageTests.cs b/src/Tests/QueryDsl/TermLevel/Terms/TermsListQueryUsageTests.cs
new file mode 100644
index 00000000000..f69b7edf224
--- /dev/null
+++ b/src/Tests/QueryDsl/TermLevel/Terms/TermsListQueryUsageTests.cs
@@ -0,0 +1,138 @@
+using System.Collections.Generic;
+using System.Linq;
+using System.Threading.Tasks;
+using FluentAssertions;
+using Nest;
+using Tests.Framework;
+using Tests.Framework.Integration;
+using Tests.Framework.MockData;
+
+namespace Tests.QueryDsl.TermLevel.Terms
+{
+ public class TermsListQueryUsageTests : QueryDslUsageTestsBase
+ {
+ public TermsListQueryUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) {}
+
+ protected override object QueryJson => new
+ {
+ terms = new
+ {
+ _name = "named_query",
+ boost = 1.1,
+ description = new[] { "term1", "term2" },
+ disable_coord = true,
+ minimum_should_match = 2
+ }
+ };
+
+ protected override QueryContainer QueryInitializer => new TermsQuery
+ {
+ Name = "named_query",
+ Boost = 1.1,
+ Field = "description",
+ Terms = new List { "term1", "term2" },
+ DisableCoord = true,
+ MinimumShouldMatch = 2
+ };
+
+ protected override QueryContainer QueryFluent(QueryContainerDescriptor q) => q
+ .Terms(c => c
+ .Name("named_query")
+ .Boost(1.1)
+ .Field(p => p.Description)
+ .DisableCoord()
+ .MinimumShouldMatch(MinimumShouldMatch.Fixed(2))
+ .Terms(new List { "term1", "term2" })
+ );
+ }
+
+ public class TermsListOfListIntegrationTests : QueryDslIntegrationTestsBase
+ {
+ public TermsListOfListIntegrationTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
+
+ private List> _terms = new List> { new List { "term1", "term2" } };
+
+ protected override object QueryJson => new
+ {
+ terms = new
+ {
+ _name = "named_query",
+ boost = 1.1,
+ description = new[] { new [] { "term1", "term2" } },
+ disable_coord = true,
+ }
+ };
+
+ protected override QueryContainer QueryInitializer => new TermsQuery
+ {
+ Name = "named_query",
+ Boost = 1.1,
+ Field = "description",
+ Terms = _terms,
+ DisableCoord = true,
+ };
+
+ protected override QueryContainer QueryFluent(QueryContainerDescriptor q) => q
+ .Terms(c => c
+ .Name("named_query")
+ .Boost(1.1)
+ .Field(p => p.Description)
+ .DisableCoord()
+ .Terms(_terms)
+ );
+
+ }
+
+ public class TermsListOfListStringAgainstNumericFieldIntegrationTests : QueryDslIntegrationTestsBase
+ {
+ public TermsListOfListStringAgainstNumericFieldIntegrationTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
+
+ private List> _terms = new List> { new List { "term1", "term2" } };
+
+
+ protected override int ExpectStatusCode => 400;
+ protected override bool ExpectIsValid => false;
+
+ protected override object QueryJson => new
+ {
+ terms = new
+ {
+ _name = "named_query",
+ boost = 1.1,
+ numberOfCommits = new[] { new [] { "term1", "term2" } },
+ disable_coord = true,
+ }
+ };
+
+ protected override QueryContainer QueryInitializer => new TermsQuery
+ {
+ Name = "named_query",
+ Boost = 1.1,
+ Field = "numberOfCommits",
+ Terms = _terms,
+ DisableCoord = true,
+ };
+
+ protected override QueryContainer QueryFluent(QueryContainerDescriptor q) => q
+ .Terms(c => c
+ .Name("named_query")
+ .Boost(1.1)
+ .Field(p => p.NumberOfCommits)
+ .DisableCoord()
+ .Terms(_terms)
+ );
+
+ [I] public Task AsserResponse() => AssertOnAllResponses(r =>
+ {
+ r.ServerError.Should().NotBeNull();
+ r.ServerError.Status.Should().Be(400);
+ r.ServerError.Error.Should().NotBeNull();
+ var rootCauses = r.ServerError.Error.RootCause;
+ rootCauses.Should().NotBeNullOrEmpty();
+ var rootCause = rootCauses.First();
+ rootCause.Type.Should().Be("number_format_exception");
+ });
+
+ }
+
+}
diff --git a/src/Tests/QueryDsl/TermLevel/Terms/TermsQueryUsageTests.cs b/src/Tests/QueryDsl/TermLevel/Terms/TermsQueryUsageTests.cs
index 0443c63f879..1984fba674b 100644
--- a/src/Tests/QueryDsl/TermLevel/Terms/TermsQueryUsageTests.cs
+++ b/src/Tests/QueryDsl/TermLevel/Terms/TermsQueryUsageTests.cs
@@ -1,3 +1,4 @@
+using System.Collections.Generic;
using System.Linq;
using Nest;
using Tests.Framework.Integration;
@@ -7,6 +8,8 @@ namespace Tests.QueryDsl.TermLevel.Terms
{
public class TermsQueryUsageTests : QueryDslUsageTestsBase
{
+ protected virtual string[] ExpectedTerms => new [] { "term1", "term2" };
+
public TermsQueryUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) {}
protected override object QueryJson => new
@@ -15,7 +18,7 @@ public TermsQueryUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base
{
_name = "named_query",
boost = 1.1,
- description = new[] { "term1", "term2" },
+ description = ExpectedTerms,
disable_coord = true,
minimum_should_match = 2
}
@@ -26,7 +29,7 @@ public TermsQueryUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base
Name = "named_query",
Boost = 1.1,
Field = "description",
- Terms = new [] { "term1", "term2" },
+ Terms = ExpectedTerms,
DisableCoord = true,
MinimumShouldMatch = 2
};
@@ -49,4 +52,23 @@ protected override QueryContainer QueryFluent(QueryContainerDescriptor
q => q.Terms = new [] { "" }
};
}
-}
\ No newline at end of file
+
+ public class SingleTermTermsQueryUsageTests : TermsQueryUsageTests
+ {
+ protected override string[] ExpectedTerms => new [] { "term1" };
+
+ public SingleTermTermsQueryUsageTests(ReadOnlyCluster cluster, EndpointUsage usage) : base(cluster, usage) { }
+
+ protected override QueryContainer QueryFluent(QueryContainerDescriptor q) => q
+ .Terms(c => c
+ .Name("named_query")
+ .Boost(1.1)
+ .Field(p => p.Description)
+ .DisableCoord()
+ .MinimumShouldMatch(MinimumShouldMatch.Fixed(2))
+ .Terms("term1")
+ );
+ }
+
+
+}
diff --git a/src/Tests/Tests.csproj b/src/Tests/Tests.csproj
index 87ed005a0ec..73db5892d1a 100644
--- a/src/Tests/Tests.csproj
+++ b/src/Tests/Tests.csproj
@@ -542,6 +542,8 @@
+
+