lucene-初识

Lucene 基于java开发全文检索工具包

小结

索引库—document(文档)—域(相当于属性,存 关键字:值)

  • 创建索引
    • IndexWriter:创建索引库 参数1:打开目录 参数2:配置
      • IndexWriter indexWriter = new IndexWriter(FSDirectory.open(new File(“索引库磁盘路径”).toPath()),new IndexWriterConfig());
    • Document:创建文档
      • Document document = new Document();
    • Field:创建域 参数1:域名称 参数2:域内容 参数3:是否存储
      • Field field = new xxxField(“域名称”,域内容, Field.Store.YES或.NO);
    • 域添加到文档:document.add(域); 文档添加到索引库:indexWriter.addDocument(document);
  • 查询索引
    • IndexReader:获取索引库
      • IndexReader indexReader = DirectoryReader.open(FSDirectory.open(new File(“索引库磁盘路径”).toPath()));
    • IndexSearcher:由索引库创建查询对象
      • IndexSearcher indexSearcher = new IndexSearcher(indexReader);
    • Query:创建具体查询
      • Query query = new TermQuery(new Term(“content”,“spring”));//查询域名为content,域内容包含spring的
    • TopDocs:执行查询获取topDocs即文档对象 参数1:查询对象 参数2:查询结果返回最大记录数
      • TopDocs topDocs = indexSearcher.search(query, 10);
        • System.out.println(“查询总记录数:”+topDocs.totalHits); //获取查询总记录数
        • ScoreDoc[] scoreDocs = topDocs.scoreDocs; //获取文档列表
          • 遍历scoreDocs可以获取文档编号id:int docID = doc.doc;
          • 通过id可查询文档: Document document = indexSearcher.doc(docID);
            • 通过文档获取域内容:document.get(“域名称”)
  • 索引库操作
    • 优化:属性—是否分析(是否分词处理)、是否索引(索引方可查)、是否存储(存入文档,存入文档后field才可从document中取出) 根据不同数据要求创建不同的域
    • 增加文档
      • 创建文档document,创建域field,域加入文档,文档加入索引库
    • 删除索引
      • 删除全部文档:indexWriter.deleteAll();
      • 选择性删除:indexWriter.deleteDocuments(new Term(“name”,“apache”));//把域名为name,其值包含apache的document删除
    • 更新索引:先删除,后添加
    • 查询索引:查询索引不同主要在于创建query对象,创建好相应query对象,其他操作一致
      • Query子类–TermQuery: Query query = new TermQuery(new Term(“content”,“spring”));
      • Query子类–RangeQuery(范围查询): Query query = LongPoint.newRangeQuery(“size”, 0l, 10000l);//最后一位为L表示long类型
      • QueryPaser查询:先分词,后查询
        - 导入jar包: lucene-queryparser-7.4.0.jar
        - QueryParser queryParser = new QueryParser(“name”,new IKAnalyzer());
        - 使用QueryPaser创建Query对象:Query query = queryParser.parse(“待检索的一句话”);
  • IndexWriter和IndexReader对象使用完后都要进行关闭:.close();

lucene实现全文检索流程

环境:

1-下载lucene(就是一些jar包,需要使用那些就引入) 地址:http://lucene.apache.org/—适应的jdk—创建java项目
2-需要的jar:

  • lucene-analyzers-common-7.4.0.jar
  • lucene-core-7.4.0.jar
  • commons-io.jar

1.创建索引

  • 1–原始文档:要基于哪些数据来进行搜索,这些数据即原始文档。
  • 2–构建文档对象:对应每个原始文档创建一个document对象----每个document对象包含多个域—域中保存原始文档数据(域名称、域值)
    每个文档有唯一编号即文档id
  • 3–分析文档:分词过程:空格拆分得单词列表----单词统一转小写----去除标点----去除停用词(无意义的词)
    每个关键词封装一个term对象中,Term中包含两部分内容:关键词所在的域、关键词本身
    不同域拆分出相同关键词是不同Term。
  • 4–创建索引:基于关键词列表创建一个索引,保存到索引库中。索引库中:索引、document对象、关键词、文档的对应关系
    倒排索引结构:通过词语找文档

2.查询索引

  • 1–用户查询接口:用户输入查询条件地方
  • 2–关键词封装成一个查询对象(查询的域、关键词)
  • 3–执行查询:根据查询关键词----域搜索----找到关键词----根据关键词找到对应文档
  • 4–渲染结果:根据文档id找到文档对象----关键词高亮----分页----展示

创建索引步骤:

1-创建director对象,指定索引库保存位置
2-基于directory对象创建一个IndexWriter对象
3-读取磁盘文件,对应每个文件创建一个文档对象
4-域添加到文档对象中
5-文档对象写入索引库
6-关闭indexwriter

package com.xiaoai.lucene;

import org.apache.commons.io.FileUtils;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.search.FieldDoc;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Test;

import java.io.File;

public class LuceneFirst {

    @Test
    public void createIndex() throws Exception{

//        1-创建director对象,指定索引库保存位置
//        Directory directory = new RAMDirectory();//把索引库保存在内存
        Directory directory = FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath());//把索引库保存在磁盘
//        2-基于directory对象创建一个IndexWriter对象
        IndexWriter indexWriter = new IndexWriter(directory,new IndexWriterConfig());
//        3-读取磁盘文件,对应每个文件创建一个文档对象
        File dir = new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\02.参考资料\\searchsource");
        File[] files = dir.listFiles();
        for (File f : files) {
            //读取文件名
            String fileName = f.getName();
            //文件路径
            String filePath = f.getPath();
            //文件内容
            String fileContent = FileUtils.readFileToString(f,"utf-8");
            //文件大小
            long fileSize = FileUtils.sizeOf(f);
            //创建field
            //参数1:域名称  参数2:域内容 参数3:是否存储
            Field fieldName = new TextField("name",fileName, Field.Store.YES);
            Field fieldPath = new TextField("path",filePath,Field.Store.YES);
            Field fieldContent = new TextField("content",fileContent,Field.Store.YES);
            Field fieldSize = new TextField("size",filePath,Field.Store.YES);

            //4-域添加到文档对象中
            Document document = new Document();
            document.add(fieldName);
            document.add(fieldPath);
            document.add(fieldContent);
            document.add(fieldSize);
//        5-文档对象写入索引库
            indexWriter.addDocument(document);

        }
//        6-关闭indexwriter
        indexWriter.close();
    }

}

可使用luke工具查看索引库中内容

查询索引步骤:

1、创建一个Director对象,指定索引库的位置
2、创建一个IndexReader对象
3、创建一个IndexSearcher对象,构造方法中的参数indexReader对象。
4、创建一个Query对象,TermQuery
5、执行查询,得到一个TopDocs对象
6、取查询结果的总记录数
7、取文档列表
8、打印文档中的内容
9、关闭IndexReader对象

package com.xiaoai.lucene;

import org.apache.commons.io.FileUtils;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.*;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Test;

import java.io.File;

public class LuceneFirst {

    //创建索引
    @Test
    public void createIndex() throws Exception{

//        1-创建director对象,指定索引库保存位置
//        Directory directory = new RAMDirectory();//把索引库保存在内存
        Directory directory = FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath());//把索引库保存在磁盘
//        2-基于directory对象创建一个IndexWriter对象
        IndexWriter indexWriter = new IndexWriter(directory,new IndexWriterConfig());
//        3-读取磁盘文件,对应每个文件创建一个文档对象
        File dir = new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\02.参考资料\\searchsource");
        File[] files = dir.listFiles();
        for (File f : files) {
            //读取文件名
            String fileName = f.getName();
            //文件路径
            String filePath = f.getPath();
            //文件内容
            String fileContent = FileUtils.readFileToString(f,"utf-8");
            //文件大小
            long fileSize = FileUtils.sizeOf(f);
            //创建field
            //参数1:域名称  参数2:域内容 参数3:是否存储
            Field fieldName = new TextField("name",fileName, Field.Store.YES);
            Field fieldPath = new TextField("path",filePath,Field.Store.YES);
            Field fieldContent = new TextField("content",fileContent,Field.Store.YES);
            Field fieldSize = new TextField("size",fileSize+"",Field.Store.YES);

            //4-域添加到文档对象中
            Document document = new Document();
            document.add(fieldName);
            document.add(fieldPath);
            document.add(fieldContent);
            document.add(fieldSize);
//        5-文档对象写入索引库
            indexWriter.addDocument(document);
        }
//        6-关闭indexwriter
        indexWriter.close();
    }

    //查询索引
    @Test
    public void searchIndex() throws Exception{
//        1、创建一个Director对象,指定索引库的位置
        Directory directory = FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath());//把索引库保存在磁盘
//        2、创建一个IndexReader对象
        IndexReader indexReader = DirectoryReader.open(directory);
//        3、创建一个IndexSearcher对象,构造方法中的参数indexReader对象。
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//        4、创建一个Query对象,TermQuery
        Query query = new TermQuery(new Term("content","spring"));
//        5、执行查询,得到一个TopDocs对象
        TopDocs topDocs = indexSearcher.search(query, 10);//参数1:查询对象  参数2:查询结果返回最大记录数
//        6、取查询结果的总记录数
        System.out.println("查询总记录数:"+topDocs.totalHits);
//        7、取文档列表
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
//        8、打印文档中的内容
        for (ScoreDoc doc : scoreDocs) {
            //取文档id
            int docID =  doc.doc;
            //根据id取文档对象
            Document document = indexSearcher.doc(docID);
            System.out.println(document.get("name"));
            System.out.println(document.get("path"));
            System.out.println(document.get("size"));
//            System.out.println(document.get("content"));
            System.out.println("---------------------------------------------分隔线");
        }
//        9、关闭IndexReader对象
        indexReader.close();
    }
}

分析器

默认使用标准分析器:StandardAnalyzer(父类:Analyzer)
查看分析器分析效果:使用Analyzer对象的tokenStream方法返回一个TokenStream对象。对象中包含了最终分词结果。

package com.xiaoai.lucene;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.junit.Test;

public class TestTokenStream {

    @Test
    public void testTokenStream() throws Exception{

//        1)创建一个Analyzer对象,StandardAnalyzer对象
        Analyzer analyzer = new StandardAnalyzer();
//        2)使用分析器对象的tokenStream方法获得一个TokenStream对象
        TokenStream tokenStream = analyzer.tokenStream("", "The Spring Framework provides a comprehensive programming and configuration model.");
//        3)向TokenStream对象中设置一个引用,相当于数一个指针
        CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
//        4)调用TokenStream对象的rest方法。如果不调用抛异常
        tokenStream.reset();
//        5)使用while循环遍历TokenStream对象
        while (tokenStream.incrementToken()){
            System.out.println(charTermAttribute.toString());
        }
//        6)关闭TokenStream对象
        tokenStream.close();
    }
}

中文分析器:IKAnalyze

  • 1-添加jar:IK-Analyzer-1.0-SNAPSHOT.jar
  • 2-配置文件、扩展词典、停用词词典添加到工程classpath下(扩展词典严禁使用windows记事本编辑,保证扩展词典编码格式是utf-8) 扩展词典:可添加新词 停用词词典:无意义的词或者敏感词

配置文件 *(IKAnalyzer.cfg.xml)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">  
<properties>  
	<comment>IK Analyzer 扩展配置</comment>
	<!--用户可以在这里配置自己的扩展字典 -->
	<entry key="ext_dict">hotword.dic;</entry>
	
	<!--用户可以在这里配置自己的扩展停止词字典-->
	<entry key="ext_stopwords">stopword.dic;</entry> 
	
</properties>

查看效果

package com.xiaoai.lucene;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

public class TestTokenStream2 {

    @Test
    public void testTokenStream() throws Exception{

//        1)创建一个Analyzer对象,StandardAnalyzer对象
        Analyzer analyzer = new IKAnalyzer();
//        2)使用分析器对象的tokenStream方法获得一个TokenStream对象
        TokenStream tokenStream = analyzer.tokenStream("", "Lucene 是apache软件基金会一个开放源代码的全文检索引擎工具包,是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎");
//        3)向TokenStream对象中设置一个引用,相当于数一个指针
        CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
//        4)调用TokenStream对象的rest方法。如果不调用抛异常
        tokenStream.reset();
//        5)使用while循环遍历TokenStream对象
        while (tokenStream.incrementToken()){
            System.out.println(charTermAttribute.toString());
        }
//        6)关闭TokenStream对象
        tokenStream.close();
    }
}

lucene-初识

使用指定分析器 在基于directory对象创建一个IndexWriter对象时使用IndexWriterConfig()带参构造方法把分析器传入

IndexWriter indexWriter = new IndexWriter(directory,new IndexWriterConfig(new IKAnalyzer()));

索引库维护

域属性:是否分析(是否分词处理)、是否索引(索引方可查)、是否存储(存入文档,存入文档后field才可从document中取出)
lucene-初识

上面创建索引可优化

 //创建索引
    @Test
    public void createIndex() throws Exception{

//        1-创建director对象,指定索引库保存位置
//        Directory directory = new RAMDirectory();//把索引库保存在内存
        Directory directory = FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath());//把索引库保存在磁盘
//        2-基于directory对象创建一个IndexWriter对象
        IndexWriter indexWriter = new IndexWriter(directory,new IndexWriterConfig(new IKAnalyzer()));
//        3-读取磁盘文件,对应每个文件创建一个文档对象
        File dir = new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\02.参考资料\\searchsource");
        File[] files = dir.listFiles();
        for (File f : files) {
            //读取文件名
            String fileName = f.getName();
            //文件路径
            String filePath = f.getPath();
            //文件内容
            String fileContent = FileUtils.readFileToString(f,"utf-8");
            //文件大小
            long fileSize = FileUtils.sizeOf(f);
            //创建field
            //参数1:域名称  参数2:域内容 参数3:是否存储
            Field fieldName = new TextField("name",fileName, Field.Store.YES);
//            Field fieldPath = new TextField("path",filePath,Field.Store.YES);
            Field fieldPath = new StoredField("path",filePath);
            Field fieldContent = new TextField("content",fileContent,Field.Store.YES);
//            Field fieldSize = new TextField("size",fileSize+"",Field.Store.YES);
            Field fieldSizeValue = new LongPoint("size",fileSize);//分析、索引、不存储
            Field fieldSizeStore = new StoredField("size",fileSize);//不分析、不索引、存储可取值

            //4-域添加到文档对象中
            Document document = new Document();
            document.add(fieldName);
            document.add(fieldPath);
            document.add(fieldContent);
//            document.add(fieldSize);
            document.add(fieldSizeValue);
            document.add(fieldSizeStore);
//        5-文档对象写入索引库
            indexWriter.addDocument(document);
        }
//        6-关闭indexwriter
        indexWriter.close();
    }

添加文档

//添加文档
@Test
public void addDocument() throws Exception{
    //1--相当于创建索引库   创建indexWriter对象,需要ikanalyzer作为分析器 参数1:打开目录   参数2:配置
    IndexWriter  indexWriter =  new IndexWriter(
            FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath()),
            new IndexWriterConfig(new IKAnalyzer())
    );
    //2--document  创建一个document对象
    Document document = new Document();
    //3--域  向document添加域
    document.add(new TextField("name","新添加的文件",Field.Store.YES));
    document.add(new TextField("content","新添加的文件内容",Field.Store.NO));
    document.add(new StoredField("path","d:/tem/helol"));
    //把文档写入索引库
    indexWriter.addDocument(document);
    //关闭索引库
    indexWriter.close();
}

lucene-初识

lucene-初识

lucene-初识

删除文档

@Test
public void deleteAllDocument() throws Exception{
    //1--相当于创建索引库   创建indexWriter对象,需要ikanalyzer作为分析器 参数1:打开目录   参数2:配置
    IndexWriter  indexWriter =  new IndexWriter(
            FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath()),
            new IndexWriterConfig(new IKAnalyzer())
    );
    //删除全部文档
    indexWriter.deleteAll();
    //关闭索引库
    indexWriter.close();
}

lucene-初识

根据term选择性删除文档

@Test
public void deleteDocumentByQuery() throws Exception{
    //1--相当于创建索引库   创建indexWriter对象,需要ikanalyzer作为分析器 参数1:打开目录   参数2:配置
    IndexWriter  indexWriter =  new IndexWriter(
            FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath()),
            new IndexWriterConfig(new IKAnalyzer())
    );
    //删除文档
    indexWriter.deleteDocuments(new Term("name","apache"));//把域名为name,其值包含apache的document删除
    //关闭索引库
    indexWriter.close();
}

修改文档: 原理:先删除,后添加

@Test
public void updateDocument() throws Exception{
    //1--相当于创建索引库   创建indexWriter对象,需要ikanalyzer作为分析器 参数1:打开目录   参数2:配置
    IndexWriter  indexWriter =  new IndexWriter(
            FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath()),
            new IndexWriterConfig(new IKAnalyzer())
    );
    //创建一个新文档
    Document document = new Document();
    //向文档中添加域
    document.add(new TextField("name","更新后的文档", Field.Store.YES));
    document.add(new TextField("name1","更新后的文档1", Field.Store.YES));
    document.add(new TextField("name2","更新后的文档2", Field.Store.YES));
    //更新操作
    indexWriter.updateDocument(new Term("name","spring"),document);//删除域名为name中包含spring值的document,又添加了上面新建的一个document
    //关闭索引库
    indexWriter.close();
}

索引库查询
1–使用query的子类

  • TermQuery
    • 根据关键词查询,需指定查询的域及要查询的关键词
  • RangeQuery:范围查询

2–QueryPaser查询:先对查询的内容分词,然后基于分词结果进行查询 以上查询都是指定域和某一词查询,当查询只为一句话时可用QueryPaser查询,例如百度查询

  • 添加jar:lucene-queryparser-7.4.0.jar

RangeQuery和QueryPaser查询代码

package com.xiaoai.lucene;

import org.apache.lucene.document.Document;
import org.apache.lucene.document.LongPoint;
import org.apache.lucene.index.*;
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.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.junit.Before;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

import java.io.File;

public class SearchIndex {
    private IndexReader indexReader;
    private IndexSearcher indexSearcher;

    @Before
    public void init() throws Exception{
        //1--创建indexReader
        indexReader = DirectoryReader.open(FSDirectory.open(new File("D:\\CurriculumNeeds\\SelfTaught\\09JavaFstack\\12 Lucene\\index").toPath()));
        //2--由indexReader创建indexSearcher
        indexSearcher = new IndexSearcher(indexReader);
    }

    //RangeQuery:范围查询
    @Test
    public void testRangeQuery() throws Exception{
        //3--创建query对象
        Query query = LongPoint.newRangeQuery("size", 0l, 100l);//最后一位为L表示long类型
        //执行查询
        printResult(query);
    }

    //查询并打印
    public void printResult(Query query) throws Exception{
        //4--执行查询获得topdocs对象
        TopDocs topDocs = indexSearcher.search(query,10);
        System.out.println("总记录数:"+topDocs.totalHits);
        //获取文档数组
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc doc:scoreDocs ) {
            //取文档id
            int docID =  doc.doc;
            //根据id取文档对象
            Document document = indexSearcher.doc(docID);
            System.out.println(document.get("name"));
            System.out.println(document.get("path"));
            System.out.println(document.get("size"));
//            System.out.println(document.get("content"));
            System.out.println("---------------------------------------------分隔线");
        }
        indexReader.close();
    }

    //QueryPaser:查询内容先分词,后查询
    @Test
    public void testQueryPaser() throws Exception{
        //创建一个QueryPaser对象,参数1:默认搜索域,参数2:分析器对象
        QueryParser queryParser = new QueryParser("name",new IKAnalyzer());
        //使用QueryPaser创建Query对象
        Query query = queryParser.parse("lucene是java开发的全文检索工具包");
        //执行查询
        printResult(query);
    }
}
上一篇:大白话告诉你Elasticsearch到底是干嘛的?


下一篇:Validform 学习笔记---基础知识整理