1 Solr的简介和原理
Solr是什么
Solr是全文检索的的框架,基于Apache Lucene(全文检索工具库)实现搜索。
为什么使用Solr
在海量数据下,对MySQL或Oracle进行模糊查询或条件查询的效率是很低的。既然使用关系型数据库进行搜索效率比较低,最直接的解决方案就是使用专用搜索工具进行搜索,从而提升搜索效率。
Solr的作用
- Solr:可扩展索引、搜索功能、高亮显示和文字解析功能。
- Solr本质就是一个Java web 项目,且内嵌了Jetty服务器,所以安装起来非常方便。客户端操作Solr的过程和平时我们所写项目一样,就是请求Solr中控制器,处理完数据后把结果响应给客户端。
正向索引和反向索引
- 正向索引:从文档内容到词组的过程。每次搜索的时候需要搜索所有文档,每个文档比较搜索条件和词组。
文档 | 词组 |
---|---|
I am a chinese | I,am,a,chinses |
- 反向索引:是正向索引的逆向。建立词组和文档的映射关系。通过找到词组就能找到文档内容。(和新华字典找字很像)
词组 | 文档 |
---|---|
I,am,a,chinses | I am a chinese |
Solr的搜索原理
- Solr能够提升检索效率的主要原因就是分词和索引(反向索引)。
- 分词:会对搜索条件/存储内容进行分词,分成日常所使用的词语。
- 索引:存储在Solr中内容会按照程序员的要求来是否建立索引。如果要求建立索引会把存储内容中关键字(分词)建立索引。
Solr的数据存储说明
- 在开发中需要把数据添加到Solr中进行初始化,每次修改完数据库中数据还需要同步Solr中的数据。
- Solr中数据存储是存储在Document对象中,对象中可以包含的属性和属性类型都定义在schema.xml中。如果需要自定义属性或自定义属性类型都需要修改schema.xml配置文件。从Solr5开始schema.xml更改名称为managed-schema(没有扩展名)。
2 Solr的安装
Solr是用java编写,因此需要先安装JDK
如没有JDK必须先安装JDK。
上传并解压
上传压缩包solr-8.2.0.tgz到/usr/local/tmp中。并进行解压
# cd /usr/local/tmp
# tar zxf solr-8.2.0.tgz
修改启动参数
# /usr/local/tmp/solr-8.2.0/bin
# vim solr.in.sh
启动Solr
Solr内嵌Jetty,直接启动即可。默认监听8983端口。
Solr默认不推荐root账户启动,如果是root账户启动需要添加-force参数。
# ./solr start -force
启动过程中出现的问题
问题1:找不到solr.log文件
解决方法:
- 在/usr/local/tmp/solr-8.2.0/server/logs/下新建此文件
问题2:local host name unknown
原因:主机名与hosts文件中对不上导致此问题
解决方法
- 查看当前主机名,执行命令 hostname
- 编辑hosts文件,执行命令 vi /etc/hosts,将原有的主机名改为当前主机名
可视化管理界面
在关闭防火墙的前提下,可以在windows的浏览器中访问Solr。
输入: http://192.168.244.10:8983 就可以访问Solr的可视化管理界面。其中左侧菜单,分别为:
- Dashboard:面板显示Solr的总体信息。
- Logging:日志
- Core Admin:Solr的核心。类似于数据的Database
- Java Perperties:所有Java相关属性。
- Thread Dump:线程相关信息。
- 如果有Core,将显示在此处。
3 IK中文分词器的安装
首先去https://search.maven.org/search?q=com.github.magese下载对应版本的ik-analyzer。
上传
上传ik-analyzer-8.2.0.jar到 /usr/local/solr/server/solr-webapp/webapp/WEB-INF/lib目录中。
修改配置文件
修改/usr/local/solr/server/solr/testcore/conf/managed-schema
# vim /usr/local/solr/server/solr/testcore/conf/managed-schema
添加下面内容
<field name="myfield" type="text_ik" indexed="true" stored="true" />
<fieldType name="text_ik" class="solr.TextField">
<analyzer type="index">
<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="false" conf="ik.conf"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
<analyzer type="query">
<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="true" conf="ik.conf"/>
<filter class="solr.LowerCaseFilterFactory"/>
</analyzer>
</fieldType>
重启
# cd /usr/local/solr/bin
# ./solr stop -all
# ./solr start -force
如果分词不成功需要在managed-schema中添加一下字段。
4 数据库导入插件
配置数据导入插件-dataimport
思路:要让solr支持dataimport插件,首先需要在solr的核心配置文件里面加载插件,因为dataimport插件需要从数据库里面获得数据,所以需要配置数据库信息。
导入相关的jar包
- 在solr的dist文件夹下找到如下jar包。放在webapp\WEB-INF\lib
- 连接数据需要mysql的驱动的支持,使用5.1.* 这个版本
在配置文件中增加配件的配置
需要修改solr的core里面的conf目录下的solrconfig.xml,增加插件的配置
<requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
<lst name="defaults">
<str name="config">data-config.xml</str>
</lst>
</requestHandler>
配置数据库连接配置文件
配置数据库连接的配置文件 data-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<dataConfig>
<dataSource type="JdbcDataSource"
driver="com.mysql.jdbc.Driver"
url="jdbc:mysql://192.168.244.10:3306/products"
user="root"
password="123456"/>
<document>
<entity name="product" query="SELECT pid,name,price from products">
<field column="pid" name="id"/>
<field column="name" name="name"/>
<field column="price" name="price"/>
</entity>
</document>
</dataConfig>
5 查询条件说明
6 使用solrj操作solr
导入依赖
<dependency>
<groupId>org.apache.solr</groupId>
<artifactId>solr-solrj</artifactId>
<version>8.2.0</version>
</dependency>
solrj操作solr
public class SolrJClient {
public static void main(String[] args) {
// add();
// delete();
query();
}
//查询
public static void query () {
HttpSolrClient client = null;
try {
//创建客户端
String url = "http://192.168.244.10:8983/solr/testcore";
client = new HttpSolrClient.Builder(url).build();
//创建查询对象
SolrQuery query = new SolrQuery();
//添加查询条件
query.set("q", "name:家天下");
query.add("fq","catalog_name:与钟不同");
query.addSort("price", SolrQuery.ORDER.desc);
query.setStart(0);
query.setRows(6);
//设置高亮数据
query.setHighlight(true);
query.addHighlightField("name");
query.setHighlightSimplePre("<font style='color:blue'>");
query.setHighlightSimplePost("</font>");
//查询
QueryResponse response = client.query(query);
//获取查询结果
SolrDocumentList results = response.getResults();
//获取高亮数据
Map<String, Map<String, List<String>>> highlighting = response.getHighlighting();
for (SolrDocument result : results) {
//获取高亮数据
Map<String, List<String>> map = highlighting.get(result.get("id"));
List<String> highlightingName = map.get("name");
System.out.println("id:" + result.get("id"));
System.out.println("高亮:" + highlightingName);
System.out.println("名字:" + result.get("name"));
System.out.println("类别:" + result.get("catalog_name"));
}
} catch (Exception e) {
e.printStackTrace();
}
}
//删除
public static void delete () {
HttpSolrClient client = null;
try {
//创建客户端
String url = "http://192.168.244.10:8983/solr/testcore";
client = new HttpSolrClient.Builder(url).build();
client.deleteById("8");
client.commit();
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
//添加
public static void add () {
HttpSolrClient client = null;
try {
//1 创建客户端
String url = "http://192.168.244.10:8983/solr/testcore";
client = new HttpSolrClient.Builder(url).build();
//2 创建要保存的duixang
SolrInputDocument fieldDoc = new SolrInputDocument();
fieldDoc.addField("id", 2500);
fieldDoc.addField("myself", "myself");
//3 执行事务保存
client.add(fieldDoc);
//4 执行事务
client.commit();
} catch (SolrServerException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
7 使用spring data solr操作solr
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-solr</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
配置文件
#配置solr服务器所在的地址
spring.data.solr.host=http://192.168.244.10:8983/solr
spring data solr操作solr
@SpringBootTest(classes = {SpringdataSolrApplication.class})
@RunWith(SpringRunner.class)
class SpringdataSolrApplicationTests {
@Autowired(required = false)
private SolrTemplate solrTemplate;
//简单查询
@Test
public void query() {
System.out.println("welcome .....");
SimpleQuery simpleQuery = new SimpleQuery();
//设置条件名
Criteria criteriaName = new Criteria("name");
//expression:分词查询
criteriaName.expression("家天下");
Criteria criteriaPrice = new Criteria("price");
//区间查询
criteriaPrice.between(1,100);
Criteria criteriaCatalog = new Criteria("catalog_name");
criteriaCatalog.is("与钟不同");
simpleQuery.addCriteria(criteriaName);
simpleQuery.addCriteria(criteriaPrice);
simpleQuery.addCriteria(criteriaCatalog);
simpleQuery.setOffset(0L);
simpleQuery.setRows(5);
//查询
ScoredPage<Product> products = solrTemplate.queryForPage("testcore", simpleQuery, Product.class);
System.out.println("==============================");
for (Product product : products) {
System.out.println(product.getName());
}
}
@Test
public void highQuery () {
SimpleHighlightQuery highlightQuery = new SimpleHighlightQuery();
//设置条件名
Criteria criteriaName = new Criteria("name");
//expression:分词查询
criteriaName.expression("家天下");
highlightQuery.addCriteria(criteriaName);
//设置高亮条件
HighlightOptions options = new HighlightOptions();
options.addField("name");
options.setSimplePrefix("----我是前缀----");
options.setSimplePostfix("----我是后缀----");
//将高亮条件设置给query
highlightQuery.setHighlightOptions(options);
HighlightPage<Product> highlightProducts = solrTemplate.queryForHighlightPage("testcore", highlightQuery, Product.class);
List<HighlightEntry<Product>> list = highlightProducts.getHighlighted();
ArrayList<Product> results = new ArrayList<>();
for (HighlightEntry<Product> entry: list) {
//获取entry中的高亮数据集合
List<HighlightEntry.Highlight> highlights = entry.getHighlights();
//获取没有经过高亮处理的结果对象
Product product = entry.getEntity();
for (HighlightEntry.Highlight highlight : highlights) {
//判断数据数据的字段是否是需要的
if (highlight.getField().getName().equals("name")) {
//获取高亮的字符串
String string = highlight.getSnipplets().get(0);
product.setName(string);
}
}
results.add(product);
}
for (int i = 0; i < results.size(); i++) {
System.out.println(results.get(i));
}
}
}