предисловие
В предыдущем разделе мы обсудили основные концепции ES и выбор схем миграции данных по разным сценариям. В этой статье мы обсудим, как выполнить интеграцию со Spring boot, и чтобы без проблемMysql
Как нам перейти на ES"翻译SQL
".
1. Spring Boot интегрирует ES
Первый шаг — реализовать интеграцию Spring boot и ES.Java REST Client
,spring-data-elasticsearch
Есть два способа, здесь я рекомендую использовать официальный Elasticsearch, предоставленныйJava High Level REST Client
Также удобно использовать облачный сервис ES от Alibaba Cloud в производственной среде. Информация о ключевой версии выглядит следующим образом:
- Кластер ES: 7.3.0
- Зависимости, связанные с ES: 7.3.0
Здесь следует отметить две вещи:
-
High Level Client
Совместимость с более ранними версиями, например, версия 7.3.0Java High Level REST Client
Может быть гарантированно больше или равно версии 7.3.0Elasticsearch
Кластерная связь. Чтобы обеспечить максимальное использование функций последней версии клиента, рекомендуется, чтобы версия клиента высокого уровня совпадала с версией кластера. -
В процессе интеграции могут быть некоторые ямки, т.к.
Spring Boot
версия,ES
версия кластера,High Level Client
Между версиями демо-версии будут «ассоциативные отношения», поэтому, когда демо-версия не запускается нормально, все, что вы можете сделать, это попробовать больше.High Level Client
Версия.
1. пом-зависимость
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.3.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>7.3.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-client</artifactId>
<version>7.3.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.plugin</groupId>
<artifactId>rank-eval-client</artifactId>
<version>7.3.0</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.plugin</groupId>
<artifactId>lang-mustache-client</artifactId>
<version>7.3.0</version>
</dependency>
2. Инициализировать клиент
@Configuration
public class EsConfig {
@Value("${elasticsearch.host}")
public String host;
/**
* 之前使用transport的接口的时候是9300端口,现在使用HighLevelClient则是9200端口
*/
@Value("${elasticsearch.port:9200}")
public int port;
public static final String SCHEME = "http";
@Value("${elasticsearch.username:admin}")
public String username;
@Value("${elasticsearch.authenticationPassword}")
public String authenticationPassword;
@Bean(name = "remoteHighLevelClient")
public RestHighLevelClient restHighLevelClient() {
final CredentialsProvider credentialsProvider = new BasicCredentialsProvider();
credentialsProvider.setCredentials(AuthScope.ANY, new UsernamePasswordCredentials(username,
authenticationPassword));
RestClientBuilder builder = RestClient.builder(new HttpHost(host, port, SCHEME)).
setHttpClientConfigCallback(httpClientBuilder -> httpClientBuilder
.setDefaultCredentialsProvider(credentialsProvider));
return new RestHighLevelClient(builder);
}
}
В приведенном выше коде следует отметить, что информация об аутентификации имени пользователя и аутентификационного пароля находится вKibana
установить в.
2. Java-API
Следующие фрагменты кода могут нормально выполняться в модульных тестах. Прежде чем выполнять модульные тесты ниже, мы сначала создадим_template
, вы можете выбрать те, которые предоставлены в KibanaDev Tools
выполнить внутри.
PUT _template/hero_template
{
"index_patterns":[
"hero*"
],
"mappings":{
"properties":{
"@timestamp":{
"type":"date"
},
"id":{
"type":"integer"
},
"name":{
"type":"keyword"
},
"country":{
"type":"keyword"
},
"birthday":{
"type":"keyword"
},
"longevity":{
"type":"integer"
}
}
}
}
1. Создайте индекс
@Test
public void createIndex() throws IOException {
IndexRequest request = new IndexRequest("hero");
request.id("1");
Map<String, String> map = new HashMap<>();
map.put("id", "1");
map.put("name", "曹操");
map.put("country", "魏");
map.put("birthday", "公元155年");
map.put("longevity", "65");
request.source(map);
IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
long version = indexResponse.getVersion();
assertEquals(DocWriteResponse.Result.CREATED, indexResponse.getResult());
assertEquals(1, version);
}
В ES индекс — это место, где мы храним и запрашиваем данные.логическая единица, что соответствует концепции таблиц в Mysql после ES7.0. В приведенном выше коде мы создаемhero
index, затем мы создаем карту в качестве первого фрагмента данных, который мы вставляем, а затем устанавливаемIndexRequest
в объекте запроса.
2, пакетная вставка
@Test
public void bulkRequestTest() throws IOException {
BulkRequest request = new BulkRequest();
request.add(new IndexRequest("hero").id("2")
.source(XContentType.JSON,"id", "2", "name", "刘备", "country", "蜀", "birthday", "公元161年", "longevity", "61"));
request.add(new IndexRequest("hero").id("3")
.source(XContentType.JSON,"id", "3", "name", "孙权", "country", "吴", "birthday", "公元182年", "longevity", "61"));
request.add(new IndexRequest("hero").id("4")
.source(XContentType.JSON,"id", "4", "name", "诸葛亮", "country", "蜀", "birthday", "公元181年", "longevity", "53"));
request.add(new IndexRequest("hero").id("5")
.source(XContentType.JSON,"id", "5", "name", "司马懿", "country", "魏", "birthday", "公元179年", "longevity", "72"));
request.add(new IndexRequest("hero").id("6")
.source(XContentType.JSON,"id", "6", "name", "荀彧", "country", "魏", "birthday", "公元163年", "longevity", "49"));
request.add(new IndexRequest("hero").id("7")
.source(XContentType.JSON,"id", "7", "name", "关羽", "country", "蜀", "birthday", "公元160年", "longevity", "60"));
request.add(new IndexRequest("hero").id("8")
.source(XContentType.JSON,"id", "8", "name", "周瑜", "country", "吴", "birthday", "公元175年", "longevity", "35"));
BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);
assertFalse(bulkResponse.hasFailures());
}
Данные, запрашиваемые в кибане, выглядят следующим образом
Наши последующие запросы, обновления и другие операции основаны на данных здесь.
3. Обновить данные
@Test
public void updateTest() throws IOException {
Map<String, Object> jsonMap = new HashMap<>();
jsonMap.put("country", "魏");
UpdateRequest request = new UpdateRequest("hero", "7").doc(jsonMap);
UpdateResponse updateResponse = client.update(request, RequestOptions.DEFAULT);
assertEquals(DocWriteResponse.Result.UPDATED, updateResponse.getResult());
}
Если приведенный выше код выражен в SQL, он будет выглядеть следующим образом.
> update hero set country='魏' where id=7;
4. Вставить/обновить данные
@Test
public void insertOrUpdateOne(){
Hero hero = new Hero();
hero.setId(5);
hero.setName("曹丕");
hero.setCountry("魏");
hero.setBirthday("公元187年");
hero.setLongevity(39);
IndexRequest request = new IndexRequest("hero");
request.id(hero.getId().toString());
request.source(JSON.toJSONString(hero), XContentType.JSON);
try {
IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT); // 1
assertEquals(DocWriteResponse.Result.UPDATED, indexResponse.getResult());
} catch (Exception e) {
throw new RuntimeException(e);
}
}
Обратите внимание, что строка кода, отмеченная 1 в приведенном выше коде, аналогична созданию индекса ранее? Здесь, используя метод index(), мы можем легко добитьсяСоздавайте индексы, вставляйте данные и обновляйте данные в одном, создать индекс, если указанный индекс не существует, вставить, если данные не существуют, и обновить, если данные существуют.
5. Удалить данные
@Test
public void deleteByIdTest() throws IOException {
DeleteRequest deleteRequest = new DeleteRequest("hero");
deleteRequest.id("1");
DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
assertEquals(DocWriteResponse.Result.DELETED, deleteResponse.getResult());
}
Выше мы удалили данные с id=1, созданные ранее, и соответствующий SQL выглядит следующим образом:
> delete from hero where id=1;
Конечно, в ES мы можем не только использовать первичный ключ для удаления, мы также можем удалять через другие условия поля.
@Test
public void deleteByQueryRequestTest() throws IOException {
DeleteByQueryRequest request = new DeleteByQueryRequest("hero");
request.setConflicts("proceed");
request.setQuery(new TermQueryBuilder("country", "吴"));
BulkByScrollResponse bulkResponse =
client.deleteByQuery(request, RequestOptions.DEFAULT);
assertEquals(0, bulkResponse.getBulkFailures().size());
}
Соответствующий SQL:
> delete from hero where country='吴';
6. Составная операция
Добавления и удаления вышеМожет работать только с одним типом одновременно, а ES также предоставляет наммного типовоперация, такая как код ниже
@Test
public void bulkDiffRequestTest() throws IOException {
BulkRequest request = new BulkRequest();
request.add(new DeleteRequest("hero", "3"));
request.add(new UpdateRequest("hero", "7")
.doc(XContentType.JSON,"longevity", "70"));
BulkResponse bulkResponse = client.bulk(request, RequestOptions.DEFAULT);
BulkItemResponse[] bulkItemResponses = bulkResponse.getItems();
for (BulkItemResponse item : bulkItemResponses){
DocWriteResponse itemResponse = item.getResponse();
switch (item.getOpType()) {
case UPDATE:
UpdateResponse updateResponse = (UpdateResponse) itemResponse;
break;
case DELETE:
DeleteResponse deleteResponse = (DeleteResponse) itemResponse;
}
assertEquals(RestStatus.OK, item.status());
}
}
мы использовалиBulkRequest
объект, воляDeleteRequest
,UpdateRequest
две операцииadd
прибытьBulkRequet
, то возвращенныйBulkItemResponse[]
Массивы можно классифицировать и обрабатывать в соответствии с различными типами операций. Конечно, насколько мне известно, в настоящее времяMysql
Поддержки подобного синтаксиса нет, если есть, надеюсь, вы оставите сообщение, чтобы меня поправить.
7. Запрос
Вот наше основное внимание.ES поддерживает различные типы запросов, такие как ** «точный» (отличный от СУБД) запрос, нечеткий запрос, корреляционный запрос, запрос диапазона, полнотекстовый поиск, поиск по страницам, функции запроса, такие как сортировка, агрегация** и т. д., большинство функций запросов в Mysql можно реализовать в ES. Это также позволяет нам выбирать между синхронным и асинхронным способами выполнения запросов.
Одиночный запрос условия + ограничение
@Test
public void selectByUserTest(){
SearchRequest request = new SearchRequest("hero");
SearchSourceBuilder builder = new SearchSourceBuilder();
builder.query(new TermQueryBuilder("country", "魏"));
// 相当于mysql里边的limit 1;
builder.size(1);
request.source(builder);
try {
SearchResponse response = client.search(request, RequestOptions.DEFAULT);
SearchHit[] hits = response.getHits().getHits();
assertEquals(1, hits.length);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
В приведенном выше модульном тесте мы используемuser
В качестве условия запроса и ограничения количества возвращаемых элементов, аналогично SQL, следующим образом.
> select * from posts where country='魏' limit 1;
Запрос с несколькими условиями + сортировка + пейджинг
@Test
public void boolQueryTest(){
SearchRequest request = new SearchRequest("hero");
SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
boolQueryBuilder.must(termQuery("country", "魏"));
boolQueryBuilder.must(rangeQuery("longevity").gte(50));
sourceBuilder.query(boolQueryBuilder);
sourceBuilder.from(0).size(2);
sourceBuilder.query(boolQueryBuilder);
sourceBuilder.sort("longevity", SortOrder.DESC);
request.source(sourceBuilder);
SearchResponse response = null;
try {
response = client.search(request, RequestOptions.DEFAULT);
} catch (IOException e) {
log.error("Query by Condition execution failed: {}", e.getMessage(), e);
}
assert response != null;
assertEquals(0, response.getShardFailures().length);
SearchHit[] hits = response.getHits().getHits();
List<Hero> herosList = new ArrayList<>(hits.length);
for (SearchHit hit : hits) {
herosList.add(JSON.parseObject(hit.getSourceAsString(), Hero.class));
}
log.info("print info: {}, size: {}", herosList.toString(), herosList.size());
}
Приведенное выше запрашивает героев группы Цао Вэй с продолжительностью жизни более 50 лет и сортирует их по продолжительности жизни от большего к меньшему.Перехватываются только два героя и их соответствующий sql:
> select * from hero where country='魏' and longevity >= 50 order by longevity DESC limit 2;
Здесь следует отметить, что когда мы используем запросы с несколькими условиями в API, предоставляемом ES, нам необходимо инкапсулировать несколько условий вBoolQueryBuilder
В объекте он поддерживает следующие типы запросов
private static final String MUSTNOT = "mustNot";
private static final String MUST_NOT = "must_not";
private static final String FILTER = "filter";
private static final String SHOULD = "should";
private static final String MUST = "must";
Ссылка на конкретное объяснениеофициальная документация
Суммировать
В этой части мы сначала расскажем, как интегрировать Spring boot и ES, а также порекомендуем лучшие практики — принятиеJava High Level REST Client
для создания нашего API, а затем поделиться соответствующими зависимостями и как инициализировать клиент.
Затем мы начинаем использоватьHigh Level REST Client
Реализованы создание индексов, пакетная вставка, обновление данных, вставка/обновление данных, удаление данных и составные операции. Наконец, мы используем два простых примера для запроса данных. Конечно, есть много примеров запросов, которые не были показаны. кЗапрос официального сайтаинструкции по использованию.