如何获取与hibernate search facet相关的对象

How to get the object related to hibernate search facet

我正在关注关于 faceting 的 hibernate-search 文档来自:https://docs.jboss.org/hibernate/stable/search/reference/en-US/html_single/#query-faceting

我能够生成我的方面,例如我有我的 authorNames + count:

name1 = 100
name2 = 200
and so on..

但我的问题是,如果我想显示它的一些其他详细信息,我该如何查询 Author 对象。

以我现在的例子为例:

FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Book.class).get();

org.apache.lucene.search.Query luceneQuery = qb.all().createQuery();
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, Book.class);

FacetingRequest authorFacet = qb.facet().name("authorFacetRequest").onField("authors.name_facet").discrete()
        .orderedBy(FacetSortOrder.FIELD_VALUE).includeZeroCounts(false).createFacetingRequest();

// retrieve facet manager and apply faceting request
FacetManager facetManager = fullTextQuery.getFacetManager();
facetManager.enableFaceting(authorFacet);

// retrieve the faceting results
List<Facet> facets = facetManager.getFacets("authorFacetRequest");
facets.forEach(p -> log.info(p.getValue() + " - " + p.getCount()));

但在 GUI 中,我想创建一个 link,id=author.id。但是 author.id 无法使用 facet。所以我想要的是:

List<facetName (authorName), count, referenceId>

实现它的最简单有效的方法是什么?有没有办法不用多次查询就可以做到这一点?

您可以定位作者标识符而不是作者姓名。

写一个class来保存你的结果:

public class EntityFacet<T> implements Facet {
  private final Facet delegate;
  private final T entity;
  public EntityFacet(Facet delegate, T entity) {
    // ...
  }

  // delegate all Facet methods to the delegate

  public T getEntity() {
    return entity;
  }
}

为作者 ID 添加字段和 Facet:

@Indexed
@Entity
public class Author {

  @Id
  // Discrete faceting only works on text fields, so we don't use the default bridge
  @Field(name = "id_for_facet", analyze = Analyze.NO, bridge = @Bridge(impl = org.hibernate.search.bridge.builtin.IntegerBridge))
  @Facet(name = "id_facet", forField = "id_for_facet")
  private Integer id;

  // ...
}

(如果您在 Book class 中的 @IndexedEmbedded 中有任何限制,例如 includePaths,请务必更新它们以包含这个新的面)

如果只需要姓名和ID,那么两个facet做两次查询就可以了。

如果您需要更多信息,请更改您的代码以针对 ID 方面:

FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
QueryBuilder qb = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Book.class).get();

org.apache.lucene.search.Query luceneQuery = qb.all().createQuery();
FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(luceneQuery, Book.class);

FacetingRequest authorFacet = qb.facet().name("authorFacetRequest").onField("authors.id_facet").discrete()
        .includeZeroCounts(false).createFacetingRequest();

// retrieve facet manager and apply faceting request
FacetManager facetManager = fullTextQuery.getFacetManager();
facetManager.enableFaceting(authorFacet);

// retrieve the faceting results
List<Facet> facets = facetManager.getFacets("authorFacetRequest");

最后,做一些post-处理:

List<Integer> authorIds = facets.stream().map( f -> {
          try {
            return Integer.parseInt(f.getValue());
          }
          catch (NumberFormatException e) {
            throw new RuntimeException("Unexpected author ID format", e);
          }
        } )
        .collect(Collectors.asList());
List<Author> authors = fullTextEntityManager.unwrap(Session.class)
    .byId(Author.class)
    .multiLoad( authorIds );
List<EntityFacet<Author>> entityFacets = new ArrayList<>(facets.size());
for (int i = 0; i < facets.size() ) {
  entityFacets.add(new EntityFacet(facets.get(i), authors.get(i)));
}
Collator nameSort = new Collator();
nameSort.setStrength(Collator.PRIMARY);
Collections.sort(entityFacets, Comparator.comparing(f -> f.getEntity().getName(), nameSort));
entityFacets.forEach(p -> log.info(p.getEntity() + " - " + p.getCount()));

我没有尝试 运行 代码,但应该就是这样,出现或出现一些语法错误。

当然,它有点太复杂了,尤其是 ID 解析的东西。但我不认为你可以做得更好,直到我们改进分面(这应该发生在搜索 6 中)。


编辑:使用 Hibernate Search 6 的(更)简单的解决方案:

为作者 ID 添加一个可聚合字段:

@Indexed
@Entity
public class Author {

  @Id
  @GenericField(aggregable = Aggregable.YES)
  private Integer id;

  // ...
}

(如果您在 Book class 中的 @IndexedEmbedded 中有任何限制,例如 includePaths,请务必更新它们以包含这个新的面)

如果只需要姓名和ID,那么两个facet做两次查询就可以了。

如果您需要更多信息,请更改您的代码以针对 ID 方面:

AggregationKey<Map<Integer, Long>> countByAuthorIdKey = AggregationKey.of("countByAuthorId");
Map<Integer, Long> countByAuthorId = Search.session(em)
        .search(Book.class)
        .where(f -> f.matchAll())
        .aggregation(countByAuthorIdKey, f -> f.terms()
                .field("authors.id", Integer.class) )
        .fetch(0)
        .aggregation(countByAuthorIdKey);

最后,做一些post-处理:

// Pre-load all authors into the session in a single operation,
// for better performance
em.unwrap(Session.class)
    .byId(Author.class)
    .multiLoad(new ArrayList<>(countByAuthorId.keys()));
Map<Author, Long> countByAuthor = new LinkedHashMap<>(countByAuthorId.size());
for (Map.Entry<Integer, Long> entry : countByAuthorId.entrySet()) {
    countByAuthor.put(em.getReference(Author.class, entry.getKey()), entry.getValue());
}

// Use the map whatever way you want
countByAuthor.forEach((author, count) -> log.info(author + " - " + count));