diff --git a/src/Nest.Tests.Unit/Nest.Tests.Unit.csproj b/src/Nest.Tests.Unit/Nest.Tests.Unit.csproj index d7f6238ed39..7d6aa37c35f 100644 --- a/src/Nest.Tests.Unit/Nest.Tests.Unit.csproj +++ b/src/Nest.Tests.Unit/Nest.Tests.Unit.csproj @@ -107,6 +107,7 @@ + diff --git a/src/Nest.Tests.Unit/Search/Filter/Singles/HasChildFilterJson.cs b/src/Nest.Tests.Unit/Search/Filter/Singles/HasChildFilterJson.cs index 9f6c1a07e4c..ae91579cc15 100644 --- a/src/Nest.Tests.Unit/Search/Filter/Singles/HasChildFilterJson.cs +++ b/src/Nest.Tests.Unit/Search/Filter/Singles/HasChildFilterJson.cs @@ -33,7 +33,7 @@ public void HasChildFilter() } } }"; - Assert.True(json.JsonEquals(expected), json); + Assert.True(json.JsonEquals(expected), json); } } } diff --git a/src/Nest.Tests.Unit/Search/Filter/Singles/HasParentFilterJson.cs b/src/Nest.Tests.Unit/Search/Filter/Singles/HasParentFilterJson.cs new file mode 100644 index 00000000000..6cdeabca750 --- /dev/null +++ b/src/Nest.Tests.Unit/Search/Filter/Singles/HasParentFilterJson.cs @@ -0,0 +1,39 @@ +using NUnit.Framework; +using Nest.Tests.MockData.Domain; + +namespace Nest.Tests.Unit.Search.Filter.Singles +{ + [TestFixture] + public class HasParentFilterJson + { + [Test] + public void HasParentFilter() + { + var s = new SearchDescriptor().From(0).Size(10) + .Filter(ff=>ff + .HasParent(d=>d + .Scope("my_scope") + .Query(q=>q.Term(p=>p.Country, "value")) + ) + ); + + var json = TestElasticClient.Serialize(s); + var expected = @"{ from: 0, size: 10, + filter : { + ""has_parent"": { + ""type"": ""elasticsearchprojects"", + ""_scope"": ""my_scope"", + ""query"": { + ""term"": { + ""country"": { + ""value"": ""value"" + } + } + } + } + } + }"; + Assert.True(json.JsonEquals(expected), json); + } + } +} diff --git a/src/Nest/DSL/Filter/HasParentFilterDescriptor.cs b/src/Nest/DSL/Filter/HasParentFilterDescriptor.cs new file mode 100644 index 00000000000..23b66ed7898 --- /dev/null +++ b/src/Nest/DSL/Filter/HasParentFilterDescriptor.cs @@ -0,0 +1,53 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Newtonsoft.Json; +using System.Linq.Expressions; +using Nest.Resolvers; + +namespace Nest +{ + [JsonObject(MemberSerialization=MemberSerialization.OptIn)] + public class HasParentFilterDescriptor : FilterBase where T : class + { + public HasParentFilterDescriptor() + { + this._Type = new TypeNameResolver().GetTypeNameFor(); + } + internal override bool IsConditionless + { + get + { + return this._QueryDescriptor == null || this._QueryDescriptor.IsConditionless || this._Type.IsNullOrEmpty(); + } + } + + [JsonProperty("type")] + internal TypeNameMarker _Type { get; set; } + + [JsonProperty("_scope")] + internal string _Scope { get; set;} + + [JsonProperty("query")] + internal BaseQuery _QueryDescriptor { get; set; } + + public HasParentFilterDescriptor Query(Func, BaseQuery> querySelector) + { + var q = new QueryDescriptor(); + this._QueryDescriptor = querySelector(q); + return this; + } + + public HasParentFilterDescriptor Scope(string scope) + { + this._Scope = scope; + return this; + } + public HasParentFilterDescriptor Type(string type) + { + this._Type = type; + return this; + } + } +} diff --git a/src/Nest/DSL/FilterDescriptor.cs b/src/Nest/DSL/FilterDescriptor.cs index f58e447767e..a46f111c140 100644 --- a/src/Nest/DSL/FilterDescriptor.cs +++ b/src/Nest/DSL/FilterDescriptor.cs @@ -75,6 +75,9 @@ public FilterDescriptor Cache(bool cache) [JsonProperty(PropertyName = "has_child")] internal object HasChildFilter { get; set; } + [JsonProperty(PropertyName = "has_parent")] + internal object HasParentFilter { get; set; } + [JsonProperty(PropertyName = "numeric_range")] internal Dictionary NumericRangeFilter { get; set; } @@ -150,6 +153,7 @@ internal FilterDescriptor Clone() TypeFilter = TypeFilter, MatchAllFilter = MatchAllFilter, HasChildFilter = HasChildFilter, + HasParentFilter = HasParentFilter, NumericRangeFilter = NumericRangeFilter, RangeFilter = RangeFilter, PrefixFilter = PrefixFilter, @@ -498,6 +502,26 @@ public BaseFilter HasChild(Action> filterSelector return new FilterDescriptor() { HasChildFilter = filter }; } + + /// + /// The has_child filter accepts a query and the child type to run against, + /// and results in parent documents that have child docs matching the query. + /// + /// Type of the child + public BaseFilter HasParent(Action> filterSelector) where K : class + { + var filter = new HasParentFilterDescriptor(); + if (filterSelector == null) + return CreateConditionlessFilterDescriptor("has_parent", filter); + + filterSelector(filter); + + if (filter.IsConditionless) + return CreateConditionlessFilterDescriptor("has_parent", filter); + + return new FilterDescriptor() { HasParentFilter = filter }; + } + /// /// A limit filter limits the number of documents (per shard) to execute on. /// diff --git a/src/Nest/DSL/IFilterDescriptor.cs b/src/Nest/DSL/IFilterDescriptor.cs index 2921d0b266f..19a57060d28 100644 --- a/src/Nest/DSL/IFilterDescriptor.cs +++ b/src/Nest/DSL/IFilterDescriptor.cs @@ -23,6 +23,7 @@ interface IFilterDescriptor BaseFilter GeoPolygon(string field, IEnumerable> points); BaseFilter GeoPolygon(string fieldName, params string[] points); BaseFilter HasChild(Action> querySelector) where K : class; + BaseFilter HasParent(Action> querySelector) where K : class; BaseFilter Ids(IEnumerable types, IEnumerable values); BaseFilter Ids(IEnumerable values); BaseFilter Ids(string type, IEnumerable values); diff --git a/src/Nest/Nest.csproj b/src/Nest/Nest.csproj index b8331c1a974..1be4a3d37ee 100644 --- a/src/Nest/Nest.csproj +++ b/src/Nest/Nest.csproj @@ -70,6 +70,7 @@ +