目录
阳光沙滩博客系统-文章搜索模块

课程资料包

请移驾到资料下载

solr搜索引擎服务器

官网

Solr is the popular, blazing-fast, open source enterprise search platform built on Apache Lucene™.

solr 安装

熟悉一下

docker-compose.yml

yml
复制代码
version: '3.1'
services:
  solr:
    image: solr:5.5.5
    restart: always
    container_name: solr_test
    ports:
      - 8983:8983

Dockerfile构建属于自己的Solr镜像

Dockfile

shell
复制代码
# 基于哪个镜像,如果不写版本则是最新的
# 不同版本的话要注意一下路径,下面的路径
FROM solr:5.5.5
# 作者
MAINTAINER sunofbeach.net
# 创建 Core,切换目录
WORKDIR /opt/solr/server/solr
# 创建目录
RUN mkdir sob_blog_core
# 切换目录
WORKDIR /opt/solr/server/solr/sob_blog_core
# 把这个Core名称添加到配置文件中,这样在前端就可以看到了
RUN echo 'name=sob_blog_core' > core.properties
# 从模版中复制一份出来配置文件出来
RUN cp -r /opt/solr/server/solr/configsets/sample_techproducts_configs/conf/ .
# 进入到容器的lib文件夹
WORKDIR /opt/solr/server/solr-webapp/webapp/WEB-INF/lib
# 中文分词器
RUN cp -r /opt/solr/contrib/analysis-extras/lucene-libs/lucene-analyzers-smartcn-5.5.5.jar .
# 复制两个jar包进去
ADD ik-analyzer-solr5-5.x.jar .
ADD solr-analyzer-ik-5.1.0.jar .
WORKDIR /opt/solr/server/solr-webapp/webapp/WEB-INF
# 复制字典,同学们可以自行修改字典内容
ADD ext.dic .
ADD stopword.dic .
# 配置扩展字典的配置文件
ADD IKAnalyzer.cfg.xml .
# 增加分词配置
COPY managed-schema /opt/solr/server/solr/sob_blog_core/conf
WORKDIR /opt/solr

在当文件夹下构建,相关的文件要复制到位,文件在课程压缩包里

shell
复制代码
docker build -t sob_blog_solr:1.0 .

等待构建完成,或者直接使用docker-compose构建也行

shell
复制代码
version: '3.1'
services:
  solr:
    build: solrWithIKAnalyzer
    restart: always
    container_name: sob_blog_solr
    ports:
      - 8983:8983

创建完成以后访问:

ip:8983

图片描述

这个就是操作界面了

项目链接solr

添加依赖

xml
复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-solr</artifactId>
</dependency>

添加链接配置项

yml
复制代码
spring:
 	 data:
        solr:
          host: http://192.168.220.141:8983/solr/sob_blog_core

数据的增删改

注入SolrClient

java
复制代码
@Autowired
private SolrClient client;

然后添加字段内容,这些字段就是我们在约束文件里配置的

xml
复制代码
<!--自定义字段,类似于设计数据表一样-->
<!--ID-->
<!--ID 已经在前面有了-->
<!--浏览量-->
<field name="blog_view_count" type="int" indexed="true" stored="true" required="true" multiValued="false" />
<!--标题-->
<field name="blog_title" type="text_cn" indexed="true" stored="true" required="true" multiValued="false" />
<!--内容-->
<field name="blog_content" type="text_cn" indexed="true" stored="true" required="true" multiValued="false" />
<!--创建时间-->
<field name="blog_create_time" type="date" indexed="true" stored="true" required="true" multiValued="false" />
<!--标签-->
<field name="blog_labels" type="string" indexed="true" stored="true" required="true" multiValued="false" />
<!--文章链接-->
<field name="blog_url" type="string" indexed="true" stored="true" required="true" multiValued="false" />
<!--分类ID-->
<field name="blog_category_id" type="string" indexed="true" stored="true" required="true" multiValued="false" />
<!--搜索item-->
<field name="search_item" type="text_cn" indexed="true" stored="true" required="true" multiValued="true" />

searchItem不需要添加,自动复制的,因为我们有以下配置

xml
复制代码
<copyField source="blog_title" dest="search_item"/>
<copyField source="blog_content" dest="search_item"/>
<copyField source="blog_labels" dest="search_item"/>

添加

代码编写:

创建SolrInputDocument

java
复制代码
SolrInputDocument doc = new SolrInputDocument();

封装字段数据

java
复制代码
doc.addField("id", id);
doc.addField("blog_view_count", 标题内容);
doc.addField("blog_title", 标题内容);
doc.addField("blog_content", 搜索内容);
doc.addField("blog_create_time", 创建时间);
doc.addField("blog_labels", 标签);
doc.addField("blog_url", 文章url);

提交

java
复制代码
try {
    client.add(doc);
    client.commit();
} catch (Exception e) {
    e.printStackTrace();
}

删除

前面我们设置了ID,ID是主键,所以我们可以通过ID来删除

java
复制代码
try {
    client.deleteById(articleId);
    client.commit();
} catch (Exception e) {
    e.printStackTrace();
}

更新

更新跟添加一样,以id为key,修改对应ID的内容,注意不可以为空的字段.

处理数据标签问题

富文本我们需要去掉Html标签,markdown我们需要转成html,再去掉里面的标签,提取纯文本内容。

markdown转成html

xml
复制代码
<!--markdown to html-->
<dependency>
    <groupId>com.vladsch.flexmark</groupId>
    <artifactId>flexmark-all</artifactId>
    <version>0.50.44</version>
</dependency>

代码

java
复制代码
// markdown to html
MutableDataSet options = new MutableDataSet().set(Parser.EXTENSIONS, Arrays.asList(
    TablesExtension.create(),
    JekyllTagExtension.create(),
    TocExtension.create(),
    SimTocExtension.create()
));
Parser parser = Parser.builder(options).build();
HtmlRenderer renderer = HtmlRenderer.builder(options).build();

Node document = parser.parse(内容);
String html = renderer.render(document);

html转text

xml
复制代码
<dependency>
    <groupId>org.jsoup</groupId>
    <artifactId>jsoup</artifactId>
    <version>1.12.1</version>
</dependency>

html转文字

java
复制代码
Jsoup.parse(html内容).text()

查询

  • q:query查询
  • fq:filter query 过滤查询
  • sort:排序
  • start,rows:分页
  • fl:返回字段
  • dl:默认的查询参考字段
  • hl:高亮
java
复制代码

    @Override
    public ResponseResult doSearch(String keyword, int page, int size, String categoryId, Integer sort) {
        //1、检查page和size
        page = checkPage(page);
        size = checkSize(size);
        SolrQuery solrQuery = new SolrQuery();
        //2、分页设置
        //先设置每页的数量
        solrQuery.setRows(size);
        //设置开始的位置
        //找个规律
        //第1页 -- > 0
        //第2页 == > size
        //第3页 == > 2*size
        //第4页 == > 3*size
        //第n页 == > (n-1)*size
        int start = (page - 1) * size;
        solrQuery.setStart(start);
        //solrQuery.set("start", start);
        //3、设置搜索条件
        //关键字
        solrQuery.set("df", "search_item");
        //条件过滤
        if (TextUtils.isEmpty(keyword)) {
            solrQuery.set("q", "*");
        } else {
            solrQuery.set("q", keyword);
        }
        //排序
        //排序有四个:根据时间的升序(1)和降序(2),根据浏览量的升序(3)和降序(4)
        if (sort != null) {
            if (sort == 1) {
                solrQuery.setSort("blog_create_time", SolrQuery.ORDER.asc);
            } else if (sort == 2) {
                solrQuery.setSort("blog_create_time", SolrQuery.ORDER.desc);
            } else if (sort == 3) {
                solrQuery.setSort("blog_view_count", SolrQuery.ORDER.asc);
            } else if (sort == 4) {
                solrQuery.setSort("blog_view_count", SolrQuery.ORDER.desc);
            }
        }
        //分类
        if (!TextUtils.isEmpty(categoryId)) {
            solrQuery.setFilterQueries("blog_category_id:" + categoryId);
        }
        //关键字高亮
        solrQuery.setHighlight(true);
        solrQuery.addHighlightField("blog_title,blog_content");
        solrQuery.setHighlightSimplePre("<font color='red'>");
        solrQuery.setHighlightSimplePost("</font>");
        solrQuery.setHighlightFragsize(500);
        //设置返回字段
        //blog_content,blog_create_time,blog_labels,blog_url,blog_title,blog_view_count
        solrQuery.addField("id,blog_content,blog_create_time,blog_labels,blog_url,blog_title,blog_view_count");
        //
        //4、搜索
        try {
            //4.1、处理搜索结果
            QueryResponse result = solrClient.query(solrQuery);
            //获取到高亮内容
            Map<String, Map<String, List<String>>> highlighting = result.getHighlighting();
            //把数据转成bean类
            List<SearchResult> resultList = result.getBeans(SearchResult.class);
            //结果列表
            for (SearchResult item : resultList) {
                Map<String, List<String>> stringListMap = highlighting.get(item.getId());
                List<String> blogContent = stringListMap.get("blog_content");
                if (blogContent != null) {
                    item.setBlogContent(blogContent.get(0));
                }
                List<String> blogTitle = stringListMap.get("blog_title");
                if (blogTitle != null) {
                    item.setBlogTitle(blogTitle.get(0));
                }
            }
            //5、返回搜索结果
            //包含内容
            //列表、页面、每页数量
            long numFound = result.getResults().getNumFound();
            PageList<SearchResult> pageList = new PageList<>(page, numFound, size);
            pageList.setContents(resultList);
            //返回结果
            return ResponseResult.SUCCESS("搜索成功.").setData(pageList);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return ResponseResult.FAILED("搜索失败,请稍后重试.");
    }
本文由 拉大锯 原创发布于 阳光沙滩 , 未经作者授权,禁止转载
评论
0 / 1024
断点
更新太快
2020-07-08 11:21回复
回复@断点最近开始忙了,应该没这么快了。今天有朋友求婚,一天没工作了。
2020-07-08 15:52回复
回复@拉大锯我还以为有人向你求婚~
2020-07-09 06:07回复
koppok
给你点赞
2020-07-06 09:56回复
推荐文章
Linux 命令行美化利器:tree 命令完全指南(安装 + 全部参数详解)
掌握 Linux 下的 `tree` 命令,快速直观地查看目录结构。本文详细讲解安装方法、参数含义及实战场景,适合开发者和系统管理员提升工作效率。
Linux常用命令(一)
本文详细介绍了Linux基础命令,包括文件操作、系统显示、网络状态、软件包管理等,适合初学者学习和查阅,是掌握Linux系统的实用指南。
大模型推理参数解码:温度、Top-p 与核采样如何塑造生成文本的灵魂
探索大语言模型的采样参数如何塑造生成内容,从温度到Top-p,从惩罚机制到熵控制,揭示背后的信息论原理与实践策略。了解如何通过参数调优,让AI既保持知识的严谨,又拥有创造力的自由。
记从0使用Claude code写代码
本文介绍了如何使用Claude Code搭配DeepSeek进行编程,详细讲解了安装Node.js、全局安装Claude、配置模型以及使用方法。对于开发者来说,这是一份实用的技术教程,尤其适合对AI编程工具感兴趣的读者。
Android-Studio-Gradle同步失败-Library为null的通用排查指南
遇到 Android Studio Gradle Sync 失败时,不要轻易删除全局缓存。本文详细解析了错误原因,并提供了一系列精准的排查步骤和解决方案,帮助开发者快速定位并解决问题,提升开发效率。
PHP实现密码加密
本文详细介绍了Bcrypt密码加密技术及其在PHP中的应用,帮助开发者提升用户密码的安全性。通过实际代码示例,展示了如何使用password_hash和password_verify函数进行密码加密与验证,是学习网络安全知识的实用指南。
WordCloud效果,滚动标签
本文详细介绍了如何在Vue项目中集成WordCloud组件,包括依赖安装、属性配置和使用方法。适合开发者学习如何实现数据可视化功能,提升项目交互体验。
从0使用WordPress搭建一个优美的网站
本文详细介绍了如何使用WordPress搭建一个美观的网站,从安装到主题配置和功能拓展,为读者提供了实用的操作指南。无论是初学者还是有一定经验的开发者,都能从中获得有价值的参考。
JS实现在网站底部添加运行时间
想知道如何在网站底部显示运行时间?本文详细讲解了通过JavaScript实现这一功能的方法,包括时间计算逻辑和代码实现。适合前端开发者学习参考,轻松为网站添加实用功能。
在Vercel上部署Hexo博客
本文详细介绍了如何在Vercel上快速部署Hexo博客,无需后端服务即可实现高效发布。相比传统方式,省去了手动生成和上传静态文件的步骤,更加便捷。适合想要搭建个人博客的开发者参考。
从0搭建一个Hexo博客
本文详细介绍了Hexo博客框架的使用方法,从安装到部署全流程讲解,适合想快速搭建个人博客的技术爱好者。内容清晰易懂,是入门Hexo的理想指南。
离线设备激活方案:古老的 Windows 光盘激活,离线算法授权等
本文深入解析了离线激活的核心原理与实现方式,从历史案例到现代技术,全面剖析了如何在无网络环境下确保软件授权的安全性。通过设备指纹、授权文件校验等手段,为开发者提供了可复用的架构设计思路,适用于工业、医疗等对网络依赖较低的场景。
Hexo实现生成站点地图
想为你的Hexo博客添加站点地图功能吗?本文详细介绍了如何通过安装插件和配置文件来实现,适合没有内置该功能的新主题或自定义主题的用户。简单步骤助你提升搜索引擎优化效果。
Hexo实现代码压缩
本文分享了如何通过Hexo插件优化博客性能,详细介绍了安装和配置过程,帮助提升网页加载速度。适合对网站优化感兴趣的开发者阅读。
记一次 GitHub 幽灵协作者大清洗:强制重写 Git 历史与穿透 CDN 缓存实践
本文详细讲解了如何解决GitHub上出现的‘幽灵协作者’问题,通过重写Git历史和穿透CDN缓存,彻底清理错误提交记录。适合开发者学习如何高效管理项目历史与优化仓库信息。
从一行 `native` 堆栈追到 InstallReferrer:一次主线程 ANR 的排查全过程
本文详细记录了一次主线程ANR的排查过程,从一行native堆栈追踪到InstallReferrer服务调用。通过分析堆栈、源码和系统调用,揭示了归因SDK在主线程同步调用Play商店服务导致的ANR问题,并提供了多维度的解决方案。对于Android开发者来说,是一篇深入浅出的技术实践指南。
Linux从 HelloWorld 到数据库服务注册
本文详细解析了Linux系统中服务注册的概念与实现,通过一个简单的Hello World示例,帮助读者理解如何将程序注册为systemd服务,并掌握相关操作命令。无论是数据库还是其他应用,了解服务注册机制都是系统管理的重要基础。
学习虚拟机的笔记
linux ps 命令详解,跟着敲一次就掌握了
深入了解 Linux 中最常用的进程查看命令 `ps`,掌握其各种用法和参数,适用于系统管理和故障排查。从基础到高级,全面解析 `ps` 的使用技巧,帮助您提升 Linux 运维技能。
ObjectMapper 入门:Java 对象与 JSON 之间的「翻译官」
了解ObjectMapper在Java中如何实现对象与JSON的转换,掌握其在Spring Boot项目中的应用及常见使用场景。本文详细解析了序列化/反序列化过程、API用法、与Spring MVC的关系以及与其他JSON库的对比,适合开发者快速上手和深入理解。
服务器一次中病毒的记录
本文详细描述了一次服务器异常流量的排查过程,发现大量外部IP与内部服务建立连接,疑似存在恶意程序。通过分析日志和图片,确认为恶意程序导致带宽占用过高,最终通过备份和删除操作解决问题。文章提供了技术排查思路和解决方案,对系统维护具有参考价值。
JavaWeb微服务脚手架搭建
本文介绍了构建微服务架构时常用的开发模板和核心组件,涵盖技术选型、依赖配置及版本差异分析。通过合理选择 Java 和 Spring Boot 版本,可以显著提升开发效率和系统性能,是开发者不可错过的实践指南。
面向 Java 程序员的 MinIO 入门教程
本文为Java程序员提供了一份详细的MinIO入门教程,涵盖MinIO的部署方法和Java SDK的集成使用。通过本文,您将学习如何在Java项目中高效管理桶和对象,快速上手MinIO这一高性能对象存储服务。
你知道:气和汽的区别吗?
了解‘气’和‘汽’的区别,掌握它们在不同语境下的含义与用法,帮助你更准确地使用中文。无论是日常交流还是写作,这对提升语言能力都大有裨益。
wsl update 下载不下来怎么办呀?
遇到Docker Desktop提示需要更新但无法解决?本文教你如何通过GitHub下载并安装WSL,轻松解决更新问题,适合使用x64芯片的用户。
今日经验:重置虚拟机的密码
本文详细记录了在KVM虚拟化环境中,如何通过virt-rescue工具重置遗忘的root密码。对于需要维护和管理虚拟机的IT人员来说,这是一份实用的排障指南,涵盖了从环境准备到密码修改的完整流程,帮助快速恢复系统访问权限。
今日工作:Android Health Connect 接入记录
本文详细讲解了如何将 Android Health Connect 接入到健康或运动类应用中,涵盖从配置、代码实现到测试验收的完整流程。适合希望统一健康数据管理、提升用户隐私合规性的开发者阅读。
Skill从入门到出家
探索AI Agent的核心能力——Skill,了解其模块化设计、渐进式披露机制和实际应用场景。从基础概念到高级实战,掌握如何构建可复用、可移植的AI技能,提升Agent处理复杂任务的能力。
Docker,Docker Compose,kubectl最近遇到的版本问题
本文分享了在使用Docker、Docker Compose和kubectl时遇到的版本问题及解决方法,适合需要更新或管理Linux系统中相关工具的开发者参考。
Google上架App退回
Google Play Console 抛出 16KB 内存页面大小合规性错误,导致应用无法上架。本文详细分析了错误原因,并提供了解决方案,帮助开发者适配 Android 15 的新要求。