如何在 Hibernate Search 中组合范围查询和关键字查询?
How do I combine a range query with a keyword query in Hibernate Search?
给定此领域模型:
class User { // <- this is the root entity
@IndexedEmbedded
private Set<UserSkill> skills = new HashSet<>();
}
class UserSkill {
@IndexedEmbedded
private Skill skill;
@Enumerated
@Field(bridge = @FieldBridge(impl = EnumAsIntegerBridge.class))
private UserSkillLevel level;
}
class Skill {
@Field
private String name;
}
如何结合使用范围查询和术语查询来搜索具有给定级别的特定技能的用户?
我试过这个查询来寻找具有高级 React 技能的用户:
var q = new BooleanQuery.Builder();
q.add(queryBuilder.keyword().onField("skills.skill.name").matching("React").createQuery(), BooleanClause.Occur.MUST);
q.add(queryBuilder.range().onField("skills.level").above(UserSkillLevel.ADVANCED.ordinal()).createQuery(), BooleanClause.Occur.MUST);
var finalQuery = q.build();
这是什么return:
- 拥有 React 技能的用户(无论级别)AND
- 拥有高级技能或更高技能的用户(无论技能名称如何)。
我所期望的return:
- 拥有高级或更高 React 技能的用户。
在您的查询中,您从未提到这两个约束(技能名称和技能级别)必须应用于同一技能。这就是你得到这个结果的原因。
在 Hibernate Search 5 中,不支持那种 "join"。您可以做的最好的事情是在单个字段(串联)中索引技能名称和技能级别,但这有点麻烦。
在 Hibernate Search 6 中,支持 嵌套 字段,解决了这个问题。然而,Hibernate Search 6 的 API 却大不相同。
这里是如何在 Hibernate Search 6 中实现你想要的。
@Indexed
class User {
@IndexedEmbedded(storage = ObjectStorage.NESTED) // Notice this change
private Set<UserSkill> skills = new HashSet<>();
}
class UserSkill {
@IndexedEmbedded
private Skill skill;
@Enumerated
@GenericField(valueBridge = @ValueBridge(impl = EnumAsIntegerBridge.class)) // This changed too
private UserSkillLevel level;
}
class Skill {
@FullTextField(analyzer = "standard") // You will have to define this analyzer, see the getting started guide
private String name;
}
// New implementation of your bridge
class EnumAsIntegerValueBridge implements ValueBridge<Enum<?>, Integer> {
@Override
public Integer toIndexedValue(Enum<?> enum) {
return enum.ordinal();
}
}
然后查询:
List<User> hits = Search.session(entityManager).search(User.class)
.where(f -> f.nested().onField("skills").nest(f.bool()
.must(f.match().field("skills.skill.name").matching("React"))
.must(f.range().field("skills.level").atLeast(UserSkillLevel.ADVANCED))))
.fetchHits(20);
请注意,您需要更新依赖项(groupId、artifactId 和版本)。您可能想先看看 getting started guide。
给定此领域模型:
class User { // <- this is the root entity
@IndexedEmbedded
private Set<UserSkill> skills = new HashSet<>();
}
class UserSkill {
@IndexedEmbedded
private Skill skill;
@Enumerated
@Field(bridge = @FieldBridge(impl = EnumAsIntegerBridge.class))
private UserSkillLevel level;
}
class Skill {
@Field
private String name;
}
如何结合使用范围查询和术语查询来搜索具有给定级别的特定技能的用户?
我试过这个查询来寻找具有高级 React 技能的用户:
var q = new BooleanQuery.Builder();
q.add(queryBuilder.keyword().onField("skills.skill.name").matching("React").createQuery(), BooleanClause.Occur.MUST);
q.add(queryBuilder.range().onField("skills.level").above(UserSkillLevel.ADVANCED.ordinal()).createQuery(), BooleanClause.Occur.MUST);
var finalQuery = q.build();
这是什么return:
- 拥有 React 技能的用户(无论级别)AND
- 拥有高级技能或更高技能的用户(无论技能名称如何)。
我所期望的return:
- 拥有高级或更高 React 技能的用户。
在您的查询中,您从未提到这两个约束(技能名称和技能级别)必须应用于同一技能。这就是你得到这个结果的原因。
在 Hibernate Search 5 中,不支持那种 "join"。您可以做的最好的事情是在单个字段(串联)中索引技能名称和技能级别,但这有点麻烦。
在 Hibernate Search 6 中,支持 嵌套 字段,解决了这个问题。然而,Hibernate Search 6 的 API 却大不相同。
这里是如何在 Hibernate Search 6 中实现你想要的。
@Indexed
class User {
@IndexedEmbedded(storage = ObjectStorage.NESTED) // Notice this change
private Set<UserSkill> skills = new HashSet<>();
}
class UserSkill {
@IndexedEmbedded
private Skill skill;
@Enumerated
@GenericField(valueBridge = @ValueBridge(impl = EnumAsIntegerBridge.class)) // This changed too
private UserSkillLevel level;
}
class Skill {
@FullTextField(analyzer = "standard") // You will have to define this analyzer, see the getting started guide
private String name;
}
// New implementation of your bridge
class EnumAsIntegerValueBridge implements ValueBridge<Enum<?>, Integer> {
@Override
public Integer toIndexedValue(Enum<?> enum) {
return enum.ordinal();
}
}
然后查询:
List<User> hits = Search.session(entityManager).search(User.class)
.where(f -> f.nested().onField("skills").nest(f.bool()
.must(f.match().field("skills.skill.name").matching("React"))
.must(f.range().field("skills.level").atLeast(UserSkillLevel.ADVANCED))))
.fetchHits(20);
请注意,您需要更新依赖项(groupId、artifactId 和版本)。您可能想先看看 getting started guide。