pub struct SearchClient { /* private fields */ }Expand description
A connection to a cluster.
Cheap to clone — everything in it is shared or small — so one client can live in the application context and be handed to every handler.
Implementations§
Source§impl SearchClient
impl SearchClient
Sourcepub fn new(base_url: impl Into<String>) -> SearchClient
pub fn new(base_url: impl Into<String>) -> SearchClient
Point at a cluster: http://localhost:9200.
A trailing slash is trimmed, because {base}//_search is a 400 that
says nothing about slashes.
Sourcepub fn from_config(config: &Config) -> SearchClient
pub fn from_config(config: &Config) -> SearchClient
Build from config/search.json, so the cluster address and credentials
can come from .env.
| Key | Meaning |
|---|---|
search.url | Cluster base URL. http://localhost:9200 by default. |
search.username | With search.password, HTTP basic authentication. |
search.api_key | The encoded API key, used as-is. |
search.token | A bearer token. |
search.timeout_ms | Per-request timeout. |
search.retries | Retries for failures that retrying can fix. |
The first credential found wins, in the order above. Nothing connects here — an application must boot while the cluster is restarting.
Sourcepub fn basic_auth(self, username: &str, password: &str) -> SearchClient
pub fn basic_auth(self, username: &str, password: &str) -> SearchClient
Authenticate with a username and password, the way elastic and a
native realm user do.
Sourcepub fn api_key(self, encoded: impl Into<String>) -> SearchClient
pub fn api_key(self, encoded: impl Into<String>) -> SearchClient
Authenticate with an API key.
This is the encoded field of what POST /_security/api_key returned —
already base64 of id:api_key, so it is passed through untouched.
Encoding it a second time produces a 401 that blames the credentials
rather than the encoding.
Sourcepub fn bearer(self, token: impl Into<String>) -> SearchClient
pub fn bearer(self, token: impl Into<String>) -> SearchClient
Authenticate with a bearer token, for a cluster behind an OAuth or service-token setup.
pub fn timeout(self, timeout: Duration) -> SearchClient
Sourcepub fn retries(self, retries: u32) -> SearchClient
pub fn retries(self, retries: u32) -> SearchClient
How many times to repeat a request the cluster refused in a way that
repeating can fix. See SearchError::is_retryable for which those
are; a mapping error is never one of them.
Sourcepub fn faking(self, fake: Fake) -> SearchClient
pub fn faking(self, fake: Fake) -> SearchClient
Answer from a script instead of the network, for tests.
Sourcepub fn fake(&self) -> Option<&Arc<Fake>>
pub fn fake(&self) -> Option<&Arc<Fake>>
The script this client is answering from, for assertions in a test.
pub fn base_url(&self) -> &str
Sourcepub fn url(&self, path: &str) -> String
pub fn url(&self, path: &str) -> String
The full URL for an API path.
Leading slashes are trimmed so _search and /_search both work — the
difference is invisible in a config file and produces a 400.
Sourcepub async fn health(&self) -> Result<ClusterHealth>
pub async fn health(&self) -> Result<ClusterHealth>
Whether the cluster is up and what state it is in.
Use this in a readiness check rather than SearchClient::info: the
root endpoint answers while shards are still recovering, and a cluster
that answers is not the same as a cluster that can serve a search.
Sourcepub async fn info(&self) -> Result<ClusterInfo>
pub async fn info(&self) -> Result<ClusterInfo>
Which server this is, and which version.
Source§impl SearchClient
impl SearchClient
Sourcepub async fn index_document(
&self,
index: &str,
id: &str,
document: Json,
) -> Result<IndexedDocument>
pub async fn index_document( &self, index: &str, id: &str, document: Json, ) -> Result<IndexedDocument>
Write a document at a known id, replacing anything already there.
Sourcepub async fn index_new_document(
&self,
index: &str,
document: Json,
) -> Result<IndexedDocument>
pub async fn index_new_document( &self, index: &str, document: Json, ) -> Result<IndexedDocument>
Write a document and let the cluster choose the id.
Faster than giving one: with no id there is nothing to look up, so the write skips the check for an existing version. Right for append-only data, wrong for anything that has to be updated later by a key you already know.
Sourcepub async fn create_document(
&self,
index: &str,
id: &str,
document: Json,
) -> Result<IndexedDocument>
pub async fn create_document( &self, index: &str, id: &str, document: Json, ) -> Result<IndexedDocument>
Write a document only if the id is free.
Fails with SearchError::VersionConflict rather than overwriting,
which is how an insert is expressed here — Elasticsearch has no other
way to say “only if it does not exist”.
Sourcepub async fn index_document_if_unchanged(
&self,
index: &str,
id: &str,
document: Json,
seq_no: i64,
primary_term: i64,
) -> Result<IndexedDocument>
pub async fn index_document_if_unchanged( &self, index: &str, id: &str, document: Json, seq_no: i64, primary_term: i64, ) -> Result<IndexedDocument>
Write a document only if it has not changed since it was read.
The two numbers come from the Document or IndexedDocument that
was read; passing them turns read-modify-write into something safe
against a concurrent writer, which it otherwise is not. If somebody
else wrote in between, this fails with
SearchError::VersionConflict instead of quietly discarding their
change — which is the entire reason the numbers exist.
Sourcepub async fn get_document(
&self,
index: &str,
id: &str,
) -> Result<Option<Document>>
pub async fn get_document( &self, index: &str, id: &str, ) -> Result<Option<Document>>
Read a document by id.
None means the index is there and the document is not, which is
ordinary. A missing index is still an error, because that is a
deployment problem rather than a missing record, and reporting the two
the same way is how a typo in an index name survives to production.
Sourcepub async fn document_exists(&self, index: &str, id: &str) -> Result<bool>
pub async fn document_exists(&self, index: &str, id: &str) -> Result<bool>
Whether a document exists, without transferring it.
_source=false leaves the reply at the handful of metadata fields,
which is as close to a HEAD as this can get. It is not one because a
real cluster answers a HEAD with a chunked framing it then never
terminates, leaving the reader waiting for a body that never comes.
Sourcepub async fn update_document(
&self,
index: &str,
id: &str,
fields: Json,
) -> Result<IndexedDocument>
pub async fn update_document( &self, index: &str, id: &str, fields: Json, ) -> Result<IndexedDocument>
Merge fields into a document, leaving the rest of it alone.
Only the named fields change. Note that a partial update is a read, a merge and a full rewrite inside the cluster, so it costs more than indexing the whole document — it saves bandwidth, not work.
Sourcepub async fn delete_document(&self, index: &str, id: &str) -> Result<bool>
pub async fn delete_document(&self, index: &str, id: &str) -> Result<bool>
Delete a document, answering whether there was one.
Sourcepub async fn bulk(
&self,
index: &str,
operations: &[BulkOperation],
) -> Result<BulkReport>
pub async fn bulk( &self, index: &str, operations: &[BulkOperation], ) -> Result<BulkReport>
Write many documents in one request.
Read the report. _bulk answers 200 as long as the cluster could
parse the request, whatever happened to the documents in it, so
? on this call is not a check that anything was written.
BulkReport::ok_or_error is the one-line version of that check;
BulkReport::failures and BulkReport::retryable_positions are
the version that lets you resubmit only what is worth resubmitting.
index is the default for operations that do not name one of their own.
Sourcepub async fn bulk_across_indices(
&self,
operations: &[BulkOperation],
) -> Result<BulkReport>
pub async fn bulk_across_indices( &self, operations: &[BulkOperation], ) -> Result<BulkReport>
A bulk request spanning several indices, where every operation names
its own with BulkOperation::in_index.
Source§impl SearchClient
impl SearchClient
Sourcepub async fn create_index(
&self,
name: &str,
definition: &IndexDefinition,
) -> Result<()>
pub async fn create_index( &self, name: &str, definition: &IndexDefinition, ) -> Result<()>
Create an index.
Fails with SearchError::IndexAlreadyExists if it is already there.
Sourcepub async fn create_index_if_missing(
&self,
name: &str,
definition: &IndexDefinition,
) -> Result<bool>
pub async fn create_index_if_missing( &self, name: &str, definition: &IndexDefinition, ) -> Result<bool>
Create an index unless it exists, answering whether it was created.
Implemented by attempting the create and forgiving the one failure that means “already there” — not by checking first. Two application instances booting together both pass a check and one still loses the create, so the check would turn a harmless race into an intermittent crash at startup.
Sourcepub async fn delete_index(&self, name: &str) -> Result<bool>
pub async fn delete_index(&self, name: &str) -> Result<bool>
Delete an index and everything in it, answering whether there was one.
Sourcepub async fn index_exists(&self, name: &str) -> Result<bool>
pub async fn index_exists(&self, name: &str) -> Result<bool>
Whether the index — or an alias by that name — exists.
filter_path matches nothing on purpose: the whole reply would be the
index’s settings and mappings, and the only part being asked about is
the status code.
Sourcepub async fn put_mapping(
&self,
name: &str,
definition: &IndexDefinition,
) -> Result<()>
pub async fn put_mapping( &self, name: &str, definition: &IndexDefinition, ) -> Result<()>
Add fields to an existing index’s mapping.
Adding is all this can do. Changing the type of a field that already exists is refused, because the data is already indexed the old way; that needs a new index and a reindex.
Sourcepub async fn get_mapping(&self, name: &str) -> Result<Json>
pub async fn get_mapping(&self, name: &str) -> Result<Json>
The mapping as the cluster holds it, including whatever dynamic mapping added along the way.
Sourcepub async fn refresh(&self, name: &str) -> Result<()>
pub async fn refresh(&self, name: &str) -> Result<()>
Make everything written so far searchable, now.
Elasticsearch is near-real-time, not real-time: an indexed document is durable immediately and searchable only after the next refresh, which is one second away by default. That gap is the single most common cause of a test that passes alone and fails in a suite, and of a “save then redirect to the list” that shows the old list.
Call it after writing and before searching in a test, and after a bulk load. Do not call it after every write in production — each refresh makes a new segment, and doing it per document is how an index ends up spending its day merging.