Elasticsearch клиент для Swift (iOS/macOS)
Elasticsearch — это мощная распределённая поисковая система, построенная на основе Apache Lucene. Для разработчиков под iOS и macOS доступен официальный клиент elasticsearch-swift, который предоставляет удобный интерфейс для работы с Elasticsearch из приложений на Swift. Библиотека полностью поддерживает REST API Elasticsearch, включая индексацию, поиск, агрегации и управление кластером.
Установка и настройка
Клиент распространяется через Swift Package Manager (SPM). Для добавления в проект:
// В Package.swift
dependencies: [
.package(url: "https://github.com/elastic/elasticsearch-swift.git", from: "8.10.0")
]
Или через Xcode: File → Add Packages → введите URL репозитория. После установки импортируйте библиотеку:
import Elasticsearch
Основные возможности
- Индексация документов — добавление, обновление и удаление записей в индексах.
- Поиск — полнотекстовый, по полям, с фильтрами и сортировкой.
- Агрегации — статистические вычисления, группировки, гистограммы.
- Управление кластером — создание/удаление индексов, настройка шардов и реплик.
- Поддержка async/await — асинхронные запросы с использованием современной модели Swift Concurrency.
Пример использования: подключение и индексация
Создадим клиент и проиндексируем несколько документов:
import Elasticsearch
let client = ElasticsearchClient(
host: "https://your-cluster-url:9200",
apiKey: "your-api-key"
)
struct Book: Codable {
let id: String
let title: String
let author: String
let year: Int
}
let books = [
Book(id: "1", title: "Swift Programming", author: "John Doe", year: 2023),
Book(id: "2", title: "iOS Development", author: "Jane Smith", year: 2024)
]
for book in books {
let response = try await client.index(
index: "books",
id: book.id,
body: book
)
print("Indexed: \(response.result)")
}
Поиск с использованием Query DSL
Elasticsearch поддерживает мощный язык запросов. Пример полнотекстового поиска:
import Elasticsearch
let query: [String: Any] = [
"query": [
"bool": [
"must": [
["match": ["title": "Swift"]]
],
"filter": [
["range": ["year": ["gte": 2023]]]
]
]
]
]
let searchResponse = try await client.search(
index: "books",
body: query
)
if let hits = searchResponse.hits?.hits {
for hit in hits {
if let source = hit.source {
let book = try JSONDecoder().decode(Book.self, from: source)
print("Found: \(book.title) by \(book.author)")
}
}
}
Агрегации для аналитики
Пример подсчёта количества книг по годам:
let aggregationQuery: [String: Any] = [
"size": 0,
"aggs": [
"by_year": [
"terms": ["field": "year", "size": 10]
]
]
]
let aggResponse = try await client.search(
index: "books",
body: aggregationQuery
)
if let buckets = aggResponse.aggregations?["by_year"] as? [String: Any],
let bucketList = buckets["buckets"] as? [[String: Any]] {
for bucket in bucketList {
let year = bucket["key"] as? Int ?? 0
let count = bucket["doc_count"] as? Int ?? 0
print("\(year): \(count) books")
}
}
Управление индексами
Создание и удаление индекса с настройками:
let settings: [String: Any] = [
"settings": [
"number_of_shards": 2,
"number_of_replicas": 1
],
"mappings": [
"properties": [
"title": ["type": "text"],
"author": ["type": "keyword"],
"year": ["type": "integer"]
]
]
]
// Создание индекса
let createResponse = try await client.indices.create(
index: "books",
body: settings
)
print("Index created: \(createResponse.acknowledged)")
// Удаление индекса
let deleteResponse = try await client.indices.delete(index: "books")
print("Index deleted: \(deleteResponse.acknowledged)")
Обработка ошибок
Клиент генерирует исключения при сетевых проблемах или ошибках Elasticsearch. Рекомендуется оборачивать вызовы в do-catch:
do {
let response = try await client.search(index: "books", body: query)
// обработка результата
} catch let error as ElasticsearchError {
print("