Мартин Тошев
Elasticsearch е едно от най-популярните приложения за търсене в днешно време, което се използва в редица приложения (като Wikipedia, Stackoverflow и много други). Базирано е на Lucene библиотеката за търсене и една от основните функционалности, които предлага е JSON-базиран език за писане на заявки, които е изграден над Lucene и предоставя доста лесен механизъм за взаимодействие с Elasticsearch платформата. Поддръжката за SQL добавена в Elasticsearch 6.3 предоставя стандартен механизъм за изпълнение на заявки за търсене и е стъпка напред към по-лесното използване на Elasticsearch от страна на програмистите.
Въпреки че стандарта SQL е разработен основно за работа с релационни бази данни, той е вече успешно реализиран в редица нерелационни бази данни (каквато е и Elasticsearch). Други такива пример включват например и приложения като Apache Spark или Apache Ignite (където SQL е в основата на самото приложение). В тази статия ще разгледаме накратко как работи Elasticsearch SQL.
Предварителен сетъп
За да изпробвате примерите показани в статияте е необходимо да си инстралирате и стартирате локално Elasticsearch (поне версия 6.3). В статията е използван Elasticsearch 7.5. Първо ще създадем posts индекса, който съхранява постове за даден форум. Ще използваме официалния Elasticsearch Java клиент , за да добавим данни в индекса без да указваме експлицитно схема за полетата в индекса (Elasticsearch ще я създаде за нас автоматично в такъв случай).
Необходимо е да създадем Maven проект със зависимост към Elasticsearch Java high level client библиотеката:
<dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-high-level-client</artifactId> <version>7.5.0</version> </dependency>
Ще създадем 10000 генерирани документа в posts индекса като използваме следния код:
RestHighLevelClient client = new RestHighLevelClient( RestClient.builder( new HttpHost("localhost", 9200, "http"))); String[] possibleUsers = new String[] {"Martin", "Jim", "John"}; String[] possibleDates = new String[] {"2019-12-15", "2019-12-16", "2019-12-17"}; String[] possibleMessages = new String[] { "Hello, Devstyler !", "Cool set of blog posts. We want more !", "Elasticsearch SQL is great."}; for (int i = 1; i <= 10000; i++) { Map<String, Object> jsonMap = new HashMap<>(); jsonMap.put("user", possibleUsers[ThreadLocalRandom.current().nextInt(0, 3)]); jsonMap.put("date", possibleDates[ThreadLocalRandom.current().nextInt(0, 3)]); jsonMap.put("message", possibleMessages[ThreadLocalRandom.current().nextInt(0, 3)]); IndexRequest request = new IndexRequest("posts") .id(String.valueOf(i)).source(jsonMap); client.index(request, RequestOptions.DEFAULT); } client.close();
Изпълнение на SQL заявките
Може да използваме Kibana, за да потърсим всички документи, които отговарят на потребителя Martin:
POST /_sql?format=txt { "query": "SELECT * FROM posts where user = 'Martin'" }
Друг пример е заявка, която брои всички документи, които съдържат ключовата дума:
POST /_sql?format=txt { "query": "SELECT count(*) FROM posts where message like '%Devstyler%'" }
Ако искаме да изпълним горните заявки през Java приложение имаме няколко опции:
Да използваме Elasticsearch JDBC драйвъра, който обаче е наличен в platinum и enterprise версиите на Elasticsearch;
Да използваме REST клиента на Elasticsearch, които е наличен и в базовата (безплатна) версия.
<dependency> <groupId>org.elasticsearch.client</groupId> <artifactId>elasticsearch-rest-client</artifactId> <version>7.5.0</version> </dependency>
Следния пример връща 10 документа от posts индекса:
RestClient restClient = RestClient.builder( new HttpHost("localhost", 9200, "http")).build(); Request request = new Request("POST", "/_sql"); request.setJsonEntity("{\"query\":\"SELECT * FROM posts limit 10\"}"); Response response = restClient.performRequest(request); String responseBody = EntityUtils.toString(response.getEntity()); System.out.println(responseBody); restClient.close();
За да видим как в действително се изпълнява дадена SQL заявка от Elasticsearch може да използваме translate функционалността предоставена чрез /_sql/translate адреса в Elasticsearch. Може да изпълним следния код в Kibana, за да видим как в действително SQL заявката се транслира до JSON-базирана Elasticsearch заявка:
POST /_sql/translate { "query": "SELECT * FROM posts limit 10", "fetch_size": 10 }
Трябва да получим резултат подобен на следния:
{ "size" : 10, "_source" : { "includes" : [ "message", "user" ], "excludes" : [ ] }, "docvalue_fields" : [ { "field" : "date", "format" : "epoch_millis" } ], "sort" : [ { "_doc" : { "order" : "asc" } } ] }
Elasticsearch SQL характеристики
Видяхме как да изпълняваме заявки в Elasticsearch SQL. В действителност имплементацията на SQL в Elasticsearch е доста богата и включва:
- Различни варианти за форматиране на резултата от SQL заявката (csv, json, yaml и други);
- Използване на JSON-базирани заявки като филтри към дадена SQL заявка;
- Команден инструмент за изпълнение на SQL заявка предоставен от elasticsearch-sql-cli инструмента в инсталацията на Elasticsearch;
От гледна точка на самия SQL стандарта следните страница описва в детайли, какво е реализирано като команди, функции и оператори:
https://www.elastic.co/guide/en/elasticsearch/reference/current/sql-commands.html
https://www.elastic.co/guide/en/elasticsearch/reference/current/sql-functions.html
Заключение
В тази статия демонстрирахме как да използваме Elasticsearch SQL, за да взаимодействаме с Elasticsearch платформата. Има голяма вероятност този механизъм да се наложи над JSON-базирания език за писане на заявки. Въпреки това SQL не е алтернатива на JSON-базирания език, а по-скоро допълнение изградено над него.
Мартин Тошев е ИТ консултант, Java ентусиаст и поддръжник на българската Java User Group. Като такъв, неговите интереси са обвързани с всички технологии около Java, както и Cloud Computing technologies, облачни софтуерни архитектури, Enterprise приложения и NoSQL бази данни.