elasticsearch 在不同索引上组合hibernate搜索查询的最佳方法
我们有以下情况 鉴于以下两个实体elasticsearch 在不同索引上组合hibernate搜索查询的最佳方法,elasticsearch,lucene,hibernate-search,elasticsearch,Lucene,Hibernate Search,我们有以下情况 鉴于以下两个实体 @Indexed @Spatial(spatialMode = SpatialMode.HASH) @Entity @Table(name = "address") Address{ @Field @Basic @Column(name = "state") private String state; @Field @Basic @Column(name = "town_city") priva
@Indexed
@Spatial(spatialMode = SpatialMode.HASH)
@Entity
@Table(name = "address")
Address{
@Field
@Basic
@Column(name = "state")
private String state;
@Field
@Basic
@Column(name = "town_city")
private String townCity;
@Field
@Longitude
@Basic
@Column(name = "x_coord")
private Double xCoord;
@Field
@Latitude
@Basic
@Column(name = "y_coord")
private Double yCoord;
}
及
这将给我们提供相关年龄范围内的人
我们有一个单独的查询来获取给定点周围半径内的地址id
public Set<Integer> getSpatialAddressResults(SpatialSearchCommand spatialSearchCommand) {
FullTextSession fullTextSession = Search.getFullTextSession(entityManagerFactory.unwrap(SessionFactory.class).openSession());
this.userSearchPreference = userSearchPreference;
this.queryBuilder = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity(Address.class)
.get();
this.bool = queryBuilder.bool();
Set<Integer> addressIdSet = new HashSet<>();
bool.must(getQueryBuilder().spatial()
.within(spatialSearchCommand.getRadius(), Unit.KM).ofLatitude
(spatialSearchCommand.getLat()).andLongitude(spatialSearchCommand.getLng()).createQuery());
FullTextQuery fullTextQuery =
fullTextSession.createFullTextQuery(bool.createQuery(), Address.class)
.setProjection("addressId")
.initializeObjectsWith(ObjectLookupMethod.SECOND_LEVEL_CACHE,
DatabaseRetrievalMethod.QUERY);
List results = fullTextQuery.list();
for (Object result : results) {
Object[] arrayResult = (Object[]) result;
addressIdSet.add(((Integer) arrayResult[0]));
}
if (addressIdSet.size() == 0) {
addressIdSet.add(-1);
}
return addressIdSet;
}
public Set getSpatialAddressResults(SpatialSearchCommand SpatialSearchCommand){
FullTextSession FullTextSession=Search.getFullTextSession(entityManagerFactory.unwrap(SessionFactory.class.openSession());
this.userSearchPreference=userSearchPreference;
this.queryBuilder=fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity(Address.class)
.get();
this.bool=queryBuilder.bool();
Set addressIdSet=new HashSet();
bool.must(getQueryBuilder().spatial())
.在(spatialSearchCommand.getRadius(),单位为.KM.)的纬度范围内
(spatialSearchCommand.getLat())和经度(spatialSearchCommand.getLng()).createQuery());
FullTextQuery FullTextQuery=
fullTextSession.createFullTextQuery(bool.createQuery(),Address.class)
.setProjection(“addressId”)
.initializeObjectsWith(ObjectLookupMethod.SECOND_LEVEL_缓存,
DatabaseRetrievalMethod.QUERY);
List results=fullTextQuery.List();
用于(对象结果:结果){
对象[]arrayResult=(对象[])结果;
addressIdSet.add(((整数)arrayResult[0]);
}
如果(addressIdSet.size()==0){
addressIdSet.add(-1);
}
返回地址集;
}
我们使用的方法如下(实际上,这些方法是在单独的类中完成的,但为了简单起见,我刚刚展示了相关的代码
Set<Integer> localAddressIds = getSpatialAddressResults(new SpatialSearchCommand(userSearchPreference.getRadius(), userSearchPreference.getLat(), userSearchPreference.getLng()));
if(localAddressIds.size() > 0){
BooleanJunction<BooleanJunction> localSquQueryBool = getQueryBuilder().bool();
for (Integer localAddressId : localAddressIds) {
localSquQueryBool.should(getQueryBuilder().keyword().onField("currentLocation.address.indexId").matching(localAddressId).createQuery());
if(!personSearchCommand.getCurrentOnly()){
localSquQueryBool.should(getQueryBuilder().keyword().onField("locations.address.indexId").matching(localAddressId).createQuery());
}
}
bool.must(localSquQueryBool.createQuery());
}
Set localAddressIds=getSpatialAddressResults(新的SpatialSearchCommand(userSearchPreference.getRadius()、userSearchPreference.getLat()、userSearchPreference.getLng());
if(localAddressIds.size()>0){
BooleanJunction localSquQueryBool=getQueryBuilder().bool();
for(整数localAddressId:localAddressId){
localSquQueryBool.should(getQueryBuilder().keyword().onField(“currentLocation.address.indexId”).matching(localAddressId.createQuery());
如果(!personSearchCommand.getCurrentOnly()){
localSquQueryBool.should(getQueryBuilder().keyword().onField(“locations.address.indexId”).matching(localAddressId.createQuery());
}
}
bool.must(localSquQueryBool.createQuery());
}
问题是可能会返回大量地址,从而导致BooleanQueryToManyClauses:maxClauseCount设置为1024
真正的问题是,组合两个不同索引实体上的查询以避免上述问题的最佳方法是什么。本质上,您正在尝试实现联接操作。正如您所看到的,联接存在一些技术难题,这些难题在客户端很难解决 通常,Elasticsearch和Lucene中推荐的方法是尽可能避免连接。相反,您将取消模式规范化:在代表每个人的文档中,嵌入每个地址的副本。然后,您将能够在针对
人
索引的单个查询中表达所有约束。
这是通过使用@IndexedEmbedded
注释Person
中的addresses
属性来完成的
现在,正如您所想象的,这种去规范化是有代价的:每当地址发生更改时,Hibernate搜索都必须更新相关人员。
为此,您需要将列表
属性添加到地址
类中,并使用@ContainedIn
对其进行注释,以便Hibernate Search能够在地址被修改时获取要重新编制索引的人员
简而言之,将您的模型更改为:
/@索引//不再需要
@spatical(spatialMode=spatialMode.HASH,name=“location”)//为空间字段命名
@实体
@表(name=“address”)
地址{
//加上这个
@ManyToMany(mappedBy=“addressSet”)
@包含
private Set personSet=new HashSet();
@场
@基本的
@列(name=“state”)
私有字符串状态;
@场
@基本的
@列(name=“town\u city”)
私人城市;
//@字段//这不是必需的
@经度
@基本的
@列(name=“x_coord”)
私人双xCoord;
//@字段//这不是必需的
@纬度
@基本的
@列(name=“y_coord”)
私人双yCoord;
}
@索引
@实体
@表(name=“person”)
人{
@场
@列(name=“weight”)
私人双倍重量;
@列(name=“age”)
私人整数年龄;
@org.hibernate.annotations.Cache(用法=
org.hibernate.annotations.cacheconcurrencystategy.READ\u WRITE)
@许多
@级联({org.hibernate.annotations.CascadeType.SAVE_UPDATE})
@JoinTable(name=“person\u address”,
joinColumns={@JoinColumn(name=“person_id”)},
inverseJoinColumns={@JoinColumn(name=“address\u id”)})
@IndexedEmbedded//添加此
private Set addressSet=new HashSet();
@短暂的
@IndexedEmbedded//还添加了以下内容
公共地址getCurrentAddress(){
//这在您的模式中丢失了,我想是一个从addressSet中选择当前地址的getter?
}
}
然后重新编制索引。您的Person
文档现在将有两个新字段:addressSet.location
和currentAddress.location
然后按如下方式编写查询:
FullTextSession FullTextSession=Search.getFullTextSession(entityManagerFactory.unwrap(SessionFactory.class).openSession());
this.queryBuilder=fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity(Person.class)
.overridesForField(“标识符.标识符\u边缘”,“标识符\u查询\u分析器”)
.get();
public Set<Integer> getSpatialAddressResults(SpatialSearchCommand spatialSearchCommand) {
FullTextSession fullTextSession = Search.getFullTextSession(entityManagerFactory.unwrap(SessionFactory.class).openSession());
this.userSearchPreference = userSearchPreference;
this.queryBuilder = fullTextSession.getSearchFactory()
.buildQueryBuilder().forEntity(Address.class)
.get();
this.bool = queryBuilder.bool();
Set<Integer> addressIdSet = new HashSet<>();
bool.must(getQueryBuilder().spatial()
.within(spatialSearchCommand.getRadius(), Unit.KM).ofLatitude
(spatialSearchCommand.getLat()).andLongitude(spatialSearchCommand.getLng()).createQuery());
FullTextQuery fullTextQuery =
fullTextSession.createFullTextQuery(bool.createQuery(), Address.class)
.setProjection("addressId")
.initializeObjectsWith(ObjectLookupMethod.SECOND_LEVEL_CACHE,
DatabaseRetrievalMethod.QUERY);
List results = fullTextQuery.list();
for (Object result : results) {
Object[] arrayResult = (Object[]) result;
addressIdSet.add(((Integer) arrayResult[0]));
}
if (addressIdSet.size() == 0) {
addressIdSet.add(-1);
}
return addressIdSet;
}
Set<Integer> localAddressIds = getSpatialAddressResults(new SpatialSearchCommand(userSearchPreference.getRadius(), userSearchPreference.getLat(), userSearchPreference.getLng()));
if(localAddressIds.size() > 0){
BooleanJunction<BooleanJunction> localSquQueryBool = getQueryBuilder().bool();
for (Integer localAddressId : localAddressIds) {
localSquQueryBool.should(getQueryBuilder().keyword().onField("currentLocation.address.indexId").matching(localAddressId).createQuery());
if(!personSearchCommand.getCurrentOnly()){
localSquQueryBool.should(getQueryBuilder().keyword().onField("locations.address.indexId").matching(localAddressId).createQuery());
}
}
bool.must(localSquQueryBool.createQuery());
}