休眠搜索以处理两个不同实体对象的相同索引

Posted

技术标签:

【中文标题】休眠搜索以处理两个不同实体对象的相同索引【英文标题】:Hibernate search to work on the same index for two different Entity objects 【发布时间】:2018-06-20 18:39:36 【问题描述】:

两个不同的实体指向数据库中的同一个表,休眠搜索可以为它们创建和管理一个索引,因为从技术上讲它们指向同一个表?在我的场景中,创建了两个不同的索引。我怎样才能告诉休眠搜索在同一个索引上工作

Person1.java

@Entity
@Indexed(index = "com.Company.Person")
@Table(name = "Person")
public class Person

    @Id
    @Column(name = "PERSON_ID")
    Long id;

   @Field
   @Column(name = "NAME_FIRST_KEY")
   String firstKey;

Person2.java

    @Entity
    @Indexed(index = "com.Company.Person")
    @Table(name = "Person")
    public class WritablePerson
    
        @Id
        @Column(name = "PERSON_ID")
        Long id;

        @Field
        @Column(name = "NAME_FIRST_KEY")
        String firstKey; 
    

我已经让两个类都指向同一个索引“com.Company.Person”,但它仍然根据索引中的 _hibernate_class 将它们分开。

我有两个实体指向同一个表的原因是,在我们的生产代码中,我们有两个不同的实体。一个用于写入,另一个用于读取。所以我希望我的索引是同步的。

如果我使用相同的实体进行读写,那么一切都会按预期工作。 但是有没有其他方法可以使上述场景正常工作?

【问题讨论】:

你考虑过使用多对一的表吗? 出于好奇,为什么你有两个不同的实体用于写入和读取同一个表?我想你正在实现 CQRS,但我认为它通常是在更高级别实现的:有一个低级、JPA 注释的模型,非常适合你的数据库,然后有两个不同的更高级别的查询和命令使用适当的表示在其之上的模型,例如使用自动对象映射器,例如mapstruct。这不是通常的做法吗? 【参考方案1】:

如果我理解正确,当您写入数据库时​​,您使用WritablePerson 实体类型,因此_hibernate_class 字段设置为org.company.WritablePerson,但是在搜索时您希望获得org.company.Person 类型的结果, 对于最初编写为 org.company.WritablePerson 的相同文档。

没有对此的内置支持。但是,有一些方法可以自己做。您将(遗憾地)必须重新实现 Hibernate Search 通常开箱即用的部分功能,并且可能还会失去我们为对象加载实现的一些性能优化,但应该是可能的。

这个想法本质上是对 ID 执行投影查询,然后显式加载与这些 ID 对应的实体:

List<Person> myQueryMethod(<some params>) 
    FullTextEntityManager em = ...;
    Query luceneQuery = ...;
    FullTextQuery query = em.createFullTextQuery( luceneQuery, WriteablePerson.class );
    query.setProjection( org.hibernate.search.engine.ProjectionConstants.ID );
    List<Object[]> projections = query.getResultList();
    return loadResults( Person.class, projections );


<T> List<T> loadResults(Class<T> clazz, List<Object[]> idProjections) 
    List<Serializable> ids = new ArrayList<>( idProjections.size() );
    for ( Object[] projection : idProjections ) 
        ids.add( (Serializable) projection[0] );
    
    return em.unwrap( Session.class ).byMultipleIds( clazz )
        .with( CacheMode.<pick a cache mode> ) // May be ommitted
        .withBatchSize( <pick a batch size> ) // May be ommitted
        .multiLoad( ids );

【讨论】:

这行得通!感谢您的帮助!我们当然会研究这是否是一个可行的解决方案,或者为了提高效率我们是否需要采取另一条路线。【参考方案2】:

您对 Hibernate Search 的选择不取决于实体在数据库上的映射方式,这是一个完全正交的方面。

您可以拥有multiple entities share the same index,无论它们如何存储在同一个表中。

您还可以拥有一个实体sharded across multiple indexes。

最后,您甚至可以use some entity property as discriminator across shards,以便特定类型的过滤可以从物理索引存储中受益,以匹配最喜欢的过滤器。

_hibernate_class 字段是一个技术细节,如果您没有其他实体,它不应该有任何影响,但如果您将来决定将另一个实体添加到同一索引中,它可能变得必不可少,并映射到不同的表。

【讨论】:

我不确定是不是这样,我相信Logan在这里的情况是,当他使用Person2实体写入数据库时​​,他是否能够通过Person1实体读取它? 啊,我理解错了。这是一个非常有趣的问题。

以上是关于休眠搜索以处理两个不同实体对象的相同索引的主要内容,如果未能解决你的问题,请参考以下文章

休眠拦截器 - 加载事件之后

如何使用单个 solr 实例索引和搜索位于同一数据源中的两个不同表或 Solr 模板字段无法正常工作

如何以任何顺序比较两个 NSArray 的相同内容?

超过两个实体的休眠复杂查询

如何配置休眠以扫描不同模块中的实体

休眠删除实体问题