Elasticsearch клиент для Rust: elasticsearch-rs
Elasticsearch — мощная распределённая поисковая и аналитическая система, построенная на Apache Lucene. Для взаимодействия с ней из приложений на Rust существует официальный клиент — elasticsearch-rs. Эта библиотека предоставляет полный набор инструментов для работы с REST API Elasticsearch, включая индексацию, поиск, агрегации и управление кластером. В этой статье мы подробно разберём установку, настройку и основные возможности elasticsearch-rs с примерами кода.
Что такое elasticsearch-rs?
elasticsearch-rs — это официальный асинхронный клиент Elasticsearch для языка Rust, разрабатываемый компанией Elastic. Он основан на популярной библиотеке асинхронного HTTP-клиента reqwest и предоставляет типобезопасный API для всех эндпоинтов Elasticsearch. Основные преимущества:
- Полная поддержка Elasticsearch 7.x и 8.x — все основные операции: CRUD, поиск, агрегации, управление индексами.
- Асинхронность — использует async/await, что позволяет эффективно обрабатывать множество запросов.
- Типобезопасность — структуры запросов и ответов строго типизированы, что снижает количество ошибок времени выполнения.
- Автоматическая сериализация/десериализация — через
serdeиserde_json. - Поддержка аутентификации — Basic Auth, API ключи, SSL/TLS.
Установка
Добавьте зависимость в Cargo.toml:
[dependencies]
elasticsearch = "8.14.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1", features = ["full"] }
Базовое подключение
Создадим клиент, подключающийся к локальному экземпляру Elasticsearch:
use elasticsearch::Elasticsearch;
use elasticsearch::http::transport::Transport;
use serde_json::json;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
let transport = Transport::single_node("http://localhost:9200")?;
let client = Elasticsearch::new(transport);
// Проверка соединения
let response = client.ping().send().await?;
println!("Elasticsearch доступен: {}", response.status_code());
Ok(())
}
Основные операции: CRUD
1. Индексация документа
Добавим документ с информацией о книге в индекс books:
#[derive(Serialize, Deserialize, Debug)]
struct Book {
title: String,
author: String,
year: u16,
isbn: String,
}
let book = Book {
title: "Rust в действии".into(),
author: "Тим Макнамара".into(),
year: 2023,
isbn: "978-5-4461-2345-6".into(),
};
let response = client
.index(IndexParts::IndexId("books", "1"))
.body(json!(book))
.send()
.await?;
println!("Документ проиндексирован: {:?}", response.body());
2. Получение документа
let response = client
.get(GetParts::IndexId("books", "1"))
.send()
.await?;
if response.status_code().is_success() {
let book: Book = response.json().await?;
println!("Найден документ: {:?}", book);
}
3. Обновление документа
let response = client
.update(UpdateParts::IndexId("books", "1"))
.body(json!({
"doc": { "year": 2024 }
}))
.send()
.await?;
println!("Обновлён: {:?}", response.body());
4. Удаление документа
let response = client
.delete(DeleteParts::IndexId("books", "1"))
.send()
.await?;
println!("Удалён: {:?}", response.body());
Поиск с использованием Query DSL
Выполним полнотекстовый поиск по полю title:
use elasticsearch::SearchParts;
let response = client
.search(SearchParts::Index(&["books"]))
.body(json!({
"query": {
"match": {
"title": "Rust"
}
}
}))
.send()
.await?;
let body = response.json::<serde_json::Value>().await?;
let hits = body["hits"]["hits"].as_array().unwrap();
for hit in hits {
let source = &hit["_source"];
println!("Найдена книга: {}", source["title"]);
}