如何在休眠搜索中索引复合主键 [英] How to index composite primary keys in hibernate search

查看:81
本文介绍了如何在休眠搜索中索引复合主键的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我具有以下类定义,对于类UserAdAccount,它同时使用adAccountId和userId作为其复合主键.我需要使用此复合主键作为索引的文档ID.

在阅读《 Hibernate Search 5.11.5.Final:参考指南》(这是我正在使用的版本)之后,我发现了以下内容:功能更强大的TwoWayFieldBridge界面允许您将多个字段存储到索引中,这对于复合属性很有用,但实现起来较为复杂.

我不确定如何继续使用TwoWayFieldBridge在这里解决我的需求.感谢您的帮助!

  @Entity(名称="JHI_USER_AD_ACCOUNT")@索引@IdClass(UserAdAccountId.class)@盖特@Setter公共类UserAdAccount实现SearchableEntity,可序列化{//新添加的带有双向字符串桥的id字段@DocumentId@Column(名称="ID")@FieldBridge(impl = UserAdAccoutPrimaryKeyBridge.class)私人UserAdAccountId ID;@ID@Column(名称="AD_ACCOUNT_ID")@GenericGenerator(name ="native",strategy ="native")@场地私人Long adAccountId;@ID@Column(名称="USER_ID")@GenericGenerator(name ="native",strategy ="native")@场地私有Long userId;@多多@JoinColumn(名称="USER_ID",referencedColumnName ="ID",可更新=假,可插入=假)@JsonIgnore私人用户用户;@多多@JoinColumn(名称="AD_ACCOUNT_ID",referencedColumnName ="ID",可更新=假,可插入=假)@IndexedEmbedded(includePaths ="name")私人AdAccount adAccount;}@盖特@Setter@EqualsAndHashCode(of = {"userId","adAccountId"})公共类UserAdAccountId实现Serializable {私有Long userId;私人Long adAccountId;}公共类UserAdAccoutPrimaryKeyBridge实现TwoWayStringBridge {@Overridepublic String objectToString(Object object){UserAdAccountId userAdAccountId =(UserAdAccountId)对象;StringBuilder buffer = new StringBuilder();buffer.append(userAdAccountId.getUserId()).append(-").append(userAdAccountId.getAdAccountId());返回buffer.toString();}@Override公共对象stringToObject(String value){String []组件= value.split(-");UserAdAccountId userAdAccountId = new UserAdAccountId();userAdAccountId.setUserId(Long.parseLong(components [0]));userAdAccountId.setAdAccountId(Long.parseLong(components [1]));返回userAdAccountId;}} 

解决方案

来自文档:

双向过程很重要,即:[...]

  • 对于TwoWayFieldBridge:对于给定的文档,由get()返回的对象必须与最初传递给set()的对象相同.

此外,为了使TwoWayFieldBridge实现在文档标识符上使用时正确运行,void set(String name,Object value,Document document,LuceneOptions luceneOptions)方法必须遵循以下约定向文档添加字段:

  • 字段名称必须是name参数中提供的名称
  • 该字段值必须编码为字符串
  • 字段值必须与value参数的给定值唯一
  • 字段值必须与给定value参数的objectToString方法返回的值匹配.

您可以找到复合主键的示例实现 公共类PersonPKBridge实现TwoWayFieldBridge,MetadataProvidingFieldBridge {私有静态最终字符串FIRST_NAME_SUFFIX ="_content.firstName";私有静态最终字符串LAST_NAME_SUFFIX ="_content.lastName";@Override公共无效configureFieldMetadata(字符串名称,FieldMetadataBuilder构建器){builder.field(名称+ FIRST_NAME_SUFFIX,FieldType.STRING).field(name + LAST_NAME_SUFFIX,FieldType.STRING);}@Overridepublic Object get(字符串名称,文档文档){PersonPK id =新的PersonPK();IndexableField字段= document.getField(名称+ FIRST_NAME_SUFFIX);id.setFirstName(field.stringValue());字段= document.getField(名称+ LAST_NAME_SUFFIX);id.setLastName(field.stringValue());返回ID;}@Overridepublic String objectToString(Object object){PersonPK id =(PersonPK)对象;StringBuilder sb = new StringBuilder();sb.append(id.getFirstName()).append(").append(id.getLastName());返回sb.toString();}@Override公共无效集(字符串名称,对象值,文档文档,LuceneOptions luceneOptions){PersonPK id =(PersonPK)值;//将每个属性存储在唯一的字段中luceneOptions.addFieldToDocument(name + FIRST_NAME_SUFFIX,id.getFirstName(),document);luceneOptions.addFieldToDocument(name + LAST_NAME_SUFFIX,id.getLastName(),document);//将唯一的字符串表示形式存储在指定的字段中luceneOptions.addFieldToDocument(name,objectToString(id),document);复制代码}}

或者,如果您确定ID的组成部分不包含某个字符串,则可以将该字符串用作ID的索引形式的分隔符,并且可以进行转换索引形式返回到原始ID.这意味着您可以使用更简单的 TwoWayStringBridge 并依靠串联/拆分:

 公共类PersonPKBridge实现TwoWayStringBridge {@Overridepublic String objectToString(Object object){PersonPK id =(PersonPK)对象;StringBuilder sb = new StringBuilder();sb.append(id.getFirstName()).append(").append(id.getLastName());返回sb.toString();}@Override公共对象stringToObject(String value){String []组件= value.split(");PersonPK id =新的PersonPK();id.setFirstName(components [0]);id.setLastName(components [1]);返回ID;}} 

I have the following class definitions and for class UserAdAccount, it uses both the adAccountId and userId as its composite primary key. I need to use this composite primary key as the document id for indexing.

After reading the Hibernate Search 5.11.5.Final: Reference Guide(This is the version I am using), I found the following: The more powerful TwoWayFieldBridge interface allows you to store more than one field into the index, which can be useful for composite properties, but is more complex to implement.

I am not sure how to proceed using the TwoWayFieldBridge to address my need here. Any help is appreciated!

@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;
    }
}

解决方案

From the documentation:

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.

You can find an example implementation for a composite primary key 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 );
    }

}

Alternatively, if you're certain the components of your ID don't contain a certain string, you will be able to use that string as a separator for the indexed form of your ID, and you'll be able to convert that indexed form back to the original ID. This means you can use the simpler TwoWayStringBridge and rely on 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;
    }

}

这篇关于如何在休眠搜索中索引复合主键的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆