springboot 集成 lucene

这篇具有很好参考价值的文章主要介绍了springboot 集成 lucene。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简介

  1. 数据每分钟产生200条,使用mysql储存。
  2. 目前有数据超过700M。
  3. 按照日期查询,按月查询包含每次超过20w条以上,时间比较长。
  4. 计划使用lucene优化查询,不适用es是因为项目较小,没有更富裕的资源。

基本步骤

  1. 引入依赖。
  2. 开发工具类。
  3. 开发索引功能,完成索引。
  4. 开发定时任务,完成数据增量更新。
  5. 开发搜索功能,可以搜索数据。

引入依赖

  1. 修改pom文件
<!-- Lucence核心包 -->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-core</artifactId>
    <version>9.7.0</version>
</dependency>

<!-- Lucene查询解析包 -->
<dependency>
    <groupId>org.apache.lucene</groupId>
    <artifactId>lucene-queryparser</artifactId>
    <version>9.7.0</version>
</dependency>
  • 注:没有使用更多的包是因为这次优化是以long类型区间计算为主,不需要全文索引,所以有基础的包就够了。

工具类

  1. 实现基本的生成、删除和查询。

import com.xxx.common.ResponseCode;
import com.xxx.common.exception.SystemException;
import com.xxx.common.util.ValidUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
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.search.IndexSearcher;
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.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;

@Component
@Slf4j
public class LuceneUtil {

	//索引文件存放路径
    @Value("${lucene.index.path}")
    private String luceneIndexPath;

	/**
	 生成索引方法
	 */
    public <T> void createIndex(List<T> list, CreateDocumentHandler handler) {
        File file = new File(luceneIndexPath);
        if (!file.exists()) {
            file.mkdir();
        }
        if (ValidUtil.isEmpty(list)) {
            return;
        }
        long startTime = System.currentTimeMillis();
        IndexWriter writer = null;
        try {
            Directory dir = FSDirectory.open(Paths.get(luceneIndexPath));
            //标准分词器,会自动去掉空格啊,is a the等单词
            Analyzer analyzer = new StandardAnalyzer();
            //将标准分词器配到写索引的配置中
            IndexWriterConfig config = new IndexWriterConfig(analyzer);
            //实例化写索引对象
            writer = new IndexWriter(dir, config);
            for (T t : list) {
                Document doc = handler.createDocument(t);
                writer.addDocument(doc);
            }
            writer.commit();
        } catch (Exception e) {
            throw new SystemException(ResponseCode.ERROR, e);
        } finally {
            try {
                if (null != writer) {
                    writer.close();
                }
            } catch (Exception e) {
                throw new SystemException(ResponseCode.ERROR, e);
            }
        }
        //记录索引结束时间
        long endTime = System.currentTimeMillis();
        log.info("建立索引耗时" + (endTime - startTime) + "毫秒");
    }

	/**
	 清楚所有索引
	 */
    public void clean() {
        File file = new File(luceneIndexPath);
        if (!file.exists()) {
            return;
        }
        long startTime = System.currentTimeMillis();
        IndexWriter writer = null;
        try {
            Directory dir = FSDirectory.open(Paths.get(luceneIndexPath));
            //标准分词器,会自动去掉空格啊,is a the等单词
            Analyzer analyzer = new StandardAnalyzer();
            //将标准分词器配到写索引的配置中
            IndexWriterConfig config = new IndexWriterConfig(analyzer);
            //实例化写索引对象
            writer = new IndexWriter(dir, config);
            writer.deleteAll();
        } catch (Exception e) {
            throw new SystemException(ResponseCode.ERROR, e);
        } finally {
            try {
                if (null != writer) {
                    writer.close();
                }
            } catch (Exception e) {
                throw new SystemException(ResponseCode.ERROR, e);
            }
        }
        //记录索引结束时间
        long endTime = System.currentTimeMillis();
        log.info("清除索引耗时" + (endTime - startTime) + "毫秒");
    }

	/**
	 查询
	 */
    public List<Document> search(CreateQueryParamsHandler handler) {
        File file = new File(luceneIndexPath + File.separator + "write.lock");
        if (!file.exists()) {
            return new ArrayList<>();
        }
        IndexReader reader = null;
        try {
            //获取要查询的路径,也就是索引所在的位置
            Directory dir = FSDirectory.open(Paths.get(luceneIndexPath));
            reader = DirectoryReader.open(dir);
            if (reader == null) {
                return new ArrayList<>();
            }
            //构建IndexSearcher
            IndexSearcher searcher = new IndexSearcher(reader);
            //记录索引开始时间
            long startTime = System.currentTimeMillis();
            //开始查询,查询前10条数据,将记录保存在docs中
            TopDocs docs = handler.handler(searcher);
            //记录索引结束时间
            long endTime = System.currentTimeMillis();
            log.info("索引查询耗时" + (endTime - startTime) + "毫秒");
            List<Document> result = new ArrayList<>(Long.valueOf(docs.totalHits.value).intValue());
            //取出每条查询结果
            for(ScoreDoc scoreDoc : docs.scoreDocs) {
                Document doc = searcher.doc(scoreDoc.doc);
                result.add(doc);
            }
            return result;
        } catch (Exception e) {
            throw new SystemException(ResponseCode.ERROR, e);
        } finally {
            try {
                assert reader != null;
                reader.close();
            } catch (IOException e) {
                throw new SystemException(ResponseCode.ERROR, e);
            }
        }
    }
}

生成索引功能

public void index(Date startDate) {
    log.info("start index! Date : " + DateUtil.format(DateUtil.now()));
    Date curStartDate = startDate;
    while (true) {
        Date curEndDate = DateUtil.datePlusDays(curStartDate, 1);
        List<CurrencyData> list = currencyDataMapper.queryLuceneList(CurrencyDataForm.builder().createTimeBegin(curStartDate.getTime()).createTimeEnd(curEndDate.getTime()).build());
        log.info(String.format("index startDate = %s, endDate = %s, size = %s", DateUtil.format(curStartDate), DateUtil.format(curEndDate), list.size()));
        if (list.size() == 0) {
            CurrencyDataForm countForm = CurrencyDataForm.builder().createTimeBegin(curStartDate.getTime()).build();
            List<CurrencyData> one = currencyDataMapper.getOne(countForm);
            log.info("has more begin:" + DateUtil.format(curEndDate) + ", result: " + (one.size() > 0 ? "yes" : "no"));
            if (one.size() == 0) {
                break;
            }
        }
        luceneUtil.createIndex(list, (CreateDocumentHandler<Data>) data -> {
            Document doc = new Document();
            //开始添加字段
            doc.add(new TextField("dId", data.getDId(), Field.Store.YES));
            doc.add(new TextField("typeId", data.getTypeId(), Field.Store.YES));
            //区间查询需要
            doc.add(new LongPoint("createTime", data.getCreateTime()));
            //储存需要
            doc.add(new StoredField("createTime", data.getCreateTime()));
            // 排序需要
            doc.add(new NumericDocValuesField("sortTime", data.getCreateTime()));
            // 第二个参数需要处理非空的情况
            doc.add(new TextField("value", (ValidUtil.isEmpty(data.getValue()) ? "" : data.getValue()) , Field.Store.YES));
            doc.add(new TextField("unit", (ValidUtil.isEmpty(data.getUnit()) ? "" : data.getUnit()) , Field.Store.YES));
            return doc;
        });
        curStartDate = curEndDate;
    }
    log.info("finish index!");
}
  • 注:每次生成1天的索引,如果本轮没数据,并且大于结束时间也没数据,结束索引。

定时任务

private ThreadPoolTaskExecutor tpe;

tpe.execute(() -> {
    Date startDate = null;
    try {
         startDate = getLastDate();
    } catch (SystemException s) {
        luceneUtil.clean();
        startDate = DateUtil.parse(initStartTime);
    }
    try {
        index(startDate);
    } catch (Exception e) {
        log.info("生成索引异常。", e);
    } finally {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
        executor.schedule(this::init, 60, TimeUnit.SECONDS);
        executor.shutdown();
    }
});
  • 注:使用线程池+延时任务,实现每60s执行一次功能。

搜索

public List<Data> queryIndex(Form form) {
    List<Data> result = new ArrayList<>();

    List<Document> documentList = luceneUtil.search((searcher) -> {

        BooleanQuery.Builder builder = new BooleanQuery.Builder();

        if (ValidUtil.isNotEmpty(form.getDId())) {
            TermQuery deviceIdQuery = new TermQuery(new Term("dId", form.getDId()));
            builder.add(deviceIdQuery, BooleanClause.Occur.MUST);
        }

        if (ValidUtil.isNotEmpty(form.getTypeId())) {
            TermQuery typeQuery = new TermQuery(new Term("typeId", form.getTypeId()));
            builder.add(deviceIdQuery, BooleanClause.Occur.MUST);
        }

        if (ValidUtil.isNotEmpty(form.getBegin()) && ValidUtil.isNotEmpty(form.getEnd())) {
            Query timeQuery = LongPoint.newRangeQuery("time", form.getBegin().getTime(), form.getEnd().getTime());
            builder.add(timeQuery, BooleanClause.Occur.MUST);
        }

        Sort sort = new Sort(new SortField("sortTime", SortField.Type.LONG, false));
        // 执行查询
        return searcher.search(builder.build(), form.getSize(), sort);
    });
    for (Document document : documentList) {
        Data data = new Data();
        data.setTypeId(Integer.valueOf(document.get("typeId")));
        data.setDId(Integer.valueOf(document.get("dId")));
        data.setTime(document.getField("time").numericValue().longValue());
        data.setValue(document.get("value"));
        data.setUnit(document.get("unit"));
        result.add(data);
    }
    return result;
}

文章来源地址https://www.toymoban.com/news/detail-695980.html

到了这里,关于springboot 集成 lucene的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 通过 Lucene.Net 支持的 .NET 索引和搜索引擎的高效使用与探索:Examine 的简单索引与搜索数据应用以及其可扩展性分析

    在当前的技术环境中,搜索和索引数据变得越来越重要,尤其是在处理大量数据时。这就使得我们需要一种能够快速、精确、高效地索引和搜索数据的工具。在本文中,我们将深入探讨一种用于 .NET 的索引和搜索引擎——Examine,这是一个封装了 Lucene.Net 的库,它能使我们更方

    2024年02月16日
    浏览(52)
  • 如何高效实现搜索引擎爬虫进行数据挖掘-搜索引擎爬虫(SERP)集成测试与分享

    身处大数据时代中,我们面对海量的互联网数据,如何自动高效地获取感兴趣的信息并为我们所用是一个非常重要的问题,以下就针对这个重要的搜索引擎爬虫问题来做一个技术分享。 什么是SERP和搜索引擎爬虫:搜索引擎会根据特定的的策略,运用特定的计算机程序搜集互

    2024年02月11日
    浏览(58)
  • 搜索引擎ElasticSearch分布式搜索和分析引擎学习,SpringBoot整合ES个人心得

    Elasticsearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。Elasticsearch是用Java语言开发的,并作为Apache许可条款下的开放源码发布,是一种流行的企业级搜索引擎。Elasticsearch用于云计算中,能够达到实时搜索,稳定,可靠,

    2024年02月04日
    浏览(72)
  • SpringBoot 使用 Elasticsearch 搜索引擎

    作者:禅与计算机程序设计艺术 Spring Boot 是由 Pivotal 团队提供的一套用于开发基于 Spring 框架的应用的工具包。其主要目标是通过提供简单易用的starter包来简化开发流程。Spring Boot 极大的地方在于其依赖自动配置,可以很好的满足开发人员的开发需求。Spring Boot 提供了数据访

    2024年02月09日
    浏览(49)
  • springboot整合MeiliSearch轻量级搜索引擎

    一、Meilisearch与Easy Search点击进入官网了解,本文主要从小微型公司业务出发,选择meilisearch来作为项目的全文搜索引擎,还可以当成来mongodb来使用。 二、starter封装 1、项目结构展示 2、引入依赖包(我是有包统一管理的fastjson用的1.2.83,gson用的2.8.6) 3、yml参数读取代码参考

    2024年02月08日
    浏览(55)
  • SpringBoot封装Elasticsearch搜索引擎实现全文检索

    注:本文实现了Java对Elasticseach的分页检索/不分页检索的封装 ES就不用过多介绍了,直接上代码: 创建Store类(与ES字段对应,用于接收ES数据) Elasticsearch全文检索接口:不分页检索 Elasticsearch全文检索接口:分页检索 本文实现了Java对Elasticsearch搜索引擎全文检索的封装 传入

    2024年02月04日
    浏览(45)
  • 《Spring Boot 实战派》--13.集成NoSQL数据库,实现Elasticsearch和Solr搜索引擎

             关于搜索引擎 我们很难实现 Elasticseach 和 Solr两大搜索框架的效果;所以本章针对两大搜索框架,非常详细地讲解 它们的原理和具体使用方法, 首先 介绍什么是搜索引擎 、如何用 MySQL实现简单的搜索引擎,以及Elasticseach 的 概念和接口类; 然后介绍Elasticseach

    2023年04月09日
    浏览(91)
  • SpringBoot 基于向量搜索引擎及虹软人脸识别SDK的大规模人脸搜索

    SpringBoot 基于向量搜索引擎及虹软人脸识别SDK的大规模向量数据搜索 在线环境demo 为了方便大家测试效果,开放了一个在线环境供大家测试并降低了识别门槛和难度,使得照片也可以通过筛选,大家使用前无比观看视频,按照视频方式操作。由于服务器昂贵,资源有限,生产

    2023年04月12日
    浏览(38)
  • ES数据存储搜索引擎入门到整合Springboot一章直达

    前言 学习一门语言,我们从熟悉其语法开始,慢慢深入动手实践,并开始将其使用到对应的场景上,当我们遇到相应的问题,能够联想到使用该技术,并能够信手拈来的时候,才是我们真正掌握了一门技术或者语言的时候。学习的时候可以和其他学过的知识点相关联,如ES可

    2024年02月02日
    浏览(43)
  • 用SpringBoot和ElasticSearch实现网盘搜索引擎,附源码,详细教学

    可以扫描小程序码体验,切换到搜索Tabbar。 小程序端界面实现 网页端实现界面 对外提供的api 接口声明 接口实现 执行搜索策略。 提供2种搜索策略,分别是MySQL和ElasticSearch搜索策略。在配置文件进行配置搜索策略。 搜索类型枚举 配置文件中的搜索策略相关配置 es搜索策略实

    2024年02月08日
    浏览(42)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包