Spring Boot 中使用 SolrCloud

Lucene是一个Java语言编写的利用倒排原理实现的文本检索类库;

Solr是以Lucene为基础实现的文本检索应用服务。Solr部署方式有单机方式、多机Master-Slaver方式、Cloud方式。

SolrCloud是基于Solr和Zookeeper的分布式搜索方案。当索引越来越大,一个单一的系统无法满足磁盘需求,查询速度缓慢,此时就需要分布式索引。在分布式索引中,原来的大索引,将会分成多个小索引,solr可以将这些小索引返回的结果合并,然后返回给客户端。

准备

环境安装

CentOs7.3 搭建 SolrCloud 集群服务

https://cloud.tencent.com/developer/article/1041152

测试用例

Github 代码

代码我已放到 Github ,导入spring-boot-solr-cloud 项目

github https://github.com/souyunku/spring-boot-examples/tree/master/spring-boot-solr-cloud

添加依赖

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-data-solr</artifactId>
</dependency>
<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-jpa</artifactId>
</dependency>

启用 Solr

@Configuration
@EnableSolrRepositories(basePackages = {"io.ymq.solr"}, multicoreSupport = true)
public class SolrConfig {

    @Value("${spring.data.solr.zk-host}")
    private String zkHost;

    @Bean
    public CloudSolrClient solrClient() {
        return new CloudSolrClient(zkHost);
    }
	
}

映射的实体类

@SolrDocument(solrCoreName = "test_collection")
public class Ymq implements Serializable {

    @Id
    @Field
    private String id;

    @Field
    private String ymqTitle;

    @Field
    private String ymqUrl;

    @Field
    private String ymqContent;


  get 。。。

  set 。。。
}

继承 SolrCrudRepository

public interface YmqRepository extends SolrCrudRepository<Ymq, String> {

    /**
     * 通过标题查询
     *
     * @param ymqTitle
     * @return
     */
    @Query(" ymqTitle:*?0* ")
    public List<Ymq> findByQueryAnnotation(String ymqTitle);
}

CloudSolrClient 工具类

package io.ymq.solr.utils;

import io.ymq.solr.pagehelper.PageInfo;
import io.ymq.solr.pagehelper.PageInfoFacet;
import io.ymq.solr.pagehelper.RowBounds;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;

import org.apache.solr.client.solrj.response.QueryResponse;

import java.io.IOException;
import java.util.Collection;
import java.util.List;

/**
 * 描述: CloudSolrClient 工具类
 *
 * @author yanpenglei
 * @create 2017-10-19 10:56
 **/
public interface BaseSolr {

    /**
     * 添加数据
     *
     * @param defaultCollection solr 库
     * @param bean              对象
     * @throws IOException
     * @throws SolrServerException
     */
    public void add(String defaultCollection, Object bean) throws IOException, SolrServerException;

    /**
     * 添加一组数据
     *
     * @param defaultCollection solr 库
     * @param beans             list集合数据添加
     * @throws IOException
     * @throws SolrServerException
     */
    public void adds(String defaultCollection, Collection<?> beans) throws IOException, SolrServerException;

    /**
     * 根据多个id删除数据
     *
     * @param defaultCollection
     * @param ids
     * @throws IOException
     * @throws SolrServerException
     */
    public void deleteByIds(String defaultCollection, List<String> ids) throws IOException, SolrServerException;

    /**
     * 根据ID删除数据
     *
     * @param defaultCollection solr 库
     * @param id                要删除的文档的id
     * @throws IOException
     * @throws SolrServerException
     */
    public void deleteById(String defaultCollection, String id) throws IOException, SolrServerException;

    /**
     * 根据指定索引(字段)模糊删除数据
     *
     * @param defaultCollection solr 库
     * @param field
     * @param fieldValue
     * @throws IOException
     * @throws SolrServerException
     */
    public void deleteByField(String defaultCollection, String field, String fieldValue) throws IOException, SolrServerException;

    /**
     * 查询数据
     *
     * @param defaultCollection solr 库
     * @param clazz             对象Po
     * @param query             查询条件
     * @param <T>               返回查询集合
     * @return
     * @throws IOException
     * @throws SolrServerException
     */
    public <T> List<T> query(String defaultCollection, Class<T> clazz, SolrQuery query) throws IOException, SolrServerException;

    /**
     * 查询
     *
     * @param defaultCollection solr 库
     * @param query             查询条件
     * @return 返回response对象
     * @throws IOException
     * @throws SolrServerException
     */
    public QueryResponse query(String defaultCollection, SolrQuery query) throws IOException, SolrServerException;

    /**
     * @param defaultCollection solr 库
     * @param clazz             查询的数据对应的对象
     * @param query             查询条件
     * @param rowBounds         分页参数
     * @param <T>
     * @return
     * @throws IOException
     * @throws SolrServerException
     */
    public <T> PageInfo query(String defaultCollection, Class<T> clazz, SolrQuery query, RowBounds rowBounds) throws IOException, SolrServerException;

    /**
     * 查询数据
     *
     * @param defaultCollection solr 库
     * @param query             查询条件
     * @param rowBounds         分页
     * @return
     * @throws IOException
     * @throws SolrServerException
     */
    public PageInfo query(String defaultCollection, SolrQuery query, RowBounds rowBounds) throws IOException, SolrServerException;

    /**
     * solrj的facet结果集查询
     *
     * @param defaultCollection solr 库
     * @param query             查询条件
     * @param rowBounds         分页数
     * @return
     * @throws IOException
     * @throws SolrServerException
     */
    public PageInfoFacet queryFacet(String defaultCollection, SolrQuery query, RowBounds rowBounds) throws IOException, SolrServerException;
}

参数配置

application.properties

#SolrCloud zookeeper
spring.data.solr.zk-host=node1:2181,node2:2181,node3:2181

单元测试

package io.ymq.solr.test;

import com.alibaba.fastjson.JSONObject;
import io.ymq.solr.YmqRepository;
import io.ymq.solr.pagehelper.PageInfo;
import io.ymq.solr.pagehelper.RowBounds;
import io.ymq.solr.po.Ymq;
import io.ymq.solr.run.Startup;
import io.ymq.solr.utils.BaseSolr;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServerException;
import org.apache.solr.client.solrj.impl.CloudSolrClient;

import org.apache.solr.client.solrj.response.QueryResponse;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.io.IOException;
import java.util.List;


/**
 * 描述: 测试 solr cloud
 *
 * @author yanpenglei
 * @create 2017-10-17 19:00
 **/
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Startup.class)
public class BaseTest {

    @Autowired
    private YmqRepository ymqRepository;

    @Autowired
    private CloudSolrClient cloudSolrClient;

    @Autowired
    private BaseSolr baseSolr;

    /**
     * 使用 ymqRepository 方式新增
     *
     * @throws Exception
     */
    @Test
    public void testAddYmqRepository() {

        Ymq ymq1 = new Ymq();
        ymq1.setId("1");
        ymq1.setYmqTitle("penglei");
        ymq1.setYmqUrl("www_ymq_io");
        ymq1.setYmqContent("ymqContent");

        Ymq ymq2 = new Ymq();
        ymq2.setId("2");//
        ymq2.setYmqTitle("penglei");
        ymq2.setYmqUrl("www_ymq_io");
        ymq2.setYmqContent("ymqContent");

        ymqRepository.save(ymq1);
        ymqRepository.save(ymq2);
    }


    /**
     * 使用 cloudSolrClient 方式新增
     *
     * @throws Exception
     */
    @Test
    public void testAddCloudSolrClient() throws IOException, SolrServerException {

        Ymq ymq = new Ymq();
        ymq.setId("3");
        ymq.setYmqTitle("penglei");
        ymq.setYmqUrl("www_ymq_io");
        ymq.setYmqContent("ymqContent");

        cloudSolrClient.setDefaultCollection("test_collection");
        cloudSolrClient.connect();

        cloudSolrClient.addBean(ymq);
        cloudSolrClient.commit();
    }

    /**
     * 删除数据
     */
    @Test
    public void testDelete() {

        Ymq ymq = new Ymq();
        ymq.setId("4");
        ymq.setYmqTitle("delete_penglei");
        ymq.setYmqUrl("www_ymq_io");
        ymq.setYmqContent("ymqContent");

        // 添加一条测试数据,用于删除的测试数据
        ymqRepository.save(ymq);

        // 通过标题查询数据ID
        List<Ymq> list = ymqRepository.findByQueryAnnotation("delete_penglei");

        for (Ymq item : list) {

            System.out.println("查询响应 :" + JSONObject.toJSONString(item));

            //通过主键 ID 删除
            ymqRepository.delete(item.getId());
        }

    }

    /**
     * data JPA 方式查询
     *
     * @throws Exception
     */
    @Test
    public void testYmqRepositorySearch() throws Exception {

        List<Ymq> list = ymqRepository.findByQueryAnnotation("penglei");

        for (Ymq item : list) {
            System.out.println(" data JPA 方式查询响应 :" + JSONObject.toJSONString(item));
        }
    }

    /**
     * SolrQuery 语法查询
     *
     * @throws Exception
     */
    @Test
    public void testYmqSolrQuery() throws Exception {

        SolrQuery query = new SolrQuery();

        String ymqTitle = "penglei";

        query.setQuery(" ymqTitle:*" + ymqTitle + "* ");

        cloudSolrClient.setDefaultCollection("test_collection");
        cloudSolrClient.connect();
        QueryResponse response = cloudSolrClient.query(query);

        List<Ymq> list = response.getBeans(Ymq.class);

        for (Ymq item : list) {
            System.out.println("SolrQuery 语法查询响应 :" + JSONObject.toJSONString(item));
        }
    }

    /**
     * 使用 baseSolr 工具类 查询
     *
     * @throws Exception
     */
    @Test
    public void testBaseSolrQuery() throws Exception {

        SolrQuery query = new SolrQuery();

        String ymqTitle = "penglei";
        String defaultCollection = "test_collection";

        query.setQuery(" ymqTitle:*" + ymqTitle + "* ");

        List<Ymq> list = baseSolr.query(defaultCollection, Ymq.class, query);

        for (Ymq item : list) {
            System.out.println("baseSolr 工具类  查询响应 :" + JSONObject.toJSONString(item));
        }
    }

    /**
     * 使用 baseSolr 工具类 分页 查询
     *
     * @throws Exception
     */
    @Test
    public void testBaseSolrPageInfoQuery() throws Exception {

        SolrQuery query = new SolrQuery();

        String ymqTitle = "penglei";
        String defaultCollection = "test_collection";

        query.setQuery(" ymqTitle:*" + ymqTitle + "* ");

        PageInfo pageInfo = baseSolr.query(defaultCollection, Ymq.class, query,new RowBounds(0,2));

        System.out.println("使用 baseSolr 工具类 分页 查询响应 :" + JSONObject.toJSONString(pageInfo));
    }
}

一些查询,响应

 data JPA 方式查询响应 :{"id":"1","ymqContent":"ymqContent","ymqTitle":"penglei","ymqUrl":"www_ymq_io"}
 data JPA 方式查询响应 :{"id":"2","ymqContent":"ymqContent","ymqTitle":"penglei","ymqUrl":"www_ymq_io"}
 data JPA 方式查询响应 :{"id":"3","ymqContent":"ymqContent","ymqTitle":"penglei","ymqUrl":"www_ymq_io"}

代码我已放到 Github ,导入spring-boot-solr-cloud 项目

github https://github.com/souyunku/spring-boot-examples/tree/master/spring-boot-solr-cloud

Contact

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏猿天地

高性能NIO框架Netty-对象传输

上篇文章高性能NIO框架Netty入门篇我们对Netty做了一个简单的介绍,并且写了一个入门的Demo,客户端往服务端发送一个字符串的消息,服务端回复一个字符串...

3118
来自专栏人工智能LeadAI

使用强大的 Mockito 来测试你的代码

这篇教程介绍了如何使用 Mockito 框架来给软件写测试用例。 1、预备知识 如果需要往下学习,你需要先理解 Junit 框架中的单元测试。 如果你不熟悉 J...

2.4K6
来自专栏精讲JAVA

接口方法上的注解无法被 @Aspect 声明的切面拦截的原因分析

在Spring中使用MyBatis的Mapper接口自动生成时,用一个自定义的注解标记在Mapper接口的方法中,再利用@Aspect定义一个切面,拦截这个注解...

2992
来自专栏菩提树下的杨过

java调用.net asmx / wcf

一、先用asmx与wcf写二个.net web service: 1.1 asmx web服务:asmx-service.asmx.cs 1 using Sy...

2335
来自专栏开发与安全

linux网络编程之POSIX 共享内存和 系列函数

在前面介绍了system v 共享内存的相关知识,现在来稍微看看posix 共享内存 和系列函数。 共享内存简单来说就是一块真正的物理内存区域,可以使用一些函数...

2270
来自专栏javathings

@Async 注解的使用

在 Spring 中,@Async 标注的方法,在执行的时候,是异步运行的,它运行在独立的线程中,程序不会被该方法所阻塞。

1.5K2
来自专栏菩提树下的杨过

java 利用JAX-RS快速开发RESTful 服务

JAX-RS(Java API for RESTful Web Services)同样也是JSR的一部分,详细规范定义见 https://jcp.org/en/...

3147
来自专栏cmazxiaoma的架构师之路

SpringBoot之路(二)之Web进阶

3604
来自专栏技术小黑屋

Javac:command Not Found

I have been getting well on with my java tool.However When I use the javac comma...

2932
来自专栏Jed的技术阶梯

Kafka 中使用 Avro 序列化框架(二):使用 Twitter 的 Bijection 类库实现 avro 的序列化与反序列化

使用传统的 avro API 自定义序列化类和反序列化类比较麻烦,需要根据 schema 生成实体类,需要调用 avro 的 API 实现 对象到 byte[]...

3044

扫码关注云+社区

领取腾讯云代金券