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 @@
+