ElasticSearch全文搜索引擎 -Spring Boot操作ES(SpringData概述、Spring Data Elasticsearch、基本操作、ElasticSearch操作文档)

本文涉及的产品
检索分析服务 Elasticsearch 版,2核4GB开发者规格 1个月
简介: ElasticSearch全文搜索引擎 -Spring Boot操作ES(SpringData概述、Spring Data Elasticsearch、基本操作、ElasticSearch操作文档)

1. Spring Data概述


Spring Data是spring提供的一套连接各种第三方数据源的框架集,它支持连接很多第三方数据源,例如:


数据库

redis

ElasticSearch

MongoDB等

包括数据库在内,很多第三方数据都可以使用SpringData操作,非常方便。

1673352451084.jpg


2. Spring Data Elasticsearch


上面章节介绍了Spring Data可以连接很多第三方数据源,其中ES就是Spring Data可以连接的对象。原生情况下,我们需要使用socket来连接ES获得响应,再解析响应,代码量非常大,我们现在可以使用Spring Data提供的封装,连接ES,方便快捷。


转到knows-search模块:


 下面我们添加Spring Data ES的依赖:

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


application.properties:

# 搜索微服务端口
server.port=8066
# 搜索微服务名称
spring.application.name=search-service
# 定位ES的位置
spring.elasticsearch.rest.uris=http://localhost:9200
# 设置日志门槛,显示ES的操作信息
logging.level.cn.tedu.knows.search=debug
# 还需要进一步设置才能使输出日志更清晰
logging.level.org.elasticsearch.client.RestClient=debug


SpringBoot启动类无需配置!


3. 实现基本操作


操作ES需要类:首先定义一个对应ES数据的类型,创建一个vo包,包中定义Item(商品)类代码如下:

package cn.tedu.knows.search.vo;
 import lombok.AllArgsConstructor;
 import lombok.Data;
 import lombok.NoArgsConstructor;
 import lombok.experimental.Accessors;
 import org.springframework.data.annotation.Id;
 import org.springframework.data.elasticsearch.annotations.Document;
 import org.springframework.data.elasticsearch.annotations.Field;
 import org.springframework.data.elasticsearch.annotations.FieldType;
 @Data   // lombok
 @Accessors(chain = true)   // 链式赋值(连续set方法)
 @AllArgsConstructor        // 全参构造
 @NoArgsConstructor         // 无参构造
 //指定当前类对象对应哪个ES中的索引
 //如果索引不存在,会自动创建
 @Document(indexName = "items")
 public class Item {
     // 表示当前ES索引的id列
     @Id
     private Long id;
     // 需要分词的属性使用Text类型,并指定分词器
     @Field(type = FieldType.Text,analyzer = "ik_smart",
             searchAnalyzer = "ik_smart")
     private String title;   //商品名称
     // 不需要分词的属性使用Keyword类型,不用写分词器
     @Field(type = FieldType.Keyword)
     private String category;//分类
     @Field(type = FieldType.Keyword)
     private String brand;   //品牌
     @Field(type = FieldType.Double)
     private Double price;   //价格
     //不会使用图片地址查询,设置index = false表示当前属性不会创建索引,节省空间,因为这个属性不会被查询
     @Field(type = FieldType.Keyword,index = false)
     private String images;  //图片地址
     //   /upload/2021/08/19/abc.jpg
 }


这个类中所有属性均配置了对应ES的属性和类型,下面我们就可以使用这个类操作ES了。


创建一个包repository,创建一个接口ItemRepository:

@Repository //将实现类的对象存到Spring容器中
 //ElasticsearchRepository实现基本的增删改查
 public interface ItemRepository extends ElasticsearchRepository<Item,Long> {
 }


这个接口和Mybatis Plus中Mapper接口继承的BaseMapper类似,会自动提供基本的增删改查方法。下面进行测试,测试类代码如下:

package cn.tedu.knows.search;
 import cn.tedu.knows.search.repository.ItemRepository;
 import cn.tedu.knows.search.vo.Item;
 import org.junit.jupiter.api.Test;
 import org.springframework.boot.test.context.SpringBootTest;
 import javax.annotation.Resource;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.Optional;
 @SpringBootTest
 class KnowsSearchApplicationTests {
     @Resource
     ItemRepository itemRepository;
     @Test
     void contextLoads() {
         //实例化对象,新增一个商品
         Item item = new Item()
                 .setId(1L)
                 .setTitle("罗技激光无线游戏鼠标")
                 .setCategory("鼠标")
                 .setBrand("罗技")
                 .setImages("/1.jpg")
                 .setPrice(285.0);
         //调用Spring Data提供的方法进行新增,save表示新增
         itemRepository.save(item);
         System.out.println("ok");
     }
     //按id查询
     @Test
     public void getId(){
         //Optional表示装着Item的盒子
         Optional<Item> optional = itemRepository.findById(1L);
         //通过get进行查询
         System.out.println(optional.get());
     }
     //批量新增
     @Test
     public void addAll(){
         List<Item> list = new ArrayList<>();
         list.add(new Item(2L,"罗技机械无线游戏键盘","键盘","罗技",360.0,"/2.jpg"));
         list.add(new Item(3L,"雷蛇激光有线游戏鼠标","鼠标","雷蛇",488.0,"/3.jpg"));
         list.add(new Item(4L,"罗技降噪蓝牙竞技耳机","耳机","罗技",378.0,"/4.jpg"));
         list.add(new Item(5L,"华为静音办公有线鼠标","鼠标","华为",220.0,"/5.jpg"));
         list.add(new Item(6L,"雷蛇竞技机械无线键盘","键盘","雷蛇",425.0,"/6.jpg"));
         itemRepository.saveAll(list);
         System.out.println("ok");
     }
     //全查
     @Test
     public void getAll(){
         //Iterable是List的父接口
         Iterable<Item> list = itemRepository.findAll();
         for(Item item : list){
             System.out.println(item);
         }
     }
 }
 //上面都是基本操作,不需要我们自己写接口中的方法


上面进行了单增、单查、批量增和全查的操作,下面进行自定义的查询。


Spring Data支持编写方法名表达操作,会自动按方法名的表达生成实现代码,这是它的一大优势!


在ItemRepository接口编写方法:

// Spring Data框架连接数据源,可以通过方法名来表达操作含义
 // 根据商品的title属性执行模糊查询
 Iterable<Item> queryItemsByTitleMatches(String title);


测试代码:

// 下面要完成一些条件查询,需要调用ItemRepository接口中编写的方法
 // 商品标题模糊匹配
 @Test
 public void queryByTitle(){
     Iterable<Item> items=itemRepository.queryItemsByTitleMatches("无线");
     for(Item item: items){
         System.out.println(item);
     }
 }


相当于运行了下面的指令:

### 单条件搜索
 POST http://localhost:9200/items/_search
 Content-Type: application/json
 {
   "query": {"match": { "title":  "无线" }}
 }


多属性条件查询:在ItemRepository接口编写方法:


// 根据商品的title和brand执行模糊查询
 Iterable<Item> queryItemsByTitleMatchesAndBrandMatches(String title,String brand);


测试类:

// 测试多条件查询
 @Test
 public void queryByTitleBrand(){
    Iterable<Item> items=itemRepository
            .queryItemsByTitleMatchesAndBrandMatches(
                    "游戏","罗技"
            );
    for(Item item: items){
        System.out.println(item);
    }
 }


实际执行的请求:

### 多字段搜索
 POST http://localhost:9200/items/_search
 Content-Type: application/json
 {
   "query": {
     "bool": {
       "must": [
         { "match": { "title": "游戏" }},
         { "match": { "brand": "罗技"}}
       ]
     }
   }
 }


排序查询:在ItemRepository接口编写方法:

// 排序查询:按照价格降序查询标题或者品牌匹配的商品
 Iterable<Item> queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc(String title,String brand);


测试代码:

// 测试排序
 @Test
 public void order(){
     Iterable<Item> items = itemRepository.queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc("游戏","罗技");
     for(Item item:items){
         System.out.println(item);
     }
 }


实际运行的请求:

### 多字段搜索
 POST http://localhost:9200/items/_search
 Content-Type: application/json
 {
   "query": {
     "bool": {
       "should": [
         { "match": { "title":  "游戏" }},
         { "match": { "brand": "罗技"}}
       ]
     }
   },"sort":[{"price":"desc"}]
 }


添加分页查询功能::在ItemRepository接口编写方法

// 分页查询 Page相当于PageHelper,Pageable规定第几页包含多少行,相当于PageInfo
 Page<Item> queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc(String title, String brand, Pageable pageable);


测试:

//分页查询
 @Test
 public void page(){
     int pageNum=1;
     int pageSize=2;
     //PageRequest.of返回Pageable,Pageable页数从第0页开始
     Page<Item> page=itemRepository.queryItemsByTitleMatchesOrBrandMatchesOrderByPriceDesc(
                     "游戏","罗技",PageRequest.of(pageNum-1,pageSize));
     //page实现了Iterable接口
     for(Item item:page){
         System.out.println(item);
     }
 }


根据条件分页查询

//Repository接口定义
public interface BookInfoEsMapper extends ElasticsearchRepository<BookInfoEs, String> {
    Page<BookInfoEs> findBookInfoEsByTitleOrIsbnOrAuthor(String title, String isbn, String author, Pageable pageable);
}
@Autowired
    private BookInfoEsMapper bookInfoEsMapper;
   @Test
    public void test() {
  //调用
  // Repository层根据findBookInfoEsByTitleOrIsbnOrAuthor 方法名自动识别条件
  Pageable pageable = PageRequest.of(0,10);
  Page<BookInfoEs> list = bookInfoEsMapper.findBookInfoEsByTitleOrIsbnOrAuthor("现代", "", "", pageable);
  System.out.println(list);
  System.out.println("总条数:" +list.getTotalElements());
  System.out.println("总页数:" +list.getTotalPages());
}


再来一种 根据条件分页查询

@Autowired
    private ElasticsearchOperations elasticsearchOperations;
    @Test
    public void test() {
     //构造条件
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        //must:必须满足的条件
        //should:非必须满足的条件
        boolQueryBuilder.should(QueryBuilders.matchQuery("title", "现代"));
        //wildcardQuery: 通配符查询
        boolQueryBuilder.should(QueryBuilders.wildcardQuery("id", "*122*"));
        boolQueryBuilder.should(QueryBuilders.matchQuery("author", ""));
        Pageable pageable = PageRequest.of(0, 10);
        NativeSearchQuery build = new NativeSearchQueryBuilder()
                .withQuery(boolQueryBuilder)
                .withPageable(pageable)
                .build();
        SearchHits<BookInfoEs> search = elasticsearchOperations.search(build, BookInfoEs.class);
        System.out.println("检索后的总分页数目为:" + search.getTotalHits());
        List<SearchHit<BookInfoEs>> searchHits = search.getSearchHits();
        System.out.println(searchHits);
}


基于RestHighLevelClient批量操作

import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.rest.RestStatus;
 @Autowired
 private RestHighLevelClient client;
BulkRequest bulkRequest = new BulkRequest();
for(int i = 0; i < 100; i++){
    //添加、删除、更新文档,操作索引都有相关类就不一一举例了
  UpdateRequest updateRequest = new UpdateRequest(indexName, IndexConst.DEFAULT_TYPE_NAME, docId);
  updateRequest.doc(docObject.toJSONString(), XContentType.JSON);
  bulkRequest.add(updateRequest);
}
if (!bulkRequest.requests().isEmpty()) {
  BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
        BulkItemResponse[] items = bulkResponse.getItems();
        for (BulkItemResponse item : items) {
              if (item.status() != RestStatus.OK) {
                  log.error(JSONObject.toJSONString(item));
              }
        }
}


4. ElasticSearch操作文档


### 创建 index
 PUT http://localhost:9200/questions
### 删除一个Index
 DELETE http://localhost:9200/questions
### 设置index中的文档属性采用ik分词
 ### type=text的才能分词,analyzer表示分词器,根据分词器对text内容进行分词,建立索引
 ### search_analyzer表示搜索内容的分词器,一般与上面的分词器相同,建立索引
 ### _mapping配合properties用来设置属性
 ### 注意下面的换行,这里是回车并换行,有严格格式要求,必须这样书写
 POST http://localhost:9200/questions/_mapping
 Content-Type: application/json
 {
   "properties": {
     "title": {
       "type": "text",
       "analyzer": "ik_max_word",
       "search_analyzer": "ik_max_word"
     },
     "content": {
       "type": "text",
       "analyzer": "ik_max_word",
       "search_analyzer": "ik_max_word"
     }
   }
 }
### questions中添加文档
 ### POST一般为新增或修改的意思,_create表示创建文档,/1中的1表示文档id,为真正的id
 ### 每执行一次请求必须通过###来分割,既是分隔符,也是注释符
 POST http://localhost:9200/questions/_create/1
 Content-Type: application/json
 {
   "id":1,
   "title":"Java基本数据类型有哪些",
   "content":"面时候为啥要问基本类型这么简单问题呀,我们要如何回答呢?"
 }
 ### questions 中添加文档
 POST http://localhost:9200/questions/_create/2
 Content-Type: application/json
 {
   "id":2,
   "title":"int类型的范围",
   "content":"为啥要了解int类型的范围呢?"
 }
 ### questions 中添加文档
 POST http://localhost:9200/questions/_create/3
 Content-Type: application/json
 {
   "id":3,
   "title":"常用集合类有哪些",
   "content":"为啥企业经常问集合呀?该如何回复呢"
 }
 ### questions 中添加文档
 POST http://localhost:9200/questions/_create/4
 Content-Type: application/json
 {
   "id":4,
   "title":"线程的run方法和start方法有啥区别",
   "content":"run方法可以执行线程的计算过程, start也可以执行线程的计算过程,用途一样么?"
 }
### 更新questions索引中的文档
 ### 此处POST是更新的意思,表示对文档4进行更新
 POST http://localhost:9200/questions/_doc/4/_update
 Content-Type: application/json
 {
   "doc": {
     "title": "Java线程的run方法和start方法有啥区别"
   }
 }
### 删除questions中的一个文档,DELETE表示删除
 DELETE http://localhost:9200/questions/_doc/2
### 查询数据,GET表示查询
 GET http://localhost:9200/questions/_doc/4
### 分词搜索 单属性模糊查询 查询分词索引,按照输出得分(_score:查询内容占整个内容的比例)由高到低排序
 POST http://localhost:9200/questions/_search
 Content-Type: application/json
 {
   "query": { "match": {"title": "类型" } }
 }
### 多字段搜索 多属性模糊查询 格式固定
 ### bool表示真假,should表示或,must表示与
 ### 查询的内容也会分词
 POST http://localhost:9200/questions/_search
 Content-Type: application/json
 {
   "query": {
     "bool": {
       "should": [
         { "match": { "title":  "java类型" }},
         { "match": { "content": "java类型"}}
       ]
     }
   }
 }


5. ElasticSearch原生API操作工具类


最后附上自己写的一个请求工具类(使用这个不需要引入spring-data-es的jar包了,是依靠es自带的http请求操作)

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import javax.annotation.PostConstruct;
import java.util.Base64;
/**
 * @author: yh
 * @Description: restTemplate工具类
 * @date: 2022/8/4 15:43
 */
@Component
public class RestTemplateUtil {
    @Value("${spring.elasticsearch.rest.uris}")
    private String host;
    @Value("${spring.elasticsearch.rest.username}")
    private String userName;
    @Value("${spring.elasticsearch.rest.password}")
    private String passWord;
    /**
     * 认证的Authorization
     */
    private static String authentication;
    /**
     * 查询ES POST-url
     */
    public static String SEARCH_INDEX_URL = null;
    /**
     * 多个搜索API-url
     */
    public static String MSEARCH_INDEX_URL = null;
    /**
     * 查询文档-url
     */
    public static String GET_DOC_ID = null;
    /**
     * 新建文档-url
     */
    public static String CREATE_DOC = null;
    /**
     * 更新文档 POST-url
     */
    public static String UPDATE_INDEX_DOC = null;
    /**
     * 按查询更新API-url
     */
    public static String UPDATE_BY_QUERY = null;
    /**
     * 创建ES索引,PUT请求-url
     */
    public static String CREATE_INDEX = null;
    /**
     * 批量增删改API  POST-url
     */
    public static String BULK_URL = null;
    /**
     * 根据文档id查询文档,也可以判断文档是否存在-url
     */
    public static String QUERY_INDEX_DOC = null;
    /**
     * Multi get (mget) API-url
     */
    public static String INDEX_MGET = null;
    /**
     * 删除索引(危险) delete请求
     */
    public static String DELETE_INDEX = null;
    @PostConstruct
    public void initProperty() {
        //{esIndex}:操作索引名称,{id}:操作文档id
        SEARCH_INDEX_URL = "http://" + host + "/{esIndex}/_search";
        MSEARCH_INDEX_URL = "http://" + host + "/{esIndex}/_msearch";
        GET_DOC_ID = "http://" + host + "/{esIndex}/_doc/{id}";
        CREATE_DOC = "http://" + host + "/{esIndex}/_create/{id}";
        UPDATE_INDEX_DOC = "http://" + host + "/{esIndex}/_doc/{id}/_update";
        UPDATE_BY_QUERY = "http://" + host + "/{esIndex}/_update_by_query";
        CREATE_INDEX = "http://" + host + "/{esIndex}";
        BULK_URL = "http://" + host + "/{esIndex}/_bulk";
        QUERY_INDEX_DOC = "http://" + host + "/{esIndex}/_doc/{id}";
        INDEX_MGET = "http://" + host + "/{esIndex}/_mget";
        DELETE_INDEX = "http://" + host + "/{esIndex}";
        authentication = "Basic " + Base64.getEncoder().encodeToString((userName + ":" + passWord).getBytes());
    }
    private static RestTemplate restTemplate;
    @Autowired
    public void setRestTemplate(RestTemplate restTemplate) {
        RestTemplateUtil.restTemplate = restTemplate;
    }
    /**
     * POST请求
     *
     * @param url     请求路径
     * @param jsonStr 参数
     * @return String
     * @author yh
     * @date 2022/8/4
     */
    public static String postForObject(String url, String jsonStr) {
        //设置header信息
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        requestHeaders.set("authorization",  authentication);
        HttpEntity<String> requestEntity = new HttpEntity<>(jsonStr, requestHeaders);
        return restTemplate.postForObject(url, requestEntity, String.class);
    }
    /**
     * ndjson格式数据请求
     *
     * @param ndjson 一种数据格式
     * @param url    url
     * @return String
     * @author yh
     * @date 2022/8/5
     */
    public static String postForNdjson(String url, String ndjson) {
        if (StringUtils.isBlank(ndjson)) {
            return "{}";
        }
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/x-ndjson");
        headers.set("authorization",  authentication);
        HttpEntity<String> request = new HttpEntity<>(ndjson, headers);
        return restTemplate.postForObject(url, request, String.class);
    }
    /**
     * get查询es
     *
     * @param url 请求路径
     * @return String
     * @author yh
     * @date 2022/8/5
     */
    public static String getForString(String url) {
        if (StringUtils.isBlank(url)) {
            return "{}";
        }
        HttpHeaders headers = new HttpHeaders();
        headers.set("authorization",  authentication);
        HttpEntity<MultiValueMap<String, Object>> request = new HttpEntity(null, headers);
        return restTemplate.exchange(url, HttpMethod.GET, request, String.class).getBody();
//        return restTemplate.getForObject(url, String.class);
    }
    /**
     * PUT请求
     *
     * @param url     请求路径
     * @param jsonStr body参数
     * @author yh
     * @date 2022/8/5
     */
    public static void putForVoid(String url, String jsonStr) {
        //设置header信息
        HttpHeaders requestHeaders = new HttpHeaders();
        requestHeaders.setContentType(MediaType.APPLICATION_JSON);
        requestHeaders.set("authorization",  authentication);
        HttpEntity<String> requestEntity = new HttpEntity<>(jsonStr, requestHeaders);
        restTemplate.exchange(url, HttpMethod.PUT, requestEntity, String.class);
//        restTemplate.put(url, JSONObject.parse(json));
    }
    /**
     * delete请求
     *
     * @param url 请求路径
     * @author yh
     * @date 2022/8/11
     */
    public static void deleteForVoid(String url) {
        restTemplate.delete(url);
    }
}


需要注意的是_bulk批量操作时,换行符的使用(_bulk操作ES)

/**
  * 不同系统的换行符
  * @date 2022/8/11
  */
 private String newLine = System.getProperty("line.separator");


还有一点注意的是:当操作es索引时,索引不存在就会返回404,不做配置的话会让程序直接抛出异常终止运行,我们希望状态码返回404时,走创建索引的逻辑,这时候就需要把RestTemplate相关的状态码加入白名单

import org.springframework.http.HttpStatus;
import org.springframework.http.client.ClientHttpResponse;
import org.springframework.web.client.DefaultResponseErrorHandler;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
 * 功能:捕获RestTemplate异常
 *
 * @author yh
 * @date 2022/8/10
 */
public class RtErrorHandler extends DefaultResponseErrorHandler {
    @Override
    public boolean hasError(ClientHttpResponse response) throws IOException {
        return super.hasError(response);
    }
    @Override
    public void handleError(ClientHttpResponse response) throws IOException {
        HttpStatus statusCode = HttpStatus.resolve(response.getRawStatusCode());
        // 白名单。白名单上的异常则不处理,直接返回
        List<HttpStatus> donotDeal = new ArrayList<>();
        // 404不要抛异常
        donotDeal.add(HttpStatus.NOT_FOUND);
        // donotDeal.add(HttpStatus.BAD_REQUEST);
        // 非白名单则处理
        if (!donotDeal.contains(statusCode)) {
            super.handleError(response);
        }
    }
}
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;
/**
 * @author: yh
 * @Description:
 * @date: 2022/8/5 10:32
 */
@Configuration
public class RestConfig {
    /**
     * 定义restTemplate请求处理
     * @param builder 可用于配置和创建RestTemplate的生成器。提供方便的方法来注册转换器、错误处理程序和UriTemplateHandlers。
     * @author yh
     * @date 2022/8/10
     * @return RestTemplate
     */
    @Bean
    public RestTemplate restTemplate(RestTemplateBuilder builder){
        RestTemplate build = builder.build();
        build.setErrorHandler(new RtErrorHandler());
        return build;
    }
}
相关实践学习
使用阿里云Elasticsearch体验信息检索加速
通过创建登录阿里云Elasticsearch集群,使用DataWorks将MySQL数据同步至Elasticsearch,体验多条件检索效果,简单展示数据同步和信息检索加速的过程和操作。
ElasticSearch 入门精讲
ElasticSearch是一个开源的、基于Lucene的、分布式、高扩展、高实时的搜索与数据分析引擎。根据DB-Engines的排名显示,Elasticsearch是最受欢迎的企业搜索引擎,其次是Apache Solr(也是基于Lucene)。 ElasticSearch的实现原理主要分为以下几个步骤: 用户将数据提交到Elastic Search 数据库中 通过分词控制器去将对应的语句分词,将其权重和分词结果一并存入数据 当用户搜索数据时候,再根据权重将结果排名、打分 将返回结果呈现给用户 Elasticsearch可以用于搜索各种文档。它提供可扩展的搜索,具有接近实时的搜索,并支持多租户。
相关文章
|
19天前
|
自然语言处理 监控 数据可视化
|
1月前
|
监控 Java 应用服务中间件
Spring和Spring Boot的区别
Spring和Spring Boot的主要区别,包括项目配置、开发模式、项目依赖、内嵌服务器和监控管理等方面,强调Spring Boot基于Spring框架,通过约定优于配置、自动配置和快速启动器等特性,简化了Spring应用的开发和部署过程。
54 19
|
1月前
|
存储 Java API
如何使用 Java 记录简化 Spring Data 中的数据实体
如何使用 Java 记录简化 Spring Data 中的数据实体
38 9
|
1月前
|
Java 测试技术 开发者
springboot学习四:Spring Boot profile多环境配置、devtools热部署
这篇文章主要介绍了如何在Spring Boot中进行多环境配置以及如何整合DevTools实现热部署,以提高开发效率。
68 2
|
1月前
|
前端开发 Java 程序员
springboot 学习十五:Spring Boot 优雅的集成Swagger2、Knife4j
这篇文章是关于如何在Spring Boot项目中集成Swagger2和Knife4j来生成和美化API接口文档的详细教程。
109 1
|
1月前
|
Java API Spring
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中拦截器的入门教程和实战项目场景实现的详细指南。
28 0
springboot学习七:Spring Boot2.x 拦截器基础入门&实战项目场景实现
|
1月前
|
Java API Spring
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
这篇文章是关于Spring Boot 2.x中过滤器的基础知识和实战项目应用的教程。
27 0
springboot学习六:Spring Boot2.x 过滤器基础入门&实战项目场景实现
|
1月前
|
自然语言处理 搜索推荐 关系型数据库
elasticsearch学习六:学习 全文搜索引擎 elasticsearch的语法,使用kibana进行模拟测试(持续更新学习)
这篇文章是关于Elasticsearch全文搜索引擎的学习指南,涵盖了基本概念、命令风格、索引操作、分词器使用,以及数据的增加、修改、删除和查询等操作。
24 0
elasticsearch学习六:学习 全文搜索引擎 elasticsearch的语法,使用kibana进行模拟测试(持续更新学习)
|
1月前
|
XML Java 应用服务中间件
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
【Spring】运行Spring Boot项目,请求响应流程分析以及404和500报错
182 2
|
1月前
|
前端开发 安全 Java
【Spring】Spring Boot项目创建和目录介绍
【Spring】Spring Boot项目创建和目录介绍
84 2
下一篇
无影云桌面