Elasticsearch — это гибкий и мощный распределенный механизм поиска и анализа в реальном времени для облака, основанный на Apache Lucene, который предоставляет возможности полнотекстового поиска. Он ориентирован на документы и не содержит схем.
Asciidoctor — это чистый процессор Ruby для преобразования исходных файлов и строк AsciiDoc в HTML 5 , DocBook 4.5 и другие форматы. Помимо части Asciidoctor Ruby , существует проект интеграции Asciidoctor-java, который позволяет нам вызывать функции Asciidoctor из Java, не замечая, что выполняется код Ruby .
В этом посте мы увидим, как мы можем использовать Elasticsearch над документами AsciiDoc, чтобы сделать их доступными для поиска по информации заголовка или по их содержимому.
Давайте добавим необходимые зависимости:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
<dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <dependency> <groupId>com.googlecode.lambdaj</groupId> <artifactId>lambdaj</artifactId> <version>2.3.3</version> </dependency> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>0.90.1</version> </dependency> <dependency> <groupId>org.asciidoctor</groupId> <artifactId>asciidoctor-java-integration</artifactId> <version>0.1.3</version> </dependency> </dependencies> |
Библиотека Lambdaj используется для преобразования файлов AsciiDoc в документы json.
Теперь мы можем запустить экземпляр Elasticsearch, который в нашем случае будет встроенным экземпляром.
|
1
|
node = nodeBuilder().local(true).node(); |
Следующим шагом является анализ заголовка документа AsciiDoc , чтение его содержимого и преобразование его в документ json .
Примером документа json, хранящегося в Elasticsearch, может быть:
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
|
{ "title":"Asciidoctor Maven plugin 0.1.2 released!", "authors":[ { "author":"Jason Porter", "email":"example@mail.com" } ], "version":null, "content":"= Asciidoctor Maven plugin 0.1.2 released!.....", "tags":[ "release", "plugin" ]} |
А для преобразования файла AsciiDoc в документ json мы будем использовать класс XContentBuilder , предоставляемый Java API Elasticsearch, для программного создания документов json .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
|
package com.lordofthejars.asciidoctor;import static org.elasticsearch.common.xcontent.XContentFactory.*;import java.io.File;import java.io.FileInputStream;import java.io.FileNotFoundException;import java.io.IOException;import java.util.List;import org.asciidoctor.Asciidoctor;import org.asciidoctor.Author;import org.asciidoctor.DocumentHeader;import org.asciidoctor.internal.IOUtils;import org.elasticsearch.common.xcontent.XContentBuilder;import ch.lambdaj.function.convert.Converter;public class AsciidoctorFileJsonConverter implements Converter<File, XContentBuilder> { private Asciidoctor asciidoctor; public AsciidoctorFileJsonConverter() { this.asciidoctor = Asciidoctor.Factory.create(); } public XContentBuilder convert(File asciidoctor) { DocumentHeader documentHeader = this.asciidoctor.readDocumentHeader(asciidoctor); XContentBuilder jsonContent = null; try { jsonContent = jsonBuilder() .startObject() .field("title", documentHeader.getDocumentTitle()) .startArray("authors"); Author mainAuthor = documentHeader.getAuthor(); jsonContent.startObject() .field("author", mainAuthor.getFullName()) .field("email", mainAuthor.getEmail()) .endObject(); List<Author> authors = documentHeader.getAuthors(); for (Author author : authors) { jsonContent.startObject() .field("author", author.getFullName()) .field("email", author.getEmail()) .endObject(); } jsonContent.endArray() .field("version", documentHeader.getRevisionInfo().getNumber()) .field("content", readContent(asciidoctor)) .array("tags", parseTags((String)documentHeader.getAttributes().get("tags"))) .endObject(); } catch (IOException e) { throw new IllegalArgumentException(e); } return jsonContent; } private String[] parseTags(String tags) { tags = tags.substring(1, tags.length()-1); return tags.split(", "); } private String readContent(File content) throws FileNotFoundException { return IOUtils.readFull(new FileInputStream(content)); } } |
По сути, мы создаем документ json , вызывая методы startObject для запуска нового объекта, метод полей для добавления новых полей и startArray для запуска массива. Затем этот конструктор будет использоваться для визуализации эквивалентного объекта в формате json . Обратите внимание, что мы используем метод readDocumentHeader из класса Asciidoctor, который возвращает атрибуты заголовка из файла AsciiDoc без чтения и рендеринга всего документа. И, наконец, поле содержимого устанавливается со всем содержимым документа.
И теперь мы готовы начать индексацию документов. Обратите внимание, что метод populateData получает в качестве параметра объект Client . Этот объект из Elasticsearch Java API и представляет собой соединение с базой данных Elasticsearch .
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
import static ch.lambdaj.Lambda.convert;//....private void populateData(Client client) throws IOException { List<File> asciidoctorFiles = new ArrayList<File>() {{ add(new File("target/test-classes/java_release.adoc")); add(new File("target/test-classes/maven_release.adoc")); }}; List<XContentBuilder> jsonDocuments = convertAsciidoctorFilesToJson(asciidoctorFiles); for (int i=0; i < jsonDocuments.size(); i++) { client.prepareIndex("docs", "asciidoctor", Integer.toString(i)).setSource(jsonDocuments.get(i)).execute().actionGet(); } client.admin().indices().refresh(new RefreshRequest("docs")).actionGet();}private List<XContentBuilder> convertAsciidoctorFilesToJson(List<File> asciidoctorFiles) { return convert(asciidoctorFiles, new AsciidoctorFileJsonConverter());} |
Важно отметить, что первая часть алгоритма конвертирует все наши файлы AsciiDoc (в нашем случае два) в экземпляры XContentBuilder с использованием предыдущего класса конвертера и метода convert проекта Lambdaj .
Если вы хотите, вы можете взглянуть на оба документа, использованных в этом примере, по адресу https://github.com/asciidoctor/asciidoctor.github.com/blob/develop/news/asciidoctor-java-integration-0-1-3- release.adoc и https://github.com/asciidoctor/asciidoctor.github.com/blob/develop/news/asciidoctor-maven-plugin-0-1-2-released.adoc .
Следующая часть — вставка документов в один указатель. Это делается с помощью метода prepareIndex , который требует имя индекса ( docs ), тип индекса ( asciidoctor ) и идентификатор вставляемого документа. Затем мы вызываем метод setSource, который преобразует объект XContentBuilder в json , и, наконец, вызывая execute (). ActionGet () , данные отправляются в базу данных.
Последний шаг необходим только потому, что мы используем встроенный экземпляр Elasticsearch (в производстве эта часть не требуется), который обновляет индексы, вызывая метод refresh .
После этого мы можем начать запрашивать Elasticsearch для получения информации из наших документов AsciiDoc .
Давайте начнем с очень простого примера, который возвращает все вставленные документы:
|
1
|
SearchResponse response = client.prepareSearch().execute().actionGet(); |
Далее мы будем искать все документы, которые были написаны Алексом Сото, который в нашем случае является одним.
|
1
2
3
4
5
|
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;//....QueryBuilder matchQuery = matchQuery("author", "Alex Soto");QueryBuilder matchQuery = matchQuery("author", "Alexander Soto"); |
Обратите внимание, что я ищу автора поля строку Другой документ написан Джейсоном . Но интересно сказать, что если вы ищете Александра Сото , тот же документ будет возвращен; Elasticsearch достаточно умен, чтобы знать, что Алекс и Александр очень похожи, поэтому он также возвращает документ.
Еще вопросы, как насчет поиска документов, написанных кем-то, кого зовут Алекс , но не Сото .
|
1
2
3
4
5
|
import static org.elasticsearch.index.query.QueryBuilders.fieldQuery;//....QueryBuilder matchQuery = fieldQuery("author", "+Alex -Soto"); |
И, конечно, в этом случае результаты не возвращаются. Обратите внимание, что в этом случае мы используем запрос поля вместо термина запроса, и мы используем символы + и — для исключения и включения слов.
Также вы можете найти все документы, которые содержат слово, выпущенное в заголовке .
|
1
2
3
4
5
|
import static org.elasticsearch.index.query.QueryBuilders.matchQuery;//....QueryBuilder matchQuery = matchQuery("title", "released"); |
И, наконец, давайте найдем все документы, в которых говорится о выпуске 0.1.2, в данном случае об этом говорит только один документ, а в другом — 0.1.3.
|
1
|
QueryBuilder matchQuery = matchQuery("content", "0.1.2"); |
Теперь нам осталось только отправить запрос в базу данных Elasticsearch , что делается с помощью метода prepareSearch .
|
01
02
03
04
05
06
07
08
09
10
11
|
SearchResponse response = client.prepareSearch("docs") .setTypes("asciidoctor") .setQuery(matchQuery) .execute() .actionGet(); SearchHits hits = response.getHits(); for (SearchHit searchHit : hits) { System.out.println(searchHit.getSource().get("content"));} |
Обратите внимание, что в этом случае мы печатаем содержимое AsciiDoc через консоль, но вы можете использовать метод asciidoctor.render (String content, Options options) для отображения содержимого в требуемом формате.
Итак, в этом посте мы увидели, как индексировать документы с помощью Elasticsearch , как получить некоторую важную информацию из файлов AsciiDoc с помощью проекта интеграции Asciidoctor-java и, наконец, как выполнить некоторые запросы к вставленным документам. Конечно, в Elasticsearch есть и другие запросы, но цель этого поста не в том, чтобы изучить все возможности Elasticsearch .
Также, как следствие, отметьте, насколько важно использовать формат AsciiDoc для написания ваших документов. Без особых усилий вы можете создать поисковую систему для вашей документации. С другой стороны, представьте весь код, который потребуется для его реализации, используя любой проприетарный двоичный формат, такой как Microsoft Word . Таким образом, мы показали другую причину использовать AsciiDoc вместо других форматов.
