首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Hibernate搜索中的单返回类型

Hibernate搜索中的单返回类型
EN

Stack Overflow用户
提问于 2021-10-27 09:11:53
回答 1查看 149关注 0票数 0

假设我有一个有很多不同实体的应用程序,它们之间没有关系。

我想创建一个查询所有这些内容的搜索,但是返回一个统一的类型,即:

代码语言:javascript
复制
class SearchResult {
  String stype;
  String title;
  String teaser;
}

因此,我的想法是对实体进行索引,并将它们的值放入一个索引中(具有相同的索引字段):

代码语言:javascript
复制
@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;
}

如您所见,索引名称和字段名在所有实体上是相同的。

现在我想询问他们得到这样的结果:

代码语言:javascript
复制
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:

代码语言:javascript
复制
final QueryBuilder queryBuilder = fullTextEntityManager
            .getSearchFactory()
            .buildQueryBuilder()
            .forEntity(SearchResult.class)
            .get();
...

然后Hibernate返回以下错误消息:

HSEARCH000331: Can't build query for type 'SearchResult' which is neither configured nor has any configured sub-types.

你认为这有办法让这件事成功吗?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-10-27 09:46:06

注意,您不需要将每个类型分配给相同的索引;Hibernate搜索完全能够在一个查询中通过多个索引进行搜索。而且性能可能是相同的(Lucene索引通常被分割成多个段,不管怎么说)。

尽管如此,假设SearchResult中有一个构造函数,您可以这样做

代码语言:javascript
复制
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;
  }
}

我将从Hibernate搜索6开始,因为如果您使用升级,这可能会大大减少尴尬。您将在下面的Hibernate搜索5中找到一个旧的答案。

对于Hibernate搜索6,您需要对映射进行一些更改:

代码语言:javascript
复制
@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;
}

但我认为搜索时的改进是值得的:

代码语言:javascript
复制
List<SearchResult> hits = Search.session(entityManager)
        .search(Arrays.asList(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 ),您甚至可以使用一个接口:

代码语言:javascript
复制
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;
  }
}

然后你就可以这样搜索:

代码语言:javascript
复制
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 );

或者,您可以直接加载实体:

代码语言:javascript
复制
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 { 
        ...
    } 
}

Hibernate搜索6.2开始,通过用@ProjectionConstructor (和编译器标志)注释投影类,可以使投影更加简单:

代码语言:javascript
复制
class SearchResult {
  String stype;
  String title;
  String teaser;

  @ProjectionConstructor
  public SearchResult(String stype, String title, String teaser) {
    this.stype = stype;
    this.title = title;
    this.teaser = teaser;
  }
}

然后你就可以这样搜索:

代码语言:javascript
复制
List<SearchResult> hits = Search.session(entityManager)
        .search(Searchable.class)
        .select(SearchResult.class)
        .where(f -> ...)
        .fetchHits( 20 );

Hibernate搜索5的旧答案:

将您的字段标记为存储:

代码语言:javascript
复制
@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;
}

然后像这样查询:

代码语言:javascript
复制
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]);
}
票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/69735853

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档