3.3.6 短语查询

如果按字索引中文文档,则查询的时候往往要求文档中的这些字是连续出现的。使用PhraseQuery可以查询连续出现的几个关键词。PhraseQuery称为短语匹配查询,用于要求精确匹配的查询。PhraseQuery使用了词保存在索引中的位置信息,因此需要索引中的相关列已经保存了位置信息。例如按字索引,按字查询“开封”:

        PhraseQuery query = new PhraseQuery();
        query.add(new Term("subject", "开"));
        query.add(new Term("subject", "封"));

搜索“软件工程师”时,标题为“安卓软件开发工程师”排第一位,而标题为“软件工程师”反而排在后面。词库为:“软件”“工程师”。为了解决这个问题,可以增加短语查询:

        PhraseQuery query = new PhraseQuery();
        query.add(new Term("subject", "软件"));
        query.add(new Term("subject", "工程师"));

有时候要匹配上的词之间有间隔,匹配上的词之间的距离称为slop。默认情况下,slop的值是0,可以通过调用setSlop()方法设置这个值。例如,用户搜索“西红柿牛腩”时,会匹配上“西红柿炖牛腩”。

        PhraseQuery query = new PhraseQuery();
        query.setSlop(1);
        query.add(new Term("subject", "西红柿"));
        query.add(new Term("subject", "牛腩"));

使用PhraseQuery完整的例子:

        public static void main(String[] args) throws Exception {
            //在内存中建立索引
            Directory directory = new RAMDirectory();
            Analyzer analyzer = new StandardAnalyzer();
            IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
            IndexWriter writer = new IndexWriter(directory, iwc);


            // 索引一些文档
            writer.addDocument(createDocument("1", "foo bar baz"));
            writer.addDocument(createDocument("2", "red green blue"));
            writer.addDocument(createDocument("3", "test foo bar test"));
            writer.close();


            // 查找包含foo bar这个短语的文档
            String sentence = "foo bar";
            IndexReader reader = IndexReader.open(directory);
            // 根据IndexReader创建IndexSearcher
            IndexSearcher searcher = new IndexSearcher(reader);
            PhraseQuery query = new PhraseQuery();
            String[] words = sentence.split(" ");
            for (String word : words) {
                query.add(new Term("contents", word));
            }


            // 显示搜索结果
            TopDocs topDocs = searcher.search(query, 10);
            for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
                Document doc = searcher.doc(scoreDoc.doc);
                System.out.println(doc);
            }
        }


        private static Document createDocument(String id, String content) {
            Document doc = new Document();
            doc.add(new Field("id", id, Store.YES, Index.NOT_ANALYZED));
            doc.add(new Field("contents", content, Store.YES, Index.ANALYZED,
                    Field.TermVector.WITH_POSITIONS_OFFSETS));
            return doc;
        }