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" , } ], "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 вместо других форматов.