///|
/// Build a validated exact Flat index from a complete corpus.
pub fn build_flat_index(docs : Array[Document]) -> FlatIndex raise VectorError {
if docs.length() > 0 {
validate_documents(docs)
}
let index = FlatIndex::new()
for doc in docs {
index.add(doc)
}
index
}
///|
/// Build a validated IVF-Flat index.
pub fn build_ivf_index(
docs : Array[Document],
k : Int,
metric : DistanceMetric,
) -> IvfIndex raise VectorError {
validate_documents(docs)
if k <= 0 {
raise InvalidK
}
if docs.length() < k {
raise ClusterError("Cluster count exceeds corpus size")
}
let index = IvfIndex::new(k, metric)
index.build(docs)
index
}
///|
/// Build a KD-Tree while checking corpus dimensions first.
pub fn build_kd_tree_index(
docs : Array[Document],
metric : DistanceMetric,
) -> KdTreeIndex raise VectorError {
validate_documents(docs)
let index = KdTreeIndex::new(metric)
index.build(docs)
index
}
///|
/// Build an LSH index with deterministic planes.
pub fn build_lsh_index(
docs : Array[Document],
num_planes : Int,
) -> LshIndex raise VectorError {
validate_documents(docs)
if num_planes <= 0 {
raise InvalidK
}
let index = LshIndex::new(num_planes, docs[0].vector.length())
for doc in docs {
index.add(doc)
}
index
}
///|
/// Apply a single metadata annotation without mutating input documents.
pub fn set_metadata(doc : Document, key : String, value : String) -> Document {
let metadata = []
let mut replaced = false
for pair in doc.metadata {
if pair.0 == key {
if !replaced {
metadata.push((key, value))
replaced = true
}
} else {
metadata.push(pair)
}
}
if !replaced {
metadata.push((key, value))
}
Document::new(doc.id, doc.vector, metadata)
}
///|
/// Remove all documents that match every filter pair.
pub fn remove_matching_documents(
docs : Array[Document],
filters : Array[(String, String)],
) -> Array[Document] {
let remaining = []
for doc in docs {
if !matches_filters(doc.metadata, filters) {
remaining.push(doc)
}
}
remaining
}
///|
/// Copy documents while selecting a stable subset of ids.
pub fn select_document_ids(
docs : Array[Document],
ids : Array[String],
) -> Array[Document] {
let wanted = Map([])
for id in ids {
wanted.set(id, true)
}
let selected = []
for doc in docs {
if wanted.contains(doc.id) {
selected.push(doc)
}
}
selected
}
///|
/// Return a vector's L2 norm without changing it.
pub fn vector_norm(vector : Array[Double]) -> Double raise VectorError {
if vector.length() == 0 {
raise EmptyVector
}
let mut squared = 0.0
for value in vector {
squared = squared + value * value
}
squared.sqrt()
}