原理:
lucene的检索算法属于索引检索,即用空间来换取时间,对需要检索的文件、字符流进行全文索引,在检索的时候对索引进行快速的检索,
得到检索位置,这个位置记录检索词出现的文件路径或者某个关键词。
在使用数据库的项目中,不使用数据库进行检索的原因主要是:数据库在非精确查询的时候使用查询语言“like %keyword%”,对数据库进行查询是对所有
记录遍历,并对字段进行“%keyword%”匹配,在数据库的数据庞大以及某个字段存储的数据量庞大的时候,这种遍历是致命的,它需要对所有的记录进行
匹配查询。因此,lucene主要适用于文档集的全文检索,以及海量数据库的模糊检索,特别是对数据库的 xml 或者大数据的字符类型。
一、建立索引的五个基础类 Document, Field, IndexWriter, Analyzer, Directory
1、Document类:用来描述文档,这里的文档可以指一个HTML页面,一封电子邮件,或者是一个文本文件。一个 Document 对象由多个Field对象组成的。可以把一个Document对象想象成数据库中的一个记录,而每个Field对象就是记录的一个字段。
2、Field类:用来描述一个文档的某个属性,比如一封电子邮件的标题和内容可以用两个Field对象分别描述。
3、Analyzer类:用来对文档内容进行分词处理,Analyzer 类是一个抽象类,它有多个实现,针对不同的语言和应用需要选择适合的Analyzer,Analyzer会把分词后的内容交给IndexWriter来建立索引。
4、IndexWriter类:用来创建索引的一个核心类,作用是把一个个的Document对象加到索引中来。
5、Directory 类:表示索引文件存储位置的抽象类。有两个常用的子类:
* FSDirectory — 在实际文件系统中存储索引的 Directory 实现。该类对于大型索引非常有用。
* RAMDirectory — 在内存中存储所有索引的实现。该类适用于较小的索引,可以完整加载到内存中,在应用程序终止之后销毁。由于索引保存在内存中,所以速度相对较快。
二、搜索内容的四个基础类:Searche、Term、Query和TopDocs
1、Searcher 是一个抽象基类,包含各种超负荷搜索方法。IndexSearcher 是一个常用的子类,允许在给定的目录中存储搜索索引。Search 方法返回一个根据计算分数排序的文档集合。Lucene 为每个匹配给定查询的文档计算分数。IndexSearcher 是线程安全的;一个实例可以供多个线程并发使用。
2、Term 是搜索的基本单位。它由两部分组成:单词文本和出现该文本的字段的名称。Term 对象也涉及索引编制,但是可以在 Lucene 内部创建。
3、Query 和子类
Query 是一个用于查询的抽象基类。搜索指定单词或词组涉及到在项中包装它们,将项添加到查询对象,将查询对象传递到 IndexSearcher 的搜索方法。
Lucene 包含各种类型的具体查询实现,比如 TermQuery、BooleanQuery、PhraseQuery、PrefixQuery、RangeQuery、 MultiTermQuery、FilteredQuery、SpanQuery 等。以下部分讨论 Lucene 查询 API 的主查询类。
4、TopDocs 封装搜索结果以及 ScoreDoc 的总数
例:
整体流程:
1、存:
1、创建一个Person对象
* 2、创建一个IndexWriter对象
* 3、把Person转化成field对象,然后添加到document中。
* 4、把document添加到索引库中(indexWriter.add())
* 5、关闭IndexWriter
public class ArticleIndex {
@Test
public void add() throws Exception{
Person person = new Person();
person.setAge(20L);
person.setName("王武");
person.setDescription("很帅");//以上是建立一个对象,什么对象都可以。
Directory directory = FSDirectory.open(new File("./database"));//设置一个路径指向,用于保存lucene库持久化信息的地方。
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30); //分析器(分词器),按照默认的标准进行分词操作。参数指定版本号
IndexWriter indexWriter = new IndexWriter(directory, analyzer, MaxFieldLength.LIMITED);//建立流。
Document document = new Document(); //建立document对象。
Field ageField = new Field("年龄", person.getAge().toString(), Store.YES, Index.ANALYZED);
Field nameField = new Field("name", person.getName().toString(), Store.YES, Index.ANALYZED);
Field descriptionField = new Field("description", person.getDescription().toString(), Store.YES, Index.ANALYZED);
/**将数据保存在field中,其中的参数,
第一个参数就是搜索时会扫描的内容,与queryParse中的对应。
第二个参数 ,既是要存入的内容,必须是String。
第三个参数 ,设置Store针对内容库存储,yes是存,NO是否。
第四个参数 ,设置Index针对目录库
* no 不存
* not_analyzer 不分词存
* analyzer 分词存
*/
document.add(descriptionField);
document.add(nameField);
document.add(ageField);
indexWriter.addDocument(document);将所有内容添加进库中。
indexWriter.close(); //切记一定要关流,因为lucene的流是带锁机制的,不关流其他流无法连接会报异常。
}
@Test
public void find()throws Exception {
List<Person> list = new ArrayList<Person>();
FSDirectory directory = FSDirectory.open(new File("./database"));
IndexSearcher indexSearcher = new IndexSearcher(directory); //建立搜索流,指定库的路径
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_30);
QueryParser queryParser = new MultiFieldQueryParser(Version.LUCENE_30, new String[]{"name","name","description"}, analyzer);
//用于描述搜索的范围,在string数组中,选择要扫描的属性,查找与之对应的内容,与增加操作时的field的第一个参数对照。
Query query = queryParser.parse("王武"); //此处填入的是搜索关键字。
TopDocs topDocs = indexSearcher.search(query, 10); //2个参数 ,一个是将query传入,另一个则是当出现相同内容时,一共列出多少条。
ScoreDoc[] scoreDocs = topDocs.scoreDocs; //将搜索内容转换为数组。TopDocs是一个内部存放的都是Document对象的数组
for(int i = 0;i<scoreDocs.length;i++){
Document document = indexSearcher.doc(scoreDocs[i].doc);//将内容取出。然后利用document对象为person赋值:最后输出
Person person = new Person();
person.setAge(Long.parseLong(document.get("年龄")));
person.setDescription(document.get("description"));
person.setName(document.get("name"));
list.add(person);
}
for(Person person:list){
System.out.println(person.getName());
System.out.println(person.getAge());
System.out.println(person.getDescription());
}
}
@Test
public void testDeleteIndex() throws Exception{
/**
* Term 关键词对象
*/
IndexWriter indexWriter = new IndexWriter(LuceneUtils.directory,LuceneUtils.analyzer,MaxFieldLength.LIMITED);
/**
* 该关键词对象包含了"title":"lucene"的关键词
*/
Term term = new Term("title", "lucene");
indexWriter.deleteDocuments(term);
indexWriter.close();
}
//删除并不是真正的删除,而是增加一个新的标识文件。
/**
* 修改原理:先删除后增加
* @throws Exception
*/
@Test
public void testUpdateIndex() throws Exception{
/**
* Term 关键词对象
*/
Article article = new Article();
article.setId(1L);
article.setTitle("lucene可以做搜索引擎");
article.setContent("aaaa");
Term term = new Term("title","lucene");
IndexWriter indexWriter = new IndexWriter(LuceneUtils.directory,LuceneUtils.analyzer,MaxFieldLength.LIMITED);
/**
* term用于删除
* document用于增加
*/
indexWriter.updateDocument(term, DocumentUtils.article2Document(article));
indexWriter.close();
}
@Test
public void testOptimize() throws Exception{
IndexWriter indexWriter = new IndexWriter(LuceneUtils.directory,LuceneUtils.analyzer,MaxFieldLength.LIMITED);
indexWriter.optimize();//优化方法,可以将多个文件合并。并未深入学习,暂时了解
indexWriter.close();
}
IndexWriter的作用:
* 1、同一个索引库在同一时刻内只能针对一个IndexWriter,如果出现两个IndexWriter,将会报错
* 2、当创建一个IndexWriter的时候,indexWriter所指向的索引库就被上锁了(writer.lock文件)
* 而这个时候不管用另外的IndexWriter还是IndexSearch去访问该索引库都访问不到
* 3、当indexWriter被关闭的时候,writer.lock文件删除
* 4、indexWriter的作用
* 1、关闭资源
* 2、释放锁资源