Lucence 学习笔记

Lucence 学习笔记

什么是全文检索

数据的分类

  1. 结构化数据: 格式固定,长度固定,数据类型固定。例如数据库中的数据。
  2. 非结构化数据: Word文档,PDF文档,邮件,XML等格式不固定,长度不固定,数据类型不固定。

数据的查询

  1. 结构化查询。 SQL语句。简单、速度块。
  2. 非结构化数据的查询。 把非结构化数据变成结构化数据即可。把字符串拆分,得到一个单词列表。基于单词列表创建一个索引。
  3. 全文检索,就是先创建索引再查询索引的过程。索引一次创建,多次使用。

全文检索的应用场景

  1. 搜索引擎。
  2. 站内搜索。
  3. 电商搜索。
  4. 只要是有搜索的地方就可以使用全文检索。

什么是lucence

Luence是基于Java开发的全文检索工具包。

lucence实现全文检索的流程

  1. 创建索引
    1)获得文档
    2)构建文档对象
    对应每个原始文档创建一个Document对象。每个Document对象中包含多个域(field),域中保存的就是原始文档的数据。域包括域的名称以及域的值。每个文档都有唯一的编号,文档ID。
    3)分析文档
    就是分词的过程。先根据空格进行单词的拆分。得到单词列表。再把单词全部转换为小写。去除标点符号。去除停用词(无意义的词)。每个关键词都封装成Term对象。Term包含两部分内容:关键词所在的域和关键词本身。不同的域中拆分出来的相同的关键词是不同的Term。
    4)创建索引。
    基于关键词列表创建一个索引,保存到索引库中。索引库中包含索引,Document对象,关键词和文档的对应关系。
  2. 查询索引。
    1)用户查询接口
    2)把关键词封装成查询对象,包括要查询的域和搜索的关键词。
    3)执行查询,根据要查询单关键词到对应的域上进行搜索。
    4)渲染结果

入门程序

  1. 引入依赖
  <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-core</artifactId>
            <version>8.2.0</version>
        </dependency>
        <dependency>
            <groupId>org.apache.lucene</groupId>
            <artifactId>lucene-analyzers-common</artifactId>
            <version>8.2.0</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
  1. 创建一个Director对象,指定索引库保存的位置。
  2. 基于Director对象,创建一个IndexWriter对象
  3. 读取磁盘上的文件,对应每个文件创建一个文档对象
  4. 向文档对象中添加域。
  5. 把文档对象写入索引库。
  6. 关闭IndexWriter对象。
 @Test
    public void createIndex() throws Exception {
        //2. 创建一个Director对象,指定索引库保存的位置。
        Directory directory = FSDirectory.open(new File("/home/gaolei/JavaProgram/Learn-Lucene/src/main/resources/index").toPath());
        //3. 基于Director对象,创建一个IndexWriter对象
        IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig());
        //4. 读取磁盘上的文件,对应每个文件创建一个文档对象
        File dir = new File("/home/gaolei/JavaProgram/Learn-Lucene/src/main/resources/file");
        System.out.println(dir.getAbsolutePath());
        System.out.println(dir.isDirectory());
        File[] files = dir.listFiles();
        for (File file : files) {
            //取文件名
            String fileName = file.getName();
            //取文件路径
            String filePath = file.getPath();
            //文件的内容
            String fileContent = FileUtils.readFileToString(file, "utf-8");
            //文件大小
            long fileSize = FileUtils.sizeOf(file);
            //创建field
            //参数1:域的名称
            //参数2:域的内容
            //参数3:是否存储
            Field filedName = 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);
            //5. 向文档对象中添加域。
            //创建文档对象
            Document document = new Document();
            document.add(filedName);
            document.add(fieldPath);
            document.add(fieldContent);
            document.add(fieldSize);
            //6. 把文档对象写入索引库。
            indexWriter.addDocument(document);
        }
        //7. 关闭IndexWriter对象。
        indexWriter.close();
    }

//写resource里的文件,就这样写:"classpath:filename"
8. 使用Luke可以查看索引里的内容
9. 查询索引库,
1)创建一个Director对象,指定索引库的位置
2)创建一个IndexReader 对象
3)创建一个IndexSearcher对象
4)创建一个Query对象,TermQuery根据关键词查询
5)执行查询,得到一个TopDocs对象
6)取查询结果的总记录数。
7)取文档列表
8)打印文档中的内容
9)关闭IndexReader对象

@Test
    public void searchIndex() throws Exception {
        //    1)创建一个Director对象,指定索引库的位置
        Directory directory = FSDirectory.open(new File("/home/gaolei/JavaProgram/Learn-Lucene/src/main/resources/index").toPath());
        //    2)创建一个IndexReader 对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //    3)创建一个IndexSearcher对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        //    4)创建一个Query对象,TermQuery根据关键词查询
        Query query = new TermQuery(new Term("content", "string"));
        //    5)执行查询,得到一个TopDocs对象
        //参数1查询对象 参数2:查询结果返回的最大记录数
        TopDocs topd = indexSearcher.search(query, 10);
        //    6)取查询结果的总记录数。
        System.out.println("总记录数" + topd.totalHits);
        //    7)取文档列表
        ScoreDoc[] scoreDocs = topd.scoreDocs;
        //    8)打印文档中的内容
        for (ScoreDoc scoreDoc : scoreDocs) {
            //取文档ID
            int docId = scoreDoc.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("------------------------------------------");
        }
        //    9)关闭IndexReader对象
        indexReader.close();
    }

分析器对象

默认使用标准分析器,StandardAnalyzer。

查看分析器的分析结果

使用Analyzer对象的tokenStream方法返回一个TokenStream对象,词对象中包含了最终分词结果。
实现步骤:

  1. 创建一个Analyzer对象,StandardAnalyzer对象
  2. 使用分析器对象的TokenStream方法获得一个TokenStream对象。
  3. 向TokenStream对象中设置一个引用,相当于一个指针。
  4. 调用TokenStream对象的reset方法,如果不调用抛异常
  5. 使用while循环来遍历TokenStream对象
  6. 关闭TokenStream对象。
 @Test
    public void testTokenStream() throws Exception {
        //1. 创建一个Analyzer对象,StandardAnalyzer对象
        Analyzer analyzer = new StandardAnalyzer();
        //2. 使用分析器对象的TokenStream方法获得一个TokenStream对象。
        TokenStream tokenStream = analyzer.tokenStream("", "i want to fuck the world~");
        //3. 向TokenStream对象中设置一个引用,相当于一个指针。
        CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
        //4. 调用TokenStream对象的reset方法,如果不调用抛异常
        tokenStream.reset();
        //5. 使用while循环来遍历TokenStream对象
        while (tokenStream.incrementToken()) {
            System.out.println(charTermAttribute.toString());
        }
        //6. 关闭TokenStream对象。
        tokenStream.close();
    }

IKAnalyzer

很棒的中文分词器

  1. 引入jar包
  2. 把配置文件,扩展词典,停用词典都添加到classpath下。
  3. 扩展词典严禁使用windows记事本编辑的。扩展词典可以添加一些新词。停用词词典是无意义的词或者是敏感词汇。

想使用ik分词器也很简单,在新建一个IndexWriterConfig对象的时候,传入一个ikAnalyzer对象参数进去就好。

索引库维护

删除索引

删除所有: indexWriter.deleteAll();
根据查询来删除: indexWriter.deleteDocuments(new Term(field, text));

修改索引

使用TermQuery来查询: indexWriter.updateDocument(new Term(field, text), new Document);
范围查询:

// 创建一个Query对象
Query query = LongPoint.newRangeQuery(field, lowvalue, upvalue);
//执行查询
TopDocs topDocs = indexSearcher.search(query, 10);
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
for (ScoreDoc scoreDoc : scoreDocs) {
    int docId = scoreDoc.doc;
    Document document = indexSearcher.doc(docId);
    ...
}

使用QueryParser来查询
可以对要查询的内容先分词,然后基于分词的结果进行查询。

@Test
    public void testQueryParser() throws Exception {
        //创建一个QueryParser对象,两个参数,1是默认搜索域,2是分析器对象
        QueryParser queryParser = new QueryParser("name", new IKAnalyzer());
        //使用QueryParser对象创建一个Query对象。
        Query query = queryParser.parse("天青色等烟雨");
        // 执行查询
        //    1)创建一个Director对象,指定索引库的位置
        Directory directory = FSDirectory.open(new File("/home/gaolei/JavaProgram/Learn-Lucene/src/main/resources/index").toPath());
        //    2)创建一个IndexReader 对象
        IndexReader indexReader = DirectoryReader.open(directory);
        //    3)创建一个IndexSearcher对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        TopDocs topd = indexSearcher.search(query, 10);
        //    6)取查询结果的总记录数。
        System.out.println("总记录数" + topd.totalHits);
        //    7)取文档列表
        ScoreDoc[] scoreDocs = topd.scoreDocs;
        //    8)打印文档中的内容
        for (ScoreDoc scoreDoc : scoreDocs) {
            //取文档ID
            int docId = scoreDoc.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("------------------------------------------");
        }
        //    9)关闭IndexReader对象
        indexReader.close();
    }
posted @ 2019-12-31 15:54  时光轻轻吹  阅读(369)  评论(0编辑  收藏  举报