Lucene使用案例

Lucene是apache软件基金会4 jakarta项目组的一个子项目,是一个开放源代码的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)。Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便的在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。Lucene是一套用于全文检索和搜寻的开源程式库,由Apache软件基金会支持和提供。Lucene提供了一个简单却强大的应用程式接口,能够做全文索引和搜寻。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。人们经常提到信息检索程序库,虽然与搜索引擎有关,但不应该将信息检索程序库与搜索引擎相混淆(来自百度百科)

点击查看百度百科:https://baike.baidu.com/item/Lucene

CSDN一篇文章介绍:https://blog.csdn.net/regan_hoo/article/details/78802897

 

关于Lucene具体介绍不多说,这里写一个应用场景来学会使用Lucene:

 

我在一个文件夹里面存了一堆txt文本,大小不一,名字不同,里面的内容不同,我要做一个功能实现:

比如我输入一个java,只要名字,路径,内容等等出现了java这个词汇,就返回结果给我

 

1.建一个java工程,导包(开发中通常是使用老版本的):

 

2.一步一步来:

我在D盘666文件夹放入一堆文本文件

然后在D盘的temp的index下创建索引库

创建索引:

package lucene;

import java.io.File;

import org.apache.commons.io.FileUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.TextField;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.LongField;
import org.apache.lucene.document.StoredField;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.junit.Test;

public class MyFirstLucene {
    // 创建索引
    @Test
    public void testIndex() throws Exception {
        // 新建一个索引库(我放在D盘某文件夹内)
        Directory directory = FSDirectory.open(new File("D:\\temp\\index"));
        // 新建分析器对象
        Analyzer analyzer = new StandardAnalyzer();
        // 新建配置对象
        IndexWriterConfig config = new IndexWriterConfig(Version.LUCENE_4_10_3, analyzer);
        // 创建一个IndexWriter对象(参数一个索引库,一个配置)
        IndexWriter indexWriter = new IndexWriter(directory, config);
        // 创建域对象
        File f = new File("D:\\666");
        File[] list = f.listFiles();
        for (File file : list) {
            // 创建一个文档对象
            Document document = new Document();
            // 文件名称
            String file_name = file.getName();
            Field fileNameField = new TextField("fileName", file_name, Store.YES);
            // 文件大小
            long file_size = FileUtils.sizeOf(file);
            Field fileSizeField = new LongField("fileSize", file_size, Store.YES);
            // 文件路径
            String file_path = file.getPath();
            Field filePathField = new StoredField("filePath", file_path);
            // 文件内容
            String file_content = FileUtils.readFileToString(file);
            Field fileContentField = new TextField("fileContent", file_content, Store.YES);

            // 添加到document
            document.add(fileNameField);
            document.add(fileSizeField);
            document.add(filePathField);
            document.add(fileContentField);

            // 创建索引
            indexWriter.addDocument(document);
        }

        // 关闭资源
        indexWriter.close();

    }
}

 

好的,运行成功,我打开D盘的temp文件夹中的Index文件夹:存入了一堆看不懂的东西,这就代表创建索引库成功:

 

3.查询索引:

    // 搜索索引
    @Test
    public void testSearch() throws Exception {
        // 第一步:创建一个Directory对象,也就是索引库存放的位置。
        Directory directory = FSDirectory.open(new File("D:\\temp\\index"));
        // 第二步:创建一个indexReader对象,需要指定Directory对象。
        IndexReader indexReader = DirectoryReader.open(directory);
        // 第三步:创建一个indexSearcher对象,需要指定IndexReader对象
        IndexSearcher indexSearcher = new IndexSearcher(indexReader);
        // 第四步:创建一个TermQuery对象,指定查询的域和查询的关键词。
        Query query = new TermQuery(new Term("fileName", "spring"));
        // 第五步:执行查询(显示条数)
        TopDocs topDocs = indexSearcher.search(query, 10);
        // 第六步:返回查询结果。遍历查询结果并输出。
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
            int doc = scoreDoc.doc;
            Document document = indexSearcher.doc(doc);
            // 文件名称
            String fileName = document.get("fileName");
            System.out.println(fileName);
            // 文件内容
            String fileContent = document.get("fileContent");
            System.out.println(fileContent);
            // 文件大小
            String fileSize = document.get("fileSize");
            System.out.println(fileSize);
            // 文件路径
            String filePath = document.get("filePath");
            System.out.println(filePath);
            System.out.println("------------");
        }
        // 第七步:关闭IndexReader对象
        indexReader.close();

    }

 

实现了基础功能

但是,这里有很大的一个问题:

无法处理中文

 

4.所以,接下来就处理中文问题:中文分析器(上边的示例采用的是标准分析器,即处理英文的分析器)

 首先,我们看看用标准分析器分析中文的结果:

    // 查看分析器的分词效果
    @Test
    public void testTokenStream() throws Exception {
        // 创建一个分析器对象
        Analyzer analyzer = new StandardAnalyzer();// 获得tokenStream对象
        // 第一个参数:域名,可以随便给一个
        // 第二个参数:要分析的文本内容
        TokenStream tokenStream = analyzer.tokenStream("test",
                "高富帅可以用二维表结构来逻辑表达实现的数据");
        // 添加一个引用,可以获得每个关键词
        CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
        // 添加一个偏移量的引用,记录了关键词的开始位置以及结束位置
        OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
        // 将指针调整到列表的头部
        tokenStream.reset();
        // 遍历关键词列表,通过incrementToken方法判断列表是否结束
        while (tokenStream.incrementToken()) {
            // 关键词的起始位置
            System.out.println("start->" + offsetAttribute.startOffset());
            // 取关键词
            System.out.println(charTermAttribute);
            // 结束位置
            System.out.println("end->" + offsetAttribute.endOffset());
        }
        tokenStream.close();
    }

结果:

 

这里截取了一部分,发现如果采用了标准分析器,每一个中文都分隔开了,显然有问题

 

于是不能采用标准分析器:

用一个SmartChinese分析器:

    // 查看分析器的分词效果
    @Test
    public void testTokenStream() throws Exception {
        Analyzer analyzer = new SmartChineseAnalyzer();
        TokenStream tokenStream = analyzer.tokenStream("test",
                "高富帅可以用二维表结构来逻辑表达实现的数据");
        CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
        OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
        tokenStream.reset();
        while (tokenStream.incrementToken()) {
            System.out.println("start->" + offsetAttribute.startOffset());
            System.out.println(charTermAttribute);
            System.out.println("end->" + offsetAttribute.endOffset());
        }
        tokenStream.close();
    }

效果有提升:

 

不过有一个问题:对于新词汇如高富帅这样的,它不识别

 

如果追求更好的效果:可以采用其他的第三方分析器

比如我这里采用一个IK分析器,可以自己添加进去“高富帅”这种词汇

    @Test
    public void testTokenStream() throws Exception {
        Analyzer analyzer = new IKAnalyzer();
        TokenStream tokenStream = analyzer.tokenStream("test",
                "高富帅可以用二维表结构来逻辑表达实现的数据");
        CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
        OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
        tokenStream.reset();
        while (tokenStream.incrementToken()) {
            System.out.println("start->" + offsetAttribute.startOffset());
            System.out.println(charTermAttribute);
            System.out.println("end->" + offsetAttribute.endOffset());
        }
        tokenStream.close();
    }

 

添加配置文件

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">ext.dic;</entry> 
    
    <!--用户可以在这里配置自己的扩展停止词字典-->
    <entry key="ext_stopwords">stopword.dic;</entry> 
    
</properties>

 

ext.doc:

高富帅
二维表

 

stopword.dic:

我
是
用
的
二
维
表
来
a
an
and
are
as
at
be
but
by
for
if
in
into
is
it
no
not
of
on
or
such
that
the
their
then
there
these
they
this
to
was
will
with
View Code

 

这时候效果:

 

解决了中文问题,并且可以扩展

 

 

上边对于索引库操作只有添加,我们还可以对索引库做其他操作:

查询的时候也可以有多种方式,下面代码示例:

package lucene;

import java.io.File;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field.Store;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.junit.Test;
import org.wltea.analyzer.lucene.IKAnalyzer;

/**
 * 索引维护 添加 (上边已完成) 删除 修改 查询
 */
public class LuceneManager {
    public IndexWriter getIndexWriter() throws Exception {
        Directory directory = FSDirectory.open(new File("D:\\temp\\index"));
        Analyzer analyzer = new StandardAnalyzer();
        IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
        return new IndexWriter(directory, config);
    }

    // 全删除
    @Test
    public void testAllDelete() throws Exception {
        IndexWriter indexWriter = getIndexWriter();
        indexWriter.deleteAll();
        indexWriter.close();
    }

    // 根据条件删除
    @Test
    public void testDelete() throws Exception {
        IndexWriter indexWriter = getIndexWriter();
        Query query = new TermQuery(new Term("fileName", "apache"));
        indexWriter.deleteDocuments(query);
        indexWriter.close();
    }

    // 修改
    @Test
    public void testUpdate() throws Exception {
        IndexWriter indexWriter = getIndexWriter();
        Document doc = new Document();
        doc.add(new TextField("fileN", "测试文件名", Store.YES));
        doc.add(new TextField("fileC", "测试文件内容", Store.YES));
        indexWriter.updateDocument(new Term("fileName", "lucene"), doc, new IKAnalyzer());
        indexWriter.close();
    }

    public IndexSearcher getIndexSearcher() throws Exception {
        Directory directory = FSDirectory.open(new File("D:\\temp\\index"));
        IndexReader indexReader = DirectoryReader.open(directory);
        return new IndexSearcher(indexReader);
    }

    // 执行查询的结果
    public void printResult(IndexSearcher indexSearcher, Query query) throws Exception {
        TopDocs topDocs = indexSearcher.search(query, 10);
        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
            int doc = scoreDoc.doc;
            Document document = indexSearcher.doc(doc);
            // 文件名称
            String fileName = document.get("fileName");
            System.out.println(fileName);
            // 文件内容
            String fileContent = document.get("fileContent");
            System.out.println(fileContent);
            // 文件大小
            String fileSize = document.get("fileSize");
            System.out.println(fileSize);
            // 文件路径
            String filePath = document.get("filePath");
            System.out.println(filePath);
            System.out.println("------------");
        }
    }

    // 查询所有
    @Test
    public void testMatchAllDocsQuery() throws Exception {
        IndexSearcher indexSearcher = getIndexSearcher();
        Query query = new MatchAllDocsQuery();
        System.out.println(query);
        printResult(indexSearcher, query);
        // 关闭资源
        indexSearcher.getIndexReader().close();
    }

    // 根据数值范围查询
    @Test
    public void testNumericRangeQuery() throws Exception {
        IndexSearcher indexSearcher = getIndexSearcher();
        // 参数的意思:文本大小在100到200字节之间,不包含100,包含200
        Query query = NumericRangeQuery.newLongRange("fileSize", 100L, 200L, false, true);
        System.out.println(query);
        printResult(indexSearcher, query);
        // 关闭资源
        indexSearcher.getIndexReader().close();
    }

    // 可以组合查询条件
    @Test
    public void testBooleanQuery() throws Exception {
        IndexSearcher indexSearcher = getIndexSearcher();
        BooleanQuery booleanQuery = new BooleanQuery();
        Query query1 = new TermQuery(new Term("fileName", "apache"));
        Query query2 = new TermQuery(new Term("fileName", "lucene"));
        // 类似 select * from user where id = ? or/and name = ?
        booleanQuery.add(query1, Occur.MUST);// and
        booleanQuery.add(query2, Occur.SHOULD);// or
        System.out.println(booleanQuery);
        printResult(indexSearcher, booleanQuery);
        // 关闭资源
        indexSearcher.getIndexReader().close();
    }

    // 条件解释的对象查询(上边的查询和这种掌握一种即可)
    @Test
    public void testQueryParser() throws Exception {
        IndexSearcher indexSearcher = getIndexSearcher();
        QueryParser queryParser = new QueryParser("fileName", new IKAnalyzer());
        // *:* 域:值
        Query query = queryParser.parse("fileName:lucene OR fileContent:apache");
        printResult(indexSearcher, query);
        // 关闭资源
        indexSearcher.getIndexReader().close();
    }

    // 条件解析的对象查询 多个默认域
    @Test
    public void testMultiFieldQueryParser() throws Exception {
        IndexSearcher indexSearcher = getIndexSearcher();
        String[] fields = { "fileName", "fileContent" };
        MultiFieldQueryParser queryParser = new MultiFieldQueryParser(fields, new IKAnalyzer());
        Query query = queryParser.parse("lucene");
        printResult(indexSearcher, query);
        // 关闭资源
        indexSearcher.getIndexReader().close();
    }

}

 

转载于:https://www.cnblogs.com/xuyiqing/p/8696660.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值