知识整理:day65_Lucene学习笔记
优采云 发布时间: 2022-10-19 13:09知识整理:day65_Lucene学习笔记
1、lucene介绍1.1、lucene1.2是什么、全文检索的应用场景1.2.1、搜索引擎1.2.2、站点搜索(关注)1.2.3、文件系统搜索1.2.4、总结1.3、全文文本检索定义2. Lucene中实现全文检索的过程
详细情况如下:
全文检索过程:索引创建过程,搜索索引过程索引创建过程:采集数据-->索引库中的文档处理和存储搜索索引过程:输入查询条件-->通过lucene的queryer查询索引- -> 从索引库中获取结果--> 视图渲染 注意:Lucene 本身不能进行视图渲染。
3、Lucene入门程序3.1、需求3.2、环境准备3.2.1、数据库脚本初始化
内容如下:
drop table if exists book;
create table book
(
id int(11) not null,
name varchar(192),
privce double,
pic varchar(96),
description text,
primary key (id)
);
复制
3.2.2、Lucene下载安装
如下:
Lucene 是一个用于开发全文搜索功能的工具包。使用时从官方网站下载并解压。官方网站:最新版本:7.5.0 下载地址:下载版本:4.10.3(学习使用版) JDK要求:1.7以上(4.8开始,不支持1.7及以下)
3个学习文件夹
3.3、项目搭建(两步) 3.3.1、第一步:创建普通java项目 3.3.2、第二步:添加jar包
如下:
启动程序只需添加以下jar包: mysql5.1驱动包:mysql-connector-java-5.1.7-bin.jar 核心包:lucene-core-4.10.3.jar 分析器常用包:lucene-analyzers -common -4.10.3.jar 查询解析器包:lucene-queryparser-4.10.3.jar junit包(非必需):junit-4.9.jar
3.4. 3.4.1 创建索引的过程。为什么需要 采集 数据
详细情况如下:
为什么是采集数据?全文检索需要搜索各种格式的数据和信息。以搜索引擎(百度、google)为例,通过搜索引擎网站可以搜索互联网上的网页(html)网站、互联网上的音乐(mp3)、视频(avi)、pdf电子书等。对于不同格式的数据,需要采集到本地,然后统一封装到lucene文档对象中,也就是说需要统一存储的内容才可以查询它。这种通过全文搜索搜索到的数据称为非结构化数据。什么是非结构化数据?结构化数据:指格式固定或长度有限的数据,如数据库、元数据等。 非结构化数据:指定长度不定或无固定格式的数据,如电子邮件、word文档等。如何搜索结构化数据?由于结构化数据是固定格式的,因此可以设计算法来搜索固定格式的数据,例如类似数据库的查询。Like 查询使用顺序扫描方法,并使用关键字来匹配内容。对于内容量大的同类查询,速度较慢。如何搜索非结构化数据?所有要搜索的非结构化数据都需要通过技术手段采集到一个固定的地方,这些非结构化数据要形成结构化数据,然后用一定的算法进行搜索。对于内容量大的同类查询,速度较慢。如何搜索非结构化数据?所有要搜索的非结构化数据都需要通过技术手段采集到一个固定的地方,这些非结构化数据要形成结构化数据,然后用一定的算法进行搜索。对于内容量大的同类查询,速度较慢。如何搜索非结构化数据?所有要搜索的非结构化数据都需要通过技术手段采集到一个固定的地方,这些非结构化数据要形成结构化数据,然后用一定的算法进行搜索。
3.4.2. 如何采集数据
详细情况如下:
采集什么是数据技术?1、对于互联网上的网页,使用http爬取网页本地生成html文件。(网页采集:使用爬虫工具(http工具)在本地爬取网页) 2、如果数据库中有数据,则连接数据库读取表中的数据。(数据库采集:对数据使用jdbc程序采集) 3、如果数据是文件系统中的文件,则通过文件系统读取文件的内容。(文件系统采集:使用io流采集)
(1)网页采集(了解)详情如下:
由于目前搜索引擎的搜索数据的主要来源是互联网,搜索引擎使用爬虫程序来爬取网页(通过http抓取html网页信息)。以下是一些爬虫项目: Solr(),solr是apache的一个子项目,支持从关系数据库和xml文档中提取原创数据。Nutch(),Nutch是apache的一个子项目,包括大型爬虫工具,可以爬取和区分web网站数据。jsoup(),jsoup是一个java HTML解析器,可以直接解析一个URL地址和HTML文本内容。它提供了一个非常省力的 API,用于通过 DOM、CSS 和类似 jQuery 的操作方法获取和操作数据。heritrix(),Heritrix是java开发的开源网络爬虫,用户可以使用它从网上爬取想要的资源。
(2) 数据库采集 (Master)
po类:Book.java
package com.itheima.lucene.po;
public class Book {
// 图书ID
private Integer id;
// 图书名称
private String name;
// 图书价格
private Float price;
// 图书图片
private String pic;
// 图书描述
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 getPic() {
return pic;
}
public void setPic(String pic) {
this.pic = pic;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
}
复制
道:BookDao.java
public interface BookDao {
// 图书查询
public List queryBookList();
}
复制
道:BookDaoImpl.java
public class BookDaoImpl implements BookDao {
@Override
public List queryBookList() {
// 数据库连接
Connection connection = null;
// 预编译statement
PreparedStatement preparedStatement = null;
// 结果集
ResultSet resultSet = null;
// 图书列表
List list = new ArrayList();
try {
// 加载数据库驱动
Class.forName("com.mysql.jdbc.Driver");
// 连接数据库
connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/solr", "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.setPic(resultSet.getString("pic"));
book.setDescription(resultSet.getString("description"));
list.add(book);
}
} catch (Exception e) {
e.printStackTrace();
}
return list;
}
复制
可以进行单元测试。此处省略!
3.4.3、索引文件的逻辑结构
详细情况如下:
Document 字段 document 字段中存储的信息是采集 接收到的信息,由 Document 对象存储,具体来说,数据是由 Document 对象中的 Field 字段存储的。对于非结构化数据,统一的格式是Document文档格式。一个文档有多个字段。不同文档中的字段数量可以不同。建议相同类型的文档收录相同的Field。例如,数据库中的一条记录将存储一个 Document 对象,而数据库中的一列将存储为 Document 中的一个字段。在文档域中,文档对象之间没有关系。并且每个 Document 中的 field 字段不一定相同。在此示例中,一个 Document 对应于 Book 表中的一条记录。索引字段用于搜索,搜索程序会一一搜索索引字段,根据单词找到对应的文档。Document中Field的内容被分词,分词成索引,index = Field域名:word。索引字段主要用于搜索。索引域的内容是经过lucene分词后存储的。倒排索引表 传统的方法是先查找文件,如何查找文件中的内容,匹配文件内容中的搜索关键字。这种方式是顺序扫描方式,数据量大时搜索速度较慢。倒排索引结构就是根据内容(单词)来查找文档。倒排索引结构也称为反向索引结构。它包括两部分:索引和文档。索引是词汇表,它与索引中的搜索关键字匹配。它是有限的,使用固定的优化算法快速搜索,在索引中找到词汇,词汇与文档相关联,最后找到文档。
3.4.4、创建索引
创建索引流程图:
详细情况如下:
IndexWriter:它是索引过程的核心组件。通过 IndexWriter 可以创建新索引、更新索引和删除索引。IndexWriter 需要通过 Directory 来存储索引。目录:描述索引的存储位置。底层封装了 I/O 操作,负责存储索引。它是一个抽象类,其子类通常包括FSDirectory(在文件系统中存储索引)和RAMDirectory(在内存中存储索引)。
(1) 创建文档
采集数据的目的是为了索引。在建立索引之前,需要将原创内容创建成一个文档(Document),并且该文档(Document)收录一个字段(Field)一一。
(2) 分词
在对Docuemnt 中的内容进行索引之前,您需要使用分词器进行分词。主要过程是分词和过滤。分词:就是将采集接收到的文档的内容一个一个的分成单词。具体来说,应该说Document中Field的值是一一分词的。过滤:包括去除标点符号、去除停用词(of、is、a、an、the等)、大写到小写、词形还原(复数形式到单数形式参数、过去时到现在时)等. 什么是停用词?停用词用于节省存储空间和提高搜索效率。搜索引擎在索引页面或处理搜索请求时会自动忽略某些单词或单词。这些词或词称为停用词。例如,情态助词、副词、介词、连词等,通常本身没有明确的意义,只有放在一个完整的句子中才有一定的效果,如常见的“de”、“zai”、“is” 、“啊”等。示例:要标记的内容:Lucene 是一个 Java 全文搜索引擎。Tokenize:Lucene 是一个Java 全文搜索引擎。过滤器:去除标点符号 Lucene是一个Java全文搜索引擎 去除停用词 Lucene Java全文搜索引擎 大写转小写 lucene java全文搜索引擎 要标记的内容:Lucene 是一个 Java 全文搜索引擎。Tokenize:Lucene 是一个Java 全文搜索引擎。过滤器:去除标点符号 Lucene是一个Java全文搜索引擎 去除停用词 Lucene Java全文搜索引擎 大写转小写 lucene java全文搜索引擎 要标记的内容:Lucene 是一个 Java 全文搜索引擎。Tokenize:Lucene 是一个Java 全文搜索引擎。过滤器:去除标点符号 Lucene是一个Java全文搜索引擎 去除停用词 Lucene Java全文搜索引擎 大写转小写 lucene java全文搜索引擎
Lucene 提供了不同国家的分词器作为工具包,如下图:
注意,由于不同语言的分析器的分词规则不同,所以本例使用的是StandardAnalyzer,它可以对英文单词进行分词。以下是org.apache.lucene.analysis.standard.standardAnalyzer的部分源码:
@Override
protected TokenStreamComponents createComponents(final String fieldName, final Reader reader) {
final StandardTokenizer src = new StandardTokenizer(getVersion(), reader);
src.setMaxTokenLength(maxTokenLength);
TokenStream tok = new StandardFilter(getVersion(), src);
tok = new LowerCaseFilter(getVersion(), tok);
tok = new StopFilter(getVersion(), tok, stopwords);
return new TokenStreamComponents(src, tok) {
@Override
protected void setReader(final Reader reader) throws IOException {
src.setMaxTokenLength(StandardAnalyzer.this.maxTokenLength);
super.setReader(reader);
}
};
}
复制
详细情况如下:
Tokenizer是一个tokenizer,负责将读者转化为词法单元,即tokenizing。Lucene 提供了很多分词器,也可以使用第三方分词器。例如,IKAnalyzer 是一个中文分词器。tokenFilter 是一个分词过滤器,负责过滤词汇单元。tokenFilter 可以是一个过滤器链。Lucene 提供了很多分词过滤器,例如:大小写转换、去除停用词等。
下图显示了词汇单元的生成过程:
从一个Reader字符流开始,创建一个基于Reader的Tokenizer分词器,通过三个TokenFilter生成词汇单元Token。
例如,在书信息中,书名中的java和书描述中的java对应不同的术语。
代码实现如下:
/**
* 将采集到的数据list集合封装到Document对象中,创建索引库库
* @author Bruce
*
*/
public class IndexManager {
/**
* 创建索引
* @throws Exception
*/
@Test
public void createIndex() throws Exception {
// 1、采集数据
BookDao dao = new BookDaoImpl();
List list = dao.queryBookList();
// 2、将采集到的数据list封装到Document对象中
// 先创建Document对象集合
List docList = new ArrayList();
Document document = null; // 开发时建议这么做,因为这样每次地址指向是同一片内存,省内存
for (Book book : list) {
// 创建Document对象,同时要创建field对象
document = new Document();
Field id = new TextField("id", book.getId().toString(), Store.YES);
Field name = new TextField("name", book.getName(), Store.YES);
Field price = new TextField("price", book.getPrice().toString(), Store.YES);
Field pic = new TextField("pic", book.getPic(), Store.YES);
Field description = new TextField("description", book.getDescription(), Store.YES);
// 把域(Field)添加到文档(Document)中
document.add(id);
document.add(name);
document.add(price);
document.add(pic);
document.add(description);
docList.add(document);
}
// 3、创建分词器对象:标准分词器
Analyzer analyzer = new StandardAnalyzer();
// 4、创建索引写对象:IndexWriter
// 指定索引库的地址
File indexFile = new File("E:\\index\\hm19");
// 创建索引目录流对象:Directory
Directory directory = FSDirectory.open(indexFile);
IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
IndexWriter indexWriter = new IndexWriter(directory, cfg);
// 5、通过索引写对象:IndexWriter,将Document写入到索引库中
for (Document doc : docList) {
indexWriter.addDocument(doc);
}
<p>
// 6、关闭索引写对象:IndexWriter
indexWriter.close();
}
}</p>
复制
3.4.5。使用 Luke 查看索引
Luke作为Lucene toolkit()中的一个工具,可以通过接口查询和修改索引文件。
打开Luke方法: 命令运行:cmd运行:java -jar lukeall-4.10.3.jar 手动执行:双击lukeall-4.10.3.jarLuke界面:
成功连接索引库的界面:
3.5、搜索索引的过程
搜索流程图:
详细情况如下:
1.查询 用户自定义查询语句,用户决定查询什么(输入什么关键字) 指定查询语法,相当于sql语句。2、IndexSearcher对搜索对象进行索引,定义了很多搜索方法,程序员调用这个方法进行搜索。3.IndexReader索引读取对象,其对应的索引维护对象IndexWriter,IndexSearcher通过IndexReader读取索引目录下的索引文件。4.目录索引流对象,IndexReader需要Directory来读取索引库,使用FSDirectory文件系统流对象。5、IndexSearcher搜索完成,返回一个TopDocs(前面有一些匹配度高的记录)。
3.5.1。输入查询语句
详细情况如下:
和数据库SQL一样,Lucene全文搜索也有固定的语法:最基本的有:AND、OR、NOT等。例如,用户要查找描述收录java关键字和spring关键字的文档。其对应的查询语句:description:java AND spring 下面是一个使用Luke进行搜索的例子:
3.5.2、搜索分词
详细情况如下:
和索引过程中的分词一样,用户输入的关键词也要在这里进行分词。一般来说,用于索引和搜索的分词器是相同的。例如:输入搜索关键字“java training”,分词为java和training后,搜索java和training相关的内容,如下:
3.5.3. 搜索索引
详细情况如下:
根据关键字从索引中找到对应的索引信息,即词项。词条与文档相关联,找到词条时,找到关联的文档,从文档中提取的Field中的信息就是要搜索的信息。
代码:
/**
* 搜索索引
* @author Bruce
*
*/
public class IndexSearch {
@Test
public void searchIndex() throws Exception {
// 1、 创建查询对象(Query对象)
// 使用QueryParser搜索时,需要指定分词器,搜索索引时使用的分词器要和创建索引时使用的分词器一致
// 创建分析器对象
Analyzer analyzer = new StandardAnalyzer();
QueryParser queryParser = new QueryParser("description", analyzer); // 第一个参数:是默认搜索的域的名称
// 通过QueryParser来创建Query对象
Query query = queryParser.parse("description:java AND spring"); // 参数:输入的是lucene的查询语句(注意:关键字一定要大写)
// 2、创建IndexSearcher(索引搜索对象)
File indexFile = new File("E:\\index\\hm19\\");
Directory directory = FSDirectory.open(indexFile);
IndexReader indexReader = DirectoryReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader );
// 3、通过IndexSearcher(索引搜索对象)来搜索索引库
TopDocs topDocs = indexSearcher.search(query, 10); // 第二个参数:指定需要显示的顶部记录的N条
int totalHits = topDocs.totalHits; // 根据查询条件匹配出的记录总数
System.out.println("匹配出的记录总数:" + totalHits);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;// 根据查询条件匹配出的记录
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc; // 获取文档的ID
Document document = indexSearcher.doc(docId); // 通过ID获取文档
System.out.println("商品ID:" + document.get("id"));
System.out.println("商品名称:" + document.get("name"));
System.out.println("商品价格:" + document.get("price"));
System.out.println("商品图片地址:" + document.get("pic"));
System.out.println("商品描述:" + document.get("description"));
}
// 关闭IndexReader
indexReader.close();
}
}
复制
4. 字段 4.1,字段属性
字段是文档中的一个字段,包括字段名称和字段值。一个文档可以收录多个字段。文档只是场的载体。字段值是要索引的内容和要搜索的内容。是否分词是:进行分词处理,即对字段值进行分词,分词的目的是为了索引。例如:商品名称、商品价格、商品介绍等,用户需要输入关键词来搜索这些内容。因为搜索的内容格式较大,内容需要分词,所以会作为词法单元索引。否:不进行分词处理。不分词,不代表索引没有被索引,而是整个内容都被索引了。例如:产品id、订单号、ID号等。是否索引(indexed)为:index. 索引Field分词后的单词或整个Field值。索引的目的是搜索。比如产品名称、产品价格、产品介绍等,分词后都会被索引。产品id、订单号、ID号不需要分词,但也要有索引。以后会以全部内容作为查询条件。否:不索引。无法搜索此域的内容。例如:文件路径、图片路径等,不需要作为查询条件进行索引。是否存储(stored)为:Field值存储在文档中,文档中存储的Field可以从Document中获取。存储的目的是显示搜索页面的价值。例如:产品名称、产品价格、订单号、产品图片地址,以后要从Document中获取的所有字段都必须保存。No:不存储字段值,不存储的字段无法通过Document获取。比如:产品介绍,因为产品描述不需要在搜索页面展示,而且产品描述的内容很大,不需要存储。如果需要产品描述,可以根据搜索到的产品ID查询数据库,然后显示产品描述信息。因为产品描述不需要在搜索页面展示,而且产品描述的内容很大,不需要存储。如果需要产品描述,可以根据搜索到的产品ID查询数据库,然后显示产品描述信息。因为产品描述不需要在搜索页面展示,而且产品描述的内容很大,不需要存储。如果需要产品描述,可以根据搜索到的产品ID查询数据库,然后显示产品描述信息。
4.2. 常见的字段类型
下面列出了开发中常用的字段类型,注意Field的属性,根据自己的需要选择:
4.3. 修改入口程序字段的代码
详细情况如下:
图书id:
是否分词:不分词。
是否索引:要索引,因为需要根据图书id进行搜索。
是否存储:要存储,因为查询结果页面需要使用id这个值。
图书名称:
是否分词:要分词,因为要将图书的名称内容分词索引,根据关键搜索图书名称抽取的词。
是否索引:要索引。
是否存储:要存储。
图书价格:
是否分词:要分词,`lucene对数字型的值只要有搜索需求的都要分词和索引`,因为`lucene对数字型的内容要特殊分词处`理,本例子可能要根据价格范围搜索,需要分词和索引。
是否索引:要索引。
是否存储:要存储。
图书图片地址:
是否分词:不分词。
是否索引:不索引。
是否存储:要存储。
图书描述:
是否分词:要分词。
是否索引:要索引。
是否存储:不存储,因为图书描述内容量大,不在查询结果页面直接显示。
`不存储是用来不在lucene的索引文件中记录`,`节省lucene的索引文件空间`,如果要在详情页面显示描述:
思路:从lucene中取出图书的id,根据图书的id查询关系数据库中book表得到描述信息。
复制
代码如下所示:
我们需要重新生成索引库,重新执行代码生成索引库。注意:在执行之前,我们需要删除原来的索引库。
5.索引维护5.1,要求5.2,添加索引
调用 indexWriter.addDocument(doc); 添加索引。有关启动程序,请参阅创建索引。
5.3、删除索引 5.3.1、删除指定索引
术语是索引字段中的最小单位。按条件删除时,建议按唯一键删除。在 Solr 中,删除和修改操作是基于 ID 执行的。根据Term项删除索引,所有符合条件的都会被删除。示例代码如下:
/**
* 删除指定索引
* @throws Exception
*/
@Test
public void deleteIndex() throws Exception {
// 4、创建索引写对象:IndexWriter
// 指定索引库的地址
File indexFile = new File("E:\\index\\hm19");
// 创建索引目录流对象:Directory
Directory directory = FSDirectory.open(indexFile);
IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3, new StandardAnalyzer());
IndexWriter indexWriter = new IndexWriter(directory, cfg);
// 通过IndexWriter来删除指定索引
indexWriter.deleteDocuments(new Term("id", "1"));
// 关闭索引写对象:IndexWriter
indexWriter.close();
}
复制
5.3.2. 删除所有索引(谨慎使用)
删除索引目录的所有索引信息,彻底删除,无法恢复。谨慎使用!!!示例代码如下:
/**
* 删除全部索引(慎用)
* @throws Exception
*/
@Test
public void deleteAllIndex() throws Exception {
// 4、创建索引写对象:IndexWriter
// 指定索引库的地址
File indexFile = new File("E:\\index\\hm19");
// 创建索引目录流对象:Directory
Directory directory = FSDirectory.open(indexFile);
IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3, new StandardAnalyzer());
IndexWriter indexWriter = new IndexWriter(directory, cfg);
// 通过IndexWriter来删除全部索引(慎用)
indexWriter.deleteAll();;
// 关闭索引写对象:IndexWriter
indexWriter.close();
}
复制
推荐参考关系型数据库基于主键删除方式,所以创建索引时需要创建一个主键Field,删除时根据这个主键Field删除。索引被删除后,会被放到Lucene的回收站中。Lucene 3.X 版本可以恢复已删除的文档,但 3.X 之后无法恢复。
5.4、修改索引
根据查询条件更新索引。如果结果可以查询到,则删除前一个,然后覆盖新的 Document 对象。如果没有查询结果,将添加一个新的 Document。修改过程是:先查询,再删除,再添加。示例代码如下:
/**
* 修改索引
* @throws Exception
*/
@Test
public void updateIndex() throws Exception {
// 4、创建索引写对象:IndexWriter
// 指定索引库的地址
File indexFile = new File("E:\\index\\hm19");
// 创建索引目录流对象:Directory
Directory directory = FSDirectory.open(indexFile);
IndexWriterConfig cfg = new IndexWriterConfig(Version.LUCENE_4_10_3, new StandardAnalyzer());
IndexWriter indexWriter = new IndexWriter(directory, cfg);
// 创建修改后的文档对象
Document document = new Document();
Field name = new TextField("name", "黑泽", Store.YES); // 文件名称
document.add(name);
// 通过IndexWriter来修改索引
// 第一个参数:指定的查询条件
// 第二个参数:修改之后的对象
// 修改时如果根据查询条件,可以查询出结果,则将以前的删掉,然后覆盖新的Document对象,如果没有查询出结果,则新增一个Document
// 修改流程即:先查询,再删除,再添加
indexWriter.updateDocument(new Term("name", "晓艺"), document);
// 关闭索引写对象:IndexWriter
indexWriter.close();
}
复制
6. 搜索 6.1. 创建查询对象的两种方法
为要搜索的信息创建一个Query查询对象,Lucene会根据Query查询对象生成最终的查询语法。与关系型数据库Sql语法类似,Lucene也有自己的查询语法,例如:“name:lucene”表示在Field字段中查询name值为“lucene”的文档信息。
查询对象可以通过两种方式创建:
1)使用Lucene提供的Query子类,不能输入lucene的查询语法,不需要指定分词器
Query是一个抽象类,lucene提供了很多查询对象,比如:TermQuery精确词项查询、NumericRangeQuery数字范围查询、BooleanQuery布尔查询(实现组合查询)等。
如下代码:
Query query = new TermQuery(new Term("name", "lucene"));
2)使用QueryParse解析查询表达式(常用)、MultiFieldQueryParser多域查询,可以输入lucene的查询语法、需要指定分词器
QueryParser会将用户输入的查询表达式解析成Query对象实例。
如下代码:
QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
Query query = queryParser.parse("name:lucene");
复制
6.2. 通过 Query 6.2.1 的子类创建查询对象。术语查询
示例代码如下:
/**
* 搜索索引
* @author Bruce
*
*/
public class IndexSearch {
/**
* 优化代码,抽取成通用搜索方法
* @param query
*/
private void doSearch(Query query) {
IndexReader indexReader = null; // 好的编程习惯
try {
// 2、创建IndexSearcher(索引搜索对象)
File indexFile = new File("E:\\index\\hm19\\");
Directory directory = FSDirectory.open(indexFile);
indexReader = DirectoryReader.open(directory);
IndexSearcher indexSearcher = new IndexSearcher(indexReader );
// 3、通过IndexSearcher(索引搜索对象)来搜索索引库
TopDocs topDocs = indexSearcher.search(query, 10); // 第二个参数:指定需要显示的顶部记录的N条
int totalHits = topDocs.totalHits; // 根据查询条件匹配出的记录总数
System.out.println("匹配出的记录总数:" + totalHits);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;// 根据查询条件匹配出的记录
<p>
Document document; // 好的编程习惯
for (ScoreDoc scoreDoc : scoreDocs) {
int docId = scoreDoc.doc; // 获取文档的ID
document = indexSearcher.doc(docId); // 通过ID获取文档
System.out.println("商品ID:" + document.get("id"));
System.out.println("商品名称:" + document.get("name"));
System.out.println("商品价格:" + document.get("price"));
System.out.println("商品图片地址:" + document.get("pic"));
System.out.println("商品描述:" + document.get("description"));
}
} catch (IOException e) {
e.printStackTrace();
} finally {
if (indexReader != null) { // 好的编程习惯
try { // 好的编程习惯
// 关闭IndexReader
indexReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/**
* 使用QueryParse解析查询表达式(常用),可以输入lucene的查询语法、需要指定分词器
* @throws Exception
*/
@Test
public void searchIndex() throws Exception {
// 1、 创建查询对象(Query对象)
// 使用QueryParser搜索时,需要指定分词器,搜索索引时使用的分词器要和创建索引时使用的分词器一致
// 创建分析器对象
Analyzer analyzer = new StandardAnalyzer();
QueryParser queryParser = new QueryParser("description", analyzer); // 第一个参数:是默认搜索的域的名称
// 通过QueryParser来创建Query对象
Query query = queryParser.parse("description:java AND spring"); // 参数:输入的是lucene的查询语句(注意:关键字一定要大写)
// 这里优化代码,抽取成通用搜索方法了
// 执行搜索
doSearch(query);
}
/**
* TermQuery 精确项查询,TermQuery 不需要指定分析器
*/
@Test
public void testTermQuery() {
// 1、 创建查询对象(Query对象)
Query query = new TermQuery(new Term("description", "java"));
// 2、执行搜索
doSearch(query);
}
}</p>
复制
控制台输出如下:
通过运气测试的结果如下:
两者得到相同的结果!
6.2.2、数值范围查询
示例代码如下:
/**
* NumericRangeQuery 指定数字范围查询,NumericRangeQuery不使用指定分析器
*/
@Test
public void testNumericRangeQuery() {
// 1、 创建查询对象(NumericRangeQuery对象)
// 第一个参数:域名
// 第二个参数:最小值
// 第三个参数:最大值
// 第四个参数:是否包含最小值
// 第五个参数:是否包含最大值
Query query = NumericRangeQuery.newFloatRange("price",50f, 70f, true, true);
// 2、执行搜索
doSearch(query);
}
复制
6.2.3. 布尔查询
示例代码如下:
/**
* BooleanQuery 布尔查询,实现组合条件查询。BooleanQuery不使用指定分析器
*/
@Test
public void testBooleanQuery() {
// 1、 创建查询对象(BooleanQuery对象)
BooleanQuery query = new BooleanQuery();
Query query1 = new TermQuery(new Term("description", "java"));
Query query2 = NumericRangeQuery.newFloatRange("price", 50f, 70f, true, true);
// MUST:查询条件必须满足,相当于AND
// SHOULD:查询条件可选,相当于OR
// MUST_NOT:查询条件不能满足,相当于NOT非
// 组合关系代表的意思如下:
// 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 表示“或”的关系,即“并集”。
query.add(query1, Occur.MUST);
query.add(query2, Occur.SHOULD);
System.out.println(query);
// 2、执行搜索
doSearch(query);
}
复制
组合关系的含义如下: 1、MUST和MUST代表“与”的关系,即“交集”。2. MUST 和 MUST_NOT 包括前者,后者不包括。3. MUST_NOT 和 MUST_NOT 没有意义。4. SHOULD and MUST 的意思是 MUST,SHOULD 失去了意义。5. SHOUlD 和 MUST_NOT 等价于 MUST 和 MUST_NOT。6、SHOULD和SHOULD代表“或”的关系,即“并”。
6.3. 通过 QueryParser 6.3.1 创建查询对象。查询解析器
只需参考我们的入门代码。示例代码如下:
/**
* 使用QueryParse解析查询表达式(常用),可以输入lucene的查询语法、需要指定分词器
* @throws Exception
*/
@Test
public void searchIndex() throws Exception {
// 1、 创建查询对象(Query对象)
// 使用QueryParser搜索时,需要指定分词器,搜索索引时使用的分词器要和创建索引时使用的分词器一致
// 创建分析器对象
Analyzer analyzer = new StandardAnalyzer();
QueryParser queryParser = new QueryParser("description", analyzer); // 第一个参数:是默认搜索的域的名称
// 通过QueryParser来创建Query对象
Query query = queryParser.parse("description:java AND spring"); // 参数:输入的是lucene的查询语句(注意:关键字一定要大写)
// 这里优化代码,抽取成通用搜索方法了
// 执行搜索
doSearch(query);
}
复制
6.3.2. 多字段查询解析器
示例代码如下:
/**
* 使用MultiFieldQueryParser 多域查询,解析查询表达式,可以输入lucene的查询语法、需要指定分词器
* @throws Exception
*/
@Test
public void testMultiFieldQueryParser() throws Exception {
// 1、 创建查询对象(MultiFieldQueryParser对象)
// 可以指定默认搜索的域是多个
String[] fields = {"name", "description"}; // 或的关系:两个条件满足其一即可。
// 创建分析器对象
Analyzer analyzer = new StandardAnalyzer();
// 创建一个MulitFiledQueryParser对象
QueryParser queryParser = new MultiFieldQueryParser(fields, analyzer);
Query query = queryParser.parse("java");
System.out.println(query);
// 执行搜索
doSearch(query);
}
复制
6.3.3、查询语法
详细情况如下:
1、基础的查询语法,关键词查询:
域名+":"+搜索的关键字
例如:description:java
2、范围查询
域名+":"+[最小值 TO 最大值]
例如:price:[1 TO 1000]
`注意`:QueryParser不支持对数字范围的搜索,它支持字符串范围。
数字范围搜索建议使用NumericRangeQuery。
3、组合条件查询
第一种写法:
Occur.MUST 查询条件必须满足,相当于and +(加号)
Occur.SHOULD 查询条件可选,相当于or 空(不用符号)
Occur.MUST_NOT 查询条件不能满足,相当于not非 -(减号)
1)+条件1 +条件2:两个条件之间是并且的关系and
例如:+name:java +description:java
2)+条件1 条件2:必须满足第一个条件,忽略第二个条件
例如:+name:java description:java
3)条件1 条件2:两个条件满足其一即可。
例如:name:java description:java
4)-条件1 条件2:必须不满足条件1,要满足条件2
例如:-name:java description:java
第二种写法:
条件1 AND 条件2
条件1 OR 条件2
条件1 NOT 条件2
复制
6.4. 顶级文档
Lucene 的搜索结果可以通过 TopDocs 进行遍历。TopDocs 类提供了少量属性,如下所示:
注意:Search方法需要指定匹配记录数n:indexSearcher.search(query, n); topDocs.totalHits; // 表示匹配索引数据库中所有记录的数量。topDocs.scoreDocs; // 表示匹配相关性高的以前记录的数组,scoreDocs的长度小于等于搜索方法指定的参数n。
7. 相关性排名 7.1. 什么是相关性排名
相关性排序是指将查询结果按照与查询关键词的相关性进行排序,相关性越高,相关性越高。例如:搜索“java”关键字,与该关键字最相关的文章应该排在第一位。Lucene 通过评分对相关性进行排序。
7.2. 相关性分数
详细情况如下:
Lucene 对查询关键字和索引文档的相关性进行评分,得分高的排名第一。如何评价它?Lucene是在用户搜索时根据搜索到的关键词实时计算出来的。分为两个步骤: 1)计算词(Term)的权重。2)根据词的权重值计算文档相关度得分。一个词的重量是多少?通过对索引部分的学习,可以明确索引的最小单位是一个Term(索引词典中的一个词)。搜索也是从 Term 中搜索,然后根据 Term 找到文档。Term 对文档的重要性称为权重,它影响 Term 的权重。有两个因素: 词频(tf):指这个词在同一个文档中出现的次数,即单词在同一文档中出现的频率。tf越大,越重要。词(Term)在文档中出现的次数越多,词(Term)对文档的重要性就越高。例如,文档中多次出现“java”一词,说明该文档主要是关于java技术的。. 文档频率(df):指有多少文档收录子词,即单词在多个文档中出现的频率。df越大,它越不重要。例如:在英文文档中,这是否出现得更频繁,表示更重要?不,收录这个词(Term)的文档越多,这个词(Term)对于区分这些文档的重要性就越小。这些是自然的评分规则。单词出现在同一文档中的频率。tf越大,越重要。词(Term)在文档中出现的次数越多,词(Term)对文档的重要性就越高。例如,文档中多次出现“java”一词,说明该文档主要是关于java技术的。. 文档频率(df):指有多少文档收录子词,即单词在多个文档中出现的频率。df越大,它越不重要。例如:在英文文档中,这是否出现得更频繁,表示更重要?不,收录这个词(Term)的文档越多,这个词(Term)对于区分这些文档的重要性就越小。这些是自然的评分规则。单词出现在同一文档中的频率。tf越大,越重要。词(Term)在文档中出现的次数越多,词(Term)对文档的重要性就越高。例如,文档中多次出现“java”一词,说明该文档主要是关于java技术的。. 文档频率(df):指有多少文档收录子词,即单词在多个文档中出现的频率。df越大,它越不重要。例如:在英文文档中,这是否出现得更频繁,表示更重要?不,收录这个词(Term)的文档越多,这个词(Term)对于区分这些文档的重要性就越小。这些是自然的评分规则。词(Term)在文档中出现的次数越多,词(Term)对文档的重要性就越高。例如,文档中多次出现“java”一词,说明该文档主要是关于java技术的。. 文档频率(df):指有多少文档收录子词,即单词在多个文档中出现的频率。df越大,它越不重要。例如:在英文文档中,这是否出现得更频繁,表示更重要?不,收录这个词(Term)的文档越多,这个词(Term)对于区分这些文档的重要性就越小。这些是自然的评分规则。词(Term)在文档中出现的次数越多,词(Term)对文档的重要性就越高。例如,文档中多次出现“java”一词,说明该文档主要是关于java技术的。. 文档频率(df):指有多少文档收录子词,即单词在多个文档中出现的频率。df越大,它越不重要。例如:在英文文档中,这是否出现得更频繁,表示更重要?不,收录这个词(Term)的文档越多,这个词(Term)对于区分这些文档的重要性就越小。这些是自然的评分规则。在文档中出现多次,说明文档主要是关于java技术的。. 文档频率(df):指有多少文档收录子词,即单词在多个文档中出现的频率。df越大,它越不重要。例如:在英文文档中,这是否出现得更频繁,表示更重要?不,收录这个词(Term)的文档越多,这个词(Term)对于区分这些文档的重要性就越小。这些是自然的评分规则。在文档中出现多次,说明文档主要是关于java技术的。. 文档频率(df):指有多少文档收录子词,即单词在多个文档中出现的频率。df越大,它越不重要。例如:在英文文档中,这是否出现得更频繁,表示更重要?不,收录这个词(Term)的文档越多,这个词(Term)对于区分这些文档的重要性就越小。这些是自然的评分规则。这是否出现得更频繁,表明更重要?不,收录这个词(Term)的文档越多,这个词(Term)对于区分这些文档的重要性就越小。这些是自然的评分规则。这是否出现得更频繁,表明更重要?不,收录这个词(Term)的文档越多,这个词(Term)对于区分这些文档的重要性就越小。这些是自然的评分规则。
7.3. 设置提升值会影响相关性排名
boost 是一个权重值(默认权重值为 1.0f),它会影响权重的计算。创建索引时,给文档中的某个字段设置一个较高的权重值,如果在搜索过程中匹配到该文档,则可能会排在第一位。搜索索引时对域进行加权,在进行组合域查询时,将具有较高权重值的域与较高的相关性分数进行匹配。即:设置权重值可以在创建索引时设置,也可以在查询索引时设置。设置提升适用于字段或文档。
7.3.1. 创建索引时设置boost值
如果想让某些文档更重要,当文档收录要查询的词时,分数应该更高,这样相关度排名才能排在第一位,可以在文档创建索引时要实现,如果不设置,Field Boost默认为1.0f。一旦设置,除非删除文档,否则无法更改。
代码:
7.3.2. 搜索索引时设置boost值
创建 MultiFieldQueryParser 时设置提升值。
代码:
8.中文分词器 8.1,什么是中文分词器
学过英语的都知道,英语是以单词为基础的,单词之间用空格或逗号隔开。汉语以字为单位,字组成词,字与词组成句子。所以对于英文,我们可以简单的用空格来判断一个字符串是不是单词,例如:I love China,love和China很容易被程序区分;但中文“我爱中国”不同,电脑不知道“中国”是一个词还是“恋爱”是一个词。将中文句子分成有意义的词称为中文分词,也称为分词。我爱中国,分词的结果是:我爱中国。
8.2、Lucene自带的中文分词器
StandardAnalyzer:单字分词:就是按照中文逐字进行分词。例如:“我爱中国”效果:“我”、“爱”、“中”、“国家” CJKAnalyzer 二分词分词:由两个词分割。例如:“I am Chinese”效果:“I am”、“Is China”、“China”、“Chinese” 上面的两个分词器不能满足需求。
8.3、第三方中文分词器
paoding:paoding捷牛最新版本最多支持Lucene 3.0,最新代码2008-06-03提交,svn最新提交也是2010年,已经过时,不予考虑。mmseg4j:最新版本已经从to移到了,支持Lucene 4.10,github上最新提交的代码是2014年6月。从2009年到2014年,一共18个版本,也就是差不多3个大小版本一年中,有较大的活跃度,使用的是mmseg算法。IK-analyzer:最新版本开启,支持Lucene 4.10。自 2006 年 12 月推出 1.0 版本以来,IKAnalyzer 已经推出了 4 个主要版本。最初,它基于开源项目 Luence,一个结合了字典分词和语法分析算法的中文分词组件。从 3.0 版本开始,IK 已经发展成为 Java 的通用分词组件,独立于 Lucene 项目,提供了 Lucene 的默认优化实现。在 2012 版本中,IK 实现了简单的分词歧义消除算法,标志着 IK 分词器从简单的字典分词到模拟语义分词的衍生。但也就是从2012年12月开始就没有更新了。 ansj_seg:最新版本在tags/,只有1.1版本,2012年到2014年大小更新了6次,但是作者本人在2014年10月10日声明:“也许我以后没有精力去维护ansj_seg”,现在由“nlp_china”管理。2014 年 11 月更新。没有说明是否支持 Lucene,它是由CRF(条件随机场)算法制作的分词算法。imdict-chinese-analyzer:最新版本在,最新更新也在2009年5月。下载源代码,不支持Lucene 4.10。它使用 HMM(隐马尔可夫链)算法。cseg:最新版本在/lionsoul/jcseg,支持Lucene 4.10,作者活跃度高。使用 mmseg 算法。
8.4. 使用中文分词器 IKAnalyzer
IKAnalyzer 继承了 Lucene 的 Analyzer 抽象类。IKAnalyzer与Lucene自带的tokenizer方法相同,将Analyzer测试代码改为IKAnalyzer,测试中文分词效果。如果使用中文分词器ik-analyzer,则在创建索引和搜索过程中使用一致的分词器ik-analyzer。
8.4.1. 将jar包添加到项目8.4.2中。修改分词器代码
创建索引时修改分词器代码:
修改搜索索引时的分词器代码:
8.5。展开中文词库
将以下文件复制到配置目录:
将配置文件从 ikanalyzer 包复制到类路径。
IK Analyzer 扩展配置
dicdata/mydict.dic
dicdata/ext_stopword.dic
复制
如果要配置扩展词和停用词,创建扩展词文件和停用词文件,文件编码为utf-8。注意:不要使用记事本保存扩展词文件和停用词文件,那样的话,格式会收录bom。
8.6. 使用luke测试中文分词效果
第一步:将ikanalyzer的jar包复制到luke工具目录下
第二步:使用命令行打开luke工具,运行lukeall。如果需要加载第三方tokenizer,需要通过-Djava.ext.dirs加载jar包。可以简单的把第三方tokenizer和lukeall放在一起,cmd下运行:command:java -Djava.ext.dirs=。-jar lukeall-4.10.3.jar 指定第三方分词器的类路径后
读书笔记:完整的网站SEO计划书
以下是分享的完整网站SEO计划
1. 了解搜索引擎:
搜索引擎的工作原理:
蜘蛛爬取并爬取我们的网站 内容,并将其存储在后台数据库中。当用户搜索结果时,后台程序被处理并显示给用户。
2、为什么要做SEO:
1、排名相对稳定。2、成本相对较低。3、搜索用户较多。
3.域名和空间:
注册品牌词域名,购买稳定空间。
4、目标定位:
(1)关键词的选择:1.选择竞争强度较小的,2.关键词不能太宽泛,3.扩大增长尾巴关键词,4.研究竞争对手' 关键词, 关键词 排名和反向链接数。
(2)关键词的竞争强度判断: 1、在百度搜索框中输入关键词,然后查看收录有多少。2.百度指数可以查看每日搜索量。3、在百度搜索框查看关键词时,看看哪些是公司的网站官网。网站 的公司越多,竞争的强度就越大。
5.网站结构优化:
(1.) 清晰的导航。(2)内部链条完整。(3)死链接检查:1.人工检查,2.使用百度站长查询。(4)优质内容,(原创文章或伪原创文章均以锚文本的形式发布)。
6、内部优化:
(1) 标题优化,(关键词要加到标题中,另一个是标题文件)。
(2)关键词的密度,每个文章中关键词的出现次数约为3-5次。
(3) 网页的三个元素(标题、关键词、描述)。
(4)H标签,写标题时尽量加H标签。
(5)页面更新度。(更新越多,爬虫越频繁)
7. 外部链接:
(1) 外部链接含义:相关性和锚文本,
(2)外部链接查询: 1.domain:域名。2.用百度统计看看有哪些外链,然后用户是从哪个外链来的。
(三)外链原则: 1、难度越大,质量越高。2.内容为王。3.稳步持续增长。
(4)友情链接:(进QQ群,上论坛,交换好友链,权重等方面都比自己高)
8、常见的网络推广方式:
(1) 博客:(2) 论坛。(3)分类信息。(4)问答推广。(百度知道) (5)QQ营销。(6) 群发。(7) 百度竞价。
9、辅助工具:
(1)百度站长平台,(2)站长工具。
完整的SEO解决方案:(站内优化+外链搭建)
一,前端。
二是内容编辑。
三、推广。
四、数据分析。
1、网站结构优化:
(一)合理规划场地结构。