嵌入式实体上的 Hibernate 过滤器
Hibernate filter on embedded Entities
我有一个使用休眠搜索的 Dropwizard 应用程序。简而言之,我的数据库中有动物及其数据,我正在尝试添加一个过滤器以获取特定品种列表的动物。不幸的是,我的过滤器似乎不起作用,我也不知道为什么。我怀疑这可能是我如何定义我的实体之间的关系以及我如何尝试在 FilterFactory
中引用品种属性,但这对我来说似乎是正确的。我已经基于 Hibernate documentation 实现了我的过滤器。相关class如下:
我的Animal
class:
@Entity
@Indexed
@FullTextFilterDef(name = "animalFilter", impl = AnimalFilterFactory.class)
@Table(name = "animal")
@JsonIgnoreProperties(ignoreUnknown = true)
public class Animal implements Serializable{
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@IndexedEmbedded
@OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "animal")
private AnimalInfo info;
// rest of the class goes here
}
我的AnimalInfo
class:
@Entity
@Table(name = "animal_info")
@JsonIgnoreProperties(ignoreUnknown = true)
public class AnimalInfo implements Serializable{
@Id
@Column(name = "animal_id")
private Long animalId;
@IndexedEmbedded
@ManyToOne( cascade = CascadeType.ALL )
@JoinColumn(name="breed_id")
private AnimalBreed breed;
// rest of the class goes here
}
我的AnimalBreed
class:
@Entity
@Table(name = "animal_breeds")
@JsonIgnoreProperties(ignoreUnknown = true)
public class AnimalBreed implements Serializable{
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* name of the breed which I want to filter my animals on.
*/
@Field
@Column(name = "name")
private String name;
我的过滤器 class:也许 "info.breed.name"
的说法不正确?
public class AnimalFilterFactory {
// list of breed names
private List<String> breeds;
public void setBreeds(List<String> breeds) {
this.breeds = breeds;
}
@Factory
public Query getFilter() {
BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();
// apply filter for all the breeds
for (String breed : breeds) {
booleanQuery.add(new TermQuery( new Term( "info.breed.name", breed ) ), Occur.SHOULD);
}
return booleanQuery.build();
}
}
在我的 DAO class 中我想应用过滤器的地方:
public List<Animal> getForBreeds(List<String> breedNames){
EntityManager em = this.currentSession();
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
QueryBuilder qb = fullTextEntityManager.getSearchFactory()
.buildQueryBuilder()
.forEntity(Animal.class)
.get();
qb.sort().byNative(SortField.FIELD_SCORE).andByNative(new SortField("id", Type.STRING, true));
org.apache.lucene.search.Query luceneQuery = qb.all().createQuery(); // get all the items
// apply filter
FullTextQuery ftq = fullTextEntityManager.createFullTextQuery(luceneQuery, Animal.class);
ftq.enableFullTextFilter("animalFilter").setParameter("breeds", breedNames);
return ftq.getResultList();
}
我已经测试过,在不应用过滤器的情况下,我的数据库中的所有 Animal 实体都会被返回。所以看来过滤器肯定是问题所在。
感谢任何帮助。
事实证明,我的做法是正确的,我只是误解了 Lucene 如何对其术语进行索引。例如品种 "Boston Terrier" 被索引为 "boston" 和 "terrier"。要过滤整个短语,必须使用 PhraseQuery 而不是 TermQuery。我更新了我的过滤器如下:
@Factory
public Query getFilter() {
BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();
String fieldId = "info.breed.name";
//booleanQuery.setMinimumNumberShouldMatch(1);
for (String breed : breeds) {
if(breed.contains(" ")) { // if multiple terms in breed
PhraseQuery.Builder builder = new PhraseQuery.Builder();
String[] terms = breed.split(" ");
for (int i = 0; i < terms.length; i++) {
builder.add(new Term( fieldId, terms[i].toLowerCase() ), i);
}
PhraseQuery pq = builder.build();
BooleanClause clause = new BooleanClause(pq, Occur.SHOULD);
booleanQuery.add(clause);
}else {
// single term
BooleanClause clause = new BooleanClause(new TermQuery( new Term(fieldId, breed.toLowerCase() ) ), Occur.SHOULD);
booleanQuery.add(clause);
}
}
BooleanQuery query = booleanQuery.build();
return query;
}
郑重声明,我是通过使用 Luke 检查索引的实体字段并相应地调整我的代码来解决这个问题的。只需确保为 Lucene 索引使用正确版本的 Luke,因为存在版本不兼容问题。 Luke 和 Lucene 版本 运行 相互平行。就我而言,我使用的是 5.5 版。
我有一个使用休眠搜索的 Dropwizard 应用程序。简而言之,我的数据库中有动物及其数据,我正在尝试添加一个过滤器以获取特定品种列表的动物。不幸的是,我的过滤器似乎不起作用,我也不知道为什么。我怀疑这可能是我如何定义我的实体之间的关系以及我如何尝试在 FilterFactory
中引用品种属性,但这对我来说似乎是正确的。我已经基于 Hibernate documentation 实现了我的过滤器。相关class如下:
我的Animal
class:
@Entity
@Indexed
@FullTextFilterDef(name = "animalFilter", impl = AnimalFilterFactory.class)
@Table(name = "animal")
@JsonIgnoreProperties(ignoreUnknown = true)
public class Animal implements Serializable{
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@IndexedEmbedded
@OneToOne(fetch = FetchType.LAZY,
cascade = CascadeType.ALL,
mappedBy = "animal")
private AnimalInfo info;
// rest of the class goes here
}
我的AnimalInfo
class:
@Entity
@Table(name = "animal_info")
@JsonIgnoreProperties(ignoreUnknown = true)
public class AnimalInfo implements Serializable{
@Id
@Column(name = "animal_id")
private Long animalId;
@IndexedEmbedded
@ManyToOne( cascade = CascadeType.ALL )
@JoinColumn(name="breed_id")
private AnimalBreed breed;
// rest of the class goes here
}
我的AnimalBreed
class:
@Entity
@Table(name = "animal_breeds")
@JsonIgnoreProperties(ignoreUnknown = true)
public class AnimalBreed implements Serializable{
@Id
@Column(name = "id")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* name of the breed which I want to filter my animals on.
*/
@Field
@Column(name = "name")
private String name;
我的过滤器 class:也许 "info.breed.name"
的说法不正确?
public class AnimalFilterFactory {
// list of breed names
private List<String> breeds;
public void setBreeds(List<String> breeds) {
this.breeds = breeds;
}
@Factory
public Query getFilter() {
BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();
// apply filter for all the breeds
for (String breed : breeds) {
booleanQuery.add(new TermQuery( new Term( "info.breed.name", breed ) ), Occur.SHOULD);
}
return booleanQuery.build();
}
}
在我的 DAO class 中我想应用过滤器的地方:
public List<Animal> getForBreeds(List<String> breedNames){
EntityManager em = this.currentSession();
FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(em);
QueryBuilder qb = fullTextEntityManager.getSearchFactory()
.buildQueryBuilder()
.forEntity(Animal.class)
.get();
qb.sort().byNative(SortField.FIELD_SCORE).andByNative(new SortField("id", Type.STRING, true));
org.apache.lucene.search.Query luceneQuery = qb.all().createQuery(); // get all the items
// apply filter
FullTextQuery ftq = fullTextEntityManager.createFullTextQuery(luceneQuery, Animal.class);
ftq.enableFullTextFilter("animalFilter").setParameter("breeds", breedNames);
return ftq.getResultList();
}
我已经测试过,在不应用过滤器的情况下,我的数据库中的所有 Animal 实体都会被返回。所以看来过滤器肯定是问题所在。
感谢任何帮助。
事实证明,我的做法是正确的,我只是误解了 Lucene 如何对其术语进行索引。例如品种 "Boston Terrier" 被索引为 "boston" 和 "terrier"。要过滤整个短语,必须使用 PhraseQuery 而不是 TermQuery。我更新了我的过滤器如下:
@Factory
public Query getFilter() {
BooleanQuery.Builder booleanQuery = new BooleanQuery.Builder();
String fieldId = "info.breed.name";
//booleanQuery.setMinimumNumberShouldMatch(1);
for (String breed : breeds) {
if(breed.contains(" ")) { // if multiple terms in breed
PhraseQuery.Builder builder = new PhraseQuery.Builder();
String[] terms = breed.split(" ");
for (int i = 0; i < terms.length; i++) {
builder.add(new Term( fieldId, terms[i].toLowerCase() ), i);
}
PhraseQuery pq = builder.build();
BooleanClause clause = new BooleanClause(pq, Occur.SHOULD);
booleanQuery.add(clause);
}else {
// single term
BooleanClause clause = new BooleanClause(new TermQuery( new Term(fieldId, breed.toLowerCase() ) ), Occur.SHOULD);
booleanQuery.add(clause);
}
}
BooleanQuery query = booleanQuery.build();
return query;
}
郑重声明,我是通过使用 Luke 检查索引的实体字段并相应地调整我的代码来解决这个问题的。只需确保为 Lucene 索引使用正确版本的 Luke,因为存在版本不兼容问题。 Luke 和 Lucene 版本 运行 相互平行。就我而言,我使用的是 5.5 版。