diff --git a/src/Nest/DSL/Filter/FilterContainer.cs b/src/Nest/DSL/Filter/FilterContainer.cs
index b6d6e19d7c0..0abbd30a8e2 100644
--- a/src/Nest/DSL/Filter/FilterContainer.cs
+++ b/src/Nest/DSL/Filter/FilterContainer.cs
@@ -56,6 +56,8 @@ public bool IsConditionless
ITypeFilter IFilterContainer.Type { get; set; }
+ IIndicesFilter IFilterContainer.Indices { get; set; }
+
IMatchAllFilter IFilterContainer.MatchAll { get; set; }
IHasChildFilter IFilterContainer.HasChild { get; set; }
diff --git a/src/Nest/DSL/Filter/FilterDescriptor.cs b/src/Nest/DSL/Filter/FilterDescriptor.cs
index eaa281d8572..32e15cef96a 100644
--- a/src/Nest/DSL/Filter/FilterDescriptor.cs
+++ b/src/Nest/DSL/Filter/FilterDescriptor.cs
@@ -606,6 +606,34 @@ public FilterContainer Type(string type)
filter.Value = type;
return this.New(filter, f => f.Type = filter);
}
+
+ ///
+ /// The indices filter can be used when executed across multiple indices, allowing to have a
+ /// filter that executes only when executed on an index that matches a specific list of indices,
+ /// and another filter that executes when it is executed on an index that does not match the listed indices.
+ ///
+ public FilterContainer Indices(Action> filterSelector) where K : class
+ {
+ var filter = new IndicesFilterDescriptor();
+ if (filterSelector != null)
+ filterSelector(filter);
+
+ return this.New(filter, f => f.Indices = filter);
+ }
+
+ ///
+ /// The indices filter can be used when executed across multiple indices, allowing to have a
+ /// filter that executes only when executed on an index that matches a specific list of indices,
+ /// and another filter that executes when it is executed on an index that does not match the listed indices.
+ ///
+ public FilterContainer Indices(Action> filterSelector)
+ {
+ var filter = new IndicesFilterDescriptor();
+ if (filterSelector != null)
+ filterSelector(filter);
+
+ return this.New(filter, f => f.Indices = filter);
+ }
///
/// Filters documents matching the provided document / mapping type.
diff --git a/src/Nest/DSL/Filter/IFilterContainer.cs b/src/Nest/DSL/Filter/IFilterContainer.cs
index 88d013d6dcb..f1fb3f17410 100644
--- a/src/Nest/DSL/Filter/IFilterContainer.cs
+++ b/src/Nest/DSL/Filter/IFilterContainer.cs
@@ -69,6 +69,9 @@ public interface IFilterContainer
[JsonProperty(PropertyName = "limit")]
ILimitFilter Limit { get; set; }
+ [JsonProperty(PropertyName = "indices")]
+ IIndicesFilter Indices { get; set; }
+
[JsonProperty(PropertyName = "type")]
ITypeFilter Type { get; set; }
diff --git a/src/Nest/DSL/Filter/IndicesFilterDescriptor.cs b/src/Nest/DSL/Filter/IndicesFilterDescriptor.cs
new file mode 100644
index 00000000000..a83b01634ff
--- /dev/null
+++ b/src/Nest/DSL/Filter/IndicesFilterDescriptor.cs
@@ -0,0 +1,140 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Nest.Resolvers.Converters;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Linq;
+
+
+namespace Nest
+{
+ [JsonObject(MemberSerialization = MemberSerialization.OptIn)]
+ [JsonConverter(typeof(ReadAsTypeConverter>))]
+ public interface IIndicesFilter : IFilter
+ {
+ [JsonProperty("indices")]
+ IEnumerable Indices { get; set; }
+
+ [JsonProperty("filter")]
+ [JsonConverter(typeof(CompositeJsonConverter>, CustomJsonConverter>))]
+ IFilterContainer Filter { get; set; }
+
+ [JsonProperty("no_match_filter")]
+ [JsonConverter(typeof(NoMatchFilterConverter))]
+ IFilterContainer NoMatchFilter { get; set; }
+
+ }
+
+ public class NoMatchFilterConverter : CompositeJsonConverter>, CustomJsonConverter>
+ {
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType == JsonToken.String)
+ {
+ var en = serializer.Deserialize(reader);
+ return new NoMatchFilterContainer {Shortcut = en};
+ }
+
+ return base.ReadJson(reader, objectType, existingValue, serializer);
+ }
+ }
+
+ public class NoMatchFilterContainer : FilterContainer, ICustomJson
+ {
+ public NoMatchShortcut? Shortcut { get; set; }
+
+ object ICustomJson.GetCustomJson()
+ {
+ if (this.Shortcut.HasValue) return this.Shortcut;
+ var f = ((IFilterContainer)this);
+ if (f.RawFilter.IsNullOrEmpty()) return f;
+ return new RawJson(f.RawFilter);
+ }
+ }
+
+ public class IndicesFilter : PlainFilter, IIndicesFilter
+ {
+ protected internal override void WrapInContainer(IFilterContainer container)
+ {
+ container.Indices = this;
+ }
+
+ bool IFilter.IsConditionless { get { return false; } }
+ public NestedScore? Score { get; set; }
+ public IFilterContainer Filter { get; set; }
+ public IFilterContainer NoMatchFilter { get; set; }
+ public IEnumerable Indices { get; set; }
+ }
+
+ public class IndicesFilterDescriptor : FilterBase, IIndicesFilter where T : class
+ {
+ IFilterContainer IIndicesFilter.Filter { get; set; }
+
+ IFilterContainer IIndicesFilter.NoMatchFilter { get; set; }
+
+ IEnumerable IIndicesFilter.Indices { get; set; }
+
+ bool IFilter.IsConditionless
+ {
+ get
+ {
+ return ((IIndicesFilter)this).NoMatchFilter == null && ((IIndicesFilter)this).Filter == null;
+ }
+ }
+
+ public IndicesFilterDescriptor Filter(Func, FilterContainer> filterSelector)
+ {
+ var qd = new FilterDescriptor();
+ var q = filterSelector(qd);
+ if (q.IsConditionless)
+ return this;
+
+
+ ((IIndicesFilter)this).Filter = q;
+ return this;
+ }
+
+ public IndicesFilterDescriptor Filter(Func, FilterContainer> filterSelector) where K : class
+ {
+ var qd = new FilterDescriptor();
+ var q = filterSelector(qd);
+ if (q.IsConditionless)
+ return this;
+
+ ((IIndicesFilter)this).Filter = q;
+ return this;
+ }
+
+ public IndicesFilterDescriptor NoMatchFilter(NoMatchShortcut shortcut)
+ {
+ ((IIndicesFilter)this).NoMatchFilter = new NoMatchFilterContainer { Shortcut = shortcut };
+ return this;
+ }
+
+ public IndicesFilterDescriptor NoMatchFilter(Func, FilterContainer> filterSelector)
+ {
+ var qd = new FilterDescriptor();
+ var q = filterSelector(qd);
+ if (q.IsConditionless)
+ return this;
+
+ ((IIndicesFilter)this).NoMatchFilter = q;
+ return this;
+ }
+ public IndicesFilterDescriptor NoMatchFilter(Func, IFilterContainer> filterSelector) where K : class
+ {
+ var qd = new FilterDescriptor();
+ var q = filterSelector(qd);
+ if (q.IsConditionless)
+ return this;
+
+ ((IIndicesFilter)this).NoMatchFilter = q;
+ return this;
+ }
+ public IndicesFilterDescriptor Indices(IEnumerable indices)
+ {
+ ((IIndicesFilter)this).Indices = indices;
+ return this;
+ }
+ }
+}
diff --git a/src/Nest/DSL/Query/IndicesQueryDescriptor.cs b/src/Nest/DSL/Query/IndicesQueryDescriptor.cs
index be073858c0f..2cdac70f4bc 100644
--- a/src/Nest/DSL/Query/IndicesQueryDescriptor.cs
+++ b/src/Nest/DSL/Query/IndicesQueryDescriptor.cs
@@ -12,6 +12,9 @@ namespace Nest
[JsonConverter(typeof(ReadAsTypeConverter>))]
public interface IIndicesQuery : IQuery
{
+ [JsonProperty("indices")]
+ IEnumerable Indices { get; set; }
+
[JsonProperty("score_mode"), JsonConverter(typeof (StringEnumConverter))]
NestedScore? Score { get; set; }
@@ -20,10 +23,35 @@ public interface IIndicesQuery : IQuery
IQueryContainer Query { get; set; }
[JsonProperty("no_match_query")]
+ [JsonConverter(typeof(NoMatchQueryConverter))]
IQueryContainer NoMatchQuery { get; set; }
+ }
- [JsonProperty("indices")]
- IEnumerable Indices { get; set; }
+ public class NoMatchQueryConverter : CompositeJsonConverter>, CustomJsonConverter>
+ {
+ public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
+ {
+ if (reader.TokenType == JsonToken.String)
+ {
+ var en = serializer.Deserialize(reader);
+ return new NoMatchQueryContainer {Shortcut = en};
+ }
+
+ return base.ReadJson(reader, objectType, existingValue, serializer);
+ }
+ }
+
+ public class NoMatchQueryContainer : QueryContainer, ICustomJson
+ {
+ public NoMatchShortcut? Shortcut { get; set; }
+
+ object ICustomJson.GetCustomJson()
+ {
+ if (this.Shortcut.HasValue) return this.Shortcut;
+ var f = ((IQueryContainer)this);
+ if (f.RawQuery.IsNullOrEmpty()) return f;
+ return new RawJson(f.RawQuery);
+ }
}
public class IndicesQuery : PlainQuery, IIndicesQuery
@@ -81,6 +109,12 @@ public IndicesQueryDescriptor Query(Func, QueryContaine
return this;
}
+ public IndicesQueryDescriptor NoMatchQuery(NoMatchShortcut shortcut)
+ {
+ ((IIndicesQuery)this).NoMatchQuery = new NoMatchQueryContainer { Shortcut = shortcut };
+ return this;
+ }
+
public IndicesQueryDescriptor NoMatchQuery(Func, QueryContainer> querySelector)
{
var qd = new QueryDescriptor();
diff --git a/src/Nest/Domain/DSL/NoMatchShortcut.cs b/src/Nest/Domain/DSL/NoMatchShortcut.cs
new file mode 100644
index 00000000000..7524e37f78f
--- /dev/null
+++ b/src/Nest/Domain/DSL/NoMatchShortcut.cs
@@ -0,0 +1,13 @@
+using System.Runtime.Serialization;
+using Newtonsoft.Json;
+using Newtonsoft.Json.Converters;
+
+namespace Nest
+{
+ [JsonConverter(typeof (StringEnumConverter))]
+ public enum NoMatchShortcut
+ {
+ [EnumMember(Value = "none")] None,
+ [EnumMember(Value = "all")] All
+ }
+}
\ No newline at end of file
diff --git a/src/Nest/Nest.csproj b/src/Nest/Nest.csproj
index 0e4ebc7c7f1..bc124e255c8 100644
--- a/src/Nest/Nest.csproj
+++ b/src/Nest/Nest.csproj
@@ -253,6 +253,8 @@
+
+
@@ -1096,4 +1098,4 @@
-->
-
+
\ No newline at end of file
diff --git a/src/Tests/Nest.Tests.Unit/Nest.Tests.Unit.csproj b/src/Tests/Nest.Tests.Unit/Nest.Tests.Unit.csproj
index d1522f3a1ec..9dc40f90d12 100644
--- a/src/Tests/Nest.Tests.Unit/Nest.Tests.Unit.csproj
+++ b/src/Tests/Nest.Tests.Unit/Nest.Tests.Unit.csproj
@@ -402,6 +402,7 @@
+
@@ -418,6 +419,7 @@
+
@@ -1318,4 +1320,4 @@
-->
-
+
\ No newline at end of file
diff --git a/src/Tests/Nest.Tests.Unit/Reproduce/Reproduce1187Tests.cs b/src/Tests/Nest.Tests.Unit/Reproduce/Reproduce1187Tests.cs
new file mode 100644
index 00000000000..da0295a9b5c
--- /dev/null
+++ b/src/Tests/Nest.Tests.Unit/Reproduce/Reproduce1187Tests.cs
@@ -0,0 +1,148 @@
+using System.IO;
+using FluentAssertions;
+using Nest.Tests.MockData.Domain;
+using Newtonsoft.Json.Linq;
+using NUnit.Framework;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Reflection;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Nest.Tests.Unit.Reproduce
+{
+ [TestFixture]
+ public class Reproduce1187Tests : BaseJsonTests
+ {
+ [Test]
+ public void IsClientSideSearchQueryDeserialisable()
+ {
+ var newDescriptor = Deserialize>(original);
+ var actual = Serialize(newDescriptor);//Should is empty
+
+ var descriptorJobject = JObject.Parse(actual);
+ var expressionList = new object[][]
+ {
+ new[] {"size"},//Works
+ new[] {"from"},//Works
+ new[] {"query", "filtered", "query", "query_string", "fields"},//Works
+ new[] {"query", "filtered", "filter", "bool", "must"},//Works
+//Can't find contents of should
+ new object[] {"query", "filtered", "filter", "bool", "should", 0, "indices", "filter"},
+ new object[] {"query", "filtered", "filter", "bool", "should", 0, "indices", "no_match_filter"},
+ new[] {"aggs", "Databases", "terms", "field"},//Works
+ new[] {"aggs", "Year", "terms", "field"}//Works
+ };
+ foreach (var e in expressionList)
+ VerifyJsonPath(descriptorJobject, e);
+
+ //If we do a search without the below line
+ //it seems to contect /_all/object/_search instead of /_search
+ newDescriptor.AllTypes().AllIndices();
+ }
+
+ private static void VerifyJsonPath(JToken descriptorJobject, IEnumerable