首页 > 解决方案 > Hibernate 搜索多个实体

问题描述

是否可以在多个实体中搜索相同的关键字,使用特定的过滤器过滤每种类型并将它们放在一个列表中?

例如,拥有实体、饲养员和动物,并在他们的数据中搜索术语“波士顿梗犬”。之后,筛选饲养员的位置和物种的动物,并返回一个包含动物和饲养员的列表。

然后可以修剪此列表以在 Web 应用程序的前端对结果进行分页:

        // FullTextQuery ftq = .... definiing and filtering the query

        int numResults = ftq.getResultSize();
        ftq.setFirstResult((pageNum-1)*numPerPage);
        ftq.setMaxResults(numPerPage);

        List<Object> results = ftq.getResultList();

标签: hibernatelucenehibernate-search

解决方案


假设您不关心饲养员和动物之间的关系,只想搜索动物或饲养员:是的,您可以。

但是有一些条件:

  • 您将应用“波士顿梗犬”术语过滤器的字段必须在班级和班级中以完全相同的方式配置:特别是相同的分析器。否则,您的搜索结果可能会不一致。这些字段不必在两个索引中都存在:对“animalName”和“breederName”字段进行过滤应该可以正常工作。AnimalBreeder
  • 您要用于按位置过滤育种者的位置字段不得存在Animal类中。否则,动物也会按位置过滤。
  • 您要用于按物种过滤动物的物种字段不得存在Breeder类中。否则,育种者也会被物种过滤。

如果以上三个项目的答案都是“是的,我很好”,那么您可以执行以下操作。


编辑:现在我强烈建议升级到 Hibernate Search 6.0 或更高版本,这使得在同一查询中搜索多个索引变得更加容易。

事实上,除了列出目标类型之外,您无需执行任何特定操作。

我的原始答案的片段,转换为 Hibernate Search 6,看起来像这样:

EntityManager em = ...;
List<Object> hits = Search.session(em)
        .search(Arrays.asList(Animal.class, Breeder.class))
        .where(f -> f.bool()
                .must(f.simpleQueryString()
                        .fields("animalName",
                                "breeder.company.legalName",
                                "breeder.firstName",
                                "breeder.lastName")
                        .matching("Boston Terrier")
                        .defaultOperator(BooleanOperator.AND))
                .must(f.bool()
                        .should(f.spatial().within()
                               .field("breederLocation")
                               .circle(42.0, 42.0,
                                        50, DistanceUnit.KILOMETERS))
                        .should(f.match()
                               .field("species")
                               .matching(<some species>)))
        .fetchHits( 20 );

原始解决方案,用于 Hibernate Search 5:

  • 创建一个QueryBuilderfor Animal,比如说animalQueryBuilder
  • 创造另一个QueryBuilderBreederbreederQueryBuilder
  • 使用animalQueryBuilder物种过滤器。
  • 使用breederQueryBuilder位置过滤器
  • 使用一个或另一个构建器(没关系)作为术语过滤器

然后,在创建查询时,将 Animal 类和 Breeder 类都传递给该createFullTextQuery方法。

查询应返回动物和饲养员。

像这样的东西:

QueryBuilder animalQueryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity( Animal.class ).get();
QueryBuilder breederQueryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity( Breeder.class ).get();

Query locationQuery = breederQueryBuilder.spatial()
    .onField( "breederLocation" )
    .within( 10.0, DistanceUnit.KM )
    .ofLatitude( 42.0 ).andLongitude( 42.0 )
    .createQuery();
Query speciesQuery = animalQueryBuilder.keyword()
    .onField( "species" )
    .matching( <some species> );
Query termsQuery = animalQueryBuilder.simpleQueryString()
    .onFields(
        "animalName", "breeder.company.legalName",
        "breeder.firstName", "breeder.lastName"
    )
    .withAndAsDefaultOperator()
    .matching( "Boston Terrier" );

Query booleanQuery = animalQueryBuilder.bool()
    .must( termsQuery )
    .must( animalQueryBuilder.bool()
        .should( locationQuery )
        .should( speciesQuery )
        .createQuery()
    )
    .createQuery();

FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery( booleanQuery, Animal.class, Breeder.class );

推荐阅读