文档章节

lucene quickstart-基本索引

1120101929
 1120101929
发布于 2015/09/05 20:49
字数 1184
阅读 18
收藏 0

    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 获取。

© 著作权归作者所有

共有 人打赏支持
1120101929
粉丝 0
博文 4
码字总数 5988
作品 0
海淀
程序员
私信 提问
lucene quickstart-基本检索

有了上一篇建立的索引,就可以进行检索了。 数据库查询使用SQL,lucene检索使用Query。 lucene提供了一个IndexSearcher类,检索的功能通过这个类完成,其构造方法需要一个IndexReader对象。I...

1120101929
2015/09/06
23
0
搜索应用参考示例 - xxl-search

《搜索应用参考示例XXL-SEARCH》 一、简介 1.1 概述 XXL-SEARCH 是以 "lucene/elasticsearch" 为核心的,Pragmatic风格的搜索应用参考示例,是索引搜索世界中的主流技术选型,最佳实践的总结...

许雪里
2016/09/26
636
0
许雪里/xxl-search

《搜索应用参考示例XXL-SEARCH》 一、简介 1.1 概述 XXL-SEARCH 是以 "lucene/elasticsearch" 为核心的,Pragmatic风格的搜索应用参考示例,是索引搜索世界中的主流技术选型,最佳实践的总结...

许雪里
2016/09/26
0
0
搜索应用参考示例XXL-SEARCH

《搜索应用参考示例XXL-SEARCH》 一、简介 1.1 概述 XXL-SEARCH 是以 "lucene/elasticsearch" 为核心的,Pragmatic风格的搜索应用参考示例,是索引搜索世界中的主流技术选型,最佳实践的总结...

许雪里
2016/09/26
718
2
企业文档管理系统 SeedDMS安装配置推荐使用

文档管理系统 SeedDMS 是一个非常好用的文档管理系统,开源软件,支持中文,推荐使用。 1.环境 centos7 mariadb php5.4 nginx1.8 seeddms4.3.22 2.安装配置php环境 略 3.安装依赖 yum instal...

Foundation
2015/12/16
1K
0

没有更多内容

加载失败,请刷新页面

加载更多

Confluence 6 升级中的一些常见问题

升级的时候遇到了问题了吗? 如果你想尝试重新进行升级的话,你需要首先重新恢复老的备份。不要尝试再次对 Confluence 进行升级或者在升级失败后重新启动老的 Confluence。 在升级过程中的一...

honeymoose
42分钟前
1
0
C++随笔(四)Nuget打包

首先把自己编译好的包全部准备到一个文件夹 像这样 接下来新建一个文本文档,后缀名叫.nuspec 填写内容 <?xml version="1.0"?><package xmlns="http://schemas.microsoft.com/packaging/201......

Pulsar-V
今天
2
0
再谈使用开源软件搭建数据分析平台

三年前,我写了这篇博客使用开源软件快速搭建数据分析平台, 当时收到了许多的反馈,有50个点赞和300+的收藏。到现在我还能收到一些关于dataplay2的问题。在过去的三年,开源社区和新技术的发...

naughty
今天
3
0
Python3的日期和时间

python 中处理日期时间数据通常使用datetime和time库 因为这两个库中的一些功能有些重复,所以,首先我们来比较一下这两个库的区别,这可以帮助我们在适当的情况下时候合适的库。 在Python文...

编程老陆
今天
2
0
分布式面试整理

并发和并行 并行是两个任务同时进行,而并发呢,则是一会做一个任务一会又切换做另一个任务。 临界区 临界区用来表示一种公共资源或者说是共享数据,可以被多个线程使用,但是每一次,只能有...

群星纪元
今天
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部