Lucene 搜索的初步探究

搜索应用程序和 Lucene 之间的关系

一般的搜索引擎都会采用这样的

Lucene 采用的是一种称为反向索引(inverted index)的机制。反向索引就是说我们维护了一个词 / 短语表,对于这个表中的每个词 / 短语,都有一个链表描述了有哪些文档包含了这个词 / 短语。这样在用户输入查询条件的时候,就能非常快的得到搜索结果。
反向索引

Lucene 软件包的介绍

org.apache.lucene.document 
封装要索引的文档所需要的类,比如 Document, Field。 文档一document对象在程序程序中运行

org.apache.lucene.analysis
对文档进行分词,因为文档在建立索引之前必须要进行分词


org.apache.lucene.index
创建索引以及对创建好的索引进行更新
IndexWriter 是用来创建索引并添加文档到索引中的,IndexReader 是用来删除索引中的文档的

org.apache.lucene.search
建立好的索引上进行搜索所需要的类
IndexSearcher 和 Hits, IndexSearcher 定义了在指定的索引上进行搜索的方法,Hits 用来保存搜索得到的结果。

Lucene建立索引和搜索的几个流程

建立索引

1 document 指文档可以死HTML,文本等。并且document是由多个Field对象组成的,可以把一个 Document 对象想象成数据库中的一个记录,而每个 Field 对象就是记录的一个字段。

2 Field Field 对象是用来描述一个文档的某个属性的,比如一封电子邮件的标题和内容可以用两个 Field 对象分别描述。

3 Analyzer 对文档进行分词

4 IndexWriter IndexWriter 是 Lucene 用来创建索引的一个核心的类,他的作用是把一个个的 Document 对象加到索引中来。

5 Directory 代表了Lucene 的索引的存储的位置 , 有两个实现,第一个是 FSDirectory, 存储在文件系统中的索引 。 RAMDirectory, 存储在内存当中的索引。

搜索文档

之前建立好的索引文档,现在我们可以进行对文档进行搜索
1 Query 是抽象类,有多个继承实现 TermQuery, BooleanQuery, PrefixQuery. 。这些类可以将用户输入的字符串封装成Lucene能够识别的Query

2 Term Term 是搜索的基本单位 基本的语法是

Term term = new Term(“fieldName”,”queryWord”);

fieldName 是指要在文档的那个field上查找,queryWord表示要查询的关键词

3 TermQuery TermQuery是抽象的query的一个之类,

	TermQuery termQuery = new TermQuery(new Term(“fieldName”,”queryWord”)); 它的构造函数只接受一个参数,那就是一个 Term 对象。

4 IndexSearcher 在建立好的索引上进行搜索,

5 Hits Hits 是用来保存搜索的结果的。

建立索引的过程和代码

	package LuceneTest.LuceneTest;

import java.io.File;
import java.io.FileReader;
import java.nio.file.Paths;

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.Store;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;

/**
 * 建立索引的类
 * @author Ni Shengwu
 *
 */
public class Indexer {

private IndexWriter writer; //写索引实例

    //构造方法,实例化IndexWriter
    public Indexer(String indexDir) throws Exception {
        Directory dir = FSDirectory.open(Paths.get(indexDir));
        Analyzer analyzer = new StandardAnalyzer(); //标准分词器,会自动去掉空格啊,is a the等单词
        IndexWriterConfig config = new IndexWriterConfig(analyzer); //将标准分词器配到写索引的配置中
        writer = new IndexWriter(dir, config); //实例化写索引对象
    }

    //关闭写索引
    public void close() throws Exception {
        writer.close();
    }

    //索引指定目录下的所有文件
    public int indexAll(String dataDir) throws Exception {
        File[] files = new File(dataDir).listFiles(); //获取该路径下的所有文件
        for(File file : files) {
            indexFile(file); //调用下面的indexFile方法,对每个文件进行索引
        }
        return writer.numDocs(); //返回索引的文件数
    }

    //索引指定的文件
    private void indexFile(File file) throws Exception {
        System.out.println("索引文件的路径:" + file.getCanonicalPath());
        Document doc = getDocument(file); //获取该文件的document
        writer.addDocument(doc); //调用下面的getDocument方法,将doc添加到索引中
    }

    //获取文档,文档里再设置每个字段,就类似于数据库中的一行记录
    private Document getDocument(File file) throws Exception{
        Document doc = new Document();
        //添加字段
        doc.add(new TextField("contents", new FileReader(file))); //添加内容
        doc.add(new TextField("fileName", file.getName(), Store.YES)); //添加文件名,并把这个字段存到索引文件里
        doc.add(new TextField("fullPath", file.getCanonicalPath(), Store.YES)); //添加文件路径
        return doc;
    }

    public static void main(String[] args) {
        String indexDir = "D:\\lucene\\index"; //将索引保存到的路径
        String dataDir = "D:\\lucene\\data"; //需要索引的文件数据存放的目录
        Indexer indexer = null;
        int indexedNum = 0;
        long startTime = System.currentTimeMillis(); //记录索引开始时间
        try {
            indexer = new Indexer(indexDir);
            indexedNum = indexer.indexAll(dataDir);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                indexer.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        long endTime = System.currentTimeMillis(); //记录索引结束时间
        System.out.println("索引耗时" + (endTime-startTime) + "毫秒");
        System.out.println("共索引了" + indexedNum + "个文件");
    }
 }

建立一个搜索的过程

	package LuceneTest.LuceneTest;

import java.nio.file.Paths;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
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;

public class Searcher {

public static void search(String indexDir, String q) throws Exception {

    Directory dir = FSDirectory.open(Paths.get(indexDir)); //获取要查询的路径,也就是索引所在的位置
    IndexReader reader = DirectoryReader.open(dir);
    IndexSearcher searcher = new IndexSearcher(reader);
    Analyzer analyzer = new StandardAnalyzer(); //标准分词器,会自动去掉空格啊,is a the等单词
    QueryParser parser = new QueryParser("contents", analyzer); //查询解析器
    Query query = parser.parse(q); //通过解析要查询的String,获取查询对象

    long startTime = System.currentTimeMillis(); //记录索引开始时间
    TopDocs docs = searcher.search(query, 10);//开始查询,查询前10条数据,将记录保存在docs中
    long endTime = System.currentTimeMillis(); //记录索引结束时间
    System.out.println("匹配" + q + "共耗时" + (endTime-startTime) + "毫秒");
    System.out.println("查询到" + docs.totalHits + "条记录");

    for(ScoreDoc scoreDoc : docs.scoreDocs) { //取出每条查询结果
        Document doc = searcher.doc(scoreDoc.doc); //scoreDoc.doc相当于docID,根据这个docID来获取文档
        System.out.println(doc.get("fullPath")); //fullPath是刚刚建立索引的时候我们定义的一个字段
    }
    reader.close();
}

public static void main(String[] args) {
    String indexDir = "D:\\lucene\\index";
    String q = "import"; //查询这个字符串
    try {
        search(indexDir, q);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
}

posted on 2017-05-17 20:59  王守昌  阅读(215)  评论(0编辑  收藏  举报

导航