Lucene搜索引擎

2017/08/01 网络

Lucene is a Java full-text search engine. Lucene is not a complete application, but rather a code library and API that can easily be used to add search capabilities to applications.

这篇文章主要介绍关于Lucene的基本指南以及使用Lucene做文本检索。Lucene是基于Java的全文检索引擎工具包,它本身不是一个完整的程序,而是一个类库与应用程序接口,你可以使用Lucene来为程序添加搜索的功能。目前最新的版本是Lucene 6.6.0。

遇到问题

生活中的数据分为两种,一种是结构化数据,例如数据库、元数据等具有固定格式或者固定长度的数据;另一种是非结构化数据,例如磁盘上的文档、电子邮件等。当我们需要检索非结构化数据的时候,例如在磁盘上寻找具有关键字为”hello”的文档,常规的思路是顺序扫描方法。顺序扫描方法的原理是扫描所有的文档,从头到尾检索是否具有关键字”hello”,如果文档中包含这个关键字,那么这篇文档就是我们希望检索的结果。这种扫描的方式的缺点很明显,就是速度非常慢。

另一种方法是全文检索(Full-text Search)。全文检索的方法试图将非结构化数据提取部分关键信息并重新组织,使得其具备结构化的属性。然后检索算法再对其进行搜索,从而达到相比与顺序扫描方法更快的搜索速度。这里对非结构化数据提取的部分关键信息的集合称为索引。常见的一种应用是字典。例如我们平时使用的汉字。将每个文字的部首提取出来并排序,按照剩余的笔画作为遍历,这样就能快速的找到我们想要查询的汉字了。

Lucene实现全文检索分为两个部分,一部分是创建索引,另一部分是查询索引。创建索引的过程是获得文档,构建文档对象并创建文档索引,保存到索引库;查询索引是用户通过查询接口创建查询,然后在索引库中寻找匹配的索引。

倒排索引结构

倒排索引源于实际应用中需要根据属性的值来查找记录。这种索引表中的每一项都包括一个属性值和具有该属性值的各记录的地址。由于不是由记录来确定属性值,而是由属性值来确定记录的位置,因而称为倒排索引(inverted index)。带有倒排索引的文件我们称为倒排索引文件,简称倒排文件(inverted file)。

demo

First, you should download the latest Lucene distribution and then extract it to a working directory.

You need four JARs: the Lucene JAR, the queryparser JAR, the common analysis JAR, and the Lucene demo JAR. You should see the Lucene JAR file in the core/ directory you created when you extracted the archive – it should be named something like lucene-core-{version}.jar. You should also see files called lucene-queryparser-{version}.jar, lucene-analyzers-common-{version}.jar and lucene-demo-{version}.jar under queryparser, analysis/common/ and demo/, respectively.

Put all four of these files in your Java CLASSPATH.

/**
 * Created by alienx on 2017/7/31.
 */
import java.io.File;
import java.io.FileReader;
import java.nio.file.FileSystems;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

class Index {
    // 建立索引
    public void index() {
        IndexWriter indexWriter = null;
        try {
            // 1、创建Directory
            Directory directory = FSDirectory.open(FileSystems.getDefault().getPath("/Users/alienx/Desktop/demo/index"));
            // 2、创建IndexWriter
            Analyzer analyzer = new StandardAnalyzer();
            IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);
            indexWriter = new IndexWriter(directory, indexWriterConfig);
            indexWriter.deleteAll();
            File dFile = new File("/Users/alienx/Desktop/demo/data");
            File[] files = dFile.listFiles();
            for (File file : files) {
                // 3、创建Document对象
                Document document = new Document();
                // 4、为Document添加Field
                document.add(new Field("content", new FileReader(file), TextField.TYPE_NOT_STORED));
                document.add(new Field("filename", file.getName(), TextField.TYPE_STORED));
                document.add(new Field("filepath", file.getAbsolutePath(), TextField.TYPE_STORED));
                // 5、通过IndexWriter添加文档到索引中
                indexWriter.addDocument(document);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (indexWriter != null) {
                    indexWriter.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

class Search {
    /**
     * 搜索
     */
    public void search(String keyWord) {
        DirectoryReader directoryReader = null;
        try {
            // 1、创建Directory
            Directory directory = FSDirectory.open(FileSystems.getDefault().getPath("/Users/alienx/Desktop/demo/index"));
            // 2、创建IndexReader
            directoryReader = DirectoryReader.open(directory);
            // 3、根据IndexReader创建IndexSearch
            IndexSearcher indexSearcher = new IndexSearcher(directoryReader);
            // 4、创建搜索的Query
            Analyzer analyzer = new StandardAnalyzer();
            // 创建parser来确定要搜索文件的内容,第一个参数为搜索的域
            QueryParser queryParser = new QueryParser("content", analyzer);
            // 创建Query表示搜索域为content包含UIMA的文档
            Query query = queryParser.parse(keyWord);
            // 5、根据searcher搜索并且返回TopDocs
            TopDocs topDocs = indexSearcher.search(query, 10);
            System.out.println("查找到的文档总共有:"+topDocs.totalHits);
            // 6、根据TopDocs获取ScoreDoc对象
            ScoreDoc[] scoreDocs = topDocs.scoreDocs;
            for (ScoreDoc scoreDoc : scoreDocs) {
                // 7、根据searcher和ScoreDoc对象获取具体的Document对象
                Document document = indexSearcher.doc(scoreDoc.doc);
                // 8、根据Document对象获取需要的值
                System.out.println(document.get("filename") + " " + document.get("filepath"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (directoryReader != null) {
                    directoryReader.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}

public class HighLightSearch {
    public static void main(String args[]) {
        Index newIndex = new Index();
        newIndex.index();
        Search newSearch = new Search();
        newSearch.search("UIMA");
    }
}

Search

    Table of Contents