SpringBoot интегрирует ES для реализации операций CRUD.

Elasticsearch

Привет, я Sky Night, давно не виделись!

В этой статье представлена ​​интеграция ElasticSearch в проект Spring Boot и реализация операций CRUD, включая подкачку, прокрутку и другие функции. Раньше я использовал ES в компании, и я всегда использовал пакет, упакованный предшественниками.В последнее время я надеюсь изучить технологии, связанные с ES, с точки зрения родного синтаксиса Spring Boot/ES. Я надеюсь быть полезным.

Эта статья является выдержкой из серии статей spring-boot-examples, и пример кода был загружен наGitHub.com/lawrence/tickets…


Установите ES и визуализатор

Перейти на официальный сайт ESwoohoo.elastic.co/capable/download…Например, для версии для Windows нужно только загрузить установочный пакет, запустить файл elasticsearch.bat и получить доступ к браузеру.http://localhost:9200

Это означает, что ES установлен. Для лучшего просмотра данных ES снова установите плагин визуализации elasticsearch-head. Перейдите по адресу загрузки:GitHub.com/Untouchable/Голодное тело…Основные шаги:

  • git clone git://github.com/mobz/elasticsearch-head.git
  • cd elasticsearch-head
  • npm install
  • npm run start
  • open http://localhost:9100/

Могут возникнуть следующие ситуации:

Обнаружение — это междоменная проблема. Решение состоит в том, чтобы добавить следующие две строки конфигурации в elasticsearch.yml в папке конфигурации elasticsearch:

http.cors.enabled: true
http.cors.allow-origin: "*"

обновить страницу:Индекс статьи здесь — это индекс, который я автоматически создал в проекте весенней загрузки. Теперь давайте перейдем к делу.


Spring Boot представляет ES

Создайте проект spring-boot и введите зависимости es:

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

Настройте application.yml:

server:
  port: 8060

spring:
  elasticsearch:
    rest:
      uris: http://localhost:9200

Создать тестовый объект, артикул:

import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;

import java.util.Date;

@Document(indexName = "article")
public class Article {

    @Id
    private String id;
    private String title;
    private String content;
    private Integer userId;
    private Date createTime;

    // ... igonre getters and setters
}

Вот три способа манипулирования данными ES в Spring Boot:

  • Реализовать интерфейс ElasticsearchRepository.
  • Знакомство с ElasticsearchRestTemplate
  • Знакомство с ElasticsearchOperations

Реализуйте соответствующий репозиторий:

import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;

public interface ArticleRepository extends ElasticsearchRepository<Article, String> {

}

Затем вы можете использовать этот репозиторий статей для управления данными статьи в ES. Мы не создавали здесь вручную индекс, соответствующий этой статье, который по умолчанию создается с помощью elasticsearch.

Следующие интерфейсы реализуют такие операции, как вставка, обновление, запрос на подкачку, запрос на прокрутку и удаление данных es при весенней загрузке. можно использовать как ссылку. Среди них Repository используется для получения, сохранения и удаления данных ES, а ElasticsearchRestTemplate или ElasticsearchOperations — для выполнения запросов на подкачку/прокрутку.

Получить/удалить данные по id

	@Autowired
    private ArticleRepository articleRepository;

    @GetMapping("{id}")
    public JsonResult findById(@PathVariable String id) {
        Optional<Article> article = articleRepository.findById(id);
        JsonResult jsonResult = new JsonResult(true);
        jsonResult.put("article", article.orElse(null));
        return jsonResult;
    }

	@DeleteMapping("{id}")
    public JsonResult delete(@PathVariable String id) {
        // 根据 id 删除
        articleRepository.deleteById(id);
        return new JsonResult(true, "删除成功");
    }

сохранять данные

	@PostMapping("")
    public JsonResult save(Article article) {
        // 新增或更新
        String verifyRes = verifySaveForm(article);
        if (!StringUtils.isEmpty(verifyRes)) {
            return new JsonResult(false, verifyRes);
        }

        if (StringUtils.isEmpty(article.getId())) {
            article.setCreateTime(new Date());
        }

        Article a = articleRepository.save(article);
        boolean res = a.getId() != null;
        return new JsonResult(res, res ? "保存成功" : "");
    }

    private String verifySaveForm(Article article) {
        if (article == null || StringUtils.isEmpty(article.getTitle())) {
            return "标题不能为空";
        } else if (StringUtils.isEmpty(article.getContent())) {
            return "内容不能为空";
        }

        return null;
    }

Данные запроса пейджинга

    @Autowired
    private ElasticsearchRestTemplate elasticsearchRestTemplate;

    @Autowired
    ElasticsearchOperations elasticsearchOperations;

    @GetMapping("list")
    public JsonResult list(Integer currentPage, Integer limit) {
        if (currentPage == null || currentPage < 0 || limit == null || limit <= 0) {
            return new JsonResult(false, "请输入合法的分页参数");
        }
        // 分页列表查询
        // 旧版本的 Repository 中的 search 方法被废弃了。
        // 这里采用 ElasticSearchRestTemplate 或 ElasticsearchOperations 来进行分页查询

        JsonResult jsonResult = new JsonResult(true);
        NativeSearchQuery query = new NativeSearchQuery(new BoolQueryBuilder());
        query.setPageable(PageRequest.of(currentPage, limit));

        // 方法1:
        SearchHits<Article> searchHits = elasticsearchRestTemplate.search(query, Article.class);

        // 方法2:
        // SearchHits<Article> searchHits = elasticsearchOperations.search(query, Article.class);

        List<Article> articles = searchHits.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
        jsonResult.put("count", searchHits.getTotalHits());
        jsonResult.put("articles", articles);
        return jsonResult;
    }

Прокрутить данные запроса

	@GetMapping("scroll")
    public JsonResult scroll(String scrollId, Integer size) {
        // 滚动查询 scroll api
        if (size == null || size <= 0) {
            return new JsonResult(false, "请输入每页查询数");
        }
        NativeSearchQuery query = new NativeSearchQuery(new BoolQueryBuilder());
        query.setPageable(PageRequest.of(0, size));
        SearchHits<Article> searchHits = null;
        if (StringUtils.isEmpty(scrollId)) {
            // 开启一个滚动查询,设置该 scroll 上下文存在 60s
            // 同一个 scroll 上下文,只需要设置一次 query(查询条件)
            searchHits = elasticsearchRestTemplate.searchScrollStart(60000, query, Article.class, IndexCoordinates.of("article"));
            if (searchHits instanceof SearchHitsImpl) {
                scrollId = ((SearchHitsImpl) searchHits).getScrollId();
            }
        } else {
            // 继续滚动
            searchHits = elasticsearchRestTemplate.searchScrollContinue(scrollId, 60000, Article.class, IndexCoordinates.of("article"));
        }

        List<Article> articles = searchHits.getSearchHits().stream().map(SearchHit::getContent).collect(Collectors.toList());
        if (articles.size() == 0) {
            // 结束滚动
            elasticsearchRestTemplate.searchScrollClear(Collections.singletonList(scrollId));
            scrollId = null;
        }

        if (scrollId == null) {
            return new JsonResult(false, "已到末尾");
        } else {
            JsonResult jsonResult = new JsonResult(true);
            jsonResult.put("count", searchHits.getTotalHits());
            jsonResult.put("size", articles.size());
            jsonResult.put("articles", articles);
            jsonResult.put("scrollId", scrollId);
            return jsonResult;
        }

    }

Глубокая разбивка на страницы ES против запроса прокрутки

В последний раз, когда я столкнулся с проблемой, мой коллега сказал мне, что интерфейс для получения журналов работает слишком медленно, и спросил, могу ли я его оптимизировать. Мы начали использовать глубокое пейджинг, то есть 1, 2, 3.. 10. Такой пейджинговый запрос имеет много условий запроса (более десяти параметров) и большой объем данных запроса (около 200 миллионов данных в одном лог-индексе).

Причина низкой скорости запроса подкачки: запрос подкачки ES, такой как запрос 100-й страницы данных, 10 записей на страницу, первые 100 * 10 обращений из каждого раздела (осколка, индекс по умолчанию равен 5 осколкам) Данные проверяются out, а затем координирующий узел выполняет такие операции, как слияние, и, наконец, выдает данные на странице 100. То есть данные, которые реально загружаются в память, гораздо более чем идеальны.

Таким образом, чем больше сегмент индекса, тем больше запрашиваемых страниц и тем ниже скорость запроса. По умолчанию max_result_window в ES равен 10 000, то есть при нормальных обстоятельствах, когда 10 000 фрагментов данных запрашиваются путем подкачки, следующая страница данных не будет возвращена.

Если вам не нужно пропускать страницы, например, прямой запрос 100-й страницы данных, или объем данных очень велик, вы можете рассмотреть возможность использования запроса прокрутки. В запросе прокрутки в первый раз вам необходимо открыть контекст прокрутки в соответствии с параметрами запроса и установить время кэширования контекста. Последующую прокрутку необходимо выполнять только в соответствии со значением scrollId, возвращенным в первый раз.

прокрутка поддерживает только прокрутку вниз. Если вы хотите прокрутить назад, вы также можете кэшировать результаты запроса в соответствии с scrollId, чтобы вы могли прокручивать вверх и вниз до запроса — так же, как прокручивать вверх и вниз при поиске часто используемых продуктов Taobao.


В последнее время я систематически изучаю Redis, RabbitMQ, ES и другие технологии, уделяя особое внимание принципам, базовым слоям, параллелизму и др. Совместное использование связанных технологий будет осуществляться постепенно. Добро пожаловать в публичный аккаунт:обезьяний язык(Я БЫ:JavaApes)