PhraseQuery使用位置信息来进行相关查询,比如TermQuery使用“我们”和“祖国”进行查询,那么文档中含有这两个词的所有记录都会被查询出来。但是有一种情况,我们可能需要查询“我们”和“中国”之间只隔一个字和两个字或者两个字等,而不是它们之间字距相差十万八千里,就可以使用PhraseQuery。比如下面的情况:
    doc.add(Field.Text("field", "the quick brown fox jumped over the lazy dog"));
那么:
    String[] phrase = new String[] {"quick", "fox"};
    assertFalse("exact phrase not found", matched(phrase, 0));
    assertTrue("close enough", matched(phrase, 1));
multi-terms:
    assertFalse("not close enough", matched(new String[] {"quick", "jumped", "lazy"}, 3));
    assertTrue("just enough", matched(new String[] {"quick", "jumped", "lazy"}, 4));
    assertFalse("almost but not quite", matched(new String[] {"lazy", "jumped", "quick"}, 7));
    assertTrue("bingo", matched(new String[] {"lazy", "jumped", "quick"}, 8));

数字表示slop,通过如下方式设置,表示按照顺序从第一个字段到第二个字段之间间隔的term个数。
    query.setSlop(slop);

顺序很重要:
    String[] phrase = new String[] {"fox", "quick"};
assertFalse("hop flop", matched(phrase, 2));
assertTrue("hop hop slop", matched(phrase, 3));

原理如下图所示:


对于查询关键字quick和fox,只需要fox移动一个位置即可匹配quick brown fox。而对于fox和quick这两个关键字
需要将fox移动三个位置。移动的距离越大,那么这项记录的score就越小,被查询出来的可能行就越小了。

SpanQuery利用位置信息查询更有意思的查询:

SpanQuery type         Description
SpanTermQuery         Used in conjunction with the other span query types. On its own, it’s
                                        functionally equivalent to TermQuery.
SpanFirstQuery         Matches spans that occur within the first part of a field.
SpanNearQuery         Matches spans that occur near one another.
SpanNotQuery         Matches spans that don’t overlap one another.
SpanOrQuery             Aggregates matches of span queries.

SpanFirstQuery:To query for spans that occur within the first n positions of a field, use Span-FirstQuery.



quick = new SpanTermQuery(new Term("f", "quick"));
brown = new SpanTermQuery(new Term("f", "brown"));
red = new SpanTermQuery(new Term("f", "red"));
fox = new SpanTermQuery(new Term("f", "fox"));
lazy = new SpanTermQuery(new Term("f", "lazy"));
sleepy = new SpanTermQuery(new Term("f", "sleepy"));
dog = new SpanTermQuery(new Term("f", "dog"));
cat = new SpanTermQuery(new Term("f", "cat"));

SpanFirstQuery sfq = new SpanFirstQuery(brown, 2);
assertNoMatches(sfq);
sfq = new SpanFirstQuery(brown, 3);
assertOnlyBrownFox(sfq);

SpanNearQuery:

彼此相邻的跨度

      首先,强调一下PhraseQuery对象,这个对象不属于跨度查询类,但能完成跨度查询功能。

      匹配到的文档所包含的项通常是彼此相邻的,考虑到原文档中在查询项之间可能有一些中间项,或为了能查询倒排的项,PhraseQuery设置了slop因子,但是这个slop因子指2个项允许最大间隔距离,不是传统意义上的距离,是按顺序组成给定的短语,所需要移动位置的次数这表示PhraseQuery是必须按照项在文档中出现的顺序计算跨度的,如quick brown fox为文档,则quick fox2个项的slop为1,quick向后移动一次.而fox quick需要quick向后移动3次,所以slop为3

      其次,来看一下SpanQuery的子类SpanTermQuery。

      它能跨度查询,并且不一定非要按项在文档中出现的顺序,可以用一个独立的标记表示查询对象必须按顺序,或允许按倒过来的顺序完成匹配。匹配的跨度也不是指移动位置的次数,是指从第一个跨度的起始位置到最后一个跨度的结束位置。

      在SpanNearQuery中将SpanTermQuery对象作为SpanQuery对象使用的效果,与使用PharseQuery的效果非常相似。在SpanNearQuery的构造函数中的第三个参数为inOrder标志,设置这个标志,表示按项在文档中出现的顺序倒过来的顺序。

      如:the quick brown fox jumps over the lazy dog这个文档

      public void testSpanNearQuery() throws Exception{

           SpanQuery[] quick_brown_dog=new SpanQuery[]{quick,brown,dog};

           SpanNearQuery snq=new SpanNearQuery(quick_brown_dog,0,true);//按正常顺序,跨度为0,对三个项进行查询

           assertNoMatches(snq);//无法匹配

           SpanNearQuery snq=new SpanNearQuery(quick_brown_dog,4,true);//按正常顺序,跨度为4,对三个项进行查询

           assertNoMatches(snq);//无法匹配

           SpanNearQuery snq=new SpanNearQuery(quick_brown_dog,4,true);//按正常顺序,跨度为5,对三个项进行查询

           assertOnlyBrownFox(snq);//匹配成功    

           SpanNearQuery snq=new SpanNearQuery(new SpanQuery[]{lazy,fox},3,false);//按相反顺序,跨度为3,对三个项进行查询

           assertOnlyBrownFox(snq);//匹配成功   

           //下面使用PhraseQuery进行查询,因为是按顺序,所以lazy和fox必须要跨度为5

           PhraseQuery pq=new PhraseQuery();

           pq.add(new Term("f","lazy"));

           pq.add(new Term("f","lazy"));

           pq.setslop(4);

           assertNoMatches(pq);//跨度4无法匹配

           //PharseQuery,slop因子为5

           pq.setSlop(5);

           assertOnlyBrownFox(pq);          

      }


3.PhrasePrefixQuery 主要用来进行同义词查询的:
    IndexWriter writer = new IndexWriter(directory, new WhitespaceAnalyzer(), true);
    Document doc1 = new Document();
    doc1.add(Field.Text("field", "the quick brown fox jumped over the lazy dog"));
    writer.addDocument(doc1);
    Document doc2 = new Document();
    doc2.add(Field.Text("field","the fast fox hopped over the hound"));
    writer.addDocument(doc2);

    PhrasePrefixQuery query = new PhrasePrefixQuery();
    query.add(new Term[] {new Term("field", "quick"), new Term("field", "fast")});
    query.add(new Term("field", "fox"));

    Hits hits = searcher.search(query);
    assertEquals("fast fox match", 1, hits.length());
    query.setSlop(1);
    hits = searcher.search(query);
    assertEquals("both match", 2, hits.length());