Hibernate 搜索 JsonB 索引
Hibernate Search JsonB indexing
我正在努力将 jsonB 列索引到 Elasicsearch 后端,使用 Hibernate Search 6.0.2
这是我的实体:
@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 Search 的 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 documentation。
@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
...
还有其他想法可以解决这个问题吗?还是我哪里出错了?
I am aware that documentation defines multiple templates for what an Object in Map can be (like in MultiTypeUserMetadataBinder example), but I really do not know what can be inside. All I know, it is a valid json and my goal is to put it into Elasticsearch as valid json structure under "fields": {...}
如果您不知道每个字段的类型是什么,Hibernate Search 将无济于事。如果您真的想将其填充到索引中,我建议您声明一个 native field and pushing the JSON as-is. But then you won't be able to apply predicates to the metadata fields easily, except using 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())));
}
}
}
我正在努力将 jsonB 列索引到 Elasicsearch 后端,使用 Hibernate Search 6.0.2
这是我的实体:
@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 Search 的 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 documentation。
@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
...
还有其他想法可以解决这个问题吗?还是我哪里出错了?
I am aware that documentation defines multiple templates for what an Object in Map can be (like in MultiTypeUserMetadataBinder example), but I really do not know what can be inside. All I know, it is a valid json and my goal is to put it into Elasticsearch as valid json structure under "fields": {...}
如果您不知道每个字段的类型是什么,Hibernate Search 将无济于事。如果您真的想将其填充到索引中,我建议您声明一个 native field and pushing the JSON as-is. But then you won't be able to apply predicates to the metadata fields easily, except using 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())));
}
}
}