【问题标题】:Hibernate search to find partial matches of a phrase休眠搜索以查找短语的部分匹配项
【发布时间】:2017-05-17 14:44:47
【问题描述】:

在我的项目中,我们使用带有 lucene 分析器和太阳能的 hibernate search 4.5。 我为我的客户提供一个文本字段。当他们输入短语时,我想查找名称中包含给定短语的所有 User 实体。

例如,考虑在数据库中有以下标题的条目列表:

[ Alan Smith, John Cane, Juno Taylor, Tom Caner Junior ]

jun 应该返回 Juno TaylorTom Caner Junior

an 应该返回 Alan SmithJohn CaneTom Caner Junior

    @AnalyzerDef(name = "customanalyzer", tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class), filters = {
            @TokenFilterDef(factory = LowerCaseFilterFactory.class),
            @TokenFilterDef(factory = SnowballPorterFilterFactory.class, params = { @Parameter(name = "language", value = "English") })

    })
@Analyzer(definition = "customanalyzer")
    public class Student implements Serializable {

        @Column(name = "Fname")
        @Field(index = Index.YES, store = Store.YES, analyze = Analyze.YES)
        private String fname;

        @Column(name = "Lname")
        @Field(index = Index.YES, store = Store.YES, analyze = Analyze.YES)
        private String lname;

    }

我尝试过通配符搜索,但是

Wildcard queries do not apply the analyzer on the matching terms. Otherwise the risk of * or ? being mangled is too high.

Query luceneQuery = mythQB
    .keyword()
      .wildcard()
    .onFields("fname")
    .matching("ju*")
    .createQuery();

我怎样才能做到这一点?

【问题讨论】:

    标签: java hibernate lucene hibernate-search solar


    【解决方案1】:

    首先,您没有将分析器分配给您的字段,因此目前没有使用它。你应该使用@Field.analyzer。

    其次,回答你的问题,这种文本最好用EdgeNGramFilter分析。您应该将此过滤器添加到您的分析器定义中。

    编辑:此外,为了防止“sathya”等查询匹配“sanchana”,您应该在查询时使用不同的分析器。

    下面是一个完整的例子。

    @AnalyzerDef(name = "customanalyzer", tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class), filters = {
            @TokenFilterDef(factory = LowerCaseFilterFactory.class),
            @TokenFilterDef(factory = SnowballPorterFilterFactory.class, params = { @Parameter(name = "language", value = "English") })
            @TokenFilterDef(factory = EdgeNGramFilterFactory.class, params = { @Parameter(name = "maxGramSize", value = "15") })
    
    })
    @AnalyzerDef(name = "customanalyzer_query", tokenizer = @TokenizerDef(factory = WhitespaceTokenizerFactory.class), filters = {
            @TokenFilterDef(factory = LowerCaseFilterFactory.class),
            @TokenFilterDef(factory = SnowballPorterFilterFactory.class, params = { @Parameter(name = "language", value = "English") })
    
    })
    public class Student implements Serializable {
    
        @Column(name = "Fname")
        @Field(index = Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "customanalyzer"))
        private String fname;
    
        @Column(name = "Lname")
        @Field(index = Index.YES, store = Store.YES, analyze = Analyze.YES, analyzer = @Analyzer(definition = "customanalyzer")))
        private String lname;
    
    }
    

    然后特别提到你想在构建查询时使用这个“查询”分析器:

    QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Student.class)
        // Here come the assignments of "query" analyzers
        .overridesForField( "fname", "customanalyzer_query" )
        .overridesForField( "lname", "customanalyzer_query" )
        .get();
    // Then it's business as usual
    Query luceneQuery = queryBuilder.keyword().onFields("fname", "lname").matching("sathya").createQuery();
    FullTextQuery query = fullTextEntityManager.createFullTextQuery(luceneQuery, Student.class);
    

    另请参阅:https://stackoverflow.com/a/43047342/6692043


    顺便说一句,如果您的数据仅包含名字和姓氏,则不应使用词干提取 (SnowballPorterFilterFactory):它只会无缘无故地降低搜索的准确性。

    【讨论】:

    • 我已经在班级级别添加了分析器,现在我已经更新了。我已经尝试过你建议的 EdgeNGramTokenFilter.class,现在问题是什么,如果我用 Sathya 搜索它的给出结果,如 sandhya、saravanan、sachin、sanchana 和 sathya。但我没有其他名字。它应该像“%sathya%”一样搜索。您能否为此建议任何其他过滤器。提前致谢。
    • @PSKumar 对,您应该在查询时使用不同的分析器。我更新了我的答案。
    • qb 来自哪里,我正在尝试使用相同的方式,但未定义 qb
    • @Tanah 我修正了这个例子,qb 应该是 queryBuilder
    【解决方案2】:

    为什么不使用标准的TypedQuery

    String term 是您的搜索词)

    TypedQuery<Student> q = em.createQuery(
            "SELECT s " +
            "FROM Student s " +
            "WHERE s.fname like :search " +
            "OR s.lname like :search";
    q.setParameter("search", "%" + term + "%");
    

    没有测试这个,但是这样的东西应该可以解决问题。

    【讨论】:

    • SQL "LIKE" 运算符非常有限。问题是关于使用正确的全文,它可以非常灵活地控制匹配的方式/内容,并且恰好表现得更好。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多