Elasticsearch 5.x中过滤子文档且保留父节点的C#实现求助
首先提个小细节:你给出的JSON数据结构里teamMembers格式有问题,它应该是数组类型(用[]包裹),否则不符合JSON规范,而且Elasticsearch要处理嵌套子文档的话,teamMembers必须是nested类型的数组,而非单个对象。这是实现需求的前提,先确认你的索引映射里teamMembers已经设置为nested类型,否则嵌套查询根本不会生效。
回到你的需求:获取所有游戏,但每个游戏只显示isVisible=true的成员。你之前的代码只做了过滤父文档(找出至少有一个可见成员的游戏),但没有指定要返回符合条件的子文档——这就是为什么你看到的结果里还是包含了所有成员。
在Elasticsearch 5中,要实现这个需求,需要结合Nested查询和InnerHits,具体步骤如下:
1. 确认索引映射(关键前提)
首先确保teamMembers字段被正确映射为nested类型,否则嵌套查询无法工作。创建索引时的映射应该类似这样:
PUT /test { "mappings": { "Game": { "properties": { "gameName": { "type": "text" }, "teamMembers": { "type": "nested", "properties": { "id": { "type": "keyword" }, "name": { "type": "text" }, "isVisible": { "type": "boolean" } } } } } } }
2. 修改C#查询代码
你需要在Nested查询中添加InnerHits,这样Elasticsearch会返回符合条件的子文档。修改后的代码如下:
var searchResponse = client.Search<Game>(s => s .Index("test") .Query(q => q .Nested(n => n .Path(p => p.teamMembers) // 指定嵌套字段路径 .Query(nq => nq .Match(m => m .Field(f => f.teamMembers.First().isVisible) .Query("true") ) ) .InnerHits() // 核心:返回符合条件的子文档集合 ) ) );
3. 客户端处理结果
Elasticsearch默认不会修改原始的_source字段,所以符合条件的子文档会放在InnerHits中返回。如果你想把过滤后的成员直接替换到游戏对象的teamMembers属性里,可以在客户端做如下处理:
// 假设你的Game类中teamMembers是List<TeamMember>类型 foreach (var hit in searchResponse.Hits) { // 从InnerHits中取出所有可见的成员 var visibleMembers = hit.InnerHits["teamMembers"].Documents<TeamMember>().ToList(); // 替换原有的teamMembers集合 hit.Source.teamMembers = visibleMembers; }
为什么之前的代码没生效?
你之前的代码只是用Nested查询过滤出了包含可见成员的游戏,但没有告诉Elasticsearch要返回过滤后的子文档——InnerHits就是用来指定这一点的。没有它的话,Elasticsearch只会返回完整的父文档_source,不会对嵌套子文档做过滤。
内容的提问来源于stack exchange,提问作者Qutaiba Mustafa

