Description
8.17.2:
v 8.17.2:
Dotnet 8:
Windows 11:
Description of the problem including expected versus actual behavior:
Since Replacing NEST library with new client library I have become unable to use has child queries where two differing document types are involved in any capacity, nor can I properly access the document types of inner hits. Nor can I find a way to explicitly define both the parent and child properties during the indexing request building.
Assuming the very simplistic type hierarchy below:
public class BaseDocument
{
public string Id { get; set; }
public required JoinField JoinField { get; set; }
}
public class Child: BaseDocument
{
public string ChildProperty { get; set; }
}
public class Parent : BaseDocument
{
public string ParentProperty { get; set; }
}
Steps to reproduce:
- Create the Index
var createResponse = await client.Indices.CreateAsync("jointest2",
m => m
.Mappings(m => m
.Routing(r => r.Required(true))
.Properties<Parent>(
p => p
.Keyword(k => k.Id)
.Join(j => j.JoinField, j => j
.Relations(r => r
.Add(client.Infer.RelationName<Parent>(), client.Infer.RelationName<Child>())
)
)
)
//cannot call properties twice without replacing prior call
//.Properties<Child>(p => p
// .Keyword(k => k.Id)
// .Text(t => t.ChildProperty)
// .Join(j => j.JoinField, j => j
// .Relations(r => r
// .Add(client.Infer.RelationName<Parent>(), client.Infer.RelationName<Child>())
// )
// )
//)
)
);
//however hand-rolling properties as dictionary does work if I don't specify a base indexing type
//var props = new Properties();
//props.Add<Parent>(p => p.Id, new KeywordProperty { });
//props.Add<Parent>(p => p.ParentProperty, new TextProperty { });
//props.Add<Parent>(p => p.JoinField, new JoinProperty
//{
// Relations = new Dictionary<string, Union<string, ICollection<string>>>
// {
// [client.Infer.RelationName<Parent>()] = client.Infer.RelationName<Child>()
// }
//});
//props.Add<Child>(p => p.ChildProperty, new TextProperty { });
var parent = new Parent { Id = "1", ParentProperty = "Parent", JoinField = JoinField.Root<Parent>() };
var child = new Child { Id = "2", ChildProperty = "Child", JoinField = JoinField.Link<Child>(1) };
await client.IndexAsync<Parent>(parent, index: "jointest", i => i.Routing(Routing.From(parent)));
await client.IndexAsync<Child>(child, index: "jointest", i => i.Routing(Infer.Route(child)));
- I am unable to define a styped searchAsync call that includes strongly typed Parent document results while that includes a strongly typed HasChild query.
This technically works, but leaves me no way to access inner hits in a deserialized form:
var query = await client.SearchAsync<Parent>("jointest", s => s.
Query(q => q
.Bool(b => b.Must(m => m
.HasChild( c => c
.Type(client.Infer.RelationName<Child>())
//MatchAll can't be empty, even though we have nothing we need to configure
.Query(cq => cq.MatchAll(new Elastic.Clients.Elasticsearch.QueryDsl.MatchAllQuery()))
.MinChildren(1)
.InnerHits(new Elastic.Clients.Elasticsearch.Core.Search.InnerHits { Name = "child" })
)
)
)
)
);
but I have no way to access strongly typed inner hits
var inners = query.Hits.SelectMany(h => h.InnerHits.SelectMany(i => i.Value.Hits.Hits.Select(h => h.Source)));
inners is an IEnumerable<object>
and casting to Child
does not work.
Expected behavior
Previously, using NEST I could issue a query like this:
var result2 = await client.SearchAsync<Parent>(
s => s
.Index("jointest")
.Query(q => q.Bool(b => b.Must(q => q.HasChild<Child>(
c => c.Query(
c => c
.Match(t => t.Field(f => f.ChildProperty).Query("Child"))
).InnerHits(ih => ih.Name("child"))
)
)
)));
I could also access strongly typed inner hits like so:
result2.Hits.SelectMany(h => h.InnerHits["child"].Documents<Child>());
As far as I can tell, there is no way to handle multiple types in the same query in such a fashion with the new library.
How can I work around the loss of these features?