Elasticsearch 5.4:嵌套文档多事件重复展示及排序方案咨询
Elasticsearch 5.4: 扁平化嵌套事件并按自定义规则排序
针对你的需求——把嵌套的事件数据拆分为单独结果行,同时按MONTH-DAY DESC、YEAR ASC全局排序,且无法在应用层处理百万级数据——在ES5.4中有两种可行的原生方案,取决于你是否能修改现有索引结构:
方案一:改用Parent-Child文档结构(推荐,适合大规模数据)
如果你的数据还能重新索引,这是最直接的方案,因为它把每个事件变成独立的文档,天然支持全局排序和单独返回。
核心特性:Parent-Child关系
ES的Parent-Child允许你将主文档(比如person)和附属事件文档(比如event)关联起来,同时保持各自的独立性。每个事件作为单独的event文档存储,这样你可以直接搜索事件,关联主文档信息,并按自定义规则排序。
步骤1:创建带Parent-Child映射的索引
PUT /people { "mappings": { "person": { "properties": { "id": {"type": "keyword"}, "fullName": {"type": "text"} } }, "event": { "_parent": {"type": "person"}, "properties": { "event_data_type": {"type": "keyword"}, "event_date": {"type": "date"} } } } }
步骤2:重新导入数据
- 先导入主文档(
person类型),比如:PUT /people/person/ID1 { "id": "ID1", "fullName": "John Smith" } - 再导入对应的事件文档,指定
parent为对应主文档的ID:PUT /people/event/1?parent=ID1 { "event_data_type": "BD", "event_date": "1971-12-30T00:00:00Z" }
步骤3:搜索并排序事件
直接搜索event类型文档,通过inner_hits获取关联的主文档信息,同时用脚本排序实现你的自定义排序规则:
GET /people/event/_search { "_source": ["event_data_type", "event_date"], "query": {"match_all": {}}, "sort": [ { "_script": { "type": "string", "script": {"inline": "doc['event_date'].value.format('MM-dd')"}, "order": "desc" } }, { "_script": { "type": "number", "script": {"inline": "doc['event_date'].value.year"}, "order": "asc" } } ], "inner_hits": { "parent": { "type": "person", "_source": ["id", "fullName"] } } }
返回的每个结果都是一个独立的事件,附带主文档的id和fullName,完全符合你期望的输出结构,且排序是全局生效的。
方案二:基于现有Nested类型(无需重新索引)
如果无法重新索引,你可以利用Nested类型 + Inner Hits + 脚本排序,配合Scroll API分批处理结果,避免应用层内存溢出。
核心特性:
- Nested类型:确保ES能独立处理每个嵌套事件(你应该已经配置了这个)。
- Inner Hits:在返回主文档的同时,返回匹配的嵌套事件列表。
- 脚本排序:实现
MONTH-DAY DESC、YEAR ASC的自定义排序逻辑。 - Scroll API:分批获取结果,降低应用层单次处理的数据量。
搜索DSL示例
GET /your_index/_search?scroll=1m { "query": { "nested": { "path": "eventsData", "query": {"match_all": {}}, "inner_hits": { "size": 100, // 每个主文档返回的最大事件数,根据你的数据调整 "_source": ["event_data_type", "event_date"], "sort": [ { "_script": { "type": "string", "script": {"inline": "doc['eventsData.event_date'].value.format('MM-dd')"}, "order": "desc" } }, { "_script": { "type": "number", "script": {"inline": "doc['eventsData.event_date'].value.year"}, "order": "asc" } } ] } } }, "_source": ["id", "fullName"], "size": 1000 // 每次scroll返回的主文档数 }
注意事项
- 这个方案需要应用层遍历每个主文档的
inner_hits,将主文档信息与每个事件组合成单独的结果行。但通过Scroll API分批处理,每次只处理少量数据,不会造成内存压力。 - 由于主文档的排序无法直接对应全局事件的排序,如果你需要严格的全局排序,方案一更合适。
总结:如果能重新索引,优先选择Parent-Child方案;如果不能,用Nested+Inner Hits+Scroll的组合,分批处理结果。
内容的提问来源于stack exchange,提问作者inferno88
相关产品推荐
相关产品推荐

