在 Hibernate Search 中输入单个 return
Single return type in Hibernate Search
假设我有一个包含许多不同实体的应用,它们之间没有关系。
我想创建一个查询所有这些的搜索,但是 returns 一个统一的类型,即:
class SearchResult {
String stype;
String title;
String teaser;
}
所以我的想法是索引实体并将它们的值放入一个索引(具有相同的索引字段):
@Indexed(index = "idx_search")
class Book {
@Field(name = "stype", analyze = ...)
final String stype = "BOOK";
@Field(name = "title", analyze = ...)
String bookTitle;
@Field(name = "teaser", analyze = ...)
String bookBlurb ;
}
@Indexed(index = "idx_search")
class Person{
@Field(name = "stype", analyze = ...)
final String stype = "PERSON";
@Field(name = "title", analyze = ...)
String fullname;
@Field(name = "teaser", analyze = ...)
String profileIntroText;
}
@Indexed(index = "idx_search")
class Location{
@Field(name = "stype", analyze = ...)
final String stype = "LOCATION";
@Field(name = "title", analyze = ...)
String streetPcAndCity;
@Field(name = "teaser", analyze = ...)
String wikiIntoText;
}
如您所见,索引名称和字段名称在所有实体上都是相同的。
现在我想查询他们得到这样的结果:
SearchResult[stype: PERSON, title: Spongebob, teaser: A funny sponge]
SearchResult[stype: BOOK, title: Clean Architecture , teaser: A Craftsmans Guide to Software...]
SearchResult[stype: PERSON, title: Patric, teaser: A funny seastar]
SearchResult[stype: LOCATION, title: Hannover, teaser: A city in Germany]
所以 SearchResult 不是一个实体,而只是将结果合并为一个类型。索引有效,但我必须在搜索时将实体类型传递到查询和 QueryBuilder 中:
final QueryBuilder queryBuilder = fullTextEntityManager
.getSearchFactory()
.buildQueryBuilder()
.forEntity(SearchResult.class)
.get();
...
Hibernate 然后 returns 这个错误信息:
HSEARCH000331: Can't build query for type 'SearchResult' which is neither configured nor has any configured sub-types.
你认为有办法让它发挥作用吗?
请注意,您不需要将每种类型分配给相同的索引; Hibernate Search 完全能够在单个查询中搜索多个索引。并且性能可能是相同的(无论如何,Lucene 索引通常在引擎盖下分成多个段)。
话虽这么说,假设 SearchResult
:
中有一个构造函数,您可以这样做
class SearchResult {
String stype;
String title;
String teaser;
public SearchResult(String stype, String title, String teaser) {
this.stype = stype;
this.title = title;
this.teaser = teaser;
}
}
将您的字段标记为已存储:
@Indexed
class Book {
@Field(name = "stype", store = Store.YES, analyze = ...)
final String stype = "BOOK";
@Field(name = "title", store = Store.YES, analyze = ...)
String bookTitle;
@Field(name = "teaser", store = Store.YES, analyze = ...)
String bookBlurb ;
}
@Indexed
class Person{
@Field(name = "stype", store = Store.YES, analyze = ...)
final String stype = "PERSON";
@Field(name = "title", store = Store.YES, analyze = ...)
String fullname;
@Field(name = "teaser", store = Store.YES, analyze = ...)
String profileIntroText;
}
@Indexed
class Location{
@Field(name = "stype", store = Store.YES, analyze = ...)
final String stype = "LOCATION";
@Field(name = "title", store = Store.YES, analyze = ...)
String streetPcAndCity;
@Field(name = "teaser", store = Store.YES, analyze = ...)
String wikiIntoText;
}
然后这样查询:
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager( entityManager);
final QueryBuilder queryBuilder = fullTextEntityManager
.getSearchFactory()
.buildQueryBuilder()
.forEntity(Book.class)
.get();
Query luceneQuery = ...;
FullTextQuery query = fullTextEntityManager.createFullTextQuery(query, Book.class, Person.class, Location.class);
query.setProjection("stype", "title", "teaser");
query.setMaxResults(20);
List<Object[]> arrayResults = query.list();
List<SearchResult> hits = new ArrayList<>();
for (Object[] array : arrayResults) {
hits.add(new SearchResult((String) array[0], (String) array[1], (String) array[2]);
}
另请注意,如果您upgraded to Hibernate Search 6.
,这可能会明显减少尴尬
您需要对映射进行一些更改:
@Indexed
class Book {
@KeywordField(name = "stype", projectable = Projectable.YES)
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
final String stype = "BOOK";
@FullTextField(name = "title", projectable = Projectable.YES, analyzer = ...)
String bookTitle;
@FullTextField(name = "teaser", projectable = Projectable.YES, analyzer = ...)
String bookBlurb ;
}
@Indexed
class Person{
@KeywordField(name = "stype", projectable = Projectable.YES)
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
final String stype = "PERSON";
@FullTextField(name = "title", projectable = Projectable.YES, analyzer = ...)
String fullname;
@FullTextField(name = "teaser", projectable = Projectable.YES, analyzer = ...)
String profileIntroText;
}
@Indexed
class Location{
@KeywordField(name = "stype", projectable = Projectable.YES)
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
final String stype = "LOCATION";
@FullTextField(name = "title", projectable = Projectable.YES, analyzer = ...)
String streetPcAndCity;
@FullTextField(name = "teaser", projectable = Projectable.YES, analyzer = ...)
String wikiIntoText;
}
但后来我认为搜索时的改进是值得的:
List<SearchResult> hits = Search.session(entityManager)
.search(Book.class, Person.class, Location.class)
.select(f -> f.composite(
SearchResult::new,
f.field("stype", String.class),
f.field("title", String.class),
f.field("teaser", String.class)))
.where(f -> ...)
.fetchHits( 20 );
仍在 Hibernate Search 6 中(尽管我相信您至少需要 6.1),您甚至可以使用接口:
interface Searchable {
@KeywordField(projectable = Projectable.YES, analyzer = ...)
String getStype();
@FullTextField(projectable = Projectable.YES, analyzer = ...)
String getTitle();
@FullTextField(projectable = Projectable.YES, analyzer = ...)
String getTeaser();
}
@Indexed
class Book implements Searchable {
String bookTitle;
String bookBlurb;
@Override
@javax.persistence.Transient
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
String getStype() {
return "BOOK";
}
@Override
@javax.persistence.Transient
@IndexingDependency(derivedFrom = @ObjectPath(
@PropertyValue(propertyName = "bookTitle")
))
String getTitle() {
return bookTitle;
}
@Override
@javax.persistence.Transient
@IndexingDependency(derivedFrom = @ObjectPath(
@PropertyValue(propertyName = "bookTitle")
))
String getTeaser() {
return bookBlurb;
}
}
@Indexed
class Person implements Searchable {
String fullname;
String profileIntroText;
@Override
@javax.persistence.Transient
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
String getStype() {
return "PERSON";
}
@Override
@javax.persistence.Transient
@IndexingDependency(derivedFrom = @ObjectPath(
@PropertyValue(propertyName = "bookTitle")
))
String getTitle() {
return bookTitle;
}
@Override
@javax.persistence.Transient
@IndexingDependency(derivedFrom = @ObjectPath(
@PropertyValue(propertyName = "bookTitle")
))
String getTeaser() {
return bookBlurb;
}
}
@Indexed
class Location implements Searchable {
String streetPcAndCity;
String wikiIntoText;
@Override
@javax.persistence.Transient
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
String getStype() {
return "LOCATION";
}
@Override
@javax.persistence.Transient
@IndexingDependency(derivedFrom = @ObjectPath(
@PropertyValue(propertyName = "streetPcAndCity")
))
String getTitle() {
return streetPcAndCity;
}
@Override
@javax.persistence.Transient
@IndexingDependency(derivedFrom = @ObjectPath(
@PropertyValue(propertyName = "wikiIntoText")
))
String getTeaser() {
return wikiIntoText;
}
}
那么你可以这样搜索:
List<SearchResult> hits = Search.session(entityManager)
.search(Searchable.class)
.select(f -> f.composite(
SearchResult::new,
f.field("stype", String.class),
f.field("title", String.class),
f.field("teaser", String.class)))
.where(f -> ...)
.fetchHits( 20 );
或者,您可以直接加载实体:
List<Searchable> hits = Search.session(entityManager)
.search(Searchable.class)
.where(f -> ...)
.fetchHits( 20 );
for (Searchable hit : hits) {
String stype = hit.getStype();
String title = hit.getTitle();
String teaser = hit.getTeaser();
if ( hit instanceof Book ) {
...
}
else if ( hit instanceof Location ) {
...
}
else {
...
}
}
假设我有一个包含许多不同实体的应用,它们之间没有关系。
我想创建一个查询所有这些的搜索,但是 returns 一个统一的类型,即:
class SearchResult {
String stype;
String title;
String teaser;
}
所以我的想法是索引实体并将它们的值放入一个索引(具有相同的索引字段):
@Indexed(index = "idx_search")
class Book {
@Field(name = "stype", analyze = ...)
final String stype = "BOOK";
@Field(name = "title", analyze = ...)
String bookTitle;
@Field(name = "teaser", analyze = ...)
String bookBlurb ;
}
@Indexed(index = "idx_search")
class Person{
@Field(name = "stype", analyze = ...)
final String stype = "PERSON";
@Field(name = "title", analyze = ...)
String fullname;
@Field(name = "teaser", analyze = ...)
String profileIntroText;
}
@Indexed(index = "idx_search")
class Location{
@Field(name = "stype", analyze = ...)
final String stype = "LOCATION";
@Field(name = "title", analyze = ...)
String streetPcAndCity;
@Field(name = "teaser", analyze = ...)
String wikiIntoText;
}
如您所见,索引名称和字段名称在所有实体上都是相同的。
现在我想查询他们得到这样的结果:
SearchResult[stype: PERSON, title: Spongebob, teaser: A funny sponge]
SearchResult[stype: BOOK, title: Clean Architecture , teaser: A Craftsmans Guide to Software...]
SearchResult[stype: PERSON, title: Patric, teaser: A funny seastar]
SearchResult[stype: LOCATION, title: Hannover, teaser: A city in Germany]
所以 SearchResult 不是一个实体,而只是将结果合并为一个类型。索引有效,但我必须在搜索时将实体类型传递到查询和 QueryBuilder 中:
final QueryBuilder queryBuilder = fullTextEntityManager
.getSearchFactory()
.buildQueryBuilder()
.forEntity(SearchResult.class)
.get();
...
Hibernate 然后 returns 这个错误信息:
HSEARCH000331: Can't build query for type 'SearchResult' which is neither configured nor has any configured sub-types.
你认为有办法让它发挥作用吗?
请注意,您不需要将每种类型分配给相同的索引; Hibernate Search 完全能够在单个查询中搜索多个索引。并且性能可能是相同的(无论如何,Lucene 索引通常在引擎盖下分成多个段)。
话虽这么说,假设 SearchResult
:
class SearchResult {
String stype;
String title;
String teaser;
public SearchResult(String stype, String title, String teaser) {
this.stype = stype;
this.title = title;
this.teaser = teaser;
}
}
将您的字段标记为已存储:
@Indexed
class Book {
@Field(name = "stype", store = Store.YES, analyze = ...)
final String stype = "BOOK";
@Field(name = "title", store = Store.YES, analyze = ...)
String bookTitle;
@Field(name = "teaser", store = Store.YES, analyze = ...)
String bookBlurb ;
}
@Indexed
class Person{
@Field(name = "stype", store = Store.YES, analyze = ...)
final String stype = "PERSON";
@Field(name = "title", store = Store.YES, analyze = ...)
String fullname;
@Field(name = "teaser", store = Store.YES, analyze = ...)
String profileIntroText;
}
@Indexed
class Location{
@Field(name = "stype", store = Store.YES, analyze = ...)
final String stype = "LOCATION";
@Field(name = "title", store = Store.YES, analyze = ...)
String streetPcAndCity;
@Field(name = "teaser", store = Store.YES, analyze = ...)
String wikiIntoText;
}
然后这样查询:
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager( entityManager);
final QueryBuilder queryBuilder = fullTextEntityManager
.getSearchFactory()
.buildQueryBuilder()
.forEntity(Book.class)
.get();
Query luceneQuery = ...;
FullTextQuery query = fullTextEntityManager.createFullTextQuery(query, Book.class, Person.class, Location.class);
query.setProjection("stype", "title", "teaser");
query.setMaxResults(20);
List<Object[]> arrayResults = query.list();
List<SearchResult> hits = new ArrayList<>();
for (Object[] array : arrayResults) {
hits.add(new SearchResult((String) array[0], (String) array[1], (String) array[2]);
}
另请注意,如果您upgraded to Hibernate Search 6.
,这可能会明显减少尴尬您需要对映射进行一些更改:
@Indexed
class Book {
@KeywordField(name = "stype", projectable = Projectable.YES)
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
final String stype = "BOOK";
@FullTextField(name = "title", projectable = Projectable.YES, analyzer = ...)
String bookTitle;
@FullTextField(name = "teaser", projectable = Projectable.YES, analyzer = ...)
String bookBlurb ;
}
@Indexed
class Person{
@KeywordField(name = "stype", projectable = Projectable.YES)
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
final String stype = "PERSON";
@FullTextField(name = "title", projectable = Projectable.YES, analyzer = ...)
String fullname;
@FullTextField(name = "teaser", projectable = Projectable.YES, analyzer = ...)
String profileIntroText;
}
@Indexed
class Location{
@KeywordField(name = "stype", projectable = Projectable.YES)
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
final String stype = "LOCATION";
@FullTextField(name = "title", projectable = Projectable.YES, analyzer = ...)
String streetPcAndCity;
@FullTextField(name = "teaser", projectable = Projectable.YES, analyzer = ...)
String wikiIntoText;
}
但后来我认为搜索时的改进是值得的:
List<SearchResult> hits = Search.session(entityManager)
.search(Book.class, Person.class, Location.class)
.select(f -> f.composite(
SearchResult::new,
f.field("stype", String.class),
f.field("title", String.class),
f.field("teaser", String.class)))
.where(f -> ...)
.fetchHits( 20 );
仍在 Hibernate Search 6 中(尽管我相信您至少需要 6.1),您甚至可以使用接口:
interface Searchable {
@KeywordField(projectable = Projectable.YES, analyzer = ...)
String getStype();
@FullTextField(projectable = Projectable.YES, analyzer = ...)
String getTitle();
@FullTextField(projectable = Projectable.YES, analyzer = ...)
String getTeaser();
}
@Indexed
class Book implements Searchable {
String bookTitle;
String bookBlurb;
@Override
@javax.persistence.Transient
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
String getStype() {
return "BOOK";
}
@Override
@javax.persistence.Transient
@IndexingDependency(derivedFrom = @ObjectPath(
@PropertyValue(propertyName = "bookTitle")
))
String getTitle() {
return bookTitle;
}
@Override
@javax.persistence.Transient
@IndexingDependency(derivedFrom = @ObjectPath(
@PropertyValue(propertyName = "bookTitle")
))
String getTeaser() {
return bookBlurb;
}
}
@Indexed
class Person implements Searchable {
String fullname;
String profileIntroText;
@Override
@javax.persistence.Transient
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
String getStype() {
return "PERSON";
}
@Override
@javax.persistence.Transient
@IndexingDependency(derivedFrom = @ObjectPath(
@PropertyValue(propertyName = "bookTitle")
))
String getTitle() {
return bookTitle;
}
@Override
@javax.persistence.Transient
@IndexingDependency(derivedFrom = @ObjectPath(
@PropertyValue(propertyName = "bookTitle")
))
String getTeaser() {
return bookBlurb;
}
}
@Indexed
class Location implements Searchable {
String streetPcAndCity;
String wikiIntoText;
@Override
@javax.persistence.Transient
@IndexingDependency(reindexOnUpdate = ReindexOnUpdate.NO)
String getStype() {
return "LOCATION";
}
@Override
@javax.persistence.Transient
@IndexingDependency(derivedFrom = @ObjectPath(
@PropertyValue(propertyName = "streetPcAndCity")
))
String getTitle() {
return streetPcAndCity;
}
@Override
@javax.persistence.Transient
@IndexingDependency(derivedFrom = @ObjectPath(
@PropertyValue(propertyName = "wikiIntoText")
))
String getTeaser() {
return wikiIntoText;
}
}
那么你可以这样搜索:
List<SearchResult> hits = Search.session(entityManager)
.search(Searchable.class)
.select(f -> f.composite(
SearchResult::new,
f.field("stype", String.class),
f.field("title", String.class),
f.field("teaser", String.class)))
.where(f -> ...)
.fetchHits( 20 );
或者,您可以直接加载实体:
List<Searchable> hits = Search.session(entityManager)
.search(Searchable.class)
.where(f -> ...)
.fetchHits( 20 );
for (Searchable hit : hits) {
String stype = hit.getStype();
String title = hit.getTitle();
String teaser = hit.getTeaser();
if ( hit instanceof Book ) {
...
}
else if ( hit instanceof Location ) {
...
}
else {
...
}
}