全文检索:Apache Lucene框架入门实例

一 简介

Lucene属于Apache开源项目的一部分,是一个开源的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)

Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。人们经常提到信息检索程序库,虽然与搜索引擎有关,但不应该将信息检索程序库与搜索引擎相混淆

注:以上介绍参考至百度百科

在使用Lucene建立索引时,可以选择将索引文件存储在内存中或者磁盘里。下面我将分别介绍基于这两种存储方式的全文索引的创建与检索

二 基于内存的索引创建与检索

首先需要做的是下载相关的jar包,下载地址是:http://lucene.apache.org/core/downloads.html

其次,在正式介绍下面的内容之前,至少需要导入以下三个jar包:

  • lucene-analyzers-common-6.2.1.jar
  • lucene-core-6.2.1.jar
  • lucene-queryparser-6.2.1.jar

基于内存的全文索引示例代码如下:

package cn.zifangsky.lucene;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

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.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.queryparser.classic.ParseException;
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.store.Directory;
import org.apache.lucene.store.RAMDirectory;

public class Demo1 {

	/**
	 * 创建索引
	 * 
	 * @param sourceMap
	 *            待索引的内容
	 * @return
	 */
	public static Directory createIndex(Map<String, String> sourceMap) {
		//1 创建一个默认的词法分析器
		Analyzer analyzer = new StandardAnalyzer();

		//2 设置索引文件存储位置,可以存储到磁盘和内存中,这里设置为存储到内存
		Directory directory = new RAMDirectory(); // 存储到内存

		//3 索引的写入
		IndexWriterConfig config = new IndexWriterConfig(analyzer);
		try {
			IndexWriter indexWriter = new IndexWriter(directory, config);

			//将内容添加到索引中,每本书表示一个“文档”,并将每个文档进行存储
			if (!sourceMap.isEmpty()) {
				for (Entry<String, String> source : sourceMap.entrySet()) {
					Document document = new Document();
					//标题需要分词,使用TextField
					document.add(new TextField("title", source.getKey(), Field.Store.YES));
					//作者不需要分词,使用StringField
					document.add(new StringField("author", source.getValue(), Field.Store.YES));
					indexWriter.addDocument(document);
				}
			}
			indexWriter.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return directory;
	}

	/**
	 * 搜索
	 * 
	 * @param directory
	 * @param searchWord
	 *            搜索关键词
	 */
	public static void readIndex(Directory directory, String searchWord) {
		int preHits = 10; //获取前面多少个结果

		try {
			//1 打开一个文档
			IndexReader indexReader = DirectoryReader.open(directory);
			IndexSearcher indexSearcher = new IndexSearcher(indexReader);
			Analyzer analyzer = new StandardAnalyzer();

			//2 设置使用关键字检索,这里是检索标题
			QueryParser parser = new QueryParser("title", analyzer);
			Query query = parser.parse(searchWord);

			//3 获取检索到的结果
			System.out.println("总共有 " + indexSearcher.count(query) + " 个结果");
			ScoreDoc[] hits = indexSearcher.search(query, preHits).scoreDocs;

			System.out.println("当前有 " + hits.length + " 个结果,内容分别如下:");
			//遍历检索到的“文档”
			for (int i = 0; i < hits.length; i++) {
				int docId = hits[i].doc;
				Document hitDoc = indexSearcher.doc(docId);
				System.out.println("《 " + hitDoc.get("title") + "》    作者: " + hitDoc.get("author"));
			}
			indexReader.close();
		} catch (IOException | ParseException e) {
			e.printStackTrace();
		}
	}

	public static void main(String[] args) {
		Map<String, String> books = new HashMap<>();
		books.put("Java编程思想", "Bruce Eckel");
		books.put("Java8实战", "Raoul-Gabriel Urma");
		books.put("Spring入门经典", "Mert Caliskan");
		books.put("Spring实战", "Craig Walls");
		books.put("Spring Boot实战", "汪云飞");
		books.put("Redis实战", "Josiah L. Carlson");

		Directory directory = Demo1.createIndex(books);
		Demo1.readIndex(directory, "Spring");
//		Demo1.readIndex(directory, "实战");
	}
}

上面代码并不复杂,而且还有详细的注释,因此一些细节流程我就不多做介绍了。只是需要强调的一点是:

在创建索引时使用了这样的代码:

document.add(new TextField("title", source.getKey(), Field.Store.YES));

其中,上面的Field.Store.YES表示在创建索引的同时将内容的原文(也就是:source.getKey())也存储到内存中。如果我们选择了Field.Store.NO,在创建索引和检索的时候都是没问题的,但是在最后是没法提取检索关键字所在的原文内容的

最后的输出结果如下:

总共有 3 个结果
当前有 3 个结果,内容分别如下:
《 Spring Boot实战》    作者: 汪云飞
《 Spring实战》    作者: Craig Walls
《 Spring入门经典》    作者: Mert Caliskan

三 基于磁盘的索引创建与检索

示例代码如下:

package cn.zifangsky.lucene;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

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.StringField;
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.store.Directory;
import org.apache.lucene.store.FSDirectory;

public class Demo2 {

	public static void main(String[] args) {
		Demo2.createIndex("D:/test/source", "D:/test/index");
		Demo2.searchIndex("D:/test/index", "apache");
	}

	/**
	 * 给一个目录下的所有文本文件创建索引
	 * 
	 * @param sourceDir
	 *            待索引的文件目录
	 * @param indexDir
	 *            索引文件存储目录
	 * @return
	 */
	public static void createIndex(String sourceDir, String indexDir) {
		List<File> fileList = getFileList(sourceDir);

		if (fileList.size() > 0) {
			// 遍历文件并分别创建索引
			for (File file : fileList) {
				StringBuffer stringBuffer = new StringBuffer();
				stringBuffer.append(getFileContent(file));

//				System.out.println("fileName: " + file.getName() + "    filePath: " + file.getPath());

				Analyzer analyzer = new StandardAnalyzer();
				try {
					File indexFile = new File(indexDir);
					if (!indexFile.exists()) {
						indexFile.mkdirs();
					}

					//存储到文件中
					Directory directory = FSDirectory.open(new File(indexDir).toPath());
					IndexWriterConfig config = new IndexWriterConfig(analyzer);
					IndexWriter indexWriter = new IndexWriter(directory, config);

					Document document = new Document();
					document.add(new TextField("fileName", file.getName(), Store.YES));
					document.add(new TextField("content", stringBuffer.toString(), Store.YES));
					document.add(new StringField("path", file.getPath(), Store.YES));

					indexWriter.addDocument(document);
					indexWriter.commit();

					indexWriter.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	/**
	 * 在索引目录下检索关键字
	 * 
	 * @param indexDir
	 *            索引文件存储目录
	 * @param searchWord
	 *            搜索的关键字
	 */
	public static void searchIndex(String indexDir, String searchWord) {
		Analyzer analyzer = new StandardAnalyzer();

		try {
			//从一个磁盘目录中检索
			Directory directory = FSDirectory.open(new File(indexDir).toPath());
			DirectoryReader directoryReader = DirectoryReader.open(directory);
			IndexSearcher indexSearcher = new IndexSearcher(directoryReader);

			// 检索正文
			QueryParser queryParser = new QueryParser("content", analyzer);
			Query query = queryParser.parse(searchWord);

			// 检索前1000个结果
			System.out.println("总共有 " + indexSearcher.count(query) + " 个结果");
			ScoreDoc[] hits = indexSearcher.search(query, 1000).scoreDocs;

			System.out.println("当前有 " + hits.length + " 个结果,分别如下:");
			for (int i = 0; i < hits.length; i++) {
				int docId = hits[i].doc;
				Document hitDoc = indexSearcher.doc(docId);
				System.out.println("文件名: " + hitDoc.get("fileName") + "    路径: " + hitDoc.get("path"));
				// System.out.println(hitDoc.get("content"));
			}
			directoryReader.close();
			directory.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/***
	 * 获取一个目录下的所有文件
	 * 
	 * @param sourceDir
	 *            文件目录
	 * @return 所有文件的集合
	 */
	private static List<File> getFileList(String sourceDir) {
		File dir = new File(sourceDir);
		if (dir.isDirectory()) {
			// 返回指定格式的文本文件
			File[] files = dir.listFiles(new FilenameFilter() {

				public boolean accept(File dir, String name) {
					return name.endsWith(".txt") || name.endsWith(".log") || name.endsWith(".xml");
				}
			});

			List<File> fileList = new ArrayList<File>();
			if (files.length > 0) {
				for (File tmpFile : files) {
					fileList.add(tmpFile);
				}
			}
			return fileList;
		}
		return null;
	}

	/**
	 * 获取一个文本文件的所有内容
	 * 
	 * @param file
	 * @return
	 */
	private static StringBuffer getFileContent(File file) {
		try {
//			BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
			BufferedReader reader = new BufferedReader(new FileReader(file));
			String line = null;
			StringBuffer stringBuffer = new StringBuffer();
			while ((line = reader.readLine()) != null) {
				stringBuffer.append(line + "\n");
			}

			reader.close();
			return stringBuffer;
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}
}

从上面的代码可以看出,索引存储到内存或者磁盘上,其基本步骤是差不多的,只是使用的类稍有区别。同时多了一些基本的IO操作代码,包括文件的读写等操作

运行上面的索引创建方法之后,打开磁盘上的“D:/test/index”,可以发现多了很多这样的文件:

最后,检索关键字“apache”,检索到的结果如下:

总共有 3 个结果
当前有 3 个结果,分别如下:
文件名: Lucene简介.txt    路径: D:\test\source\Lucene简介.txt
文件名: H5_ws.log    路径: D:\test\source\H5_ws.log
文件名: Shiro简介.txt    路径: D:\test\source\Shiro简介.txt

可以发现,有三个文件包含“apache”这个关键字

至此,Lucene的入门介绍到此结束

参考文章:

PS:上面图片中的水印是我个人博客的域名,因此还请管理员手下留情不要给我标为“转载文章”,谢谢!!!

时间: 2024-08-05 23:14:00

全文检索:Apache Lucene框架入门实例的相关文章

spring mvc+ibatis+mysql的组合框架入门实例demo源码下载

原文:spring mvc+ibatis+mysql的组合框架入门实例demo源码下载 源代码下载地址:http://www.zuidaima.com/share/1550463678958592.htm spring mvc+ibatis+mysql的组合框架实例 首页 http://localhost:端口/项目/index.jsp 添加用户 添加地址 项目截图 jar包截图

CXF框架入门实例

首先:什么是CXF?为什么要用CXF? CXF 包含了大量的功能特性,但是主要集中在以下几个方面:支持 Web Services 标准:CXF 支持多种 Web Services 标准,包含 SOAP.Basic Profile.WS-Addressing.WS-Policy.WS-ReliableMessaging 和 WS-Security.Frontends:CXF 支持多种“Frontend”编程模型,CXF 实现了 JAX-WS API (遵循 JAX-WS 2.0 TCK 版本),它

Apache Camel框架入门示例

Apache Camel是Apache基金会下的一个开源项目,它是一个基于规则路由和中介引擎,提供企业集成模式的Java对象的实现,通过应用程序接口(或称为陈述式的Java领域特定语言(DSL))来配置路由和中介的规则.领域特定语言意味着Apache Camel支持你在的集成开发工具中使用平常的,类型安全的,可自动补全的Java代码来编写路由规则,而不需要大量的XML配置文件.同时,也支持在Spring中使用XML配置定义路由和中介规则. Camel提供的基于规则的路由(Routing)引擎,可

【Lucene】Apache Lucene全文检索引擎架构之入门实战

Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供.Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻.在Java开发环境里Lucene是一个成熟的免费开源工具.就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库.--<百度百科> 这篇博文主要从两个方面出发,首先介绍一下Lucene中的全文搜索原理,其次通过程序示例来展现如何使用Lucene.关于全文搜索原理部分我上网搜索了一下,也看了好几篇文章,最后在写这篇文

【Lucene】Apache Lucene全文检索引擎架构之入门实战1

Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供.Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻.在Java开发环境里Lucene是一个成熟的免费开源工具.就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库.——<百度百科> 这篇博文主要从两个方面出发,首先介绍一下Lucene中的全文搜索原理,其次通过程序示例来展现如何使用Lucene.关于全文搜索原理部分我上网搜索了一下,也看了好几篇文章,最后在写这篇文

[全文检索]Lucene基础入门.

本打算直接来学习Solr, 现在先把Lucene的只是捋一遍. 本文内容: 1. 搜索引擎的发展史 2. Lucene入门 3. Lucene的API详解 4. 索引调优 5. Lucene搜索结果排名规则 1 搜索引擎的发展史 1.1 搜索引擎的发展史 萌芽:Archie.Gopher 起步:Robot(网络机器人)和spider(网络爬虫) 1. Robot:网络机器人,自动在网络中运行,完成特定任务的程序,如刷票器.抢票软件等. 2. spider:网络爬虫,是一中特殊的机器人,抓取(下载

【Lucene】Apache Lucene全文检索引擎架构之构建索引

上一篇博文中已经对全文检索有了一定的了解,这篇文章主要来总结一下全文检索的第一步:构建索引.其实上一篇博文中的示例程序已经对构建索引写了一段程序了,而且那个程序还是挺完善的.不过从知识点的完整性来考虑,我想从Lucene的添加文档.删除文档.修改文档以及文档域加权四个部分来展开对构建索引的总结,也便于我后期的查看.会重点分析一下删除文档(因为有两中方式)和文档域加权这(实际中会用到比较多)两个部分. 1. 准备阶段 新建一个maven工程,pom.xml如下: <project xmlns=&quo

Lucene入门实例-CRUD

1.导入jar包 lucene-analyzers-common-7.6.0.jar lucene-analyzers-smartcn-7.6.0.jar lucene-core-7.6.0.jar 2.代码 package org.longIt.Lucene_app; import org.apache.lucene.analysis.Analyzer; import org.apache.lucene.analysis.standard.StandardAnalyzer; import or

【Lucene】Apache Lucene全文检索引擎架构之中文分词和高亮显示

前面总结的都是使用Lucene的标准分词器,这是针对英文的,但是中文的话就不顶用了,因为中文的语汇与英文是不同的,所以一般我们开发的时候,有中文的话肯定要使用中文分词了,这一篇博文主要介绍一下如何使用smartcn中文分词器以及对结果的高亮显示. 1. 中文分词 使用中文分词的话,首先到添加中文分词的jar包. <!-- lucene中文分词器 --> <dependency> <groupId>org.apache.lucene</groupId> <