Lucene-入门

  1.  关于JDK版本
    至少使用JDK8版本,请下载JDK8或者更高版本: 下载以及配置JDK环境
  2.  Lucene 概念
    Lucene 这个开源项目,使得 Java开发人员可以很方便地得到像搜索引擎google baidu那样的搜索效果。
  3.  先运行,看到效果,再学习
    老规矩,先下载右上角的可运行项目,配置运行起来,确认可用之后,再学习做了哪些步骤以达到这样的效果。
    运行TestLucene类,期望看到如图所示的效果。
    一共是10条数据,通过关键字查询出来6条命中结果,不同的命中结果有不同的匹配度得分,比如第一条,命中都就很高,既有 护眼, 也有 带光源。 其他的命中度就比较低,没有护眼关键字的匹配,只有光源关键字的匹配。
  4. Lucene 版本
    当前使用的Lucene版本是截至2018.3.9最新版本 7.2.1
  5.  jar 包
    一系列需要的jar包都放在项目里了,直接使用就好了,包括兼容 lucene 7.2.1 的中文分词器
  6.  TestLucene.java
    这是TestLucene.java 的完整代码,后续会对代码详细讲解
    public class TestLucene {
     
        public static void main(String[] args) throws Exception {
            // 1. 准备中文分词器
            IKAnalyzer analyzer = new IKAnalyzer();
     
            // 2. 索引
            List<String> productNames = new ArrayList<>();
            productNames.add("飞利浦led灯泡e27螺口暖白球泡灯家用照明超亮节能灯泡转色温灯泡");
            productNames.add("飞利浦led灯泡e14螺口蜡烛灯泡3W尖泡拉尾节能灯泡暖黄光源Lamp");
            productNames.add("雷士照明 LED灯泡 e27大螺口节能灯3W球泡灯 Lamp led节能灯泡");
            productNames.add("飞利浦 led灯泡 e27螺口家用3w暖白球泡灯节能灯5W灯泡LED单灯7w");
            productNames.add("飞利浦led小球泡e14螺口4.5w透明款led节能灯泡照明光源lamp单灯");
            productNames.add("飞利浦蒲公英护眼台灯工作学习阅读节能灯具30508带光源");
            productNames.add("欧普照明led灯泡蜡烛节能灯泡e14螺口球泡灯超亮照明单灯光源");
            productNames.add("欧普照明led灯泡节能灯泡超亮光源e14e27螺旋螺口小球泡暖黄家用");
            productNames.add("聚欧普照明led灯泡节能灯泡e27螺口球泡家用led照明单灯超亮光源");    
            Directory index = createIndex(analyzer, productNames);
     
            // 3. 查询器
            String keyword = "护眼带光源";
            Query query = new QueryParser("name", analyzer).parse(keyword);
             
            // 4. 搜索
            IndexReader reader = DirectoryReader.open(index);
            IndexSearcher searcher = new IndexSearcher(reader);
            int numberPerPage = 1000;
            System.out.printf("当前一共有%d条数据%n",productNames.size());
            System.out.printf("查询关键字是:\"%s\"%n",keyword);
            ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;
     
            // 5. 显示查询结果
            showSearchResults(searcher, hits, query, analyzer);
            // 6. 关闭查询
            reader.close();
        }
     
        private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer)
                throws Exception {
            System.out.println("找到 " + hits.length + " 个命中.");
            System.out.println("序号\t匹配度得分\t结果");
            for (int i = 0; i < hits.length; ++i) {
                ScoreDoc scoreDoc= hits[i];
                int docId = scoreDoc.doc;
                Document d = searcher.doc(docId);
                List<IndexableField> fields = d.getFields();
                System.out.print((i + 1));
                System.out.print("\t" + scoreDoc.score);
                for (IndexableField f : fields) {
                    System.out.print("\t" + d.get(f.name()));
                }
                System.out.println();
            }
        }
     
        private static Directory createIndex(IKAnalyzer analyzer, List<String> products) throws IOException {
            Directory index = new RAMDirectory();
            IndexWriterConfig config = new IndexWriterConfig(analyzer);
            IndexWriter writer = new IndexWriter(index, config);
     
            for (String name : products) {
                addDoc(writer, name);
            }
            writer.close();
            return index;
        }
     
        private static void addDoc(IndexWriter w, String name) throws IOException {
            Document doc = new Document();
            doc.add(new TextField("name", name, Field.Store.YES));
            w.addDocument(doc);
        }
    }
  7. 分词器
    准备中文分词器,关于分词器更多概念在分词器概念 中有详细讲解,这里先使用
    // 1. 准备中文分词器
    IKAnalyzer analyzer = new IKAnalyzer();
  8. 创建索引
    1. 首先准备10条数据
    这10条数据都是字符串,相当于产品表里的数据
    2. 通过createIndex方法,把它加入到索引当中

    创建内存索引,为什么Lucene会比数据库快?因为它是从内存里查,自然就比数据库里快多了呀
    Directory index = new RAMDirectory();
    根据中文分词器创建配置对象
    IndexWriterConfig config = new IndexWriterConfig(analyzer);
    创建索引 writer
    IndexWriter writer = new IndexWriter(index, config);
    遍历那10条数据,把他们挨个放进索引里
    for (String name : products) {
        addDoc(writer, name);
    }
    每条数据创建一个Document,并把这个Document放进索引里。 这个Document有一个字段,叫做"name"。 TestLucene.java 第49行创建查询器,就会指定查询这个字段
    private static void addDoc(IndexWriter w, String name) throws IOException {
    	Document doc = new Document();
    	doc.add(new TextField("name", name, Field.Store.YES));
    	w.addDocument(doc);
    }
    准备数据
    // 2. 索引
    List<String> productNames = new ArrayList<>();
    productNames.add("飞利浦led灯泡e27螺口暖白球泡灯家用照明超亮节能灯泡转色温灯泡");
    productNames.add("飞利浦led灯泡e14螺口蜡烛灯泡3W尖泡拉尾节能灯泡暖黄光源Lamp");
    productNames.add("雷士照明 LED灯泡 e27大螺口节能灯3W球泡灯 Lamp led节能灯泡");
    productNames.add("飞利浦 led灯泡 e27螺口家用3w暖白球泡灯节能灯5W灯泡LED单灯7w");
    productNames.add("飞利浦led小球泡e14螺口4.5w透明款led节能灯泡照明光源lamp单灯");
    productNames.add("飞利浦蒲公英护眼台灯工作学习阅读节能灯具30508带光源");
    productNames.add("欧普照明led灯泡蜡烛节能灯泡e14螺口球泡灯超亮照明单灯光源");
    productNames.add("欧普照明led灯泡节能灯泡超亮光源e14e27螺旋螺口小球泡暖黄家用");
    productNames.add("聚欧普照明led灯泡节能灯泡e27螺口球泡家用led照明单灯超亮光源");    
    Directory index = createIndex(analyzer, productNames);
    createIndex方法
    private static Directory createIndex(IKAnalyzer analyzer, List<String> products) throws IOException {
        Directory index = new RAMDirectory();
        IndexWriterConfig config = new IndexWriterConfig(analyzer);
        IndexWriter writer = new IndexWriter(index, config);
     
        for (String name : products) {
            addDoc(writer, name);
        }
        writer.close();
        return index;
    }
    addDoc方法
    private static void addDoc(IndexWriter w, String name) throws IOException {
        Document doc = new Document();
        doc.add(new TextField("name", name, Field.Store.YES));
        w.addDocument(doc);
    }
  9. 创建查询器
    根据关键字 护眼带光源,基于 "name" 字段进行查询。 这个 "name" 字段就是在创建索引步骤里每个Document的 "name" 字段,相当于表的字段名
    String keyword = "护眼带光源";
    Query query = new QueryParser("name", analyzer).parse(keyword);
  10.  执行搜索
    接着就执行搜索:
    创建索引 reader:
    IndexReader reader = DirectoryReader.open(index);
    基于 reader 创建搜索器:
    IndexSearcher searcher = new IndexSearcher(reader);
    指定每页要显示多少条数据:
    int numberPerPage = 1000;
    执行搜索
    ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;
    // 4. 搜索
    IndexReader reader = DirectoryReader.open(index);
    IndexSearcher searcher = new IndexSearcher(reader);
    int numberPerPage = 1000;
    System.out.printf("当前一共有%d条数据%n",productNames.size());
    System.out.printf("查询关键字是:\"%s\"%n",keyword);
    ScoreDoc[] hits = searcher.search(query, numberPerPage).scoreDocs;
  11. 显示查询结果
    每一个ScoreDoc[] hits 就是一个搜索结果,首先把他遍历出来
    for (int i = 0; i < hits.length; ++i) {
    ScoreDoc scoreDoc= hits[i];
    然后获取当前结果的docid, 这个docid相当于就是这个数据在索引中的主键
    int docId = scoreDoc.doc;
    再根据主键docid,通过搜索器从索引里把对应的Document取出来
    Document d = searcher.doc(docId);
    接着就打印出这个Document里面的数据。 虽然当前Document只有name一个字段,但是代码还是通过遍历所有字段的形式,打印出里面的值,这样当Docment有多个字段的时候,代码就不用修改了,兼容性更好点。
    scoreDoc.score 表示当前命中的匹配度得分,越高表示匹配程度越高
    List<IndexableField> fields = d.getFields();
    System.out.print((i + 1));
    System.out.print("\t" + scoreDoc.score);
    for (IndexableField f : fields) {
            System.out.print("\t" + d.get(f.name()));
    }
    private static void showSearchResults(IndexSearcher searcher, ScoreDoc[] hits, Query query, IKAnalyzer analyzer)
            throws Exception {
        System.out.println("找到 " + hits.length + " 个命中.");
        System.out.println("序号\t匹配度得分\t结果");
        for (int i = 0; i < hits.length; ++i) {
            ScoreDoc scoreDoc= hits[i];
            int docId = scoreDoc.doc;
            Document d = searcher.doc(docId);
            List<IndexableField> fields = d.getFields();
            System.out.print((i + 1));
            System.out.print("\t" + scoreDoc.score);
            for (IndexableField f : fields) {
                System.out.print("\t" + d.get(f.name()));
            }
            System.out.println();
        }
    }
  12. 运行结果
    如图所示,一共是10条数据,通过关键字查询出来6条命中结果,不同的命中结果有不同的匹配度得分,比如第一条,命中都就很高,既有 护眼, 也有 带光源。 其他的命中度就比较低,没有护眼关键字的匹配,只有光源关键字的匹配。
  13.  和 like 的区别
    like 也可以进行查询,那么使用lucene 的方式有什么区别呢? 主要是两点:
    1. 相关度
    通过观察运行结果,可以看到不同相关度的结果都会查询出来,但是使用 like,就做不到这一点了
    2. 性能
    数据量小的时候,like 也会有很好的表现,但是数据量一大,like 的表现就差很多了。 在接下来的教程里会演示对 14万条数据 的查询
  14.  思路图
    现在通过自己做了一遍 Lucene了,有了感性的认识,接着来整理一下做 Lucene的思路。
    1. 首先搜集数据
    数据可以使文件系统,数据库,网络上,手工输入的,或者像本例直接写在内存上的
    2. 通过数据创建索引
    3. 用户输入关键字
    4. 通过关键字创建查询器
    5. 根据查询器到索引里获取数据
    6. 然后把查询结果展示在用户面前







评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值