当前位置: 移动技术网 > IT编程>开发语言>Java > Lucene的基本使用

Lucene的基本使用

2018年12月04日  | 移动技术网IT编程  | 我要评论
1、了解搜索技术 2、搜索引擎的原理 索引:类似于书的目录 3、实现搜索技术的方式 方式1:数据库搜索 利用SQL语句进行模糊搜索: select * from items where title like “%上海%”; select * from items where title like “ ...

1、了解搜索技术

2、搜索引擎的原理

索引:类似于书的目录

3、实现搜索技术的方式

方式1:数据库搜索

         利用sql语句进行模糊搜索:

         select * from items where title like “%上海%”;

         select * from items where title like “上海%”;----走索引

         问题:

                   在数据量很大的情况下,模糊搜索不一定走索引,因此效率就会很低。

方式2:lucene技术

         解决在海量数据的情况下,利用倒排索引技术,实现快速的搜索、打分、排序等功能

4、倒排索引技术

创建倒排索引,分为以下几步:

1)创建文档列表:

l  lucene首先对原始文档数据进行编号(docid),形成列表,就是一个文档列表

2)创建倒排索引列表

l  然后对文档中数据进行分词,得到词条(term)。对词条进行编号,以词条创建索引。然后记录下包含该词条的所有文档编号(及其它信息)。

拉斯跳槽 ---》拉斯、跳槽 –》0234

l  倒排索引创建索引的流程:

1)  首先把所有的原始数据进行编号,形成文档列表

2)  把文档数据进行分词,得到很多的词条,以词条为索引。保存包含这些词条的文档的编号信息。

l  搜索的过程:

1)  当用户输入任意的内容时,首先对用户输入的内容进行分词,得到用户要搜索的所有词条

2)  然后拿着这些词条去倒排索引列表中进行匹配。找到这些词条就能找到包含这些词条的所有文档的编号。

3)  然后根据这些编号去文档列表中找到文档

5、lucene技术的增、删、改、查

1)导入依赖和插件

<dependencies>
<!-- junit单元测试 -->
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>4.12</version>
</dependency>
<!-- lucene核心库 -->
<dependency>
<groupid>org.apache.lucene</groupid>
<artifactid>lucene-core</artifactid>
<version>4.10.2</version>
</dependency>
<!-- lucene的查询解析器 -->
<dependency>
<groupid>org.apache.lucene</groupid>
<artifactid>lucene-queryparser</artifactid>
<version>4.10.2</version>
</dependency>
<!-- lucene的默认分词器库 -->
<dependency>
<groupid>org.apache.lucene</groupid>
<artifactid>lucene-analyzers-common</artifactid>
<version>4.10.2</version>
</dependency>
<!-- lucene的高亮显示 -->
<dependency>
<groupid>org.apache.lucene</groupid>
<artifactid>lucene-highlighter</artifactid>
<version>4.10.2</version>
</dependency>
<!--ik分词器-->
<dependency>
<groupid>com.janeluo</groupid>
<artifactid>ikanalyzer</artifactid>
<version>2012_u6</version>
</dependency>
</dependencies>

 

<build>
<plugins>
<!-- java编译插件 -->
<plugin>
<groupid>org.apache.maven.plugins</groupid>
<artifactid>maven-compiler-plugin</artifactid>
<version>3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>

2)创建索引
public class lucenecreatetest {
@test
public void testcreate() throws ioexception {
//创建文档对象
document document = new document();

//创建并添加字段信息,参数:字段的名称、字段的值、是否储存,这里选用store.yes代表存储到文档列表
//store.no代表不存储
document.add(new stringfield("id","1", field.store.yes));
//这里的title字段需要用textfield,即创建索引又会被分词,stringfield会创建索引,但是不会被分词
document.add(new textfield("title","谷歌之父跳槽facebook,屌爆了", field.store.yes));

//索引目录类,指定索引在硬盘中的位置
directory directory = fsdirectory.open(new file("indexdir"));

//创建分词器对象
// analyzer analyzer = new standardanalyzer();
//引用ik分词器
analyzer analyzer = new ikanalyzer();

//索引写出工具的配置对象
indexwriterconfig conf = new indexwriterconfig(version.latest,analyzer);
//创建索引的写出工具类,参数:索引的目录和配置信息
indexwriter indexwriter = new indexwriter(directory,conf);

//把文档交给indexwriter
indexwriter.adddocument(document);

//提交
indexwriter.commit();
//关闭
indexwriter.close();
}
3)批量创建索引
@test
public void testcreate2() throws ioexception {
//创建文档的集合
collection<document> docs = new arraylist<>();

//创建文档对象
document document1 = new document();
document1.add(new stringfield("id","1", field.store.yes));
document1.add(new textfield("title","谷歌地图之父跳槽facebook", field.store.yes));
docs.add(document1);

//创建文档对象
document document2 = new document();
document2.add(new stringfield("id","2", field.store.yes));
document2.add(new textfield("title","谷歌地图创始人拉斯离开谷歌加盟facebook", field.store.yes));
docs.add(document2);


// 创建文档对象
document document3 = new document();
document3.add(new stringfield("id", "3", field.store.yes));
document3.add(new textfield("title", "谷歌地图创始人拉斯离开谷歌加盟facebook", field.store.yes));
docs.add(document3);

// 创建文档对象
document document4 = new document();
document4.add(new stringfield("id", "4", field.store.yes));
//document4.add(new textfield("title", "谷歌地图之父跳槽facebook与wave项目取消有关", field.store.yes));
field field = new textfield("title","谷歌地图之父跳槽facebook与wave项目取消有关", field.store.yes);
//设置激励因子,作弊
field.setboost(10.0f);
document4.add(field);
docs.add(document4);

// 创建文档对象
document document5 = new document();
document5.add(new stringfield("id", "5", field.store.yes));
document5.add(new textfield("title", "谷歌地图之父拉斯加盟社交网站facebook", field.store.yes));
docs.add(document5);

//索引目录类,指定索引在硬盘的位置
directory directory = fsdirectory.open(new file("indexdir"));

//引入ik分词器
analyzer analyzer = new ikanalyzer();
//索引写出工具的配置对象
indexwriterconfig config = new indexwriterconfig(version.latest,analyzer);

//设置打开方式:openmode.append会在索引的基础上追加新索引
config.setopenmode(indexwriterconfig.openmode.create);
//创建索引的写出工具类,参数:索引的目录和配置信息
indexwriter indexwriter = new indexwriter(directory,config);

//把文档集合交给indexwriter
indexwriter.adddocuments(docs);
//提交
indexwriter.commit();
//关闭
indexwriter.close();
}
4)删除索引
/*
* 删除索引
* 注意事项:
* 1、一般,为了进行精确删除,我们会根据唯一字段来删除,比如id
* 2、如果是用term删除,要求id也必须是字符串类型
* */
@test
public void testdelete() throws ioexception {
//创建目录对象
directory directory = fsdirectory.open(new file("indexdir"));
//创建配置对象
indexwriterconfig config = new indexwriterconfig(version.latest,new ikanalyzer());
//创建索引写出工具
indexwriter writer = new indexwriter(directory,config);
//根据词条进行删除
// writer.deletedocuments(new term("id","2"));
//根据query对象删除,如果id是数值类型,那么我们可以用数值范围查询锁定一个具体的id
// query query = numericrangequery.newlongrange("2d",2l,2l,true,true);
//// writer.deletedocuments(query);

//删除所有
writer.deleteall();
//提交
writer.commit();
//关闭
writer.close();
}
5)查询索引
@test
public void testsearch() throws ioexception, parseexception {
//索引目录对象
directory directory = fsdirectory.open(new file("indexdir"));

//索引读取工具
indexreader reader = directoryreader.open(directory);
//索引搜索工具
indexsearcher indexsearcher = new indexsearcher(reader);

//索引查询解析器,两个参数:默认要查询字段的名称、分词器
// queryparser parser = new queryparser("title",new ikanalyzer());

//多字段查询解析器
queryparser parser = new multifieldqueryparser(new string[]{"id","title"},new ikanalyzer());
//创建查询对象
query query = parser.parse("硅谷地图之父拉斯");

//搜索数据,两个参数:查询条件对象,要查询的最大结果条数(总共就5个文档,如果不知道文档数据数据,也可以
//使用integer.max_value)
//返回的结果是 按照匹配度排名得分前n名的文档信息(包括查询到的总条数信息、所有符合条件的文档的编号信息)
//topdocs:两个属性:总记录数、文档数组
topdocs topdocs = indexsearcher.search(query,10);

//获取总条数
system.out.println("本次搜索共找到" + topdocs.totalhits + "条数据");
//获取得分文档对象(scoredoc)数组 scoredao中包含:文档的编号、文档的得分
scoredoc[] scoredocs = topdocs.scoredocs;
for (scoredoc scoredoc : scoredocs) {
//取出文档编号
int docid = scoredoc.doc;
//根据编号去找文档
document document = reader.document(docid);

system.out.println("id" + document.get("id"));
system.out.println("title" + document.get("title"));

//取出文档得分
system.out.println("得分:" + scoredoc.score);
}
6)为了查询的方便,可以把上边的公共的部分代码抽取出来
//抽取公共的方法,提取一个查询数据的通用方法
public void search(query query) throws ioexception {
//索引目录对象
directory directory = fsdirectory.open(new file("indexdir"));

//索引读取对象
indexreader reader = directoryreader.open(directory);
//索引搜索工具
indexsearcher searcher = new indexsearcher(reader);

//搜索数据,两个参数:查询条件对象,要查询的最大结果条数
//返回的结果是 按照匹配度排名得分前n名的文档信息(包括查询到的总条数信息、所有符合条件的文档的编号信息)
//topdocs:两个属性:总记录数、文档数组

topdocs topdocs = searcher.search(query,10);
//获取总条数
system.out.println("本次搜索共找到" + topdocs.totalhits + "条数据");
//获取得分文档对象
scoredoc[] scoredocs = topdocs.scoredocs;
for (scoredoc scoredoc : scoredocs) {
//取出文档编号
int docid = scoredoc.doc;
//根据编号去找文档
document doc = reader.document(docid);
system.out.println("id: " + doc.get("id"));
system.out.println("title: " + doc.get("title"));
//取出文档得分
system.out.println("得分:" + scoredoc.score);
}
}
/**
* 测试普通词条查询
* 注意:term(词条)是搜索的最小单位,不可在分词,值必须是字符串
* 一般用来搜索唯一字段,比如id(对不需要分词的关键字进行查询)
* @throws ioexception
*/
@test
public void testtermquery() throws ioexception {
//创建词条查询对象
query query = new termquery(new term("title","谷歌地图"));
search(query);
}

/*
* 通配符查询
* ? 可以代表任意一个字符
* * 可以任意多个任意字符
* */
@test
public void testwildcardquery() throws ioexception {
//创建查询对象
query query = new wildcardquery(new term("title","*歌*"));
search(query);
}

/*
* 模糊查询
*
* */
@test
public void testfuzzyquery() throws ioexception {
//创建模糊查询对象:允许用户输错,但是要求错误的最大编辑距离不能超过2
//编辑距离:一个单词到另一个单词最少修改的次数
//可以手动指定编辑距离,但是参数必须在0~2之间
query query = new fuzzyquery(new term("title","facevool"),2);
search(query);
}


/*
* 数值范围查询
* 注意:数值范围查询,可以用来对非string类型的id进行精确的查找
* */
@test
public void testnumericrangequery() throws ioexception {
//数值范围查询对象,参数:字段名称,最小值、最大值、是否包含最小值、是否包含最大值
query query = numericrangequery.newlongrange("id",2l,2l,true,true);
search(query);
}

/*
* 布尔查询
* 布尔查询本身没有查询条件,可以把查询通过逻辑运算进行组合!
* 交集:occur.must + occur.must
* 并集:occur.should + occur.should
* 非:occur.must
* */
@test
public void testbooleanquery() throws ioexception {
query query1 = numericrangequery.newlongrange("id",1l,3l,true,true);
query query2 = numericrangequery.newlongrange("id",2l,4l,true,true);

// 创建布尔查询的对象
booleanquery query = new booleanquery();
// 组合其它查询
query.add(query1, booleanclause.occur.must_not);
query.add(query2, booleanclause.occur.should);

search(query);

}

7)修改索引
public class luceneupdate {
/*
* 修改索引
* 注意事项:
* 1、lucene修改功能底层会先删除,再把新的文档添加
* 2、修改功能会根据term进行匹配,所有匹配到的都会被删除,这样不好
* 3、因此,一般我们修改时,都会根据一个唯一不重复字段进行匹配修改,例如id
* 4、但是词条搜索,要求id必须是字符串,如果不是,这个方法就不能用
*
* 如果id是数值类型,我们不能直接去修改,可以先手动删除deletedocument(数值范围查询锁定id)。再添加
* */

@test
public void testupdate() throws ioexception {
//创建目录对象
directory directory = fsdirectory.open(new file("indexdir"));

//创建配置对象
indexwriterconfig config = new indexwriterconfig(version.latest,new ikanalyzer());

//创建索引写出工具
indexwriter writer = new indexwriter(directory,config);
//创建新的文档数据
document doc = new document();
doc.add(new stringfield("id","1", field.store.yes));
doc.add(new textfield("title","谷歌地图之父跳槽facebook为了加入传智播客 屌爆了呀", field.store.yes));


/*
* 修改索引,参数
* 词条:根据这个词条匹配到的所有的文档都会被修改
* 文档信息:要修改的新的文档数据
* */

writer.updatedocument(new term("id","1"),doc);
//提交
writer.commit();
//关闭
writer.close();
}



如您对本文有疑问或者有任何想说的,请点击进行留言回复,万千网友为您解惑!

相关文章:

验证码:
移动技术网