【问题标题】:Get matched terms from Lucene query从 Lucene 查询中获取匹配的术语
【发布时间】:2011-10-25 21:35:07
【问题描述】:

给定一个 Lucene 搜索查询,例如:+(letter:A letter:B letter:C) +(style:Capital),我如何判断这三个字母中的哪一个实际上与任何给定文档匹配?我不在乎它们在哪里匹配,或者它们匹配了多少次,我只需要知道它们是否匹配。

目的是获取初始查询(“A B C”),删除成功匹配的术语(A和B),然后对剩余的(C)进行进一步处理。

【问题讨论】:

    标签: lucene


    【解决方案1】:

    虽然示例是用 c# 编写的,但 Lucene API 非常相似(一些大小写差异)。我认为翻译成java并不难。

    这是用法

    List<Term> terms = new List<Term>();    //will be filled with non-matched terms
    List<Term> hitTerms = new List<Term>(); //will be filled with matched terms
    GetHitTerms(query, searcher,docId, hitTerms,terms);
    

    这是方法

    void GetHitTerms(Query query,IndexSearcher searcher,int docId,List<Term> hitTerms,List<Term>rest)
    {
        if (query is TermQuery)
        {
            if (searcher.Explain(query, docId).IsMatch() == true) 
                hitTerms.Add((query as TermQuery).GetTerm());
            else
                rest.Add((query as TermQuery).GetTerm());
            return;
        }
    
        if (query is BooleanQuery)
        {
            BooleanClause[] clauses = (query as BooleanQuery).GetClauses();
            if (clauses == null) return;
    
            foreach (BooleanClause bc in clauses)
            {
                GetHitTerms(bc.GetQuery(), searcher, docId,hitTerms,rest);
            }
            return;
        }
    
        if (query is MultiTermQuery)
        {
            if (!(query is FuzzyQuery)) //FuzzQuery doesn't support SetRewriteMethod
                (query as MultiTermQuery).SetRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE);
    
            GetHitTerms(query.Rewrite(searcher.GetIndexReader()), searcher, docId,hitTerms,rest);
        }
    }
    

    【讨论】:

    • 这正是我所需要的,而且由于我已经在使用 Lucene.net,所以我什至不需要翻译它。谢谢! (我意识到我可能应该使用 lucene.net 标签,但我认为这是关于系统的一般问题,而不是 .net 风格的问题,因为它们的工作方式相同)。
    【解决方案2】:

    作为@L.B 给出的答案,这是适用于我的 JAVA 转换代码:

    void GetHitTerms(Query query,IndexSearcher searcher,int docId,List<Term> hitTerms,List<Term>rest) throws IOException
        {
            if(query instanceof TermQuery )
            {
                if (searcher.explain(query, docId).isMatch())
                    hitTerms.add(((TermQuery) query).getTerm());
                else
                    rest.add(((TermQuery) query).getTerm());
                return;
            }
    
                if(query instanceof BooleanQuery )
                {
                    for (BooleanClause clause : (BooleanQuery)query) {
                        GetHitTerms(clause.getQuery(), searcher, docId,hitTerms,rest);
                }
                return;
            }
    
            if (query instanceof MultiTermQuery)
            {
                if (!(query instanceof FuzzyQuery)) //FuzzQuery doesn't support SetRewriteMethod
                    ((MultiTermQuery)query).setRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_QUERY_REWRITE);
    
                GetHitTerms(query.rewrite(searcher.getIndexReader()), searcher, docId,hitTerms,rest);
            }
        }
    

    【讨论】:

      【解决方案3】:

      我基本上使用了与@L.B 相同的方法,但更新了它以用于最新的 Lucene 版本 7.4.0。注意:FuzzyQuery 现在支持 .setRewriteMethod(这就是我删除 if 的原因)。

      我还包括了对 BoostQuerys 的处理,并将 Lucene 找到的单词保存在 HashSet 中以避免重复而不是条款。

      private void saveHitWordInList(Query query, IndexSearcher indexSearcher,
          int docId, HashSet<String> hitWords) throws IOException {
        if (query instanceof TermQuery)
          if (indexSearcher.explain(query, docId).isMatch())
            hitWords.add(((TermQuery) query).getTerm().toString().split(":")[1]);
        if (query instanceof BooleanQuery) {
          for (BooleanClause clause : (BooleanQuery) query) {
            saveHitWordInList(clause.getQuery(), indexSearcher, docId, hitWords);
          }
        }
      
        if (query instanceof MultiTermQuery) {
          ((MultiTermQuery) query)
              .setRewriteMethod(MultiTermQuery.SCORING_BOOLEAN_REWRITE);
          saveHitWordInList(query.rewrite(indexSearcher.getIndexReader()),
              indexSearcher, docId, hitWords);
        }
      
        if (query instanceof BoostQuery)
          saveHitWordInList(((BoostQuery) query).getQuery(), indexSearcher, docId,
              hitWords);
      }
      

      【讨论】:

        【解决方案4】:

        这是 Lucene.NET 4.8 的简化和非递归版本。
        未经验证,但这也应该适用于 Lucene.NET 3.x

        IEnumerable<Term> GetHitTermsForDoc(Query query, IndexSearcher searcher, int docId)
        {
            //Rewrite query into simpler internal form, required for ExtractTerms
            var simplifiedQuery = query.Rewrite(searcher.IndexReader);
            HashSet<Term> queryTerms = new HashSet<Term>();
            simplifiedQuery.ExtractTerms(queryTerms);
        
            List<Term> hitTerms = new List<Term>();
            foreach (var term in queryTerms)
            {
                var termQuery = new TermQuery(term);
        
                var explanation = searcher.Explain(termQuery, docId);
                if (explanation.IsMatch)
                {
                    hitTerms.Add(term);
                }
            }
            return hitTerms;
        }
        

        【讨论】:

        • 我不再从事那份工作,所以我无法对其进行测试,但这确实看起来更干净。自 2011 年以来是否添加了一些特别支持此功能的内容?
        • 我没有尝试在 lucene.net 3.0.3 上运行它,但我认为应该可以。这里的两个关键方法是query.Rewritequery.ExtractTerms。这两种方法都存在于 Lucene.net 3.0.3
        【解决方案5】:

        您可以为每个单独的术语使用cached filter,并根据BitSets 快速检查每个文档ID。

        【讨论】:

        • 这可能是因为我是 Lucene 的新手,但我不知道如何使用它。我现在没有过滤器 - 只是一个查询,并且查询术语每次都会改变 - 一次可能是“A B”,而另一个可能是“A F B Q”。我可以从 Query 创建一个 QueryFilter,但这似乎是多余的(尽管可能是必要的)。我也没有用于过滤器的 GetDocIdSet() 函数的 indexReader,只有一个 IndexSearcher()。你能提供一个使用例子吗?
        • 实际上,我刚刚意识到如何获得 IndexReader - 我已经在代码的其他地方进行了操作。不过,其余的评论仍然适用。
        猜你喜欢
        • 2011-02-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-06-08
        相关资源
        最近更新 更多