上一篇文章中提到,使用lucene包括两个步骤:一是索引;二是检索。索引是基础、是前提,检索是目的。本文讲的是lucene的基本索引。
本文以及后面的文章都以存储在磁盘的文件为背景,进行索引和检索的演示。
对磁盘文件,我们可能有以下的检索需求:
- 按照文件名检索(这个经常有)
- 按照文件路径检索(这个。。。)开玩笑,我们没有这样的检索需求,但是我们需要从检索结果中了解这个信息
- 按照文件类型检索
- 按照文件大小检索
- 按照修改时间检索
- 按照文件内容检索
- ……
撇开lucene不谈,想一下我们人会怎么处理这样的需求。
好了,我们可能会拿一张纸、一支笔,然后填写类似下面的表格:
序号 | 文件名 | 文件路径 | 文件类型 | 文件大小 | 修改时间 | 文件内容 | …… |
有了这样的表格,我们就可以“按图索骥”,完成上面的检索任务了。
这个填充表格的过程就是索引的过程,与lucene的对应为:
-
纸:也就是保存索引的地方。在lucene中对应为Directory。lucene中有几种Directory的实现,最常用的是FSDirectory和RAMDirectory。从名称中不难知道:FSDIrectory是将索引保存到磁盘文件中,就相当于本例中的纸;RAMDirectory是将索引保存到内存中,就相当于本例中把内容保存到大脑中。
-
笔:也就是写索引的工具。在lucene中对应为IndexWriter。有Java基础的人应该可以推测,这个IndexWriter是一个Writer的子类。
- 记录:在lucene中对应为Document。
- 字段:在lucene中对应为Field(IndexableField)。每个Field实例可以设置:字段名、是否建立索引、是否分词、是否存储等几个属性。对于设置为存储的字段,我们可以从Document中直接读取该字段的值,而当试图从Document中取未存储字段的值时,返回null值。经常使用的Field子类有以下几个:
Field的子类 | 是否建立索引 | 是否分词 | 是否存储 |
StringField | 是 | 否 | 可以控制 |
DoubleField FloatField LongField IntField |
是 | 否 | 可以控制 |
TextField | 是 | 是 | 可以控制 |
StoredField | 否 | 否 | 是 |
- 序号:数据库中有自增的Id,在lucene中,也有一个自增的Id,称为docId。这个字段不需要你指定,而是lucene自动生成的。在检索时,lucene可以根据docId,确定唯一的Document。
有了上面的分析,我们需要记录的字段有:
- 序号:这个是lucene自动生成的,不需要处理
- 文件名:StringField,存储。字段名为filename
- 文件路径:StoredField,存储。字段名为pathname
- 文件类型:StringField,存储。字段名为type
- 文件大小:LongField,存储。字段名为size
- 修改时间:LongField,存储。字段名为lastmodified
- 文件内容:TextField,不存储。字段名为content
下面进行程序设计。
伪代码如下:
创建一个对磁盘文件进行索引的类,并提供一个索引的方法,接收两个参数:第一个参数为索引保存路径,第二个参数为磁盘文件(夹)路径(可以是多个)。 这个方法的实现: 1. 获得Directory对象-->获得IndexWriter对象; 2. 对每个文件(夹)进行索引: 如果是文件,则将文件信息转为Document对象,并将Document对象加入IndexWriter对象中; 如果是文件夹,则对于文件夹下的文件或文件夹,重复2过程。 3. 关闭IndexWriter对象和Directory对象。
编程实现:
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 StoredField("pathname", file.getAbsolutePath())); 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和StreamUtil。
FileUtil:
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:
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; } } } } } }
好了,写一个单元测试测试一下:
package cn.lym.lucene.quickstart.index; import org.junit.Before; import org.junit.Test; public class IndexerTest { private Indexer indexer; @Before public void init() { this.indexer = new Indexer(); } @Test public void testIndex() throws Exception { String indexDir = "E:\\Documents\\lucene-quickstart\\"; String dataDir = "D:\\"; this.indexer.index(indexDir, dataDir); } }
程序正常运行完成之后,在索引存放目录下,应该有如下的文件:
本文的代码可以从 https://git.oschina.net/coding4j/lucene-quickstart 获得。
相关推荐
lucene学习笔记 1 .txt lucene学习笔记 2.txt lucene学习笔记 3 .txt lucene入门实战.txt Lucene 的学习 .txt Lucene-2.0学习文档 .txt Lucene入门与使用 .txt lucene性能.txt 大富翁全文索引和查询的例子...
1> lucene学习笔记 2> 全文检索的实现机制 【1】lucene学习笔记的目录如下 1. 概述 3 2. lucene 的包结构 3 3. 索引文件格式 3 4. lucene中主要的类 4 4.1. Document文档类 4 4.1.1. 常用方法 4 4.1.2. 示例 4 4.2...
2.4 查询索引的基本信息 5 2.5 删除和更新索引 5 (1) 使用writer删除 5 (2) 使用reader删除 5 (3) 恢复删除 5 (4) 彻底删除 6 (5) 更新索引 6 (6) 手动优化 6 2.6 索引文件作用 7 第三章 搜索功能 8 3.1 简单搜索 8 ...
很好的Lucene学习入门资料。lucene是纯java开发的,支持索引的建立和搜索
介绍lucene3.5的相关技术,包括基本用法、分析器、索引建立与查询,扩展的高亮、分页、以及solr3.5的相关用法
lucene.net2.3.1开发介绍、类库、示例、建立索引、代码、分词、分词过程、分词器分词、分词器调用流程、二元分词等
(狂神)ElasticSearch快速入门笔记,ElasticSearch基本操作以及爬虫(Java-ES仿京东实战),包含了小狂神讲的东西,特别适合新手学习,笔记保存下来可以多看看。好记性不如烂笔头哦~,ElasticSearch,简称es,es是一个...
│ 02.淘淘商城介绍.avi │ 03.创建后台工程-taotao-parent.avi │ 04.创建taotao-manager.avi │ 05.svn的使用.avi │ 06.ssm框架整合思路.avi │ 07.ssm框架整合.avi │ 08.测试工程.avi │ 打开必读.txt │ 淘淘...
++++++++搜索引擎ElasticSearch学习笔记++++++++ 注意:Github README文件编辑方法以及解决无法显示图片问题 修改hosts文件添加一下内容(C:\Windows\System32\drivers\etc\hosts) 199.232.68.133 raw....
Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎...
ext学习笔记一 小试iBatis RIA(Rich Internet Application)的现状和未来 Java应用中域名解析不过期的解决方法 Java编程那些事儿45—数组使用示例1 一步步熟悉OFBiz 用Java做客户端调用.NET写...
系统设计 1 jive设计思路 2 jive的工作内幕 3 Jive源代码研究 4 Jive中的设计模式 5 jive学习笔记 <br> 设计模式 1 大道至简-Java之23种模式一点就通 2 设计模式...
系统设计 1 jive设计思路 2 jive的工作内幕 3 Jive源代码研究 4 Jive中的设计模式 5 jive学习笔记 <br> <br> 数据库设计 1 Jive Forums数据库说明(英文) 2 Jive KB...