Статьи

Документы для поиска? Да, ты можешь. Еще одна причина выбрать AsciiDoc


Elasticsearch  — это гибкий и мощный распределенный механизм поиска и анализа в реальном времени для облака, основанный на 
Apache Lucene,  который предоставляет возможности полнотекстового поиска. Он ориентирован на документы и не содержит схем.

Asciidoctor  — это чистый процессор Ruby для преобразования 
 исходных файлов и строк
AsciiDoc в 
HTML 5
DocBook 4.5  и другие форматы. Помимо 
Ruby-  компонента
Asciidoctor 
, существует 
 проект интеграции Asciidoctor-java, который позволяет вызывать 
 функции Asciidoctor из 
Java,  не замечая, что 
 выполняется код Ruby .


В этой статье мы будем видеть ,
как мы можем использовать 
Elasticsearch  над 
AsciiDoc документов , чтобы сделать их доступными для поиска их информации заголовка или их содержанием.

Давайте добавим необходимые зависимости:

<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, который в нашем случае будет встроенным экземпляром.
node = nodeBuilder().local(true).node();

Следующим шагом является анализ  заголовка  документа AsciiDoc , чтение его содержимого и преобразование его в   документ json .

Примером   документа json, хранящегося в  Elasticsearch,  может быть:

{
   "title":"Asciidoctor Maven plugin 0.1.2 released!",
   "authors":[
      {
         "author":"Jason Porter",
         "email":"[email protected]"
      }
   ],
   "version":null,
   "content":"= Asciidoctor Maven plugin 0.1.2 released!.....",
   "tags":[
      "release",
      "plugin"
   ]
}

И для преобразования  AsciiDoc  файла в формате JSON документа мы будем использовать XContentBuilder  класс , который предоставляется  Elasticsearch Java API  для создания  JSon документов программно.

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  метод получает в качестве параметра 
клиентского  объекта. Этот объект из 
Elasticsearch  
Java API и представляет собой соединение с 
 базой данных
Elasticsearch .
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 .

Давайте начнем с очень простого примера, который возвращает все вставленные документы:

SearchResponse response = client.prepareSearch().execute().actionGet();

Далее мы будем искать все документы, которые были написаны  Алексом Сото, который в нашем случае является одним.

import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
//....
QueryBuilder matchQuery =  matchQuery("author", "Alex Soto");
 
QueryBuilder matchQuery =  matchQuery("author", "Alexander Soto");

Обратите внимание, что я ищу
автора поля 
 строку 
Alex Soto , которая возвращает только один. Другой документ написан 
Джейсоном . Но интересно сказать, что если вы ищете 
Александра Сото , тот же документ будет возвращен; 
Elasticsearch  достаточно умен, чтобы знать, что 
Алекс  и 
Александр  очень похожи, поэтому он также возвращает документ.

Еще вопросы, как насчет поиска документов, написанных кем-то, кого зовут  Алекс , но не  Сото .

import static org.elasticsearch.index.query.QueryBuilders.fieldQuery;
 
//....
 
QueryBuilder matchQuery =  fieldQuery("author", "+Alex -Soto");

И, конечно, в этом случае результаты не возвращаются.
Обратите внимание, что в этом случае мы используем
 запрос
поля вместо 
термина  запроса, и мы используем символы + и — для исключения и включения слов.

Также вы можете найти все документы, которые содержат слово,  выпущенное  в  заголовке .

import static org.elasticsearch.index.query.QueryBuilders.matchQuery;
 
//....
 
QueryBuilder matchQuery =  matchQuery("title", "released");

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

QueryBuilder matchQuery =  matchQuery("content", "0.1.2");

Теперь нам осталось только отправить запрос в   базу данных Elasticsearch , что делается с помощью   метода prepareSearch .

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