Hibernate 搜索嵌入式复合主键
Hibernate search on embedded composite primary key
我将 class UserAdAccountId 定义为嵌入式 ID class。使用此 ID class,我定义了 class UserAdAccount 并为复合主键使用了双向字符串字段桥。然后,我尝试对实体 class AdAccount 进行休眠搜索,但 运行 进入此异常:无法在 AdAccount 中找到字段 userAdAccounts.id.userId。
如您所见,我将“userAdAccounts.id.userId”作为字段路径传递给 onField(),因为 userAdAccounts 是一组 UserAdAccount。 UserAdAccount 的 id 是 UserAdAccountId 类型,它的字段有 userId 和 adAccountId。我使用 @IndexedEmbedded(includeEmbeddedObjectId = true) 来确保 UserAdAccountId 类型的 id 包含在索引中。
我的问题是为什么我仍然看到这个不正确的字段路径错误?
@Indexed
@Embeddable
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = {"userId", "adAccountId"})
@ToString
public class UserAdAccountId implements Serializable {
@Column(name = "USER_ID")
@GenericGenerator( name = "native", strategy = "native")
@Field
private Long userId;
@Column(name = "AD_ACCOUNT_ID")
@GenericGenerator( name = "native", strategy = "native")
@Field
private Long adAccountId;
}
@Entity (name = "JHI_USER_AD_ACCOUNT")
@Indexed
@Getter
@Setter
public class UserAdAccount implements SearchableEntity, Serializable {
@EmbeddedId
@DocumentId
@FieldBridge(impl = UserAdAccoutPrimaryKeyBridge.class)
@IndexedEmbedded(includePaths = {"userId"})
private UserAdAccountId id;
@ManyToOne
@JoinColumn(name = "USER_ID", referencedColumnName = "ID", updatable = false, insertable = false)
private User user;
@ManyToOne
@JoinColumn(name = "AD_ACCOUNT_ID", referencedColumnName = "ID", updatable = false, insertable = false)
private AdAccount adAccount;
}
@Entity
@Indexed
@Table(name = "AD_ACCOUNT")
@Getter
@Setter
@ToString
public class AdAccount implements SearchableEntity, Serializable {
@Id
@DocumentId
@SortableField
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.AUTO, generator="native")
@GenericGenerator( name = "native", strategy = "native")
private Long id;
@IndexedEmbedded(includeEmbeddedObjectId = true)
@OneToMany(mappedBy = "adAccount", fetch = FetchType.LAZY)
private Set<UserAdAccount> userAdAccounts = new HashSet<>();
}
我实现的hibernate搜索逻辑:
if(this.searchRequest.getExactMatchFilters().containsKey("userId")) {
Set<String> userIds = this.searchRequest.getExactMatchFilters().get("userId");
BooleanJunction<BooleanJunction> combined = queryBuilder.bool();
combined.minimumShouldMatchNumber(1);
for(String userId : userIds) {
combined.should(queryBuilder.keyword().onField("userAdAccounts.id.userId").matching(userId).createQuery());
}
filters.add(combined.createQuery());
}
更新:关于双向字段桥的动态映射错误。我在官方文档上找到了这个:我在官方文档上找到了这个:
当您的 MetadataProvidingFieldBridge 注册一个字段,其名称是现有字段的名称,附加一个点和另一个字符串,如 name + ".mySubField",Hibernate Search 将 运行 将其列为一个在发送到 Elasticsearch 的 JSON 文档中带有 属性 mySubField 的对象。
因此,子字段只能有 OBJECT 类型的父字段:显然,Elasticsearch 会拒绝带有 mySubField 属性 的字符串或整数。因此每次注册名为 foo.bar 的字段时,其父字段 foo 必须使用 OBJECT 类型进行注册,如下例所示。不这样做会导致 Hibernate Search 生成 Elasticsearch 模式时出错。
因此,就我而言,我执行了以下操作。我可能应该用 Object 类型注册 id 并将 USER_ID_SUFFIX 更改为 .userId 和
AD_ACCOUNT_ID_SUFFIX 到 .adaccountId?
private static final String USER_ID_SUFFIX = "_userId";
private static final String AD_ACCOUNT_ID_SUFFIX = "_adaccountId";
@Override
public void configureFieldMetadata(String id, FieldMetadataBuilder builder) {
builder.field(id + USER_ID_SUFFIX, FieldType.LONG)
.field(id + AD_ACCOUNT_ID_SUFFIX, FieldType.LONG);
}
您根本没有映射字段userAdAccounts.id.userId
。您映射了字段 userAdAccounts.id
,仅此而已。 Hibernate Search 只会向 AdAccount
文档添加一个名为 userAdAccounts.id
且类型为 String
的字段。 Hibernate Search 通常不会添加您没有要求它添加的字段。
如果您还想为 userId
添加一个单独的字段:
- 要么使用我在
中提到的 TwoWayFieldBridge
实现
- 或将
@IndexedEmbedded
添加到UserAdAccount.id
并将@Field
添加到UserAdAccountId.userId
。
我将 class UserAdAccountId 定义为嵌入式 ID class。使用此 ID class,我定义了 class UserAdAccount 并为复合主键使用了双向字符串字段桥。然后,我尝试对实体 class AdAccount 进行休眠搜索,但 运行 进入此异常:无法在 AdAccount 中找到字段 userAdAccounts.id.userId。
如您所见,我将“userAdAccounts.id.userId”作为字段路径传递给 onField(),因为 userAdAccounts 是一组 UserAdAccount。 UserAdAccount 的 id 是 UserAdAccountId 类型,它的字段有 userId 和 adAccountId。我使用 @IndexedEmbedded(includeEmbeddedObjectId = true) 来确保 UserAdAccountId 类型的 id 包含在索引中。
我的问题是为什么我仍然看到这个不正确的字段路径错误?
@Indexed
@Embeddable
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
@EqualsAndHashCode(of = {"userId", "adAccountId"})
@ToString
public class UserAdAccountId implements Serializable {
@Column(name = "USER_ID")
@GenericGenerator( name = "native", strategy = "native")
@Field
private Long userId;
@Column(name = "AD_ACCOUNT_ID")
@GenericGenerator( name = "native", strategy = "native")
@Field
private Long adAccountId;
}
@Entity (name = "JHI_USER_AD_ACCOUNT")
@Indexed
@Getter
@Setter
public class UserAdAccount implements SearchableEntity, Serializable {
@EmbeddedId
@DocumentId
@FieldBridge(impl = UserAdAccoutPrimaryKeyBridge.class)
@IndexedEmbedded(includePaths = {"userId"})
private UserAdAccountId id;
@ManyToOne
@JoinColumn(name = "USER_ID", referencedColumnName = "ID", updatable = false, insertable = false)
private User user;
@ManyToOne
@JoinColumn(name = "AD_ACCOUNT_ID", referencedColumnName = "ID", updatable = false, insertable = false)
private AdAccount adAccount;
}
@Entity
@Indexed
@Table(name = "AD_ACCOUNT")
@Getter
@Setter
@ToString
public class AdAccount implements SearchableEntity, Serializable {
@Id
@DocumentId
@SortableField
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.AUTO, generator="native")
@GenericGenerator( name = "native", strategy = "native")
private Long id;
@IndexedEmbedded(includeEmbeddedObjectId = true)
@OneToMany(mappedBy = "adAccount", fetch = FetchType.LAZY)
private Set<UserAdAccount> userAdAccounts = new HashSet<>();
}
我实现的hibernate搜索逻辑:
if(this.searchRequest.getExactMatchFilters().containsKey("userId")) {
Set<String> userIds = this.searchRequest.getExactMatchFilters().get("userId");
BooleanJunction<BooleanJunction> combined = queryBuilder.bool();
combined.minimumShouldMatchNumber(1);
for(String userId : userIds) {
combined.should(queryBuilder.keyword().onField("userAdAccounts.id.userId").matching(userId).createQuery());
}
filters.add(combined.createQuery());
}
更新:关于双向字段桥的动态映射错误。我在官方文档上找到了这个:我在官方文档上找到了这个:
当您的 MetadataProvidingFieldBridge 注册一个字段,其名称是现有字段的名称,附加一个点和另一个字符串,如 name + ".mySubField",Hibernate Search 将 运行 将其列为一个在发送到 Elasticsearch 的 JSON 文档中带有 属性 mySubField 的对象。
因此,子字段只能有 OBJECT 类型的父字段:显然,Elasticsearch 会拒绝带有 mySubField 属性 的字符串或整数。因此每次注册名为 foo.bar 的字段时,其父字段 foo 必须使用 OBJECT 类型进行注册,如下例所示。不这样做会导致 Hibernate Search 生成 Elasticsearch 模式时出错。
因此,就我而言,我执行了以下操作。我可能应该用 Object 类型注册 id 并将 USER_ID_SUFFIX 更改为 .userId 和 AD_ACCOUNT_ID_SUFFIX 到 .adaccountId?
private static final String USER_ID_SUFFIX = "_userId";
private static final String AD_ACCOUNT_ID_SUFFIX = "_adaccountId";
@Override
public void configureFieldMetadata(String id, FieldMetadataBuilder builder) {
builder.field(id + USER_ID_SUFFIX, FieldType.LONG)
.field(id + AD_ACCOUNT_ID_SUFFIX, FieldType.LONG);
}
您根本没有映射字段userAdAccounts.id.userId
。您映射了字段 userAdAccounts.id
,仅此而已。 Hibernate Search 只会向 AdAccount
文档添加一个名为 userAdAccounts.id
且类型为 String
的字段。 Hibernate Search 通常不会添加您没有要求它添加的字段。
如果您还想为 userId
添加一个单独的字段:
- 要么使用我在
- 或将
@IndexedEmbedded
添加到UserAdAccount.id
并将@Field
添加到UserAdAccountId.userId
。
TwoWayFieldBridge
实现