Elasticsearch клиент для Rust: elasticsearch-rs
elasticsearch-rs — это официальный асинхронный клиент для взаимодействия с Elasticsearch из экосистемы Rust. Библиотека предоставляет полный набор инструментов для работы с поисковым движком: от простых CRUD операций до сложных агрегаций и bulk-запросов. Разрабатывается и поддерживается командой Elastic, что гарантирует актуальность API и совместимость с последними версиями Elasticsearch.
Ключевые возможности
- Полная поддержка Elasticsearch API — индексация, поиск, агрегации, управление индексами, маппингом и настройками кластера.
- Асинхронность — построена на базе tokio и reqwest, что позволяет обрабатывать тысячи запросов без блокировки потоков.
- Типобезопасность — все запросы и ответы строго типизированы, ошибки валидации отлавливаются на этапе компиляции.
- Поддержка bulk-операций — массовая загрузка и обновление документов с автоматической обработкой ошибок.
- SSL/TLS и аутентификация — встроенная поддержка HTTPS, Basic Auth, API-ключей и токенов.
- Сериализация через serde — документы автоматически преобразуются в/из JSON с помощью крейта serde.
Установка и настройка
Добавьте зависимость в Cargo.toml:
[dependencies]
elasticsearch = "8.14.0"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
tokio = { version = "1.0", features = ["full"] }
Базовое подключение к локальному экземпляру Elasticsearch:
use elasticsearch::{Elasticsearch, Error};
use url::Url;
#[tokio::main]
async fn main() -> Result<(), Error> {
let client = Elasticsearch::new(
elasticsearch::http::transport::Transport::single_node(
Url::parse("http://localhost:9200")?
)?
);
// Проверка соединения
let response = client.ping().send().await?;
println!("Elasticsearch доступен: {}", response.status_code());
Ok(())
}
Работа с документами
Индексация документа
Определим структуру для сериализации:
use serde::{Serialize, Deserialize};
#[derive(Debug, Serialize, Deserialize)]
struct Book {
title: String,
author: String,
year: u16,
isbn: String,
}
Индексация нового документа:
use elasticsearch::{IndexParts, SearchParts};
use serde_json::json;
async fn index_book(client: &Elasticsearch, book: &Book) -> Result<(), Error> {
let response = client
.index(IndexParts::IndexId("books", &book.isbn))
.body(book)
.send()
.await?;
println!("Документ проиндексирован: {:?}", response);
Ok(())
}
Поиск по индексу
Пример полнотекстового поиска:
async fn search_books(client: &Elasticsearch, query: &str) -> Result, Error> {
let response = client
.search(SearchParts::Index(&["books"]))
.body(json!({
"query": {
"multi_match": {
"query": query,
"fields": ["title^2", "author"]
}
}
}))
.send()
.await?;
let response_body = response.json::().await?;
let hits = response_body["hits"]["hits"]
.as_array()
.unwrap_or(&vec![]);
let books: Vec = hits
.iter()
.filter_map(|hit| {
let source = hit["_source"].clone();
serde_json::from_value(source).ok()
})
.collect();
Ok(books)
}
Bulk-операции для массовой загрузки
Эффективная загрузка тысяч документов:
use elasticsearch::BulkParts;
use elasticsearch::http::request::BulkBody;
async fn bulk_index_books(client: &Elasticsearch, books: &[Book]) -> Result<(), Error> {
let mut body: Vec> = Vec::new();
for book in books {
body.push(BulkBody::index(book));
}
let response = client
.bulk(BulkParts::Index("books"))
.body(body)
.send()
.await?;
let response_body = response.json::().await?;
if response_body["errors"].as_bool().unwrap_or(true) {
eprintln!("Обнаружены ошибки при массовой загрузке");
}
Ok(())
}
Работа с агрегациями
Пример агрегации для получения статистики по авторам: