Lucene全文检索引擎工具包使用方法总结

Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。人们经常提到信息检索程序库,虽然与搜索引擎有关,但不应该将信息检索程序库与搜索引擎相混淆。

在使用Lucene时有一些注意事项:

1.由于Lucene目前已经更新至6.6.0版本,所以对于JDK版本会有一些要求,建议使用低版本JDK的同学相应下载低版本Lucene。

2.Lucene版本更新迅速,所以各版本之间部分代码实现会有差异,所以在实现相关功能时,注意版本问题。

1.Lucene全文检索流程:


通过对于原数据的采集,并创建索引(Lucene的核心),将索引存放至索引库中,用户进行查询时,直接通过索引查询,从而实现了Lucene搜索的快捷,这也是我们学习使用Lucene的原因。相比较传统方式搜索,通过索引,无需依次扫描大量文件,提升了搜索速度。

2.Lucene下载:点击打开链接



3.Lucene环境搭建:

3.1环境准备:

JDK:1.7

lucene:4.10(最新为6.6.0)

MySql:提供原数据


4.Eclipse创建工程,导入相关jar包:Mysql驱动包,Analysis分词包,Core核心包,QueryParser包


注意:jUnit包为项目单元测试包,具体使用方法,大家可以百度Eclipse中如何使用jUnit,这里不做过多介绍。

5.通过java程序实现数据采集与索引创建:

5.1创建Book.java实体类:


public class Book {
	    // 图书ID
		private Integer id;
		// 图书名称
		private String name;
		// 图书价格
		private Float price;
		// 图书描述
		private String description;
		public Integer getId() {
			return id;
		}
		public void setId(Integer id) {
			this.id = id;
		}
		public String getName() {
			return name;
		}
		public void setName(String name) {
			this.name = name;
		}
		public Float getPrice() {
			return price;
		}
		public void setPrice(Float price) {
			this.price = price;
		}
		public String getDescription() {
			return description;
		}
		public void setDescription(String description) {
			this.description = description;
		}

}

5.2创建数据库使用对象BookDao.java:


import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import com.jsoft.vo.Book;
public class  BookDao{
	public List<Book> queryBookList() throws Exception {
		        // 数据库链接
				Connection connection = null;
				// 预编译statement
				PreparedStatement preparedStatement = null;
				// 结果集
				ResultSet resultSet = null;
				// 图书列表
				List<Book> list = new ArrayList<Book>();
				try {
					// 加载数据库驱动
					Class.forName("com.mysql.jdbc.Driver");
					// 连接数据库
					connection = DriverManager.getConnection(
							"jdbc:mysql://localhost:3306/book", "root", "root");
					// SQL语句
					String sql = "SELECT * FROM book";
					// 创建preparedStatement
					preparedStatement = connection.prepareStatement(sql);
					// 获取结果集
					resultSet = preparedStatement.executeQuery();
					// 结果集解析
					while (resultSet.next()) {
						Book book = new Book();
						book.setId(resultSet.getInt("id"));
						book.setName(resultSet.getString("name"));
						book.setPrice(resultSet.getFloat("price"));
						book.setDescription(resultSet.getString("description"));
						list.add(book);
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
				return list;
	}
}

5.3创建索引管理类IndexManager,并进行索引创建:


public class IndexManager {
	@Test
	public void createIndex() throws Exception {
		// 采集数据
		BookDao dao = new BookDao();
		List<Book> list = dao.queryBookList();
		// 将采集到的数据封装到Document对象中
		List<Document> docList = new ArrayList<>();
		Document document;
		for (Book book : list) {
			document = new Document();
			// store:如果是yes,则说明存储到文档域中
			// 图书ID
			//不分词、索引、存储
			Field id = new StringField("id", book.getId().toString(), Store.YES);
			// 图书名称
			//分词、索引、存储
			Field name = new TextField("name", book.getName(), Store.YES);
			// 图书价格
			//分词、索引、存储
			Field price = new FloatField("price", book.getPrice(),Store.YES);
			// 图书描述
			//不分词、索引、不存储
			Field description = new TextField("description",book.getDescription(), Store.YES);
			// 将field域设置到Document对象中
			document.add(id);
			document.add(name);
			document.add(price);
			document.add(description);
			docList.add(document);
		}
		// 创建分词器,标准分词器
		Analyzer analyzer = new StandardAnalyzer();
		// 创建IndexWriter
		IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3,
				analyzer);
		// 指定索引库的地址
		File indexFile = new File("D:/LueneIndex/");
		Directory directory = FSDirectory.open(indexFile);
		IndexWriter writer = new IndexWriter(directory, cfg);
		// 通过IndexWriter对象将Document写入到索引库中
		for (Document doc : docList) {
			writer.addDocument(doc);
		}
		// 关闭writer
		writer.close();
	}
}

5.4通过jUnit进行单元测试,测试结果如下:


5.5查看自定义索引库地址,发现有新创建文件:


通过上述步骤,Lucene架构索引即创建成功。

6.创建IndexSearch进行搜索:

public class IndexSearch {
	@Test
	public void indexSearch() throws Exception {
		// 创建query对象
		// 使用QueryParser搜索时,需要指定分词器,搜索时的分词器要和索引时的分词器一致
		// 第一个参数:默认搜索的域的名称
		QueryParser parser = new QueryParser("description",new StandardAnalyzer());
		// 通过queryparser来创建query对象
		// 参数:输入的lucene的查询语句(关键字一定要大写)
		Query query = parser.parse("description:java OR lucene");
		// 创建IndexSearcher
		// 指定索引库的地址
		File indexFile = new File("D:/LueneIndex/");
		Directory directory = FSDirectory.open(indexFile);
		IndexReader reader = DirectoryReader.open(directory);
		IndexSearcher searcher = new IndexSearcher(reader);
		// 通过searcher来搜索索引库
		// 第二个参数:指定需要显示的顶部记录的N条
		TopDocs topDocs = searcher.search(query, 10);
		// 根据查询条件匹配出的记录总数
		int count = topDocs.totalHits;
		System.out.println("匹配出的记录总数:" + count);
		// 根据查询条件匹配出的记录
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
		for (ScoreDoc scoreDoc : scoreDocs) {
			// 获取文档的ID
			int docId = scoreDoc.doc;
			// 通过ID获取文档
			Document doc = searcher.doc(docId);
			System.out.println("商品ID:" + doc.get("id"));
			System.out.println("商品名称:" + doc.get("name"));
			System.out.println("商品价格:" + doc.get("price"));
			// System.out.println("商品描述:" + doc.get("description"));
			System.out.println("==========================");
		}
		// 关闭资源
		reader.close();
	}
}

结果如下:


7.进行Lecene索引维护:

7.1.根据id删除索引:

@Test
	public void deleteIndex() throws Exception {
		// 创建分词器,标准分词器
		Analyzer analyzer = new StandardAnalyzer();
		// 创建IndexWriter
		IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3,analyzer);
		Directory directory = FSDirectory.open(new File("D:/LueneIndex/"));
		// 创建IndexWriter
		IndexWriter writer = new IndexWriter(directory, cfg);
		// Terms
		 writer.deleteDocuments(new Term("id", "1"));
		// 删除全部
		//writer.deleteAll();
		writer.close();

	}

7.2根据name更新索引:

	@Test
	public void updateIndex() throws Exception {
		// 创建分词器,标准分词器
		Analyzer analyzer = new StandardAnalyzer();
		// 创建IndexWriter
		IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
		Directory directory = FSDirectory.open(new File("D:/LueneIndex/"));
		// 创建IndexWriter
		IndexWriter writer = new IndexWriter(directory, cfg);
		// 第一个参数:指定查询条件
		// 第二个参数:修改之后的对象
		// 修改时如果根据查询条件,可以查询出结果,则将以前的删掉,然后覆盖新的Document对象,如果没有查询出结果,则新增一个Document
		// 修改流程即:先查询,再删除,在添加
		Document doc = new Document();
		doc.add(new TextField("name", "Python", Store.YES));
		writer.updateDocument(new Term("name", "C"), doc);
		writer.close();
	}

8.Lucene进行条件搜索:

8.1创建QuerySearch类

public class QuerySearch {
	private void search(Query query) {
		// 创建QuerySearcher
		// 指定索引库的地址
		try {
			File indexFile = new File("D:/LueneIndex/");
			Directory directory = FSDirectory.open(indexFile);
			IndexReader reader = DirectoryReader.open(directory);
			IndexSearcher searcher = new IndexSearcher(reader);
			// 通过searcher搜索索引库
			// 第二个参数:指定需要显示的顶部记录的N条
			TopDocs topDocs = searcher.search(query, 10);
			// 根据查询条件匹配出的记录总数
			int count = topDocs.totalHits;
			System.out.println("匹配出的记录总数:" + count);
			// 根据查询条件匹配出的记录
//TopDocs.totalHits:是匹配索引库中所有记录的数量
			//TopDocs.scoreDocs:匹配相关度高的前边记录数组
			ScoreDoc[] scoreDocs = topDocs.scoreDocs;
			for (ScoreDoc scoreDoc : scoreDocs) {
				// 获取文档的ID
				int docId = scoreDoc.doc;
				Document doc = searcher.doc(docId);
				System.out.println("商品ID:" + doc.get("id"));
				System.out.println("商品名称:" + doc.get("name"));
				System.out.println("商品价格:" + doc.get("price"));
				// System.out.println("商品描述:" + doc.get("description"));
				System.out.println("==========================");
			}
			// 关闭资源
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
}

8.2通过以下两种方式创建查询对象:

(1)通过Query子类来创建查询对象

Query子类常用的有:TermQuery、NumericRangeQuery、BooleanQuery

不能输入lucene的查询语法,不需要指定分词器 

(2)通过QueryParser来创建查询对象(常用)

QueryParser、MultiFieldQueryParser

可以输入lucene的查询语法、可以指定分词器

8.2.1 TermQuery:

 	// 创建TermQuery对象
	@Test
	public void termQuery() {
	Query query = new TermQuery(new Term("description", "java"));
	search(query);
}

结果如下:


8.2.2 NumericRangeQuery:

 	@Test
	public void numericRangeQuery() {
	// 创建NumericRangeQuery对象
	// 参数:域的名称、最小值、最大值、是否包含最小值、是否包含最大值
	Query query = NumericRangeQuery.newFloatRange("price", 55f, 60f, true,false);
	search(query);
	}
结果如下:


8.2.3 BooleanQuery:

 @Test
 	public void booleanQuery() {
 		 // 创建BooleanQuery
 		BooleanQuery query = new BooleanQuery();
 		// 创建TermQuery对象
 		Query q1 = new TermQuery(new Term("description", "oracle"));
 		// 创建NumericRangeQuery对象
 		// 参数:域的名称、最小值、最大值、是否包含最小值、是否包含最大值
 		Query q2 = NumericRangeQuery.newFloatRange("price", 15f, 60f, true,false);
 	// 组合关系代表的意思如下:
 		// 1、MUST和MUST表示“与”的关系,即“交集”。
 		// 2、MUST和MUST_NOT前者包含后者不包含。
 		// 3、MUST_NOT和MUST_NOT没意义
 		// 4、SHOULD与MUST表示MUST,SHOULD失去意义;
 		// 5、SHOUlD与MUST_NOT相当于MUST与MUST_NOT。
 		// 6、SHOULD与SHOULD表示“或”的概念。

 		// Occur.MUST 查询条件必须满足,相当于and  (+)
 	// Occur.SHOULD 查询条件可选,相当于or  (空)
 	// Occur.MUST_NOT 查询条件不能满足,相当于not  (-)
 	query.add(q1, Occur.MUST);
 		query.add(q2, Occur.MUST);
 	search(query);
 		}

结果如下:


8.2.4 MultiFieldQueryParser

 	 @Test
 	public void multiFieldQueryParser() throws Exception {
 		// 默认搜索的多个域
 		String[] fields = { "name", "description" };
 		Analyzer analyzer = new StandardAnalyzer();
 		MultiFieldQueryParser parser = new MultiFieldQueryParser(fields,analyzer);
 		Query query = parser.parse("paradigm");
 		//等同于
 		// Query query = parser.parse("name:paradigm OR description:paradigm");
 		  search(query);
 		}

结果如下:


9.分词器:

由于Lucene标准分词器不支持汉语分词,所以建议使用第三方分词器。

10.Lucene搜索相关度排序:boost值设置

10.1 创建索引时设置boost值:


10.2  搜索时设置Boost值



总结:以上是Lucene使用方法的简单总结,欢迎批评指正。













猜你喜欢

转载自blog.csdn.net/csdnshenzhen/article/details/73480437
今日推荐