【问题标题】:How do I combine a range query with a keyword query in Hibernate Search?如何在 Hibernate Search 中将范围查询与关键字查询结合起来?
【发布时间】:2020-06-10 10:45:38
【问题描述】:

鉴于此域模型:

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();

这会返回什么:

  • 具有 React 技能的用户(无论级别如何)AND
  • 拥有高级或更高技能的用户(无论技能名称如何)。

我期望它返回的结果:

  • 拥有高级或更高 React 技能的用户。

【问题讨论】:

标签: java hibernate lucene hibernate-search


【解决方案1】:

在您的查询中,您从未提到这两个约束(技能名称和技能级别)必须适用于同一个技能。这就是你得到这个结果的原因。

在 Hibernate Search 5 中,不支持这种“加入”。您可以做的最好的事情是在单个字段(连接)中索引技能名称和技能级别,但这有点小技巧。

在 Hibernate Search 6 中,支持 nested 字段,从而解决了这个问题。然而,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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-11
    • 1970-01-01
    • 2011-12-22
    • 1970-01-01
    • 2017-01-28
    • 1970-01-01
    相关资源
    最近更新 更多