一、RediSearch介绍:

在Redis之上实现了一个搜索引擎,但与其他Redis搜索库不同的是,它不使用诸如Sorted Sets之类的内部数据结构。
反向索引存储为特殊的压缩数据类型,可实现快速索引和搜索速度,并减少内存占用。
这还启用了更高级的功能,例如精确的词组匹配和文本查询的数字过滤,这是传统Redis搜索方法无法实现或无法实现的。

二、RediSearch开源地址:

官方地址:https://oss.redislabs.com/redisearch/

开源地址:https://github.com/RediSearch/RediSearch

三、安装命令:

https://hub.docker.com/r/redislabs/redisearch/

$ docker run -p 63796379 redislabs / redisearch:latest

四、Springboot集成RediSearch:

客户端:https://github.com/RediSearch/JRediSearch

依赖:

<?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.3.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.citydo</groupId>
    <artifactId>redisearchspringboot</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>redisearchspringboot</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </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>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>com.redislabs</groupId>
            <artifactId>jredisearch</artifactId>
            <version>1.8.1</version>
        </dependency>
        <dependency>
            <groupId>cn.hutool</groupId>
            <artifactId>hutool-all</artifactId>
            <version>5.3.10</version>
        </dependency>
        <dependency>
            <groupId>com.hankcs</groupId>
            <artifactId>hanlp</artifactId>
            <version>portable-1.7.8</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

工具类:

package com.citydo.redisearchspringboot;

import com.hankcs.hanlp.seg.common.Term;
import com.hankcs.hanlp.suggest.Suggester;
import com.hankcs.hanlp.tokenizer.NLPTokenizer;
import io.redisearch.AggregationResult;
import io.redisearch.Query;
import io.redisearch.Schema;
import io.redisearch.SearchResult;
import io.redisearch.aggregation.AggregationBuilder;
import io.redisearch.aggregation.SortedField;
import io.redisearch.aggregation.reducers.Reducers;
import io.redisearch.client.Client;
import org.springframework.util.StringUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class RedisSearchUtils {

    private Client client = null;


    /**
     * 配置redis搜索
     */
    private RedisSearchUtils() {
        if (client == null) {
            String indexName = "test";
            String host = "localhost";
            int port = 6379;
            String password = "124";
            int timeout = 3000;
            int poolSize = 0;
            if (!StringUtils.isEmpty(password)) {
                // redis 设置了密码
                client = new Client(indexName, host, port, timeout, poolSize, password);
            }else {
                // redis 未设置密码
                client = new Client(indexName, host, port);
            }
        }
    }


    /**
     * 创建索引
     */
    public void createIndex(String title, String body, String price){
        Schema sc = new Schema()
                .addTextField(title, 5.0)
                .addTextField(body, 1.0)
                .addNumericField(price);
        client.createIndex(sc, Client.IndexOptions.defaultOptions());
    }

    /**
     * 将文档添加到索引
     * fields.put("title", "hello world");
     * fields.put("state", "NY");
     * fields.put("body", "lorem ipsum");
     * fields.put("price", 1337);
     * @param fields
     */
    public void addDocument(String docId, Map<String, Object> fields){
        client.addDocument(docId, fields);
    }

    /**
     * 创建复杂查询
     * @param queryString
     * @param price
     * @return
     */
    public SearchResult search(String queryString, String price){
        Query query = new Query(queryString)
                .addFilter(new Query.NumericFilter(price, 0, 1000))
                .limit(0,Integer.MAX_VALUE);
        return client.search(query);
    }


    /**
     * 聚合查询
     * @param query
     * @param price
     * @param state
     * @param avgprice
     * @param k
     * @return
     */
    public AggregationResult aggregate(String query, String price, String state, String avgprice, String k){
        AggregationBuilder builder = new AggregationBuilder(query)
                .apply("@".concat(price).concat("/1000"), k)
                .groupBy("@".concat(state), Reducers.avg("@".concat(k)).as(avgprice))
                .filter("@".concat(avgprice).concat(">=2"))
                .sortBy(Integer.MAX_VALUE, SortedField.asc("@".concat(state)));
        return client.aggregate(builder);
    }


    /**
     * 分词查询 先分词 在合并
     */
    public List<SearchResult> searchParticiple(String queryString, String price){
        List<SearchResult> result  = new ArrayList<>();
        //分词
        List<Term> termList = NLPTokenizer.segment(queryString);
        termList.stream().forEach(e->{
            Query query = new Query(e.word)
                    .addFilter(new Query.NumericFilter(price, 0, 1000))
                    .limit(0,Integer.MAX_VALUE);
            SearchResult search = client.search(query);
            result.add(search);
        });
        return result;
    }

    /**
     * 出推荐查询
     * @param queryString
     * @param price
     * @return
     */
    public List<SearchResult> searchSuggest(String queryString, String price){
        List<SearchResult> result  = new ArrayList<>();
        //推荐
        List<String> suggest = new Suggester().suggest(queryString, Integer.MAX_VALUE);
        suggest.stream().forEach(e->{
            Query query = new Query(e)
                    .addFilter(new Query.NumericFilter(price, 0, 1000))
                    .limit(0,Integer.MAX_VALUE);
            SearchResult search = client.search(query);
            result.add(search);
        });
        return result;
    }


}

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐