如何在休眠搜索中索引复合主键
How to index composite primary keys in hibernate search
我有以下 class 定义,对于 class UserAdAccount,它同时使用 adAccountId 和 userId 作为其复合主键。我需要使用这个复合主键作为索引的文档id。
阅读了Hibernate Search 5.11.5.Final: Reference Guide(这是我正在使用的版本)后,我发现了以下内容:
更强大的 TwoWayFieldBridge 接口允许您将多个字段存储到索引中,这对于复合属性很有用,但实现起来更复杂。
我不确定如何继续使用 TwoWayFieldBridge 来解决我的需求。感谢您的帮助!
@Entity (name = "JHI_USER_AD_ACCOUNT")
@Indexed
@IdClass(UserAdAccountId.class)
@Getter
@Setter
public class UserAdAccount implements SearchableEntity, Serializable {
//newly added id field with two way string bridge
@DocumentId
@Column(name = "ID")
@FieldBridge(impl = UserAdAccoutPrimaryKeyBridge.class)
private UserAdAccountId id;
@Id
@Column(name = "AD_ACCOUNT_ID")
@GenericGenerator( name = "native", strategy = "native")
@Field
private Long adAccountId;
@Id
@Column(name = "USER_ID")
@GenericGenerator( name = "native", strategy = "native")
@Field
private Long userId;
@ManyToOne
@JoinColumn(name = "USER_ID", referencedColumnName = "ID", updatable = false, insertable = false)
@JsonIgnore
private User user;
@ManyToOne
@JoinColumn(name = "AD_ACCOUNT_ID", referencedColumnName = "ID", updatable = false, insertable = false)
@IndexedEmbedded(includePaths = "name")
private AdAccount adAccount;
}
@Getter
@Setter
@EqualsAndHashCode(of = {"userId", "adAccountId"})
public class UserAdAccountId implements Serializable {
private Long userId;
private Long adAccountId;
}
public class UserAdAccoutPrimaryKeyBridge implements TwoWayStringBridge {
@Override
public String objectToString(Object object) {
UserAdAccountId userAdAccountId = (UserAdAccountId) object;
StringBuilder buffer = new StringBuilder();
buffer.append(userAdAccountId.getUserId()).append("-").append(userAdAccountId.getAdAccountId());
return buffer.toString();
}
@Override
public Object stringToObject(String value) {
String[] components = value.split("-");
UserAdAccountId userAdAccountId = new UserAdAccountId();
userAdAccountId.setUserId(Long.parseLong(components[0]));
userAdAccountId.setAdAccountId(Long.parseLong(components[1]));
return userAdAccountId;
}
}
It is important for the two-way process to be idempotent, i.e.:
[...]
- for TwoWayFieldBridge: for a given document, the object returned by get() must be the same as the one that was originally passed to set().
Also, in order for TwoWayFieldBridge implementations to work correctly when used on a document identifier, the void set(String name, Object value, Document document, LuceneOptions luceneOptions) method must add a field to the document following these conventions:
- the field name must be the name provided in the name parameter
- the field value must be encoded as a String
- the field value must be unique to the given value of the value parameter
- the field value must match what the objectToString method would return for the given value parameter.
您可以找到复合主键的示例实现 on github:
public class PersonPKBridge implements TwoWayFieldBridge, MetadataProvidingFieldBridge {
private static final String FIRST_NAME_SUFFIX = "_content.firstName";
private static final String LAST_NAME_SUFFIX = "_content.lastName";
@Override
public void configureFieldMetadata(String name, FieldMetadataBuilder builder) {
builder.field( name + FIRST_NAME_SUFFIX, FieldType.STRING )
.field( name + LAST_NAME_SUFFIX, FieldType.STRING );
}
@Override
public Object get(String name, Document document) {
PersonPK id = new PersonPK();
IndexableField field = document.getField( name + FIRST_NAME_SUFFIX );
id.setFirstName( field.stringValue() );
field = document.getField( name + LAST_NAME_SUFFIX );
id.setLastName( field.stringValue() );
return id;
}
@Override
public String objectToString(Object object) {
PersonPK id = (PersonPK) object;
StringBuilder sb = new StringBuilder();
sb.append( id.getFirstName() ).append( " " ).append( id.getLastName() );
return sb.toString();
}
@Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
PersonPK id = (PersonPK) value;
//store each property in a unique field
luceneOptions.addFieldToDocument( name + FIRST_NAME_SUFFIX, id.getFirstName(), document );
luceneOptions.addFieldToDocument( name + LAST_NAME_SUFFIX, id.getLastName(), document );
//store the unique string representation in the named field
luceneOptions.addFieldToDocument( name, objectToString( id ), document );
}
}
或者,如果您确定您的 ID 的组成部分不包含特定字符串,您将能够使用该字符串作为 ID 索引形式的分隔符,并且您将能够将该索引形式转换回原始 ID。这意味着您可以使用更简单的 TwoWayStringBridge
并依赖 concatenation/splitting:
public class PersonPKBridge implements TwoWayStringBridge {
@Override
public String objectToString(Object object) {
PersonPK id = (PersonPK) object;
StringBuilder sb = new StringBuilder();
sb.append( id.getFirstName() ).append( " " ).append( id.getLastName() );
return sb.toString();
}
@Override
public Object stringToObject(String value) {
String[] components = value.split(" ");
PersonPK id = new PersonPK();
id.setFirstName( components[0] );
id.setLastName( components[1] );
return id;
}
}
我有以下 class 定义,对于 class UserAdAccount,它同时使用 adAccountId 和 userId 作为其复合主键。我需要使用这个复合主键作为索引的文档id。
阅读了Hibernate Search 5.11.5.Final: Reference Guide(这是我正在使用的版本)后,我发现了以下内容: 更强大的 TwoWayFieldBridge 接口允许您将多个字段存储到索引中,这对于复合属性很有用,但实现起来更复杂。
我不确定如何继续使用 TwoWayFieldBridge 来解决我的需求。感谢您的帮助!
@Entity (name = "JHI_USER_AD_ACCOUNT")
@Indexed
@IdClass(UserAdAccountId.class)
@Getter
@Setter
public class UserAdAccount implements SearchableEntity, Serializable {
//newly added id field with two way string bridge
@DocumentId
@Column(name = "ID")
@FieldBridge(impl = UserAdAccoutPrimaryKeyBridge.class)
private UserAdAccountId id;
@Id
@Column(name = "AD_ACCOUNT_ID")
@GenericGenerator( name = "native", strategy = "native")
@Field
private Long adAccountId;
@Id
@Column(name = "USER_ID")
@GenericGenerator( name = "native", strategy = "native")
@Field
private Long userId;
@ManyToOne
@JoinColumn(name = "USER_ID", referencedColumnName = "ID", updatable = false, insertable = false)
@JsonIgnore
private User user;
@ManyToOne
@JoinColumn(name = "AD_ACCOUNT_ID", referencedColumnName = "ID", updatable = false, insertable = false)
@IndexedEmbedded(includePaths = "name")
private AdAccount adAccount;
}
@Getter
@Setter
@EqualsAndHashCode(of = {"userId", "adAccountId"})
public class UserAdAccountId implements Serializable {
private Long userId;
private Long adAccountId;
}
public class UserAdAccoutPrimaryKeyBridge implements TwoWayStringBridge {
@Override
public String objectToString(Object object) {
UserAdAccountId userAdAccountId = (UserAdAccountId) object;
StringBuilder buffer = new StringBuilder();
buffer.append(userAdAccountId.getUserId()).append("-").append(userAdAccountId.getAdAccountId());
return buffer.toString();
}
@Override
public Object stringToObject(String value) {
String[] components = value.split("-");
UserAdAccountId userAdAccountId = new UserAdAccountId();
userAdAccountId.setUserId(Long.parseLong(components[0]));
userAdAccountId.setAdAccountId(Long.parseLong(components[1]));
return userAdAccountId;
}
}
It is important for the two-way process to be idempotent, i.e.: [...]
- for TwoWayFieldBridge: for a given document, the object returned by get() must be the same as the one that was originally passed to set().
Also, in order for TwoWayFieldBridge implementations to work correctly when used on a document identifier, the void set(String name, Object value, Document document, LuceneOptions luceneOptions) method must add a field to the document following these conventions:
- the field name must be the name provided in the name parameter
- the field value must be encoded as a String
- the field value must be unique to the given value of the value parameter
- the field value must match what the objectToString method would return for the given value parameter.
您可以找到复合主键的示例实现 on github:
public class PersonPKBridge implements TwoWayFieldBridge, MetadataProvidingFieldBridge {
private static final String FIRST_NAME_SUFFIX = "_content.firstName";
private static final String LAST_NAME_SUFFIX = "_content.lastName";
@Override
public void configureFieldMetadata(String name, FieldMetadataBuilder builder) {
builder.field( name + FIRST_NAME_SUFFIX, FieldType.STRING )
.field( name + LAST_NAME_SUFFIX, FieldType.STRING );
}
@Override
public Object get(String name, Document document) {
PersonPK id = new PersonPK();
IndexableField field = document.getField( name + FIRST_NAME_SUFFIX );
id.setFirstName( field.stringValue() );
field = document.getField( name + LAST_NAME_SUFFIX );
id.setLastName( field.stringValue() );
return id;
}
@Override
public String objectToString(Object object) {
PersonPK id = (PersonPK) object;
StringBuilder sb = new StringBuilder();
sb.append( id.getFirstName() ).append( " " ).append( id.getLastName() );
return sb.toString();
}
@Override
public void set(String name, Object value, Document document, LuceneOptions luceneOptions) {
PersonPK id = (PersonPK) value;
//store each property in a unique field
luceneOptions.addFieldToDocument( name + FIRST_NAME_SUFFIX, id.getFirstName(), document );
luceneOptions.addFieldToDocument( name + LAST_NAME_SUFFIX, id.getLastName(), document );
//store the unique string representation in the named field
luceneOptions.addFieldToDocument( name, objectToString( id ), document );
}
}
或者,如果您确定您的 ID 的组成部分不包含特定字符串,您将能够使用该字符串作为 ID 索引形式的分隔符,并且您将能够将该索引形式转换回原始 ID。这意味着您可以使用更简单的 TwoWayStringBridge
并依赖 concatenation/splitting:
public class PersonPKBridge implements TwoWayStringBridge {
@Override
public String objectToString(Object object) {
PersonPK id = (PersonPK) object;
StringBuilder sb = new StringBuilder();
sb.append( id.getFirstName() ).append( " " ).append( id.getLastName() );
return sb.toString();
}
@Override
public Object stringToObject(String value) {
String[] components = value.split(" ");
PersonPK id = new PersonPK();
id.setFirstName( components[0] );
id.setLastName( components[1] );
return id;
}
}