【问题标题】:Lucene: searching/filtering by field's value lengthLucene:按字段的值长度搜索/过滤
【发布时间】:2011-07-12 22:18:58
【问题描述】:

我需要一些帮助来进行搜索。 假设我有一个非常简单的文档结构,只有 1 个字段,标记为名称。 我需要检索长度大于或小于指定值的所有名称。长度是指 String.length()。 范围过滤器在概念上似乎很接近,但我找不到一个很好的例子来编写我的具体案例。 感谢您的帮助。

【问题讨论】:

  • 最简单的解决方案似乎是创建第二个包含长度的字段。我不确定如何使用 name 字段的索引来有效地按长度查询。
  • 我想我可以创建该字段,按其索引,然后进行范围过滤/查询。在我看来应该有一个更直接的解决方案,但这是一个很好的建议。
  • 可能有一个更直接的解决方案,我只是 Lucene 的新手(这就是为什么我将我的建议写成评论而不是答案)但这当然是我会做的。
  • 我从 lucene 用户列表中得到了相同的答案。我要再等一会儿,然后走那条路。如果您关心这些点,您可以将其添加为答案,我会将其标记为已解决。
  • 使用长度创建 NumericField 是一个合理的建议,然后您可以使用 RangeQuery 来提取结果

标签: java search lucene


【解决方案1】:

使用长度添加 NumericField,然后使用 RangeQuery。有关示例,请参见 NumericField javadoc。

【讨论】:

    【解决方案2】:

    这是 MultiTermQuery 的经典示例。它不在盒子里,但很容易实现。看看WildCardQuery,它扩展了MultiTermQuery。这做了非常相似的事情。只需使用不同的 FilterredTermEnum 像这样使用 term.text 的长度来过滤术语(而不是术语文本本身)。

    奇迹发生在这里(这段代码在我帖子底部的自定义术语枚举器中):

    protected internal override bool TermCompare(Term term)
    {
      if (field == term.Field())
      {
        System.String searchText = term.Text();
        if (searchText.Length >= text.Length())
        {
          return true;
        }
      }
      endEnum = true;
      return false;
    }
    

    上面的代码查看了该字段的所有术语,并根据构造函数中传递的术语长度检查它们的长度。对于任何至少有这么长的字段,它都会产生 true。

    public class MinLengthQuery : MultiTermQuery
    {
      public MinLengthQuery(Term term) : base(term)
      {
      }
    
      protected internal override FilteredTermEnum GetEnum(IndexReader reader)
      {
        return new MinLengthTermEnum(reader, GetTerm());
      }
    }
    

    这个类完成了所有的工作:

    public class MinLengthTermEnum : FilteredTermEnum
    {
    internal Term searchTerm;
    internal System.String field = "";
    internal System.String text = "";
    internal System.String pre = "";
    internal int preLen = 0;
    internal bool endEnum = false;
    
    public MinLengthTermEnum(IndexReader reader, Term term):base()
    {
      searchTerm = term;
      field = searchTerm.Field();
      text = searchTerm.Text();
      SetEnum(reader.Terms(new Term(searchTerm.Field(), "")));
    }
    
    protected internal override bool TermCompare(Term term)
    {
      if (field == term.Field())
      {
        System.String searchText = term.Text();
        if (searchText.Length >= text.Length())
        {
          return true;
        }
      }
      endEnum = true;
      return false;
    }
    
    public override float Difference()
    {
      return 1.0f;
    }
    
    public override bool EndEnum()
    {
      return endEnum;
    }
    public override void  Close()
    {
      base.Close();
      searchTerm = null;
      field = null;
      text = null;
    }
    }
    

    (我是一个 lucene.net 人,但翻译应该很容易......从您的 Lucene 版本的 WildCardQuery 和 TermEnum 源代码开始并使用它可能会更容易)。

    【讨论】:

    • 非常感谢您的详细解答!将其转换为 java 确实很容易。但是我认为有一个问题:假设这些术语是按列举的标准排序的,我认为情况并非如此。如果索引具有以下内容:aaaa aaaabbbb bbbb ...并且查询要求长度小于 5,这似乎将停止枚举第二个元素并错过第三个元素。我调整了您的 endEnum() 方法以使用 return actualEnum.term()==null;并且可行,但是似乎将索引搜索转换为对该术语的线性搜索。
    • 我用一个简单的查询和一个小型数据库(约 17k 文档)对其进行了测试,添加此标准将查询时间从
    • 看起来我并不像我想象的那样理解那些东西。我在这里从臀部射击,但它不应该确保它正在查看正确的字段......否则,它不会继续到下一个字段吗?(actualEnum.term().field() == expectedField || actualEnum.term()==null)。我认为 TermEnumerator 将从一个字段开始,遍历该字段的所有术语,然后前进到下一个字段......直到它到达最后一个字段的最后一个术语。
    • 我现在意识到我指的是文档,而枚举器是关于术语的。这对我来说太耗时了。我将尝试第一个建议,如果这不起作用,我会回来尝试这个。仍然非常感谢您的帮助!
    猜你喜欢
    • 2011-12-13
    • 1970-01-01
    • 2011-02-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-13
    • 1970-01-01
    相关资源
    最近更新 更多