Hibernate 搜索 JsonB 索引

问题描述 投票:0回答:1

我正在努力使用 Hibernate Search 6.0.2 将 jsonB 列索引到 Elasicsearch 后端

这是我的实体:

@Data
@NoArgsConstructor
@Entity
@Table(name = "examples")
public class Example {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private UUID id;

    @NotNull
    @Column(name = "fields")
    @Type(type = "jsonb")
    private Map<String, Object> fields;
}

这是我针对 Hibernate 搜索的 elasticsearch 后端的编程映射:

@Configuration
@RequiredArgsConstructor
public class ElasticsearchMappingConfig implements HibernateOrmSearchMappingConfigurer {

    private final JsonPropertyBinder jsonPropertyBinder;

    @Override
    public void configure(HibernateOrmMappingConfigurationContext context) {
        var mapping = context.programmaticMapping();
        var exampleMapping = mapping.type(Example.class);
        exampleMapping.indexed();
        exampleMapping.property("fields").binder(jsonPropertyBinder);
    }
}

我的自定义属性绑定器实现基于 Hibernate Search 6.0.2 文档

@Component
public class JsonPropertyBinder implements PropertyBinder {

    @Override
    public void bind(PropertyBindingContext context) {
        context.dependencies().useRootOnly();
        var schemaElement = context.indexSchemaElement();
        var userMetadataField = schemaElement.objectField("metadata");
        context.bridge(Map.class, new Bridge(userMetadataField.toReference()));
    }

    @RequiredArgsConstructor
    private static class Bridge implements PropertyBridge<Map> {

        private final IndexObjectFieldReference fieldReference;

        @Override
        public void write(DocumentElement target, Map bridgedElement, PropertyBridgeWriteContext context) {
            var map = target.addObject(fieldReference);
            ((Map<String, Object>) bridgedElement).forEach(map::addValue);
        }
    }

}

我知道文档为 Map 中的对象定义了多个模板(如 MultiTypeUserMetadataBinder 示例),但我真的不知道里面有什么。据我所知,这是一个有效的 json,我的目标是将其作为“字段”下的有效 json 结构放入 Elasticsearch:{...}

在我的例子中,jsonB 列可能包含这样的内容:

    {
        "testString": "298",
        "testNumber": 123,
        "testBoolean": true,
        "testNull": null,
        "testArray": [
            5,
            4,
            3
        ],
        "testObject": {
            "testString": "298",
            "testNumber": 123,
            "testBoolean": true,
            "testNull": null,
            "testArray": [
                5,
                4,
                3
            ]
        }

但它抛出异常:

org.hibernate.search.util.common.SearchException: HSEARCH400609: Unknown field 'metadata.testNumber'.

我还在我的Spring应用程序中将dynamic_mapping设置为true:

...
spring.jpa.properties.hibernate.search.backend.hosts=127.0.0.3:9200
spring.jpa.properties.hibernate.search.backend.dynamic_mapping=true
...

还有其他想法可以解决这个问题吗?或者也许我在某个地方犯了错误?

java spring-boot elasticsearch jsonb hibernate-search
1个回答
1
投票

我知道文档为 Map 中的对象定义了多个模板(如 MultiTypeUserMetadataBinder 示例),但我真的不知道里面有什么。据我所知,这是一个有效的 json,我的目标是将其作为“字段”下的有效 json 结构放入 Elasticsearch:{...}

如果您不知道每个字段的类型是什么,Hibernate Search 将无法提供太大帮助。如果您确实想将其填充到索引中,我建议声明一个 native field 并按原样推送 JSON。但是,除了使用native JSON之外,您将无法轻松地将谓词应用于元数据字段。

类似这样的:

@Component
public class JsonPropertyBinder implements PropertyBinder {

    @Override
    public void bind(PropertyBindingContext context) {
        context.dependencies().useRootOnly();
        var schemaElement = context.indexSchemaElement();

        // CHANGE THIS
        IndexFieldReference<JsonElement> userMetadataField = schemaElement.field( 
                "metadata",
                f -> f.extension(ElasticsearchExtension.get())
                        .asNative().mapping("{\"type\": \"object\", \"dynamic\":\"true\"}");
        )
                .toReference();

        context.bridge(Map.class, new Bridge(userMetadataField));
    }

    @RequiredArgsConstructor
    private static class Bridge implements PropertyBridge<Map> {
        private static final Gson GSON = new Gson();

        private final IndexFieldReference<JsonElement> fieldReference;

        @Override
        public void write(DocumentElement target, Map bridgedElement, PropertyBridgeWriteContext context) {
            // CHANGE THIS
            target.addValue(fieldReference, GSON.toJsonTree(bridgedElement));
        }
    }

}

或者,您可以将所有字段声明为字符串。然后 Hibernate Search 在字符串类型上提供的所有功能都将可用。但是,当然,范围谓词或排序之类的东西会导致数值出现奇怪的结果(

2
10
之前,但
"2"
之后
"10"
)。

类似这样的:

@Component
public class JsonPropertyBinder implements PropertyBinder {

    @Override
    public void bind(PropertyBindingContext context) {
        context.dependencies().useRootOnly();
        var schemaElement = context.indexSchemaElement();
        var userMetadataField = schemaElement.objectField("metadata");

        // ADD THIS
        userMetadataField.fieldTemplate( 
                "userMetadataValueTemplate_default",
                f -> f.asString().analyzer( "english" )
        );

        context.bridge(Map.class, new Bridge(userMetadataField.toReference()));
    }

    @RequiredArgsConstructor
    private static class Bridge implements PropertyBridge<Map> {

        private final IndexObjectFieldReference fieldReference;

        @Override
        public void write(DocumentElement target, Map bridgedElement, PropertyBridgeWriteContext context) {
            var map = target.addObject(fieldReference);
            // CHANGE THIS
            ((Map<String, Object>) bridgedElement).forEach(entry -> map.addValue( entry.getKey(), String.valueOf(entry.getValue())));
        }
    }

}
© www.soinside.com 2019 - 2024. All rights reserved.