Lucene

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/QuietHRH/article/details/82850031

思维导图

搜索引擎

1. 运行原理

运行原理

2. 倒排索引

​ 倒排索引, 又称为反向索引: 以字或者词,甚至是一句话一段话作为一个关键字进行索引, 每一个关键字都会对应着一个记录项, 记录项中记录了这个关键字出现在那些文档中, 已经在此文档的什么位置上

为什么说倒排索引可以提升查询的效率和精准度呢?

​ 倒排索引, 是将数据提前按照格式分词放好,建立索引, 当用户进行搜索, 将用户的关键字进行分词, 然后根据分词后的单词到索引库中寻找对应词条,根据词条, 查到对应所在的文档位置, 将其文档内容直接获取即可
倒排索引

lucene

Lucene是Apache提供的一个开源的全文检索引擎工具包, 其本质就是一个工具包, 而非一个完整的搜索引擎, 但是我们可以通过Lucene来构建一个搜索引擎

lucene

1. API

1.1 增删改

  • IndexWriter: 索引写入器对象

    其主要的作用, 添加索引, 修改索引和删除索引

    • 创建此对象的时候, 需要传入Directory和indexWriterConfig对象
  • Directory: 目录类, 用来指定索引库的目录

    • 常用的实现类:
      • FSDirectory: 用来指定文件系统的目录, 将索引信息保存到磁盘上
        • 优点: 索引可以进行长期保存, 安全系数高
        • 缺点: 读取略慢
      • RAMDriectory: 内存目录, 将索引库信息存放到内存中
        • 优点: 读取速度快
        • 缺点: 不安全, 无法长期保存, 关机后就消失了
  • IndexWriterConfig: 索引写入器的配置类

    • 创建此对象, 需要传递Lucene的版本和分词器
    • 作用:
      • 作用1 : 指定Lucene的版本和需要使用的分词器
      • 作用2: 设置Lucene的打开索引库的方式: setOpenMode();
//1. 需要创建 indexwriter对象
        //1.1 创建 索引库
        FSDirectory directory = FSDirectory.open(new File("E:\\test"));
        //1.2 创建 写入器配置对象:  参数1  版本号,  参数2  分词器
        IndexWriterConfig config = new IndexWriterConfig(Version.LATEST,new StandardAnalyzer());
        IndexWriter indexWriter = new IndexWriter(directory,config);

  • Document: 文档

     在Lucene中, 每一条数据以文档的形式进行存储, 文档中也有其对应的属性和值, Lucene中一个文档类似数据库的一个表, 表中的字段类似于文档中的字段,只不过这个文档只能保存一条数据
    

    Document看做是一个文件, 文件的属性就是文档的属性, 文件对应属性的值就是文档的属性的值 content

  • 一个文档中可以有多个字段, 每一个字段就是一个field对象,不同的文档可以有不同的属性

  • 字段也有其对应数据类型, 故Field类也提供了各种数据类型的实现类
    添加

 //2. 写入文档
        //2.1 创建文档对象
        Document doc = new Document();
        //2.2 添加文档的属性
        doc.add(new StringField("title","Lucene介绍", Field.Store.YES));
        doc.add(new IntField("id",1, Field.Store.YES));
        doc.add(new TextField("content","Lucene是一个全文检索的工具包", Field.Store.YES));

        indexWriter.addDocument(doc);

        //3. 提交数据
        indexWriter.commit();

// 执行删除操作(根据词条),要求id字段必须是字符串类型
		// indexWriter.deleteDocuments(new Term("id", "5"));
		// 根据查询条件删除
		// indexWriter.deleteDocuments(NumericRangeQuery.newLongRange("id", 2l, 4l, true, false));
		// 删除所有
		indexWriter.deleteAll();

/**
	 * 更新索引
	 * 本质先删除再添加
	 * 先删除所有满足条件的文档,再创建文档
	 * 因此,更新索引通常要根据唯一字段
	 */
	 
	 	// 创建文档对象
		Document document = new Document();
		document.add(new StringField("id", "9", Store.YES));
		document.add(new TextField("title", "谷歌地图之父跳槽FaceBook", Store.YES));
		// 索引写入器对象
		IndexWriter indexWriter = new IndexWriter(directory, conf);
		
		// 执行更新操作
		indexWriter.updateDocument(new Term("id", "1"), document);
		// 提交
		indexWriter.commit();
		// 关闭
		indexWriter.close();

1.2 查

  • IndexSearcher: Lucene中查询对象, 用来执行查询和排序操作
    • 常用方法:
      • search(Query query, int n);//执行查询
        • 参数1: 查询条件
        • 参数2: 返回的最大条数
      • search(Query query, int n,Sort sort);
        • 参数1: 查询的条件
        • 参数2: 返回的最大的条数
        • 参数3: 排序
      • doc(int id);//根据文档id查询文档对象
  • IndexReader: 索引库读取工具
    • 使用DirectoryReader来打开索引库
  • Query:查询对象
    • 获取方式:
      • 通过查询解析器
        • 单字段的解析器: queryParse
        • 多字段的解析器: multiFieldQueryParse
      • 使用Lucene自定义的实现类
        • Lucene中提供了五种常用的多样化的查询
  • TopDocs:查询结果对象
    • 第一部分: 查询到的总条数
      • int topDocs.totalHits
    • 第二部分: 得分文档的数组
      • ScoreDoc[] topDocs.scoreDocs;
  • ScoreDoc: 得分文档对象
    • 第一部分: 文档的id
      • topDoc.doc
    • 第二部分: 文档的得分
      • topDoc.score
		//1. 创建查询的核心对象
        FSDirectory d = FSDirectory.open(new File("E:\\test"));
        IndexReader reader = DirectoryReader.open(d);
        IndexSearcher indexSearcher = new IndexSearcher(reader);

单字段查询

//2. 执行查询
        QueryParser queryParser = new QueryParser("content", new IKAnalyzer());
        Query query = queryParser.parse("全文");
        TopDocs topDocs = indexSearcher.search(query, 10);

        //3. 获取文档id
        ScoreDoc[] scoreDocs = topDocs.scoreDocs; //获取得分文档集合
        for (ScoreDoc scoreDoc : scoreDocs) {
            int id = scoreDoc.doc;//获取文档id
            float score = scoreDoc.score;// 返回此文档的得分

            Document doc = indexSearcher.doc(id);
            String docId = doc.get("id");
            String content = doc.get("content");
            System.out.println(docId+"   "+content+"  "+"得分为:"+score);

特殊查询

  • 词条查询

    创建词条对象
    注意: 词条是不可在分割的, 词条可以是一个字, 也可以是一句话
    使用场景: 主要是针对的是不可在分割的字段, 例如id
    由于其不可再分, 可以搜索 全文, 但是不能搜索 全文检索
    TermQuery termQuery = new TermQuery(new Term(“content”,“全文”));

  • 通配符查询

    //通配符:
    *: 代表多个字符
    ?: 代表一个占位符
    WildcardQuery wildcardQuery = new WildcardQuery(new Term(“content”,"?uce*"));

  • 模糊查询
    指的是通过替换, 补位, 移动 能够在二次切换内查询数据即可返回

    ​ 参数1: term 指定查询的字段和内容

    ​ 参数2: int n 表示最大编辑的次数 最大2

    FuzzyQuery fuzzyQuery = new FuzzyQuery(new Term(“content”,“lucene”),1);

  • 数值范围查询

    获取NumericRangeQuery的方式:

    ​ 通过提供的静态方法获取:

    ​ NumericRangeQuery.newIntRange()

    ​ NumericRangeQuery.newFloatRange()

    ​ NumericRangeQuery.newDoubleRange()

    ​ NumericRangeQuery.newLongRange()

    ​ 参数1: 指定要查询的字段

    ​ 参数2: 指定要查询的开始值

    ​ 参数3: 指定要查询的结束值

    ​ 参数4: 是否包含开始

    ​ 参数5: 是否包含结束

    ​ NumericRangeQuery numericRangeQuery =NumericRangeQuery.newIntRange(“id”,2,4,false,false);

  • 组合查询

    	Query query1 = NumericRangeQuery.newLongRange("id", 2l, 4l, true, true);
    		Query query2 = NumericRangeQuery.newLongRange("id", 0l, 3l, true, true);
    	// boolean查询本身没有查询条件,它可以组合其他查询
    	BooleanQuery query = new BooleanQuery();
    	// 交集: Occur.MUST + Occur.MUST
    	// 并集:Occur.SHOULD + Occur.SHOULD
    	// 非:Occur.MUST_NOT
    	query.add(query1, Occur.SHOULD);
    	query.add(query2, Occur.SHOULD);
    

lucene其他

1. 高亮显示

    // 高亮显示
	@Test
	public void testHighlighter() throws Exception {
		// 目录对象
		Directory directory = FSDirectory.open(new File("indexDir"));
		// 创建读取工具
		IndexReader reader = DirectoryReader.open(directory);
		// 创建搜索工具
		IndexSearcher searcher = new IndexSearcher(reader);

		QueryParser parser = new QueryParser("title", new IKAnalyzer());
		Query query = parser.parse("谷歌地图");
		
		// 格式化器
		Formatter formatter = new SimpleHTMLFormatter("<em>", "</em>");
		Scorer scorer = new QueryScorer(query);
		// 准备高亮工具
		Highlighter highlighter = new Highlighter(formatter, scorer);
		// 搜索
		TopDocs topDocs = searcher.search(query, 10);
		System.out.println("本次搜索共" + topDocs.totalHits + "条数据");
		
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
		for (ScoreDoc scoreDoc : scoreDocs) {
			// 获取文档编号
			int docID = scoreDoc.doc;
			Document doc = reader.document(docID);
			System.out.println("id: " + doc.get("id"));
			
			String title = doc.get("title");
			// 用高亮工具处理普通的查询结果,参数:分词器,要高亮的字段的名称,高亮字段的原始值
			String hTitle = highlighter.getBestFragment(new IKAnalyzer(), "title", title);
			
			System.out.println("title: " + hTitle);
			// 获取文档的得分
			System.out.println("得分:" + scoreDoc.score);
		}
	}

2. 排序

	// 排序
	@Test
	public void testSortQuery() throws Exception {
		// 目录对象
		Directory directory = FSDirectory.open(new File("indexDir"));
		// 创建读取工具
		IndexReader reader = DirectoryReader.open(directory);
		// 创建搜索工具
		IndexSearcher searcher = new IndexSearcher(reader);
		
		QueryParser parser = new QueryParser("title", new IKAnalyzer());
		Query query = parser.parse("谷歌地图");
		
		// 创建排序对象,需要排序字段SortField,参数:字段的名称、字段的类型、是否反转如果是false,升序。true降序
		Sort sort = new Sort(new SortField("id", Type.LONG, true));
		// 搜索
		TopDocs topDocs = searcher.search(query, 10,sort);
		System.out.println("本次搜索共" + topDocs.totalHits + "条数据");
		
		ScoreDoc[] scoreDocs = topDocs.scoreDocs;
		for (ScoreDoc scoreDoc : scoreDocs) {
			// 获取文档编号
			int docID = scoreDoc.doc;
			Document doc = reader.document(docID);
			System.out.println("id: " + doc.get("id"));
			System.out.println("title: " + doc.get("title"));
		}
	}

3.分页

@Test
    public void page() throws IOException {
        FSDirectory directory = FSDirectory.open(new File("e:/test"));
        DirectoryReader reader = DirectoryReader.open(directory);
        IndexSearcher indexSearcher = new IndexSearcher(reader);

        int page=2;
        int num=3;
        int start=(page-1)*num;
        int end=start+num;

        Query query1=new WildcardQuery(new Term("content","有点"));
        TopDocs topDocs1 = indexSearcher.search(query1, 30);
        int totalHits1 = topDocs1.totalHits;
        System.out.println("总数"+totalHits1);

        int ceil = (int) Math.ceil((totalHits1+0.0) / num);
        System.out.println("总页数"+ceil);

        // 3 4 5
        NumericRangeQuery query = NumericRangeQuery.newIntRange("id", start, end, true, false);
        Sort sort = new Sort(new SortField("id", SortField.Type.INT, true));

        BooleanQuery  query3=new BooleanQuery();
        query3.add(query1, BooleanClause.Occur.MUST);
        query3.add(query, BooleanClause.Occur.MUST);
        TopDocs topDocs = indexSearcher.search(query3, 10,sort);
        int totalHits = topDocs.totalHits;

        System.out.println("当前页的显示数量"+totalHits);


        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
        for (ScoreDoc scoreDoc : scoreDocs) {
            int docId = scoreDoc.doc;
            Document doc = indexSearcher.doc(docId);
            String id = doc.get("id");
            String content = doc.get("content");
            System.out.println(id+"..."+content);
        }

    }

4. 加权因子

  • Lucene会对搜索的结果的匹配度进行一个加分, 用来表示数据和词条关联性的强弱, 得分越高, 表示匹配度越高, 排名越靠前
  • Lucene支持对某一个字段设置加权因子, 来提高其打分, 使其排名更加靠前, 这样当用户搜索的时候, 便可以将此词条对应的文档展示在最前面
TextField textField = new TextField("content",
				"学习lucene需要掌握搜索引擎的基本原理和lucene创建索引和查询索引,boots", Store.YES);
textField.setBoost(10);
展开阅读全文

没有更多推荐了,返回首页