lucene quickstart-基本索引

    lucene是java语言实现的全文检索工具。使用lucene包括两个步骤,首先建立索引,然后对建立的索引进行检索。本文讲的是建立索引过程。

    本文及后面的文章都以磁盘文件为例,进行lucene建立索引、检索的演示。

    我们的磁盘上有一堆文件,我们可能有如下的需求:

  • 按文件名搜索文件(使用最多)

  • 按文件路径搜索文件(这个。。。)

  • 按文件类型搜索文件

  • 按文件大小搜索文件

  • 按修改日期搜索文件

  • 按文件内容搜索文件

  • ……

    想想我们人是怎么做的?

    我们拿一张纸、一支笔,填写下面的表格:

序号

文件名

文件路径

文件类型

文件大小

修改时间

内容

……









 

    填完以后,搜索的时候就可以照着这张纸“按图索骥”了。

    在lucene中,这张纸叫做Directory(也就是索引保存的目录),这支笔叫做IndexWriter,表格中一条记录叫做Document,记录中的每项叫做Field。

    OK,新建一个Indexer的类,并对外提供index(String indexDir, String... dataDirs)的方法建立索引。

    伪代码如下:

public class Indexer {
    /**
     * 建立索引
     * 
     * @param indexDir
     *            索引保存路径
     * @param dataDirs
     *            数据文件路径
     * @throws Exception
     */
    public void index(String indexDir, String... dataDirs) throws Exception {
        实例化IndexerWriter对象:IndexWriter writer = ....
        
        for dataDir in dataDirs
            建立索引:index(writer, new File(dataDir))
        
        关闭流
    }
    
    /**
     * 对文件(或目录)建立索引
     * 
     * @param writer
     *            IndexWriter对象
     * @param file
     *            文件或目录
     */
    private void index(IndexWriter writer, File file) {
        如果file为目录:
            对目录下的子文件(夹),调用index(writer, file)方法
        如果file为文件:
            生成一条Document记录,并将各项填充到该Document中,然后将该Document添加到writer
    }
}

    上面的伪代码不难转为Java代码。

package cn.lym.lucene.quickstart.index;

import java.io.File;
import java.io.FileReader;
import java.io.Reader;

import org.apache.log4j.LogManager;
import org.apache.log4j.Logger;
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.LongField;
import org.apache.lucene.document.StringField;
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;
import org.apache.lucene.util.Version;

import cn.lym.lucene.quickstart.util.FileUtil;
import cn.lym.lucene.quickstart.util.StreamUtil;

/**
 * 提供对磁盘文件建立索引的功能
 * 
 * @author liuyimin
 *
 */
public class Indexer {
    /**
     * Logger对象
     */
    private static final Logger logger = LogManager.getLogger(Indexer.class);

    /**
     * 建立索引
     * 
     * @param indexDir
     *            索引保存路径
     * @param dataDirs
     *            数据文件路径
     * @throws Exception
     */
    public void index(String indexDir, String... dataDirs) throws Exception {
        Directory directory = FSDirectory.open(new File(indexDir));
        IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, new StandardAnalyzer());
        IndexWriter writer = new IndexWriter(directory, config);

        for (String dataDir : dataDirs) {
            index(writer, new File(dataDir));

            writer.commit();
        }

        // 关闭流
        StreamUtil.close(writer, directory);
    }

    /**
     * 对文件(或目录)建立索引
     * 
     * @param writer
     *            IndexWriter对象
     * @param file
     *            文件或目录
     */
    private void index(IndexWriter writer, File file) {
        if (file.isDirectory()) {// 目录,需要递归建立索引
            File[] subFiles = file.listFiles();
            if (subFiles != null) {
                for (File subFile : subFiles) {
                    index(writer, subFile);
                }
            }
        } else if (file.isFile()) {// 文件,对文件建立索引
            if (logger.isDebugEnabled()) {
                logger.debug("indexing file: " + file.getAbsolutePath());
            }

            try {
                Document document = file2Document(file);
                writer.addDocument(document);
            } catch (Exception e) {
                logger.error(
                        "An error occurred while adding a document to indexwriter. File: " + file.getAbsolutePath(), e);
            }
        }
    }

    /**
     * 将文件转为lucene的{@link Document}类型<br/>
     * 其中包括:
     * <ul>
     * <li>pathname:路径名</li>
     * <li>filename:文件名</li>
     * <li>size:文件大小(字节)</li>
     * <li>type:文件类型</li>
     * <li>content:文件内容(只有明文文件有,判断是否是明文文件:{@link FileUtil#isPlainTextFile(File)}
     * )</li>
     * </ul>
     * 
     * @param file
     * @return
     */
    private Document file2Document(File file) {
        Document document = new Document();
        document.add(new StringField("pathname", file.getAbsolutePath(), Store.YES));
        document.add(new StringField("filename", file.getName(), Store.YES));
        document.add(new StringField("type", FileUtil.getFileType(file), Store.YES));
        document.add(new LongField("size", file.length(), Store.YES));
        document.add(new LongField("lastmodified", file.lastModified(), Store.YES));
        if (FileUtil.isPlainTextFile(file)) {// 对明文文件的内容建立索引
            try {
                Reader reader = new FileReader(file);
                document.add(new TextField("content", reader));
            } catch (Exception e) {
                logger.error("An error occurred while indexing " + file.getAbsolutePath(), e);
            }
        }
        return document;
    }
}


   使用了两个工具类。

   FileUtil.java

package cn.lym.lucene.quickstart.util;

import java.io.File;

/**
 * 文件有关的工具类
 * 
 * @author liuyimin
 *
 */
public class FileUtil {
    /**
     * 获得文件类型
     * 
     * @param file
     * @return
     */
    public static String getFileType(File file) {
        String fileName = file.getName();
        int index = fileName.lastIndexOf(".");
        if (index != -1) {
            return fileName.substring(index + 1);
        }
        return fileName;
    }

    /**
     * 判断文件是否是明文的文件
     * 
     * @param file
     * @return
     */
    public static boolean isPlainTextFile(File file) {
        // 为了简化,这里只将txt文件作为明文文件
        String fileType = getFileType(file);
        return "txt".equals(fileType);
    }
}

    StreamUtil.java

package cn.lym.lucene.quickstart.util;

import java.io.Closeable;

/**
 * 流操作有关的工具类
 * 
 * @author liuyimin
 *
 */
public class StreamUtil {
    /**
     * 关闭流操作
     * 
     * @param closeables
     */
    public static void close(Closeable... closeables) {
        if (closeables != null) {
            for (Closeable closeable : closeables) {
                if (closeable != null) {
                    try {
                        closeable.close();
                    } catch (Exception e) {
                    } finally {
                        closeable = null;
                    }
                }
            }
        }
    }
}


    需要说明的几点:

  1. 关于Field。Field可控的参数包括:是否建立索引、是否保存、是否分词以及类型(数值类型或者字符类型)。

    常用的Field有下面几种:

    1. StringField:字符类型、建立索引并且不分词。存储与否可控。

    2. LongField:数值类型、建立索引并且不分词。存储与否可控。

    3. TextField:字符类型、建立索引并且分词。存储与否可控。

  2. 关于Directory。Directory为索引存放的目录,可以存放在磁盘中(例子中的就是写在磁盘中)也就是FSDirectory;也可以放在内存中,也就是RAMDirectory。

    本文的代码可以在 https://git.oschina.net/coding4j/lucene-quickstart 获取。

你可能感兴趣的:(lucene quickstart-基本索引)