嵌入式实体上的 Hibernate 过滤器

Hibernate filter on embedded Entities

我有一个使用休眠搜索的 Dropwizard 应用程序。简而言之,我的数据库中有动物及其数据,我正在尝试添加一个过滤器以获取特定品种列表的动物。不幸的是,我的过滤器似乎不起作用,我也不知道为什么。我怀疑这可能是我如何定义我的实体之间的关系以及我如何尝试在 FilterFactory 中引用品种属性,但这对我来说似乎是正确的。我已经基于 Hibernate documentation 实现了我的过滤器。相关class如下:

我的Animalclass:

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

我的AnimalInfoclass:

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

我的AnimalBreedclass:

@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 版。