Skip to main content

SearchClient

Struct SearchClient 

Source
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

Source

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.

Source

pub fn from_config(config: &Config) -> SearchClient

Build from config/search.json, so the cluster address and credentials can come from .env.

KeyMeaning
search.urlCluster base URL. http://localhost:9200 by default.
search.usernameWith search.password, HTTP basic authentication.
search.api_keyThe encoded API key, used as-is.
search.tokenA bearer token.
search.timeout_msPer-request timeout.
search.retriesRetries 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.

Source

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.

Source

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.

Source

pub fn bearer(self, token: impl Into<String>) -> SearchClient

Authenticate with a bearer token, for a cluster behind an OAuth or service-token setup.

Source

pub fn timeout(self, timeout: Duration) -> SearchClient

Source

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.

Source

pub fn faking(self, fake: Fake) -> SearchClient

Answer from a script instead of the network, for tests.

Source

pub fn fake(&self) -> Option<&Arc<Fake>>

The script this client is answering from, for assertions in a test.

Source

pub fn base_url(&self) -> &str

Source

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.

Source

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.

Source

pub async fn info(&self) -> Result<ClusterInfo>

Which server this is, and which version.

Source

pub async fn search( &self, index: &str, search: &Search, ) -> Result<SearchResults>

Run a search and read the hits back.

index may name several indices (posts,comments) or a pattern (logs-*), which is what the cluster’s own URL accepts.

Source

pub async fn count(&self, index: &str, search: &Search) -> Result<u64>

How many documents match, without fetching any of them.

Unlike the total on a search this is never approximate, because _count has no track_total_hits cutoff to stop early at.

Source§

impl SearchClient

Source

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.

Source

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.

Source

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”.

Source

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.

Source

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.

Source

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.

Source

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.

Source

pub async fn delete_document(&self, index: &str, id: &str) -> Result<bool>

Delete a document, answering whether there was one.

Source

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.

Source

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

Source

pub async fn create_index( &self, name: &str, definition: &IndexDefinition, ) -> Result<()>

Create an index.

Fails with SearchError::IndexAlreadyExists if it is already there.

Source

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.

Source

pub async fn delete_index(&self, name: &str) -> Result<bool>

Delete an index and everything in it, answering whether there was one.

Source

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.

Source

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.

Source

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.

Source

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.

Trait Implementations§

Source§

impl Clone for SearchClient

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for SearchClient

Deliberately hand-written, so a password or API key cannot reach a log.

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.