基于Hibernate Search+Elasticsearch实现父文档紧随子文档查询
Hey there! Let’s work through your problem where you need parent records to show up right before their child documents when querying with Hibernate Search and Elasticsearch. Since you’re using id and parentId to track parent-child relationships, here’s a step-by-step solution tailored to your needs:
1. Update Your Entity Mapping
First, make sure your entity class is indexed with all the fields we need for sorting and relationship tracking. We’ll add a computed field to flag parent documents (those with parentId == null) and ensure key fields are sortable:
@Entity @Indexed public class YourEntity { @Id @GeneratedValue @GenericField(sortable = Sortable.YES) private Long id; @GenericField(sortable = Sortable.YES) private Long parentId; @FullTextField private String name; // Computed field to identify parent documents @GenericField(sortable = Sortable.YES) public boolean isParent() { return parentId == null; } // Getters and setters omitted for brevity }
2. Build the Query with Custom Sorting
The core of the solution is a two-layer sorting strategy:
- Group parents and children together: Use
parentIdfor child documents, and fall back to the parent’s ownid(since parents have a nullparentId). - Ensure parents come first in each group: Sort by the
isParentfield in descending order (parent documents will rank higher).
Here’s how to implement this in your search query:
SearchSession searchSession = Search.session(entityManager); List<YourEntity> results = searchSession.search(YourEntity.class) // Your core query: match documents where name equals "Samule" .where(f -> f.match().field("name").matching("Samule")) // Custom sorting to group parents + children, with parents first .sort(f -> f.composite() // Group related documents: children use parentId, parents use their own id .add(f.field("parentId").missing().sortFirst().then().field("id")) // Within the group, parents come first (true = 1, false = 0 in descending order) .add(f.field("isParent").order(SortOrder.DESC)) ) .fetchHits(20); // Adjust the number of returned hits as needed
Bonus: Include Related Parents/Children Even If They Don’t Match the Query
If you need to return the parent document even when only the child matches (or vice versa), expand the query to include related documents using hasParent and hasChild:
List<YourEntity> results = searchSession.search(YourEntity.class) .where(f -> f.bool() // Match documents where name is "Samule" .should(f.match().field("name").matching("Samule")) // Include parents whose children match the query .should(f.hasChild().type(YourEntity.class).subquery(sub -> sub.match().field("name").matching("Samule"))) // Include children whose parents match the query .should(f.hasParent().type(YourEntity.class).subquery(sub -> sub.match().field("name").matching("Samule"))) ) .sort(f -> f.composite() .add(f.field("parentId").missing().sortFirst().then().field("id")) .add(f.field("isParent").order(SortOrder.DESC)) ) .fetchHits(20);
3. Verify Your Setup
- Reindex your data to ensure the new
isParentfield and sortable attributes are applied to the Elasticsearch index. - Test the query to confirm parents are immediately followed by their children in the results.
内容的提问来源于stack exchange,提问作者Susanta Mohapatra

