2011-07-12 34 views
3

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

+1

最简单的解决方案似乎是创建包含长度的第二个字段。我不确定'name'字段的索引可以如何有效地查询其长度。 – biziclop

+0

我想我可以创建该字段,通过它索引,然后做一个范围过滤器/查询。在我看来,应该有一个更直接的解决方案,但这是一个很好的建议。 – Federico

+0

可能有一个更直接的解决方案,我只是一个新手Lucene(这就是为什么我写我的建议作为一个评论,而不是一个答案),但这当然是我会做的。 – biziclop

回答

2

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

0

这是一个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; 
} 

上面的代码看起来通过对现场所有的条款,并检查它们的长度对长度该术语在构造函数中传递。它适用于任何至少有那么长的领域。

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源代码的版本,并从中获益)。

+0

非常感谢您的详细解答!将它转换为java确实很容易。 但我认为存在一个问题:这里假设条款是按枚举标准排序的,我不认为是这种情况。 如果该指数具有以下内容: AAAA aaaabbbb BBBB ......和查询请求长度小于5看起来这将停在第二个元素枚举和错过第三次。 我调整你的endEnum()方法使用return actualEnum.term()== null;而且工作起来,但似乎是将索引搜索转换为该术语的线性搜索。 – Federico

+0

我用一个简单的查询和一个小型数据库(约17k文档)对它进行了测试,并添加了这个标准将查询时间从<10ms改为284ms。 我可以通过它添加一个长度字段和索引,但现在非常接近最初的建议。 你觉得呢? – Federico

+0

看起来像我不明白,以及我认为的东西。我在这里从臀部射击,但不应该确保它正在查看正确的字段......否则,它会不会继续到下一个字段?('actualEnum.term()。field()= = expectedField || actualEnum.term()== null')。我认为TermEnumerator将从一个领域开始,遍历该领域的所有术语,然后前进到下一个领域......直到达到最后一个领域的最后一个领域。 –