【问题标题】:Hibernate/Lucene Fields added in Custom FieldBridge unknown when search is performed执行搜索时,自定义 FieldBridge 中添加的 Hibernate/Lucene 字段未知
【发布时间】:2021-11-05 03:36:19
【问题描述】:

我们需要从一个数据属性索引不同的字段。这已通过实现 FieldBridge 并在那里添加字段来解决。 从 hibernate 搜索参考的这个例子中可以看出:

/**
 * Store the date in 3 different fields - year, month, day - to ease the creation of RangeQuery per
 * year, month or day (eg get all the elements of December for the last 5 years).
 * @author Emmanuel Bernard
 */
public class DateSplitBridge implements FieldBridge {
    private final static TimeZone GMT = TimeZone.getTimeZone("GMT");

    public void set(String name, Object value, Document document,
                    LuceneOptions luceneOptions) {
        Date date = (Date) value;
        Calendar cal = GregorianCalendar.getInstance(GMT);
        cal.setTime(date);
        int year = cal.get(Calendar.YEAR);
        int month = cal.get(Calendar.MONTH) + 1;
        int day = cal.get(Calendar.DAY_OF_MONTH);

        // set year
        luceneOptions.addFieldToDocument(
            name + ".year",
            String.valueOf( year ),
            document );

        // set month and pad it if needed
        luceneOptions.addFieldToDocument(
            name + ".month",
            month < 10 ? "0" : "" + String.valueOf( month ),
            document );

        // set day and pad it if needed
        luceneOptions.addFieldToDocument(
            name + ".day",
            day < 10 ? "0" : "" + String.valueOf( day ),
            document );
    }
}
//property
@FieldBridge(impl = DateSplitBridge.class)
private Date date;

除了上面的例子,我们将我们的设置为 twoWayFieldBridge:

 public class DateSplitBridge implements TwoWayFieldBridge {
    [...]
    @Override
    public Object get(String name, Document document) {
        final IndexableField field = document.getField(name);
        if (field != null) {
            return field.stringValue();
        } else {
            return null;
        }
    }

    @Override
    public String objectToString(Object object) {
        return object.toString();
    }

问题是,当搜索通过 FieldBridge 定义的字段之一时,搜索似乎不知道它:

原因:org.hibernate.search.exception.SearchException:无法 在 [...] 中查找字段 date.day org.hibernate.search.engine.spi.DocumentBuilderIndexedEntity.objectToString(DocumentBuilderIndexedEntity.java:1052)

如果字段被映射(每个注释或 API),则不会发生错误:

//property
@Fields({@Field(name="date.day", bridge = @FieldBridge(impl=DateSplitBridge.class)),
        @Field(name="date.month", bridge = @FieldBridge(impl=DateSplitBridge.class)),
        @Field(name="date.year", bridge = @FieldBridge(impl=DateSplitBridge.class))})
private Date date;

但是在索引时出现错误,因为该字段随后在映射中定义,而导致此问题的 FieldBridge:

2021-09-08 08:42:50,063 错误 org.hibernate.search.exception.impl.LogErrorHandler:71 - HSEARCH000058:HSEARCH000183:无法索引类型的实例 [...] java.lang.IllegalArgumentException: DocValuesField "date.day" 出现不止一次 此文档(每个字段只允许一个值)在 org.apache.lucene.index.SortedDocValuesWriter.addValue(SortedDocValuesWriter.java:62) ...

应该如何处理?我们如何将 FieldBridge 中的字段传播到搜索,以使它们首先可搜索?

【问题讨论】:

    标签: java hibernate indexing lucene hibernate-search


    【解决方案1】:

    Hibernate Search 无法将您传递给 Search DSL 的值转换为在 Lucene 查询中使用的值,因为在 Hibernate Search 元数据的任何地方都没有引用字段 date.day,因此它不知道它的预期类型。

    让您的网桥实现MetadataProvidingFieldBridge,以便 Hibernate Search 了解该字段。

    如果这不起作用,您可以在使用搜索 DSL 构建查询时始终使用.ignoreFieldBridge().ignoreAnalyzer()。例如:

    queryBuilder.keyword()
        .onField( "date.day" )
        .ignoreFieldBridge().ignoreAnalyzer()
        .matching( "10" )
        .createQuery()
    

    提醒没有看到其他答案的其他用户:如果可以,您应该考虑upgrading to Hibernate Search 6,其中bridge support is much more powerfulbetter documented

    【讨论】:

    • 再次感谢您的回复。可悲的是,更改为 MetadataProvidingFieldBridge 并没有改变行为。如果没有 @Field 注释,字段将写入索引文档,但不可用于搜索(无法在 [...] at 中找到字段 date.day),MetadataProvidingFieldBridge 不会改变这一点. public void configureFieldMetadata(String name, FieldMetadataBuilder builder) { builder.field( name, FieldType.STRING ); builder.field(name + ".eng", FieldType.STRING);
    • 还测试了.ignoreFieldBridge().ignoreAnalyzer(),也没有成功。
    【解决方案2】:

    问题在于您的查询,而不是网桥。第一个桥定义的字段不是day,而是&lt;whatever your property name is&gt;.day。在你的情况下,那将是date.day

    查看这部分代码:

            luceneOptions.addFieldToDocument(
                name + ".day", // HERE
                day < 10 ? "0" : "" + String.valueOf( day ),
                document );
    

    只需将您的查询更改为使用date.day 而不是day

    无耻插件:如果可以的话,你真的应该考虑upgrading to Hibernate Search 6,其中bridge support is much more powerfulbetter documented

    【讨论】:

    • - 原始帖子已编辑感谢您的指出,很抱歉,此错误来自将问题适应给定的示例“日期”。在真实代码中,发生错误的字段之一实际上只是简单地称为“名称”,它在所有出现、映射、桥接、搜索以及抛出的错误中都是正确的......也许进入 Hibernate 6 并没有看起来那么痛苦,但就目前而言,截止日期会否认这一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多