Hibernate Search 不返回新创建实体的结果

Hibernate Search not returning results on newly created entities

我在多租户 Hibernate 应用程序中使用 Hibernate Search。根据 this link,我希望它可以开箱即用,但搜索结果似乎不是 return 新创建的实体。

这是我的映射:

@Entity
@Indexed
public class ItemEntity implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Field
    private String name;

    @Field
    private String description;

    @OneToMany(mappedBy = "item", cascade = CascadeType.ALL, orphanRemoval = true)
    @OrderBy("versionNo DESC, lastSaveDate DESC")
    @IndexedEmbedded
    private List<ItemVersionEntity> versions;

    ...
}
@Entity
public class ItemVersionEntity implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Field
    private String description;

    @OneToMany(mappedBy = "version", cascade = CascadeType.ALL, orphanRemoval = true)
    @Fetch(FetchMode.SUBSELECT)
    @IndexedEmbedded
    private List<ItemEdgeEntity> edges;

    @OneToMany(mappedBy = "version", cascade = CascadeType.ALL, orphanRemoval = true)
    @Fetch(FetchMode.SUBSELECT)
    @IndexedEmbedded
    private List<ItemNodeEntity> nodes;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "item_id")
    @ContainedIn
    private ItemEntity item;
}
@Entity
public class ItemEdgeEntity implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Field
    private String text;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "version_id")
    @ContainedIn
    private ItemVersionEntity version;
}
@Entity
public class ItemNodeEntity implements Serializable {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Field
    private String text;

    @Field
    private String description;

    @ManyToOne(fetch = FetchType.LAZY, optional = false)
    @JoinColumn(name = "version_id")
    @ContainedIn
    private ItemVersionEntity version;
}

添加了一个叫foobarItemNodeEntity后,我正在用this工具查看它出现在索引中(虽然我不太明白索引的结构, 不确定不同的 Segments 是否有问题):
Marple image
Luke image

这是 returns 0 结果的一些查询代码:

SessionFactory sessionFactory = entityManagerFactory.unwrap(SessionFactory.class);
Session session = sessionFactory.withOptions().tenantIdentifier(tenantIdResolver.resolveCurrentTenantIdentifier()).openSession();
FullTextSession fullTextEM = Search.getFullTextSession(session);

QueryBuilder itemQB = fullTextEM.getSearchFactory().buildQueryBuilder().forEntity(ItemEntity.class).get();

Query mq = itemQB
                .keyword()
                .onField("versions.nodes.text")
                .matching("foobar")
                .createQuery();

FullTextQuery ft = fullTextEM.createFullTextQuery(mq, ItemEntity.class);
List<Object> result = ft.getResultList();

请注意,如果我重新索引整个数据库,上述搜索有效:

FullTextSession fullTextEntityManager = Search.getFullTextSession(session);
fullTextEntityManager.createIndexer().startAndWait();

休眠:5.2.13 休眠搜索:5.9.3

编辑:

似乎在保存 ItemVersionEntity 之后,__HSearch_TenantId 字段从 Lucene 文档中删除: Luke document image

这是实现多租户的方式(基本上遵循 this 指南):

@Configuration
public class HibernateConfig {

    @Autowired
    private JpaProperties jpaProperties;

    @Bean
    public JpaVendorAdapter jpaVendorAdapter() {
        return new HibernateJpaVendorAdapter();
    }

    @Bean
    public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
                                                                       MultiTenantConnectionProvider multiTenantConnectionProviderImpl,
                                                                       CurrentTenantIdentifierResolver currentTenantIdentifierResolverImpl) {

        Map<String, Object> properties = new HashMap<>();
        properties.putAll(jpaProperties.getHibernateProperties(new HibernateSettings()));

        properties.put(Environment.MULTI_TENANT, MultiTenancyStrategy.SCHEMA);
        properties.put(Environment.MULTI_TENANT_CONNECTION_PROVIDER, multiTenantConnectionProviderImpl);
        properties.put(Environment.MULTI_TENANT_IDENTIFIER_RESOLVER, currentTenantIdentifierResolverImpl);

        LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
        em.setDataSource(dataSource);
        em.setPackagesToScan(...);
        em.setJpaVendorAdapter(jpaVendorAdapter());
        em.setJpaPropertyMap(properties);

        return em;
    }
}

您的映射似乎缺少从 ItemVersionEntityItemEntity 的关联。您需要此关联以及 @ContainedIn 注释,以便 Hibernate Search 能够检索相关的 ItemEntity 实例并在 ItemVersionEntity (或嵌入式节点)更改时重新索引它.

基本上,如果您希望自动重建索引正常工作,只要实体 A 在与类型 B 的关联上带有 @IndexedEmbedded 注释,您就需要实体 B 声明该关联的反面为键入 A,并在该反向关联上添加 @ContainedIn 注释。

正如我在其他答案的评论中所讨论的那样,这是一个仅在非常特定的情况下使用多租户时才会出现的错误(显然是 @IndexedEmbedded 的 2 层或更多层):https://hibernate.atlassian.net/browse/HSEARCH-3647

要解决此错误,最好的办法是在合并后显式重新索引:

Search.getFullTextSession(s).index(updatedVersion.getItem());

如果您实际上无法提前知道哪些实体需要重新编制索引,您可以使用可能的 hack...但这确实是 hack:一定要正确测试它。这个想法是让 Hibernate Search 执行一个不会改变任何东西的操作,但是会产生正确初始化一些内部结构的副作用。 开始交易后,只需触发一个您绝对确定不会产生任何影响的清除操作,方法是使用您绝对确定不匹配任何实体的实体 ID:

Search.getFullTextSession(s).purge(ItemEntity.class, -1);

然后该实体类型的内部结构将被正确初始化,@ContainedIn 处理将对事务的其余部分正常工作。 如果您有其他索引类型,则必须对可能受您的交易影响的每种类型执行相同的操作。

请注意,清除操作仍将执行,只是不会有任何效果,因为 ID 不匹配任何内容。