如何获取与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));
我正在关注关于 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));