您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

开发人员如何快速定制化实现一个基于Solr的搜索引擎

时间:2021-04-02 13:52:59  来源:搜狐网  作者:攻城狮Chova

Solr实现全文搜索

  • Solr
  • Apache Solr特点
  • 搜索引擎
  • 搜索引擎组件
  • 搜索引擎工作流程
  • 分词技术
  • 中文分词算法
  • 基于字符串匹配
  • 基于统计及机器学习的分词方式
  • IKAnalyzer
  • 部署Solr并安装IKAnalyzer
  • Solr分析功能
  • 修改managed-schema配置业务系统字段
  • 复制配置到容器
  • 重启容器
  • SpringBoot整合Solr
  • 创建搜索服务接口
  • 创建搜索服务提供者
  • 创建搜索服务消费者

 

Solr

  • Solr是一个可扩展的,可部署,搜索,存储引擎,优化搜索大量以文本为中心的数据库Solr是开源搜索平台,用于构建搜索应用程序建立在Lucene(全文搜索引擎)之上Solr是企业级的,快速的和高度可扩展的,使用Solr构建的应用程序可以提供高性能,但是非常复杂Solr可以和Hadoop一起使用:由于Hadoop处理大量数据,Solr可以从大的数据源中找到所需信息.Solr不仅限于搜索,也可以用于存储.和其它NoSQL数据库一样,是一种非关系数据存储和处理技术

Apache Solr特点

Solr是Lucene的JAVA API包装,使用Solr,就可以使用Lucene的所有功能

  • RESTful API: 要与Solr通信,可以使用RESTful服务与Solr通信,可以使用XML,JSON,CSV等格式的文件作为输入文档,并以相同的文件格式获取结果
  • 全文搜索: Solr提供了全文搜索所需的所有功能:令牌,短语,拼写检查,通配符,自动完成
  • 企业准备: 根据企业或组织的需要,Solr可以部署在任何类型的系统:独立,分布式,云
  • 灵活可扩展: 通过扩展Java类并进行相关配置,可以定制Solr组件
  • NoSQL数据库: Solr可以用作大数量级的NoSQL数据库,可以沿着集群分布搜索任务

搜索引擎

  • 搜索引擎:搜索引擎是庞大的互联网资源数据库,如网页,新闻组,程序,图像等有助于在网上定位信息用户可以通过以关键字或短语的形式将查询传递到搜索引擎中来搜索信息,然后搜索引擎搜索其数据库并向用户返回相关链接

搜索引擎组件

搜索引擎有三个组件:

  • Web爬虫: 一个收集网络信息的软件组件
  • 数据库: Web上的所有信息都存储在数据库中,包含大量的Web资源
  • 搜索接口: 这个组件是用户和数据库之间的接口,帮助用户搜索数据库

搜索引擎工作流程

  • 获取原始内容: 任何搜索应用程序的第一步是收集要进行搜索的目标内容
  • 构建文档: 从原始内容构建文档,让搜索应用程序可以很容易的理解和解释
  • 分析文档: 在索引开始之前,将对文档进行分析
  • 索引文档: 当文档被构建和分析后,下一步是对文档建立索引,以便可以基于特定键而不是文档的全部内容来检索该文档.索引类似于在书开始页或末尾处的目录索引,其中常见单词以页码显示,使得这些单词可以快速追踪,而不是搜索整本书
  • 用于搜索的用户接口: 当索引数据库就绪,应用程序就可以执行搜索操作.为了帮助用户进行搜索,应用必须提供用户接口,用户可以在用户接口中输入文本并启动搜索过程
  • 构建查询: 当用户做出搜索文本的请求,应用程序应该使用该文本准备查询对象,然后可以使该查询对象来查询索引数据库以获得相关细节
  • 搜索查询: 使用查询对象,检查索引数据库以获取相关详细信息和内容文档
  • 渲染结果: 当收到所需结果,应用程序应决定如何使用用户界面向用户显示搜索结果

分词技术

  • 分词技术: 搜索引擎针对用户提交查询的关键词串进行的查询处理后,根据用户的关键词串用各种匹配方法进行分词的一种技术

 

中文分词算法

基于字符串匹配

  • 基于字符串匹配:即扫描字符串,如果发现字符串的子串和词相同,就算匹配这类分词通常会加入一些启发式规则:正向/反向最大匹配,长词优先
  • 基于字符串匹配算法优点:速度快都是O(n)时间复杂度实现简单效果尚可
  • 基于字符串匹配算法缺点:对歧义和未登录词处理不好
  • ikanalyzer,paoding等就是基于字符串匹配的分词

基于统计及机器学习的分词方式

  • 基于统计及机器学习的分词方式:基于人工标注的词性和统计特征,对中文进行建模. 即根据观测到的数据(标注好的语料)对模型参数进行估计.即 训练在分词阶段再通过模型计算各种分词出现的概率,将概率最大的分词结果作为最终结果常见的序列标注模型:HMM,CRF
  • 基于统计及机器学习的分词方式优点:可以很好地处理歧义和未登录问题效果比基于字符串匹配算法更好
  • 基于统计及机器学习的分词方式缺点:需要大量的人工标注数据较慢的分词速度

IKAnalyzer

  • IKAnalyzer是一个开源的,基于Java语言开发的轻量级中文分词工具包
  • 基于文本匹配,不需要投入大量的人力进行训练和标注
  • 可以自定词典,方便加入特定领域的词语,能分出多粒度的结果

部署Solr并安装IKAnalyzer

  • 创建/usr/local/Docker/solr/ikanalyzer目录

/usr/local/docker/solr 用于存放docker-compose.yml配置文件

/usr/local/docker/solr/ikanalyzer 用于存放Dockerfile镜像配置文件

  • docker-compose.yml

version: '3.1'

services:

solr:

build: ikanalyzer

restart: always

container_name: solr

ports:

- 8983:8983

volumes:

- ./solrdata:/opt/solrdata

  • Dockerfile(在/usr/local/docker/solr/ikanalyzer中需要有文件:ik-analyzer-solr5-5.x.jar,solr-analyzer-ik-5.1.0.jar,ext.dic,stopword.dic,IKAnalyzer.cfg.xml,managed-schema)

FROM solr

# 创建Core

WORKDIR /opt/solr/server/solr

RUN mkdir ik_core

WORKDIR /opt/solr/server/solr/ik_core

RUN echo 'name=ik_core' > core.properties

RUN mkdir data

RUN cp -r ../configsets/sample_techproducts_configs/conf/ .

# 安装中文分词

WORKDIR /opt/solr/server/solr-webApp/webapp/WEB-INF/lib

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/ik_core/conf

WORKDIR /opt/solr

  • 构建镜像: 在/usr/local/docker/solr中执行命令

docker-compose up -d

 

Solr分析功能

修改managed-schema配置业务系统字段

  • Solr中自带的相同字段无需再添加,其它字段需要手动添加Solr字段(通过编辑managed-schema配置文件来手动添加Solr字段)

<!-- 字段域 -->

<field name="tb_item_cid" type="plong" indexed="true" stored="true" />

<field name="tb_item_cname" type="text_ik" indexed="true" stored="true" />

<field name="tb_item_title" type="text_ik" indexed="true" stored="true" />

<field name="tb_item_sell_point" type="text_ik" indexed="true" stored="true" />

<field name="tb_item_desc" type="text_ik" indexed="true" stored="true" />

<!-- 复制域:Solr的搜索优化功能,,将多个字段复制到一个域,提高查询效率 -->

<field name="tb_item_keywords" type="text_ik" indexed="true" stored="false" multiValued="true" />

<copyField source="tb_item_cname" dest="tb_item_keywords">

<copyField source="tb_item_title" dest="tb_item_keywords">

<copyField source="tb_item_sell_point" dest="tb_item_keywords">

<copyField source="tb_item_desc" dest="tb_item_keywords">

复制配置到容器

docker cp managed-schema solr:/opt/solr/server/solr/ik_core/conf

重启容器

docker-compose restart

  • 在Solr的Web界面可以进行CRUD操作

SpringBoot整合Solr

创建搜索服务接口

  • 创建myshop-service-search-api项目,该项目只负责定义定义接口
  • pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">

<modelVersion>4.0.0</modelVersion>

<parent>

<groupId>com.funtl</groupId>

<artifactId>myshop-dependencies</artifactId>

<version>1.0.0-SNAPSHOT</version>

<relativePath>../myshop-dependencies/pom.xml</relativePath>

</parent>

<artifactId>myshop-service-search-api</artifacteId>

<packaging>jar<packaging>

</project>

  • 在项目中创建SearchService接口

package com.oxford.myshop.service.search.api;

public interface SearchService {

List<TbItemResult> search(String query,int page,int rows);

}

  • 创建TbItemResult用于返回Solr结果集

package com.oxford.myshop.service.search.domain;

import java.io.Serializable;

public class TbItemResult implements Serializable {

private long id;

private long tbTtemCid;

private String tbItemCname;

private String tbItemTitle;

private String tbItemSellPoint;

private String tbItemDesc;

public long getId(){

return id;

}

public void setId(long id){

this.id=id;

}

public long getTbTtemCid(){

return tbTtemCid;

}

public void setTbTtemCid(long tbTtemCid){

this.tbTtemCid=tbTtemCid;

}

public String getTbItemCname(){

return tbItemCname;

}

public void setTbItemCname(String tbItemCname){

this.tbItemCname=tbItemCname;

}

public String getTbItemTitle(){

return tbItemTitle;

}

public void setTbItemTitle(String tbItemTitle){

this.tbItemTitle=tbItemTitle;

}

public String getTbItemSellPoint(){

return tbItemSellPoint;

}

public void setTbItemSellPoint(String tbItemSellPoint){

this.tbItemSellPoint=tbItemSellPoint;

}

public String getTbItemDesc(){

return tbItemDesc;

}

public void setTbItemDesc(String tbItemDesc){

this.tbItemDesc=tbItemDesc;

}

}

创建搜索服务提供者

  • 创建myshop-service-search-provider服务提供者项目
  • MyShopServiceSearchProviderApplication

package com.oxford.myshop.service.search.provider;

@EnableHystrix

@EnableHystrixDashboard

@SpringBootApplication(scanBasePackages="com.oxfrod.myshop")

@MapperScan(basePackages="com.oxford.myshop.service.search.provider.mapper")

public class MyShopServiceSearchProviderApplication {

public static void main(String[] args) {

SpringApplication.run(MyShopServiceSearchProviderApplication.class,args);

Main.main(args);

}

}

  • 在项目中创建TbItemResultMapper接口用于查询MySQL中的数据,用于插入到Solr数据库中

package com.oxford.myshop.service.search.provider.mapper;

@Respository

public interface TbItemResultMapper {

List<TbItemResult> selectAll();

}

Spring的四大注解:

1. @Controller

2. @Service

3. @Component

4. @Repository

  • 在resource中创建mapper包用于创建TbContentCategoryMapper.xml

<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.oxford.myshop.service.search.provider.mapper.TbItemResultMapper">

<resultMap id="BaseResultMap" type="com.oxford.myshop.service.search.domainTbItemResult">

<id column="id" jdbcType="BIGINT" property="id" />

<result column="tb_item_cid" jdbcType="BIGINT" property="tbItemCid" />

<result column="tb_item_cname" jdbcType="VARCHAR" property="tbItemCname" />

<result column="tb_item_title" jdbcType="VARCHAR" property="tbItemTitle" />

<result column="tb_item_sell_point" jdbcType="VARCHAR" property="tbItemSellPoint" />

<result column="tb_item_desc" jdbcType="VARCHAR" property="tbItemDesc" />

</reslutMap>

<select id="selectAll" resultMap="BaseResultMap">

select

a.id,

a.title as tb_item_title,

a.sell_point as tb_item_sell_point,

a.cid as tb_item_cid,

b.name as tb_item_cname,

c.item_desc as tb_item_desc

from

tb_item as a

left join tb_item_cat as b

on a.cid=b.id

left join tb_item_desc as c

on a.id=c.item_id

</select>

</mapper>

初始化Solr:

public void initSolr() {

List<TbItemResult> tbItemResult=tbItemResultMapper.selectAll();

try{

SolrInputDocument document=null;

for(TbItemResult tbItemResult:tbItemResults){

document=new SolrInputDocument();

document.addFiled("id",tbItemResult.getId());

document.addFiled("tb_item_cid",tbItemResult.getTbItemCid());

document.addFiled("tb_item_cname",tbItemResult.getTbItemCname());

document.addFiled("tb_item_title",tbItemResult.getTbItemTitle());

document.addFiled("tb_item_sell_point",tbItemResult.getTbItemSellPoint());

document.addFiled("tb_item_desc",tbItemResult.getTbItemDesc());

solrClient.add(document);

solrClient.commit();

}

}catch(SolrServerException e){

e.printStackTrace();

}catch(IOException e){

e.printStackTrace();

}

}

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

搜索Solr:

public void searchSolr(){

SolrQuery query=new SolrQuery();

// 设置查询条件

query.setQuery("手机");

// 分页查询

query.setStart(0);

query.setRows(10);

// 设置查询的默认域

query.set("df","tb_item_keywords");

// 设置高亮显示

query.setHighlight(true);

query.addHighlightField("tb_item_title");

query.setHighlightSimplePre("<span style='color:red;'>");

query.setHighlightSimplePost("</span>");

// 开始查询

try{

QueryResponse queryResponse=solrClient.query(query);

SolrDocumentList results=queryResponse.getResults();

// 获取高亮

Map<String,Map<String,List<String>>> highlighting=queryResponse.getHighlighting();

for(SolrDocument result:results){

List<String> strings=highlighting.get(result.get("id")).get(result.get("tb_item_title"))

if(strings!=null&&strings.size()>0){

String title=strings.get(0);

System.out.println(title);

}

}

}catch(SolrServerException e){

e.printStackTrace();

}catch(IOException e){

e.printStackTrace();

}

}

  • 创建SearchServiceImpl实现SearchService接口

package com.oxford.myshop.service.search.provider.api.impl;

@Service(version="${services.versions.search.v1}")

public class SearchServiceImpl implements SearchService{

@Autowired

private SolrClient solrClient;

@Override

public List<TbItemResult> search(String query,int page,int rows){

List<TbItemResult> searchResults=Lists.newArrayList();

SolrQuery query=new SolrQuery();

// 设置查询条件

query.setQuery("手机");

// 分页查询

query.setStart((page-1)*rows);

query.setRows(rows);

// 设置查询的默认域

query.set("df","tb_item_keywords");

// 设置高亮显示

query.setHighlight(true);

query.addHighlightField("tb_item_title");

query.setHighlightSimplePre("<span style='color:red;'>");

query.setHighlightSimplePost("</span>");

// 开始查询

try{

QueryResponse queryResponse=solrClient.query(query);

SolrDocumentList results=queryResponse.getResults();

// 获取高亮

Map<String,Map<String,List<String>>> highlighting=queryResponse.getHighlighting();

for(SolrDocument solrDocument:solrDocuments){

TbItemResult result=new TbResult();

result.setId(Long.parseLong(String.valueOf(solrDocument.get("id"))));

result.setTbItemCid(Long.parseLong(String.valueOf(solrDocument.get("tb_item_cid"))));

result.setTbItemCname((String)solrDocument.get("tb_item_cname"));

result.setTbItemTitle((String)solrDocument.get("tb_item_title"));

result.setTbItemSellPoint((String)solrDocument.get("tb_item_sell_point"));

result.setTbItemDesc((String)solrDocument.get("tb_item_desc"));

String tbItemTitle="";

List<String> list=highlighting.get(result.get("id")).get(result.get("tb_item_title"))

if(list!=null&&lsit.size()>0){

String title=list.get(0);

}else{

tbItemTitle=(String)solrDocument.get("tb_item_title");

}

result.setTbItemTitle(tbItemTitle);

searchResults.add(result);

}

}catch(SolrServerException e){

e.printStackTrace();

}catch(IOException e){

e.printStackTrace();

}

return searchResults;

}

}

创建搜索服务消费者

  • 创建搜索服务消费者myshop-service-search-consumer对Solr数据库中的数据进行检索
  • MyShopServiceSearchConsumerApplication

package com.oxford.myshop.service.search.consumer;

@EnableHystrix

@EnableHystrixDashboard

@SpringBootApplication(scanBasePackages="com.oxford.myshop",exclude=DataSourceAutoConfiguration.class)

public class MyShopServiceSearchConsumerApplication{

public static void main(String[] args){

SpringApplication.run(MyShopServiceSearchConsumerApplication.class,args);

Main.main(args);

}

}

  • SearchController

package com.oxford.myshop.service.search.consumer.controller;

@RestController

public class SearchController{

@Reference(version="${services.versions.search.v1}")

private SearchService searchService;

@RequestMapping(value="search/{query}/{page}/{rows}",method=RequestMethod.GET)

public List<TbItemResult> search(

@PathVariable(required=true) String query,

@PathVariable(required=true) int page,

@PathVariable(required=true) int rows

){

return searchService.search(query,page,rows)

}

}



Tags:搜索引擎   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
▌简易百科推荐
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(1)  评论:(0)  加入收藏
程序是如何被执行的&emsp;&emsp;程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(9)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(19)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(23)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(24)  评论:(0)  加入收藏
一个项目的大部分API,测试用例在参数和参数值等信息会有很多相似的地方。我们可以复制API,复制用例来快速生成,然后做细微调整既可以满足我们的测试需求1.复制API:在菜单发布单...【详细内容】
2021-12-14  AutoMeter    Tags:AutoMeter   点击:(20)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条