Skip to main content

Collection

Struct Collection 

Source
pub struct Collection { /* private fields */ }
Expand description

Vectors under keys: the index, the vectors it reranks against, and the table that turns one into the other.

Implementations§

Source§

impl Collection

Source

pub fn new(dim: usize, metric: Metric) -> Result<Collection>

An empty collection that has allocated nothing yet.

§Errors

Code::Invalid for a dimension of zero or past MAX_DIM, and Code::Unsupported for a metric this build does not measure.

Source

pub fn dim(&self) -> usize

How many coordinates a vector here has.

Source

pub fn metric(&self) -> Metric

What nearness means here.

Source

pub fn len(&self) -> usize

How many vectors are in the collection.

Source

pub fn is_empty(&self) -> bool

Whether there are none.

Source

pub fn partitions(&self) -> usize

How many partitions the collection has grown to.

Source

pub fn entries(&self) -> usize

How many coded members the partitions hold between them, which is more than Collection::len by however many boundary copies Tuning::spill has made.

Source

pub fn tuning(&self) -> Tuning

The knobs the index is searched with.

Source

pub fn retune(&mut self, tuning: Tuning)

Change them, which is what EF_RUNTIME on the wire means.

Source

pub fn contains(&self, key: &[u8]) -> bool

Whether the collection holds a vector under key.

Source

pub fn get(&self, key: &[u8]) -> Option<&[f32]>

The vector under key, where it lies.

Source

pub fn keys(&self) -> impl Iterator<Item = &[u8]>

Every key in the collection, in no order worth relying on.

Source

pub fn key_at(&self, n: usize) -> Option<&[u8]>

The nth key, counting from zero, in that same order.

For a caller that wants one member and not all of them, which is what VRANDMEMBER is and what walking the whole table to throw it away would be the wrong way to answer.

Source

pub fn id(&self, key: &[u8]) -> Option<u64>

The id the index knows key by.

An id is the slot the vector sits in. It is stable while the key is there, it changes if the key is removed and written again, and it is handed out so that a caller keeping something else per vector can key it by a small integer rather than by a second copy of the key. The attribute a vector set holds is the first of those and a pushed down filter’s tag will be the next.

Source

pub fn put(&mut self, key: &[u8], v: &[f32]) -> Result<bool>

Put a vector in under key, and say whether the key is new.

Replacing is the same call. The old code comes out of its partition and the new one goes into whichever partition it belongs to now, so nothing accumulates and there is no rebuild waiting at the end of it.

§Errors

Code::Invalid when the vector is not Collection::dim long, when a coordinate is not a number, or when a cosine collection is handed a vector of length zero, which has no direction to store. Code::Full for a key past the length limit.

Source

pub fn put_tagged(&mut self, key: &[u8], v: &[f32], tag: u64) -> Result<bool>

The same, with the tag a filtered search will meet in the posting scan.

A tag is 64 bits and it travels beside the code rather than beside the vector, which is the whole reason a filter here costs nothing: the scan is already reading that cache line to get at the code, so testing the tag is one instruction on a word that has arrived anyway. See Signature for how a set of field and value pairs becomes one, and Collection::search_where for the other end of it.

A tag of zero passes no filter except Any, which is what an untagged collection wants: Collection::put is this with a zero and every search over it is unfiltered.

§Errors

As Collection::put.

Source

pub fn tag(&self, key: &[u8]) -> Option<u64>

The tag key was stored with, if it is here.

Source

pub fn retag(&mut self, key: &[u8], tag: u64) -> bool

Change the tag under key without touching the vector, and say whether there was one.

The tag summarises something outside the vector, so it can go stale while the vector is still right. Rewriting it is one store into the posting, with no requantisation and no maintenance, which is what makes it cheap enough to redo every tag in a collection when the summary changes.

Source

pub fn remove(&mut self, key: &[u8]) -> bool

Take a vector out, saying whether it was there.

A delete here is a delete and not a tombstone: the member leaves its posting and the last member of that posting moves into the hole.

Source

pub fn search( &self, q: &[f32], k: usize, skip: Option<&[u8]>, ) -> Result<Vec<Match>>

The k nearest keys to q, nearest first, with skip left out.

skip is what makes a more-like-this search work: the vector already stored under a key is always nearest to itself, and nobody asked what a thing is most similar to itself.

§Errors

Code::Invalid when q is not Collection::dim long or holds a coordinate that is not a number.

Source

pub fn search_where( &self, q: &[f32], k: usize, skip: Option<&[u8]>, filter: &impl Filter, ) -> Result<Vec<Match>>

The same, over only the members whose tag filter allows.

The filter runs inside the posting scan and not on the answers, which is the difference between a filtered search and a search followed by a filter. A filter matching one member in a thousand, applied to the nearest ten, returns nothing almost every time; applied in the scan it keeps reading further partitions until it has k or until it has spent Tuning::widen, so it returns the nearest ten that pass.

It can still come back with fewer than k. That is the trade every engine makes here and it is the right one, because the alternative to giving up after a bounded widen is reading the whole collection for a query that was going to find nothing anyway.

§Errors

As Collection::search.

Source

pub fn search_exact( &self, q: &[f32], k: usize, skip: Option<&[u8]>, ) -> Result<Vec<Match>>

The same answer, arrived at by measuring every vector in the collection.

This is what the index is an approximation of, so it is the thing recall is measured against, and it is what VSIM ... TRUTH asks for. It reads no codes at all: the estimator exists to avoid this walk and there is nothing it can contribute to a walk that is happening anyway.

Linear in the collection, which is the point. A client asking for it on a million vectors is asking for a million distances and should get them rather than a refusal, because the reason to ask is to find out what the index missed.

§Errors

As Collection::search.

Source

pub fn search_exact_where( &self, q: &[f32], k: usize, skip: Option<&[u8]>, filter: &impl Filter, ) -> Result<Vec<Match>>

The same walk, over only the members the filter allows.

There is no scan to push the filter into here, because there is no scan: this measures everything. It exists so that a client asking for the exact answer and asking for a filter gets the exact answer to the question it asked, rather than being told the two options do not go together.

§Errors

As Collection::search.

Source

pub fn maintain(&mut self, budget: usize) -> usize

Do bounded maintenance, and say how many vectors it looked at.

A caller with a maintenance slice runs this until it returns less than the budget. A caller without one gets what Collection::put does on its own, which is the same work in smaller pieces.

Source

pub fn memory_bytes(&self) -> usize

What the collection is holding: the vectors, the codes and the keys.

Source

pub fn code_bytes(&self) -> usize

The searchable size of the collection, which is the number the 32x claim is about.

Source§

impl Collection

Source

pub fn save<B: Blocks>( &self, blocks: &mut B, scratch: &mut Scratch, ) -> Result<Chain>

Write the collection down and say where the root went.

The root’s address and length are what a checkpoint entry records, so this returns the pair rather than putting it anywhere: which checkpoint this belongs to is the shard’s business.

§Errors

Code::Full if a section is longer than a chain holds, which for the centroids means a collection with more partitions than 512 MiB of them, and whatever the store returns while it is being written to.

Source

pub fn load<B: Blocks>( blocks: &mut B, at: Chain, stored: &impl Stored, ) -> Result<Restored>

Read a collection back out of an image, taking the vectors from stored.

§Errors

Code::Corrupt for an image that does not describe a collection this build can hold: a kind or a metric it does not know, sections that disagree with the header that named them, an id in two partitions, or an id in a partition that the key table does not have.

Trait Implementations§

Source§

impl Debug for Collection

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