09、全文检索 -- Solr -- SpringBoot 整合 Spring Data Solr (生成DAO组件 和 实现自定义查询方法)

news2024/11/28 3:34:42

目录

  • SpringBoot 整合 Spring Data Solr
    • Spring Data Solr的功能(生成DAO组件):
      • Spring Data Solr大致包括如下几方面功能:
      • @Query查询(属于半自动)
      • 代码演示:
        • 1、演示通过dao组件来保存文档
          • 1、实体类指定索引库
          • 2、修改日志级别
          • 3、创建 Dao 接口
          • 4、先删除所有文档
          • 5、创建测试类
          • 6、演示结果
        • 2、根据 title_cn 字段是否包含关键字来查询
        • 3、查询指定价格范围的文档
        • 4、查询Description 字段中包含关键词的文档
        • 5、查询集合中的这些id的文档
        • 6、@Query查询(自定义的半自动查询)
    • Spring Data Solr的功能(实现自定义查询方法):
      • 自定义查询方法(属于全手动)
      • 代码演示:
        • 自定义查询方法且高亮显示
          • 1、自定义CustomBookDao组件
          • 2、CustomBookDaoImpl 实现类写自定义方法
          • 3、测试:成功实现关键字的高亮显示
          • 4、部分代码解释
        • 代码优化:
    • @EnableSolrRepositories 注解解释
    • 完整代码
      • SolrConfig 手动配置自定义的SolrClient
      • Book 实体类
      • BookDao 组件
      • CustomBookDao 自定义dao组件
      • CustomBookDaoImpl 自定义查询方法
      • application.properties 配置文件
      • BookDaoTest 基于DAO组件测试
      • SolrClientTest 基于SolrClient测试
      • pom.xml 依赖文档

SpringBoot 整合 Spring Data Solr

测试类使用 solrClient 进行添加、查询、删除文档的操作在这篇的代码基础上继续演示的

两篇文章的区别:
上一篇是通过SolrClient 连接 Solr,然后用 SolrClient 来调用查询方法进行全文检索
这一篇是 自定义dao组件,通过继承CrudRepository 接口,用 dao 接口来调用查询方法进行全文检索


Spring Data Solr的功能(生成DAO组件):

这种方式在Spring Boot 2.5已经不再推荐使用了,需要手动添加@EnableSolrRepositories来启动DAO组件。

通过这种方式访问Solr索引库,确实不如直接用SolrClient那么灵活。


Spring Data Solr大致包括如下几方面功能:

DAO接口只需继承 CrudRepository,Spring Data Solr 就能为DAO组件提供实现类。


1、Spring Data Solr 支持方法名关键字查询,只不过Solr查询都是全文检查查询。

2、Spring Data Solr 同样支持用 @Query注解指定自定义的查询语句

3、Spring Data Solr 同样支持 DAO组件添加自定义的查询方法

通过添加额外的父接口,并为额外的该接口提供实现类,Spring Data Solr 就能该实现类中的方法“移植”到DAO组件中。

4、不支持Example查询和Specification查询。


【说明:】
与前面介绍的NoSQL技术不同的是,Solr属于全文检索引擎,因此它的方法名关键字查询也是基于全文检索的。

例如对于findByName(String name)方法,假如传入参数为“疯狂”,
这意味着查询name字段中包含“疯狂”关键字的文档,而不是查询name字段值等于“疯狂”的文档。

@SolrDocument注解 可指定一个collection属性,用于指定该实体类映射哪个索引库(单机就是Core或集群模式就是Collection)。


@Query查询(属于半自动)

@Query注解所指定的就是标准的 Lucene 的查询语法

@Query("?0:?1")
List<Book> findByQuery(String field, Double term);

在这里插入图片描述


代码演示:

代码是基于:使用 SolrClient 连接 Solr 这篇文章来修改的


1、演示通过dao组件来保存文档

1、实体类指定索引库

指定该 Book 类映射到 books 索引库里面

在这里插入图片描述


2、修改日志级别

日志级别,可以用来看sql具体的执行语句
在这里插入图片描述


3、创建 Dao 接口

DAO接口只需继承 CrudRepository,Spring Data Solr 就能为DAO组件提供实现类。
此时写的方法还不需要用到。
只是需要用到这个BookDao接口。

在这里插入图片描述


4、先删除所有文档

用前面写的测试方法,先把前面测试的文档先删除干净。

在这里插入图片描述


5、创建测试类

在这里插入图片描述


区别:

添加文档的代码都是一样的,只是整合 Spring Data Solr 用到的是 bookDao 接口来实现,之前是使用solrClient来实现。

在这里插入图片描述


6、演示结果

成功通过 dao 组件来添加文档。

在这里插入图片描述



如果代码出错,因为版本问题显示没有bookDao这个bean,那么就通过注解手动启用Dao组件

在这里插入图片描述


我依赖是这个,演示的时候能成功执行代码,所以没有手动启用上面说的注解@EnableSolrRepositories
在这里插入图片描述



为了后面方便演示,把文档删了,改成这些。

在这里插入图片描述


2、根据 title_cn 字段是否包含关键字来查询

在这里插入图片描述


3、查询指定价格范围的文档

在这里插入图片描述


4、查询Description 字段中包含关键词的文档

查询Description 字段中包含关键词的文档(Matches 需要正则表达式)

如图:正则表达式里面 ,/故事.+/ 两个字的就查不到,如果是 /故.+/ 一个字就查的到。

/故事.+/ 两个字的就查不到
在这里插入图片描述


/故.+/ 一个字就查的到

在这里插入图片描述


5、查询集合中的这些id的文档

在这里插入图片描述


6、@Query查询(自定义的半自动查询)

在这里插入图片描述



Spring Data Solr的功能(实现自定义查询方法):


自定义查询方法(属于全手动)

让DAO接口继承自定义DAO接口、并为自定义DAO接口提供实现类,可以为DAO组件添加自定义查询方法。

Spring Data solr会将自定义DAO组件实现的方法移植DAO组件中。

自定义查询方法可使用SolrClient来实现查询方法,SolrClient 是 Solr本身提供的API,功能比较强大。


代码演示:

自定义查询方法且高亮显示

需求:自定义一个关键字查询的方法,返回的结果中,对关键字进行高亮显示。

代码解释:
下面的代码:
1、先在查询的时候添加实现高亮的代码
2、在查询后返回来的文档中,获取高亮信息
3、将获取到的高亮信息封装回 Book 对象即可。
(其实就是要返回的book对象中的description字段里面的关键字前后多了< span > 标签字符串, 就算是实现了关键字的高亮效果)


1、自定义CustomBookDao组件

在这里插入图片描述


同时让 bookDao 也继承这个 CustomBookDao
为了方便统一用 bookDao 来调用查询方法

在这里插入图片描述



2、CustomBookDaoImpl 实现类写自定义方法

在这里插入图片描述

在这里插入图片描述


3、测试:成功实现关键字的高亮显示

在这里插入图片描述


4、部分代码解释

1、代码中的设置对应图形界面中的设置

在查询条件中,添加高亮设置。

如图:通过图形界面跟测试代码的相同条件的查询,来演示代码设置高亮效果时对应的样子

在这里插入图片描述



2、对获取高亮信息并封装回book对象的解释

对这部分代码进行详细解释

在这里插入图片描述


查询时对关键字添加了高亮的操作,此时把具体的高亮信息(就是关键字前后添加了高亮的 < span > 代码,在查询后返回的结果文档里面,生成了如图的这个字符串)拿出来,封装回 description 这个属性里面。

比如查的时候,是查 description 这个字段里面包含“热血” 的关键词,

如果不加高亮的代码,那么返回来的数据是:
“description”:[“讲述废材纲成为第十代首领的热血故事A”]},

如果加高亮的代码,那么返回来的数据是:
“description”:[“讲述废材纲成为第十代首领的<span style=“color:red”>热血< /span >故事A”]},

关键字前后多了 <span style=“color:red”> < /span > 这两个字符串。

所以我们这一步就是要把加高亮后返回的这个多了这两个高亮显示的字符串的数据,给封装到 Book 对象里面的description 字段里面。


如图:通过图形界面跟测试代码的相同条件的查询,来演示代码具体获取到的高亮信息长啥样

在这里插入图片描述


如果不封装的话,那么查询后返回来的结果,这个description字段的数据依然是没有加高亮代码的。

在这里插入图片描述


所以需要把高亮的信息获取出来,再设置到 Book 对象里面的 description 字段里面,这样再返回这个 book对象,此时Book对象里面的 description 值就有高亮显示的功能了。
(其实就是要返回的book对象中的description字段里面的关键字前后多了< span > 标签字符串, 就算是实现了关键字的高亮效果)

在这里插入图片描述



代码优化:

如图:根据上面的代码解释,可以看出此时的 highLightDesc 字符串相当于就是具有高亮效果的 description 的值。

description 的值长这样:
[“讲述废材纲成为第十代首领的热血故事A”]},

highLightDesc 的值长这样
[“讲述废材纲成为第十代首领的<span style=“color:red”>热血< /span >故事A”]},

我们需要把
[“讲述废材纲成为第十代首领的<span style=“color:red”>热血< /span >故事A”]},
这个具有高亮效果的值封装回 Book 对象里面。

但是这个highLightDesc 这个字符串毕竟不是 description ,
所以如图直接把 highLightDesc 作为 description 字段封装进去,其实是不太合逻辑的。
所以可以优化下

在这里插入图片描述



优化代码:

假如我不希望改变Book对象的属性值,但是又希望能将高亮的信息传出去,因此可以考虑为Book对象增加一个Map类型的属性,该属性用于保存高亮信息。


1、先添加一个用于保存高亮信息的字段

在这里插入图片描述


2、封装对象时,把 highLightDesc 改回正常的 description 字段,此时可以看到,关键字并没有高亮显示。

在这里插入图片描述


3、把该id的文档的高亮信息设置到这个字段里面
如图:这样也能实现高亮效果

这种写法的好处:
既不会破坏原来这个 Book 对象的属性值,同时也能将高亮信息传出来

在这里插入图片描述



@EnableSolrRepositories 注解解释

该注解其实和Spring Boot的其他 @EnableXxxRepositories注解大同小异。

@EnableSolrRepositories注解用于启用Solr Repository支持。
一旦程序显式使用该注解,那Spring Data Solr 的 Repository自动配置就会失效(Spring Boot 2.5本来就没有启用Repository的自动配置)。

因此,当需要连接多个Solr索引库时或进行更多定制时,可手动使用该注解。

使用@EnableSolrRepositories注解时要指定如下属性:

- basePackages:指定扫描哪个包下的DAO组件(Repository组件)。

- solrClientRef:指定基于哪个 SolrClient 来实现 Repository 组件,默认值是 solrClient。

- solrTemplateRef:指定基于哪个 SolrTemplate 来实现Repository组件,默认是 solrTemplate。

上面 solrClientRef 与 solrTemplateRef 两个属性只要指定其中之一即可。


如图:不过这个注解我暂时没用到。

在这里插入图片描述



完整代码



SolrConfig 手动配置自定义的SolrClient

package cn.ljh.solrboot.config;

import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.impl.CloudSolrClient;
import org.apache.solr.client.solrj.impl.HttpSolrClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.solr.SolrProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.solr.repository.config.EnableSolrRepositories;
import org.springframework.util.StringUtils;

import java.util.Arrays;
import java.util.Optional;

//手动配置自定义的SolrClient
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({HttpSolrClient.class, CloudSolrClient.class})
@EnableConfigurationProperties(SolrProperties.class)

//由于本项目没有配置 SpringBoot 为 Spring Data Solr 提供的 Starter 组件,因此需要手动启用Dao组件
//@EnableSolrRepositories(basePackages = "cn.ljh.solrboot.dao",solrClientRef = "solrClient")
public class SolrConfig
{

    //把配置文件中的属性值注入到这个成员变量里面
    @Value("${spring.data.solr.username}")
    private String username;
    @Value("${spring.data.solr.password}")
    private String password;


    //此处需要配置一个 SolrClient(直接抄SolrAutoConfiguration的源码就可以了)
    @Bean
    public SolrClient solrClient(SolrProperties properties)
    {
        //通过系统属性来设置连接Solr所使用的认证信息

        // 设置使用基本认证的客户端
        System.setProperty("solr.httpclient.builder.factory",
                "org.apache.solr.client.solrj.impl.PreemptiveBasicAuthClientBuilderFactory");
        // 设置认证的用户名和密码
        System.setProperty("basicauth", username + ":" + password);

        if (StringUtils.hasText(properties.getZkHost()))
        {
            return new CloudSolrClient.Builder(Arrays.asList(properties.getZkHost()), Optional.empty()).build();
        }
        return new HttpSolrClient.Builder(properties.getHost()).build();
    }

}



Book 实体类

package cn.ljh.solrboot.domain;

import lombok.Data;
import org.apache.solr.client.solrj.beans.Field;
import org.springframework.data.solr.core.mapping.SolrDocument;

import java.util.List;
import java.util.Map;

/**
 * author JH
 */
@Data
//指定该 Book 类映射到 books 索引库里面
@SolrDocument(collection = "books")
public class Book
{
    //该id字段会被映射到索引库的 id field,而在索引库中 ,id field 被自定为标志属性
    //@Id
    //添加这个 @Field 注解,就能自动映射到索引库中同名的field
    @Field
    private Integer id;
    //这就表示这个 title ,映射到索引库中的 title_cn 这个字段
    @Field("title_cn")
    private String title;
    @Field
    private String description;
    @Field
    private Double price;

    //用于保存高亮信息
    private Map<String, List<String>> highLight;

    //构造器
    public Book(Integer id, String title, String description, Double price)
    {
        this.id = id;
        this.title = title;
        this.description = description;
        this.price = price;
    }


}




BookDao 组件

package cn.ljh.solrboot.dao;

import cn.ljh.solrboot.domain.Book;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.solr.repository.Query;

import java.util.List;

// DAO接口只需继承 CrudRepository,Spring Data Solr 就能为DAO组件提供实现类
//参数1:指定要进行数据访问的实体类是 Book, 也就是指定要操作的实体是 Book    参数2:Book 实体的主键的类型将被映射为整数类型
public interface BookDao extends CrudRepository<Book,Integer>,CustomBookDao
{
    //这些基于方法名关键字查询,都是全文检索的查询---------------------------------------------

    //根据 title_cn 字段是否包含关键字来查询
    List<Book> findByTitle(String term);

    //查询指定价格范围的文档
    List<Book> findByPriceBetween(Double start , Double end);

    //查询Description 字段中包含关键词的文档(Matches 需要正则表达式)
    List<Book> findByDescriptionMatches(String descPattern);

    //查询集合中的这些id的文档
    List<Book> findByIdIn(List<Integer> ids);

    //@Query查询(自定义的半自动查询)------------------------------------------------------

    //查 field 字段包含 term 这个关键词 的文档;  ?0 就是指第一个参数 field  ; ?1 就是指第二个参数 term
    @Query("?0:?1")
    //field:查这个字段    term:关键词
    List<Book> findByQuery(String field, String term);



}





CustomBookDao 自定义dao组件

package cn.ljh.solrboot.dao;

import cn.ljh.solrboot.domain.Book;

import java.util.List;

/**
 * author JH  2024-02
 */
public interface CustomBookDao
{
    //通过关键字查询并高亮显示
    List<Book> highLightFindByDescription(String term);
}



CustomBookDaoImpl 自定义查询方法

package cn.ljh.solrboot.dao.impl;

import cn.ljh.solrboot.dao.CustomBookDao;
import cn.ljh.solrboot.domain.Book;
import lombok.SneakyThrows;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * author JH  2024-02
 */
public class CustomBookDaoImpl implements CustomBookDao
{
    // 借助于 SolrClient ,可以实现 solr 的所有功能,比如: 高亮功能
    private final SolrClient solrClient;

    //通过构造器进行依赖注入
    public CustomBookDaoImpl(SolrClient solrClient)
    {
        this.solrClient = solrClient;
    }


    //通过关键字查询并高亮显示
    @SneakyThrows
    @Override
    public List<Book> highLightFindByDescription(String term)
    {

        //定义查询
        SolrQuery solrQuery = new SolrQuery("description:" + term);

        //开启高亮显示功能(对应图形界面中的 hl 复选框)
        solrQuery.setHighlight(true);

        //所谓高亮,就是在关键词前面和后面设置要添加的字符串片段
        //(对应图形界面中的 hl.simple.pre)
        solrQuery.setHighlightSimplePre("<span style=\"color:red\">");
        //(对应图形界面中的 hl.simple.post)
        solrQuery.setHighlightSimplePost("</span>");

        //设置对哪个字段进行高亮显示(对应图形界面中的 hl.fl)
        solrQuery.addHighlightField("description");

        //执行查询  参数1:到“books”索引库查询 , 参数2:查询的条件
        QueryResponse response = solrClient.query("books", solrQuery);

        //获取查询后返回的文档列表
        SolrDocumentList docLists = response.getResults();

        //获取高亮信息
        //该Map的key就是文档的id
        Map<String, Map<String, List<String>>> high = response.getHighlighting();

        //创建一个集合,用来存放封装了高亮信息的Book对象
        List<Book> bookList = new ArrayList<>();

        for (SolrDocument doc : docLists)
        {
            //此处就要将 SolrDocument 文档中的信息提取出来,封装到 Book 对象中
            String id = (String) doc.getFieldValue("id");
            String title = (String) doc.getFieldValue("title_cn");
            String description = (String) doc.getFieldValue("description");
            double price = (Float) doc.getFieldValue("price");

            //获取高亮后的字段值

            //通过文档的id,获取该文档的高亮信息
            Map<String, List<String>> highLightDoc = high.get(id);

            //再通过description这个key获取对应的value值(List类型)
            List<String> stringList = highLightDoc.get("description");

            //因为该list集合中只有一个元素,所以只获取第一个元素即可
            String highLightDesc = stringList.get(0);


            //将从 SolrDocument 文档中取出的高亮信息再封装回Book对象就可以了
            //这里是把 highLightDesc 作为 description 这个属性的值封装进去,其实不太好。
            //Book book = new Book(Integer.parseInt(id),title,highLightDesc,price);

            //假如我不希望改变Book对象的属性值,但是又希望能将高亮的信息传出去,因此可以考虑为Book对象增加一个Map类型的属性,该属性用于保存高亮信息
            Book book = new Book(Integer.parseInt(id),title,description,price);

            //把该id的文档的高亮信息设置到这个字段里面
            book.setHighLight(highLightDoc);


            bookList.add(book);
        }
        return bookList;
    }
}







application.properties 配置文件


# 现在演示的是单机模式,所以先指定这个host
spring.data.solr.host=http://127.0.0.1:8983/solr
# 连接 Solr 索引库的用户名和密码(就是Solr的图形界面)
spring.data.solr.username=root
spring.data.solr.password=123456

# 日志级别,可以用来看sql具体的执行语句
logging.level.org.springframework.data.solr=debug




BookDaoTest 基于DAO组件测试

package cn.ljh.solrboot;

import cn.ljh.solrboot.dao.BookDao;
import cn.ljh.solrboot.domain.Book;
import lombok.SneakyThrows;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.solr.repository.Query;

import java.util.List;

/**
 * author JH  2024-02
 */

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class BookDaoTest
{
    //依赖注入
    @Autowired
    private BookDao bookDao;

    //添加文档到books索引库的测试类
    @ParameterizedTest
    //这些参数相当于一个个文档
    @CsvSource({
            "1,火影忍者,讲述鸣人成为村长的事情,200",
            "2,七龙珠,讲述赛亚人的成长故事,300",
            "3,家庭教师A,讲述废材纲成为第十代首领的热血故事A,400",
            "4,家庭教师B,讲述废材纲成为第十代首领的热血故事B,500"
    })
    public void testSave(Integer id, String title, String description, Double price)
    {

        Book book = new Book(id, title, description, price);
        //使用bookDao来保存数据对象时,无需如之前一样指定索引库的名称,
        //因为Book类上面有@SolrDocument(collection = "books")修饰,已经指定了该数据类所映射的索引库
        bookDao.save(book);


    }

    //根据 title_cn 字段是否包含关键字来查询
    @ParameterizedTest
    //测试时只需要一个参数用这个注解
    @ValueSource(strings = {
            "龙珠",
            "家庭"
    })
    public void testFindByTitle(String term){
        //查询
        List<Book> books = bookDao.findByTitle(term);
        //打印
        books.forEach(System.err::println);

    }


    //查询指定价格范围的文档
    @ParameterizedTest
    //测试时需要多个参数用这个注解,多个参数在一个双引号里面用逗号隔开
    @CsvSource({
            "100,200",
            "200,300"
    })
    public void testFindByPriceBetween(Double start , Double end){

        List<Book> books = bookDao.findByPriceBetween(start, end);
        books.forEach(System.err::println);

    }




    //查询Description 字段中包含关键词的文档(Matches 需要正则表达式)
    @ParameterizedTest
    //正则表达式必须放在两个斜杠//里面, . 这个点表示匹配任意字符 , + 号表示出现 1 到多次
    @ValueSource(strings = {
            "/故.+/",
            "/热.+/"
    })
    public void testFindByDescriptionMatches(String descPattern){
        List<Book> books = bookDao.findByDescriptionMatches(descPattern);
        books.forEach(System.err::println);
    }





    //查询集合中的这些id的文档
    @ParameterizedTest
    @CsvSource({
            "1,2",
            "1,3"
    })
    public void testFindByIdIn(Integer id1 , Integer id2){
        List<Integer> ids = List.of(id1, id2);
        List<Book> books = bookDao.findByIdIn(ids);
        books.forEach(System.err::println);
    }


    //@Query查询(自定义的半自动查询)------------------------------------------------------

    @ParameterizedTest
    @CsvSource({
            "title_cn,家庭",
            "description,故事*"
    })
    public void testFindByQuery(String field, String term){
        List<Book> books = bookDao.findByQuery(field, term);
        books.forEach(System.err::println);
    }


    //自定义查询方法且高亮显示

    @ParameterizedTest
    @ValueSource(strings = {
            "热*",
            "村*"
    })
    public void testHighLightFindByDescription(String term){
        List<Book> books = bookDao.highLightFindByDescription(term);
        books.forEach(System.err::println);
    }

}





SolrClientTest 基于SolrClient测试

package cn.ljh.solrboot;

import cn.ljh.solrboot.domain.Book;
import lombok.SneakyThrows;
import org.apache.solr.client.solrj.SolrClient;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.apache.solr.common.params.SolrParams;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;


// SpringBootTest.WebEnvironment.NONE:  指定测试的web环境为非Web环境。
// 通常情况下,我们会将该参数设置为NONE,表示不需要启动内嵌的Web容器,从而更加快速地执行测试
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
public class SolrClientTest
{

    @Autowired
    private SolrClient solrClient;


    //添加文档到books索引库的测试类
    @SneakyThrows
    @ParameterizedTest
    //这些参数相当于一个个文档
    @CsvSource({
            "1,火影忍者,讲述成为村长的故事,200",
            "2,七龙珠,讲述赛亚人的成长故事,300",
            "3,家庭教师,讲述废材纲成为第十代首领的热血故事,400"
    })
    public void testSave(Integer id, String title, String description, Double price)
    {

        Book book = new Book(id, title, description, price);

        //向 books 逻辑索引库添加文档 ; 参数1:索引库的名字 , 参数2:映射文档的实体对象  ,参数3 :500 毫秒的提交时间
        solrClient.addBean("books", book, 500);

    }


    //查询索引库中的文档的测试类
    @SneakyThrows
    @ParameterizedTest
    //这个注解用来进行多次测试,一个字符串代表一次测试方法。
    @CsvSource({
            "title_cn,忍者",
            "description,成为",
            "description,成*"
    })
    //参数1:要查询的字段   参数2:要查询的关键词
    public void testQuery(String field, String term)
    {

        //创建查询,表明在 field 字段中查询 term 关键字
        SolrParams params = new SolrQuery(field + ":" + term);
        //执行查询操作,去 books 这个索引库里面查询,得到响应
        QueryResponse queryResponse = solrClient.query("books", params);
        //返回所得到的文档
        SolrDocumentList docList = queryResponse.getResults();
        //遍历所有的文档
        for (SolrDocument doc : docList)
        {
            System.err.println("获取所有 field 的名字:" + doc.getFieldNames());
            //遍历文档中的每个字段名
            for (String fn : doc.getFieldNames())
            {
                //通过字段名获取字段值
                System.err.println("filed名称:【 " + fn + " 】,field 的值:【" + doc.getFieldValue(fn) + " 】");
            }
        }

    }

    //根据文档的id来删除文档
    @SneakyThrows
    @ParameterizedTest
    @ValueSource(strings = {"1"})
    public void testDeleteById(String id)
    {
        //根据文档的id来删除
        //参数1:指定删除哪个索引库的文档     参数2:删除这个id的文档    参数3:指定多久提交执行这个删除操作,这里是500毫秒
        solrClient.deleteById("books",id,500);
    }


    //根据提供的字段和关键词,通过查询,如果该字段包含该关键词,则该文档会被删除掉
    @SneakyThrows
    @ParameterizedTest
    @CsvSource({
            //"title_cn,龙珠"
            //匹配所有的文档,也就是删除所有文档
            "*,*"
    })
    //这个字段包含这个关键词的则会被删除掉
    public void testDeleteByQuery(String field, String term)
    {
        //因为 参数2 需要的类型是String,所以这里不用创建SolrQuery查询对象
        //str 打印出来 ===> q=title_cn:龙珠
        String str = field + ":" + term;

        //参数1:指定删除哪个索引库的文档     参数2:字段名+关键词    参数3:指定多久提交执行这个删除操作,这里是500毫秒
        solrClient.deleteByQuery("books",str,500);
    }



}



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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.5</version>
    </parent>
    <groupId>cn.ljh</groupId>
    <artifactId>solrboot</artifactId>
    <version>1.0.0</version>
    <name>solrboot</name>

    <properties>
        <java.version>11</java.version>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <!-- 最基础的Spring Boot Starter -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- SpringBoot 2.5.3 不再为 Spring Data Solr 提供 Starter,因此只能手动添加 Spring Data Solr 依赖 -->
        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-solr</artifactId>
            <version>4.3.11</version>
        </dependency>

        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
            <version>1.9</version>
        </dependency>


        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1450626.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

GPU独显下ubuntu屏幕亮度不能调节解决方法

GPU独显下屏幕亮度不能调节&#xff08;假设你已经安装了合适的nvidia显卡驱动&#xff09;&#xff0c;我试过修改 /etc/default/grub 的 GRUB_CMDLINE_LINUX_DEFAULT"quiet splash acpi_backlightvendor" &#xff0c;没用。修改和xorg.conf相关的文件&#xff0c;…

C++ 双向广度搜索,嚯嚯!不就是双指针理念吗

1. 前言 在线性数据结构中搜索时&#xff0c;常使用线性搜索算法&#xff0c;但其性能偏低下&#xff0c;其性能改善方案常有二分搜索和双指针或多指针搜索算法。在复杂的数据结构如树和图中&#xff0c;常规搜索算法是深度和广度搜索。在深度搜索算法过程中常借助剪枝或记忆化…

掌握Go并发:Go语言并发编程深度解析

&#x1f3f7;️个人主页&#xff1a;鼠鼠我捏&#xff0c;要死了捏的主页 &#x1f3f7;️系列专栏&#xff1a;Golang全栈-专栏 &#x1f3f7;️个人学习笔记&#xff0c;若有缺误&#xff0c;欢迎评论区指正 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&…

高程 | 数据的共享与保护(c++)

文章目录 &#x1f4da;标识符的作用域与可见性&#x1f407;作用域&#x1f407;可见性 &#x1f4da;对象的生存期&#x1f407;静态生存期&#x1f407;动态生存期 &#x1f4da;类的静态成员&#x1f407;静态数据成员&#x1f407;静态函数成员 &#x1f4da;类的友元&…

什么是位段?位段的作用是什么?他与结构体有什么关系?

目录 1.什么是位段&#xff1f; 2.位段的内存分配 判断当前机器位段的内存分配形式 1.什么是位段&#xff1f; 位段的声明和结构是类似的&#xff0c;有两个不同&#xff1a; 1.位段的成员必须是 int、unsigned int 或signed int或char 。 2.位段的成员名后边有一个冒号和…

相机图像质量研究(13)常见问题总结:光学结构对成像的影响--鬼影

系列文章目录 相机图像质量研究(1)Camera成像流程介绍 相机图像质量研究(2)ISP专用平台调优介绍 相机图像质量研究(3)图像质量测试介绍 相机图像质量研究(4)常见问题总结&#xff1a;光学结构对成像的影响--焦距 相机图像质量研究(5)常见问题总结&#xff1a;光学结构对成…

STM32 I2C

目录 I2C通信 软件I2C读写MPU6050 I2C通信外设 硬件I2C读写MPU6050 I2C通信 R/W&#xff1a;0写1读 十轴&#xff1a;3轴加速度&#xff0c;3轴角速度&#xff0c;3轴磁场强度和一个气压强度 软件I2C读写MPU6050 MyI2C.c #include "stm32f10x.h" …

【智能家居入门4】(FreeRTOS、MQTT服务器、MQTT协议、微信小程序)

前面已经发了智能家居入门的1、2、3了&#xff0c;在实际开发中一般都会使用到实时操作系统&#xff0c;这里就以FreeRTOS为例子&#xff0c;使用标准库。记录由裸机转到实时操作系统所遇到的问题以及总体流程。相较于裸机&#xff0c;系统实时性强了很多&#xff0c;小程序下发…

[NSSRound#16 Basic]Web

1.RCE但是没有完全RCE 显示md5强比较&#xff0c;然后md5_3随便传 md5_1M%C9h%FF%0E%E3%5C%20%95r%D4w%7Br%15%87%D3o%A7%B2%1B%DCV%B7J%3D%C0x%3E%7B%95%18%AF%BF%A2%00%A8%28K%F3n%8EKU%B3_Bu%93%D8Igm%A0%D1U%5D%83%60%FB_%07%FE%A2&md5_2M%C9h%FF%0E%E3%5C%20%95r%D4w…

物流快递管理系统

文章目录 物流快递管理系统一、系统演示二、项目介绍三、13000字论文参考四、系统部分页面展示五、部分代码展示六、底部获取项目源码和万字论文参考&#xff08;9.9&#xffe5;带走&#xff09; 物流快递管理系统 一、系统演示 校园物流快递管理系统 二、项目介绍 主要技术…

用HTML5实现动画

用HTML5实现动画 要在HTML5中实现动画&#xff0c;可以使用以下几种方法&#xff1a;CSS动画、使用<canvas>元素和JavaScript来实现动画、使用JavaScript动画库。重点介绍前两种。 一、CSS动画 CSS3 动画&#xff1a;使用CSS3的动画属性和关键帧&#xff08;keyframes&…

备战蓝桥杯---数据结构之好题分享1

最近几天在刷学校的题单时&#xff0c;发现了几道十分巧妙又有启发性的题&#xff0c;借此来记录分享一下。 看题&#xff1a; 从整体上看似乎没有什么规律&#xff0c;于是我们从小地方入手&#xff0c;下面是图解&#xff1a; 因此&#xff0c;我们用栈的数据结构实现即可&a…

模拟算法总结(Java)

目录 模拟算法概述 练习 练习1&#xff1a;替换所有的问号 练习2&#xff1a;提莫攻击 练习3&#xff1a;Z字形变换 模拟算法概述 模拟&#xff1a;根据题目要求的实现过程进行编程模拟&#xff0c;即题目要求什么就实现什么 解决这类题目&#xff0c;需要&#xff1a; 1…

C 语言 devc++ 使用 winsock 实现 windows UDP 局域网发送消息

U参考来源 U 这里移植到windows 上 &#xff0c;使用 devc 开发。 服务端代码 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <winsock2.h>int main() {WORD sockVersion MAKEWORD(2, 2);WSAD…

【嵌入式移植】6、U-Boot源码分析3—make

U-Boot源码分析3—make all 从【嵌入式移植】4、U-Boot源码分析1—Makefile文章中可知执行make命令的时候&#xff0c;没有指定目标则使用默认目标PHONY&#xff0c;PHONY依赖项为_all all scripts_basic outputmakefile scripts dtbs。 all Makefile中第129行指定默认目标PH…

协调尺度:特征缩放在机器学习中的重要作用

目录 一、介绍 二、背景知识 三、了解功能缩放 四、特征缩放方法 五、特征缩放的重要性 六、实际意义 七、代码 八、结论 一、介绍 特征缩放是机器学习和数据分析预处理阶段的关键步骤&#xff0c;在优化各种算法的性能和效率方面起着至关重要的作用。本文深入探讨了特征缩放的…

蓝桥杯每日一题----单调栈和单调队列

单调栈和单调队列 单调栈 单调栈即栈内的元素是单调递减或者单调递增的&#xff0c;我们通过一个题目来理解。 单调栈模板题 题目描述 给出项数为 n 的整数数列 a 1 … a n a_1…a_n a1​…an​。 定义函数 f ( i ) f(i) f(i)代表数列中第 i 个元素之后第一个大于 a i …

安卓游戏开发框架应用场景以及优劣分析

一、引言 在移动游戏开发领域&#xff0c;选择合适的开发框架是项目成功的关键因素之一。特别是对于安卓平台&#xff0c;由于其开放性和庞大的用户基础&#xff0c;不同的游戏开发框架应运而生&#xff0c;旨在帮助开发者高效地构建游戏应用。以下是一些流行的安卓游戏开发框架…

OpenAI全新发布文生视频模型Sora - 现实,不存在了

OpenAI&#xff0c;发他们的文生视频大模型&#xff0c;Sora了。。。。。 而且&#xff0c;是强到&#xff0c;能震惊我一万年的程度。。。 https://openai.com/sora 如果非要用三个词来总结Sora&#xff0c;那就是“60s超长长度”、“单视频多角度镜头”和“世界模型” &am…

五、DataX源码分析、性能参数优化

DataX源码分析 一、总体流程二、程序入口1.datax.py2.com.alibaba.datax.core.Engine.java3.切分的逻辑并发数的确认 3.调度3.1 确定组数和分组算法3.2 数据传输 三、DataX性能优化1.关键参数2.优化&#xff1a;提升每个 channel 的速度3.优化&#xff1a;提升 DataX Job 内 Ch…