01-Solr

 


Solr01

1.Solr简介

  1. Solr是基于Apache Lucene构建的用于搜索和分析的开源框架,提供搜索、高亮显示和文字解析等功能。
  2. Solr是一个Java Web项目,且内嵌Jetty服务器。Solr6之前是一个war包,需要放在Tomcat上运行。Solr8+,内嵌Jetty服务器,Solr8+的启动流程:通过批处理文件(Solr安装目录下bin目录的.cmd或者.sh文件),先启动Jetty服务器,然后在Jetty启动后运行war包。
  3. 正向索引,Forward Index,也叫正排索引。从文档内容到词组的过程,每次搜索的时候需要搜索所有文档,每个文档比较搜索条件和词组。
  4. 反向索引,Inverted Index,也叫倒排索引。反向索引是正向索引的逆向,通过建立词组和文档的映射关系,通过词组就可以找到文档。

2.Solr搜索和存储原理

  1. 搜索原理。Solr高效搜索的主要是分词和索引(反向索引)。

    1. 分词。对搜索条件和存储内容进行分词,分成日常所使用的词语。
    2. 索引。存储数据时按照某些要求建立索引,如果要求建立索引,会对存储内容中的关键字(分词)建立索引。
  2. Solr数据存储原理。

    1. Solr中使用索引库保存数据(可以理解为索引库对应MySQL中的一章表),没有表的概念,一个索引库中可以有n个字段。
    2. Solr数据存储在Document文档对象中,文档对象包含的属性和属性类型都定义在schema.xml中,如果需要自定义属性或者自定义属性类型都需要修改schema.xml配置文件。从Solr5开始schema.xml更名为managed-schema(没有扩展名)。
    3. Solr中一条数据,就相当于MySQL中的一行数据,一条数据就是一个文档,文档可以使用json描述,也可以使用XML描述。
    4. MySQL中user表有10个字段,u1u10。对应到Solr就是有一个user的索引库,索引库有10个字段,u1u10。user索引库中的数据使用json描述就是{u1:'', u2:'', u3:'', u4:''..., u10:''};user索引库的数据使用xml描述就是:
    <dot>
        <u1></u1>
        <u2></u2>
        ...
        <u10></u10>
    </dot>
    

3.Solr的安装

  1. https://solr.apache.org/,Solr官网;https://solr.apache.org/downloads.html,Solr下载地址。

  2. Solr压缩包解压之后的目录说明。

    1. bin,可执行文件目录,包括启动和停止的脚本。
    2. contrib,Solr核心jar包。
    3. dist,常用插件的jar包。如需要使用数据导入则需要将这个目录中的solr-dataimporthandler-8.9.0.jar和solr-dataimporthandler-extras-8.9.0.jar拷贝到server\solr-webapp\webapp\WEB-INF\lib。
    4. docs,文档目录。
    5. example,案列目录。
    6. server,Solr搜索应用服务器核心目录。Solr是基于Web写的Java项目,通过bin目录下的脚本来启动server这个Web写的Java项目。
      1. start.jar。bin下的可执行文件通过启动start.jar来启动Jerry,start.jar中封装Jetty。
      2. contexts,上下文配置文件。
      3. etc,Solr核心配置文件。
      4. lib,常用的Jar包。
      5. logs,日志目录,包含GC日志、慢请求日志等。
      6. modules,用于加载插件。
      7. resources,日志配置信息。
      8. scripts,一些可执行文件。
      9. solr,保存索引库,保存索引库配置(字段类型和字段名称等)和文档数据。
      10. solr-webapp,Solr的war的核心。Solr-webapp是Solr完整的war包,war运行需要解压,解压是需要时间的,所以这么目录保存的是加压后的war包。
  3. 修改Solr配置文件关闭启动用户检查。

    1. 如果启动过多的线程会占用大量的空间。Centos低版本,一个线程占用128K;Centos高版本,一个线程占用256K。
    2. Solr中需要使用多线程进行分析和查询,Linux下root用户可以无限的创建线程,所以使用root用户启动Solr时,会抛出很多警告,而ES则不允许使用root用户启动。
    3. bin/solr.in.sh文件中SOLR_ULIMIT_CHECKS=true,默认开启用户检查,当使用root用户启动Solr时,就有很多的警告信息;当SOLR_ULIMIT_CHECKS=false,即关闭用户检查,即使使用root用户启动,也不会有告警信息。
  4. Solr启动。

    1. Solr内嵌Jetty,可直接启动,默认监听8983端口。
    2. Solr默认不推荐使用root用户启动,如果需要使用root启动,则需要制定-force参数。普通用户启动solr/bin/solr start;root用户启动solr/bin/solr start -force
    3. http://127.0.0.1:8983/solr/#/,启动之后的访问地址。

4.创建索引库

  1. 服务器操作。
    1. 在server/solr下创建user文件夹,user就是索引库。然后将server/solr/configsets/_default(索引库的默认配置文件)下的所有文件复制到server/solr/user文件夹下。
  2. http://127.0.0.1:8983/地址的页面操作。
    1. 选择Core Admin,然后Add Core,即初始化创建的user索引库。
    2. Add Core的一些配置参数。
      1. name,user。
      2. instanceDir,user。
      3. dataDir data,data。会自动在server/solr/user下创建data文件夹。
      4. config,solrconfig.xml。会寻找server/solr/user/conf下的solrconfig.xml。
      5. schema,managed-schema。会寻找server/solr/user/conf下的managed-schema文件。
  3. Solr页面中选择user索引库后中的选项解释。
    1. Overview,索引库信息。
    2. Analysis,分词信息,可以获取到一段文字的分词结果。
    3. Dataimport,数据导入,可以将MySQL中的数据导入。
    4. Document,文档的增删改查。
    5. Files,server\solr\user\conf\lang下的配置文件信息。
    6. Ping,服务连通性测试。
    7. Plugins,插件信息。
    8. Query,查询、搜索。
    9. Replication,副本信息。
    10. Schema,用于添加索引库的字段。
    11. Segments info,提高查询效率生产的一个过度文件。
      1. Solr数据分析完成后,先将数据写入到Segment,然后由操作系统将Segment写入到磁盘中。
      2. Segments info中可以看到Deletinos(文件碎片的百分比)。这个百分比记录的是Solr文件中的碎片。Solr中要删除一个文件时,先将其标记为要删除的状态,然后统一删除。Deletinos表示要删除文件的多少。
  4. Solr实际开发中使用的过程。
    1. 创建索引库。
      1. 索引库和MySQL中的表相互对应,对于复杂的查询,索引库和MySQL中的视图对应。
      2. 索引库会被单独作为一个项目,并且保存到Git上(方面维护Solr字段和MySQL列的关系)。在使用时只需要将整个索引库放到server\solr下即可。
      3. 如果Solr是第一次部署,则需要去Web页面初始化索引库(Web页面初始化数据的本质是发送Http请求,所以也可以使用curl初始化)。
    2. 数据导入。索引库创建并且初始化完成之后,就需要进行数据导入,可以使用Web页面进行数据导入(Web页面数据导入的本质是发送Http请求,所以也可以使用curl进行数据导入)。

5.安装ik分词器

  1. 下载ik分词器。Maven仓库中下载和solr版本对应的ik分词器的jar包,下载地址:https://mvnrepository.com/artifact/com.github.magese/ik-analyzer
  2. 将ik分词器的jar包放到solr-8.9.0\server\solr-webapp\webapp\WEB-INF\lib下。
  3. 修改server\solr\user\conf\managed-schema配置文件,为user索引库中需要中文分词的字段添加ik分词配置。
<schema name="default-config" version="1.6">
    <!-- 定义字段,字段类型为not_user_smart,当前字段就会使用ik分词器。 -->
    <field name="not_smart" type="not_user_smart" indexed="true" stored="true" />
    <!-- 定义字段,字段类型为user_smart,当前字段就会使用ik分词器。 -->
	<field name="smart" type="user_smart" indexed="true" stored="true" />
    
    <!-- 定义使用ik分词器的字段类型,并且字段没有useSmart。 -->
	<fieldType name="not_use_smart" 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="false" conf="ik.conf"/>
			<filter class="solr.LowerCaseFilterFactory"/>
		</analyzer>
	</fieldType>
    
    <!-- 定义使用ik分词器的字段类型,并且字段useSmart。 -->
	<fieldType name="use_smart" class="solr.TextField">
		<analyzer type="index">
			<tokenizer class="org.wltea.analyzer.lucene.IKTokenizerFactory" useSmart="true" 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>
</schema>
  1. 先停止solr(solr.cmd stop -all),然后再启动(solr.cmd start)。

  2. useSmart值的区别(以我们去吃饭为例)。useSmart="false",分词量多,分的细致,分为:我们、我、们、去吃、吃饭;userSmart="true",分词量少,更智能化,分为:我们、去、吃饭。

6.Solr页面中数据的增删改查

  1. 增加数据。选择Documents,Request-Handler为/update。

    1. Document Type为XML,Commit Within=1000,Overwrite=true。
    <doc>
        <field name="id">100</field>
    
        <field name="not_smart">我们去吃饭</field>
        <field name="smart">我们去吃饭</field>
    </doc>
    
    1. Document Type为JSON,Commit Within=1000,Overwrite=true。
    {"id": "101", "not_smart": "我们去吃饭", "smart": "我们去吃饭"}
    
  2. 修改数据。修改数据和新增数据的操作一样,当id不存在是为新增;当id存在时为修改数据。

  3. 查询数据。选择Query进行数据查询,Request-Handler (qt)为/select。

    1. q,查询条件。*:*,:左边是查询的key,:右边是查询的value,*表示查询所有数据。
    2. q.op,默认or,即查询条件之间是or的关系。q=id:100 not_smart:我们去吃饭,q.op=or,查询id=100或者not_smart为我们去吃饭的数据;q=id:100 not_smart:我们去吃饭,q.op=and,查询id=100并且not_smart为我们去吃饭的数据。
    3. sort,排序。id desc,按照id降序;id asc,按照id升序。
    4. start, rows,分页。start,数据开始位置,从0开始;rows,查询数据数量,相当与每页返回的数据数量。0,10,查询前10条数据。
    5. hl,高亮。hl.fl,需要高亮的字段;hl.simple.pre,高亮词的前缀;hl.simple.post,高亮词后缀。高亮需要配置q查询一起使用,如q=not_smart:我们,hl.fl为not_smart,则返回的高亮数据为<em>我们</em>去吃饭,并且q=not_smart:我们查询条件中的我们需要被分词。
  4. 删除数据。选择Documents,Request-Handler为/update。

    1. Document Type为XML,Commit Within=1000,Overwrite=true。
    <!-- 通过id删除数据。 -->
    <delete>
        <id>100</id>
    </delete>
    
    <!-- 通过查询条件删除数据。 -->
    <delete>
        <query>not_smart:我们</query>
    </delete>
    
    1. Solr在服务器使用curl删除数据。
    # 删除数据
    curl http://localhost:8080/update --data-binary "<delete><id>100</id></delete>" -H 'Content-type:text/xml; charset=utf-8'
    # 提交命令
    curl http://localhost:8080/update --data-binary "<commit/>"  -H 'Content-type:text/xml; charset=utf-8'
    
    1. 使用example\exampledocs\post.jar删除数据。
    # 删除数据
    java -Ddata=args -jar post.jar "<delete><id>100</id></delete>"
    # 查看post.jar帮助文档
    java -jar post.jar -help
    

7.dataimport数据导入

  1. 修改server\solr\user\conf\solrconfig.xml配置文件。
<config>
    ...
	<!-- 请求http://127.0.0.1:8983/solr/user/dataimport时,
		使用DataImportHandler处理。 
	-->
  <requestHandler name="/dataimport" class="org.apache.solr.handler.dataimport.DataImportHandler">
		<lst name="defaults">
			<str name="config">data-config.xml</str>
		</lst>
  </requestHandler>
  ...
</config>
  1. server\solr\user\conf\下新建data-config.xml,配置数据库信息和字段的映射信息。
<?xml version="1.0" encoding="UTF-8"?>
<dataConfig>
	<dataSource type="JdbcDataSource" 
		driver="com.mysql.cj.jdbc.Driver" 
		url="jdbc:mysql://127.0.0.1:3306/solr?serverTimeZone=Asia/Shanghai"
		user="root"
		password="123456"/>
	<document>
        <!-- 数据库的name字段,映射solr的not_smart字段。 -->
		<entity name="user" query="select id, from user">
			<field column="id" name="id"/>
			<field column="name" name="not_smart"/>
			<field column="address" name="smart"/>
		</entity>
	</document>
</dataConfig>
  1. 将MySQL驱动包(mysql-connector-java-8.0.28.jar)复制到server\solr-webapp\webapp\WEB-INF\lib下。
  2. Solr数据导入需要使用DataImportHandler,因此需要将dist下的solr-dataimporthandler-8.9.0.jarsolr-dataimporthandler-extras-8.9.0.jar拷贝到server\solr-webapp\webapp\WEB-INF\lib下,然后重启。

8.SolrJ实现数据的增删改查

  1. 导入solrj依赖,solr-solrj的版本尽量和Solr版本一致。
<dependency>
    <groupId>org.apache.solr</groupId>
    <artifactId>solr-solrj</artifactId>
    <version>8.9.0</version>
</dependency>
  1. 新增和更新数据。
public static void test01() {
    // 基础路径,Solr服务和索引库所在路径。
    String baseSolrUrl = "http://127.0.0.1:8983/solr/user";
    // 通过基础路径构建客户端连接。
    HttpSolrClient httpSolrClient = new HttpSolrClient.Builder(baseSolrUrl).build();

    // id存在则更新数据,id不存在则新增数据。
    SolrInputDocument document = new SolrInputDocument();
    document.addField("id", "100");
    document.addField("not_smart", "我们一起去吃饭。");
    try {
        // 更新的响应结果和web页面更新的操作的响应结果一致。
        UpdateResponse updateResponse = httpSolrClient.add(document);
        // 返回的状态。
        System.out.println(updateResponse.getStatus());
        // 执行时间。
        System.out.println(updateResponse.getQTime());
    } catch (Exception e) {
        // 发生异常回滚
        try {
            httpSolrClient.rollback();
        } catch (Exception solrServerException) {
            solrServerException.printStackTrace();
        }
    }

    // 在solr服务中,数据的写也是有事务的。
    // Web管理平台,一次操作就是一次事务,默认提交事务;
    // solr-solrj事务需要手动提交事务。
    // 执行成功,提交事务。
    try {
        httpSolrClient.commit();
    } catch (Exception e) {
        e.printStackTrace();
    }

    // 回收资源。
    try {
        httpSolrClient.close();
    } catch (IOException e) {
        e.printStackTrace();
    }
}
  1. 删除数据。
public static void test02() {
    String baseSolrUrl = "http://127.0.0.1:8983/solr/user";
    HttpSolrClient httpSolrClient = new HttpSolrClient.Builder(baseSolrUrl).build();

    try {
        UpdateResponse updateResponse = httpSolrClient.deleteById("001");
        System.out.println(updateResponse.getStatus());
    } catch (Exception e) {
        e.printStackTrace();
    }

    try {
        httpSolrClient.commit();
    } catch (Exception e) {
        e.printStackTrace();
    }
}
  1. 查询。条件查询、排序、分页。
public static void test03() {
    String baseSolrUrl = "http://127.0.0.1:8983/solr/user";
    HttpSolrClient httpSolrClient = new HttpSolrClient.Builder(baseSolrUrl).build();

    try {
        SolrQuery solrQuery = new SolrQuery();
        // 查询条件。
        solrQuery.setQuery("id:*1*");
        // 排序
        solrQuery.setSort("id", SolrQuery.ORDER.asc);
        // 分页
        solrQuery.setStart(0);
        solrQuery.setRows(20);

        QueryResponse queryResponse = httpSolrClient.query(solrQuery);
        // 对应Web页面查询的响应头。
        NamedList<Object> namedList = queryResponse.getHeader();
        /*
        status=0
        QTime=118
        params={q=id:*1*,start=0,sort=id asc,rows=20,wt=javabin,version=2}
        */
        Iterator<Map.Entry<String, Object>> iterator = namedList.iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        // 响应体,SolrDocumentList是ArrayList的子类。
        SolrDocumentList results = queryResponse.getResults();
        // 查询到的总数。
        System.out.println("NumFound" + results.getNumFound());

        // 遍历查询结果,result.getFieldValue("")用于获取字段对应的值。
        for (SolrDocument result : results) {
            System.out.print(result.getFieldValue("id") + "\t");
            System.out.println(result.getFieldValue("not_smart"));
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
  1. 高亮查询。
public static void test04() {
    String baseSolrUrl = "http://127.0.0.1:8983/solr/user";
    HttpSolrClient httpSolrClient = new HttpSolrClient.Builder(baseSolrUrl).build();

    try {
        SolrQuery solrQuery = new SolrQuery();
        // 查询条件。
        solrQuery.setQuery("not_smart:*吃饭*");
        // 排序
        solrQuery.setSort("id", SolrQuery.ORDER.asc);
        // 分页
        solrQuery.setStart(0);
        solrQuery.setRows(20);

        // 设置开启高亮
        solrQuery.setHighlight(true);
        // 高亮字段。
        solrQuery.addHighlightField("not_smart");
        // 高亮前缀
        solrQuery.setHighlightSimplePre("<em>");
        // 高亮后缀。
        solrQuery.setHighlightSimplePost("</em>");

        QueryResponse queryResponse = httpSolrClient.query(solrQuery);

        // 高亮数据。
        Map<String, Map<String, List<String>>> highlighting = queryResponse.getHighlighting();

        SolrDocumentList results = queryResponse.getResults();
        for (SolrDocument result : results) {
            System.out.print(result.getFieldValue("id"));
            System.out.print(result.getFieldValue("not_smart"));

            // 高亮数据需要通过唯一id值获取。
            Map<String, List<String>> map = highlighting.get(result.getFieldValue("id"));
            if (map != null) {
                System.out.print("有高亮数据 " + map.get("not_smart"));
            }

            System.out.println();
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
{
  "responseHeader":{
    "status":0,
    "QTime":13,
    "params":{
      "q":"not_smart:*吃饭*",
      "hl":"true",
      "hl.simple.post":"</em>",
      "start":"0",
      "q.op":"OR",
      "hl.fl":"zh_all",
      "sort":"id asc",
      "rows":"2",
      "hl.simple.pre":"<em>",
      "_":"1681473821442"}},
  "response":{"numFound":380,"start":0,"numFoundExact":true,"docs":[
      {
        "not_smart":"我们去吃饭",
        "id":"127",
        "_version_":1762052434657542155},
      {
        "not_smart":"我们去吃饭",
        "id":"128",
        "_version_":1762052434657542156}]
  },
  "highlighting":{
    "127":{ # 高亮数据字段,127是id的值。
      "not_smart":["我们去<em>吃饭</em>"]},
    "128":{
      "not_smart":["我们去<em>吃饭</em>"]}}}

9.spring-boot-starter-data-solr

  1. spring-boot-starter-data-solr提供了两种操作solr的方式,使用SolrTemplate或者继承SolrCrudRepository。
  2. SolrTemplate比较灵活,可以实现SolrCrudRepository的全部功能。当前端查询条件过多,需要进行条件拼接时,就可以使用SolrTemplate,如需要通过姓名、手机号、身份证、时间、地址等条件进行查询时,这些条件有可能全部使用,也有可能只使用了其中的一个或者两个,即需要根据前端传值判断是否为空后进行条件拼接,就可以使用SolrTemplate。
  3. SolrCrudRepository的特点是方便简单,适用于指定条件的查询,如通过姓名查询。

10.SolrCrudRepository实现数据的增删改查--待更新

11.SolrTemplate实现数据增删改查--待更新

posted @   行稳致远方  阅读(11)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· 没有Manus邀请码?试试免邀请码的MGX或者开源的OpenManus吧
· 园子的第一款AI主题卫衣上架——"HELLO! HOW CAN I ASSIST YOU TODAY
· 【自荐】一款简洁、开源的在线白板工具 Drawnix
点击右上角即可分享
微信分享提示