Swift · Retrieval and Streaming

Retrieval and Streaming

import USearch 3.0.0-rc.1 · 4c3e5023

get lands every vector stored under a key in its arena, one row per entry sharing the key, converted to the scalar kind as: names.

let vectors = try index.get(key: 42, as: Float.self, arena: arena)
print(vectors.count, vectors.columns, vectors.stride, Array(vectors[0]))

cursor() opens a walk over the contents in batches, which is what export and migration want. Each shard’s publication watermark is read as the walk enters it, so an entry once passed is never revisited. Without starts: it yields every entry as stored; with them, the starts’ own entries first, then the entries those link to breadth-first, each once. Every pull lands up to wanted keys in its arena, and nextLinks lands the keys each links to beside them; the vectors themselves come from get.

let cursor = try index.on(executor).cursor()
while true {
    let batch = try cursor.nextLinks(wanted: 1024, arena: arena)
    guard !batch.keys.isEmpty else { break }
    print(Array(batch.keys), batch.links[0])
}

searchStream opens a resumable nearest-neighbor stream over one query row, so a caller that post-filters aggressively pulls until satisfied rather than guessing a wanted count upfront. The stream keeps walk state rather than a thread, every pull is an ordinary call on its receiver’s executor, and a failed pull throws. Ordering beyond the expansion horizon is best-effort nearest rather than globally exact. The engine declares the stream before implementing it, so opening one reports unsupported for now.

let stream = try index.on(executor).searchStream(query)
while true {
    let pulled = try stream.next(wanted: 64, arena: arena)[0]              // nearest first, empty once drained
    let close = pulled.prefix { $0.distance < 0.25 }
    for match in close { print(match.key) }
    guard !close.isEmpty, close.count == pulled.count else { break }
}

Cursors and streams keep their index alive until they go.

§ Sampling

sample draws keys around focus, or across the whole index, each hanging from a shown or earlier draw and placed in three dimensions beside the shown keys already on screen, so a view grows one connected sample as it zooms. Equal seeds, shown keys and focus draw equally, and a larger wanted draws a superset.

let draws = try index.on(executor).sample(wanted: 256, arena: arena, config: DenseSampleConfig(
    shown: shown, shownCoordinates: places, focus: near, seed: 42))
for draw in 0 ..< draws.count {
    let (key, entryOrdinal, parent, point) = (draws.keys[draw], draws.entryOrdinal(draw), draws.parents[draw], draws.coordinates[draw])
}

API

StructDenseSampleConfigThe knobs of sample.9
DenseIndex.swift:474 ↗
struct DenseSampleConfig

Left alone, it draws across the whole index with nothing shown.

shownThe keys already shown: never drawn again, and the paths reach toward them.
DenseIndex.swift:476 ↗
var shown: [Key]
shownCoordinatesWhere each shown key sits, three values apiece, end to end.
DenseIndex.swift:478 ↗
var shownCoordinates: [Float]
focusThe keys to look around; empty looks around the whole index.
DenseIndex.swift:480 ↗
var focus: [Key]
seedEqual seeds, shown keys and focus over an unchanged index draw equally.
DenseIndex.swift:482 ↗
var seed: UInt64
expansionFactorThe breadth of the candidates of the walks behind the draws; zero derives.
DenseIndex.swift:484 ↗
var expansionFactor: Int
progressCalled with draws made; a stop answers cancelled.
DenseIndex.swift:486 ↗
var progress: Progress?
progressStepsReports the call makes at most, the last at completion; zero derives to ten.
DenseIndex.swift:488 ↗
var progressSteps: Int
statsThe traversal telemetry to write.
DenseIndex.swift:490 ↗
var stats: DenseTraversalStats?
init
DenseIndex.swift:492 ↗
init(shown: [Key] = [], shownCoordinates: [Float] = [], focus: [Key] = [], seed: UInt64 = 0, expansionFactor: Int = 0, progress: Progress? = nil, progressSteps: Int = 0, stats: DenseTraversalStats? = nil)
StructDenseSearchStreamInitConfigThe knobs of searchStream.2
DenseIndex.swift:540 ↗
struct DenseSearchStreamInitConfig
expansionFactorThe breadth of the candidates the walk explores; zero derives it from the geometry.
DenseIndex.swift:542 ↗
var expansionFactor: Int
init
DenseIndex.swift:544 ↗
init(expansionFactor: Int = 0)
StructDenseVectorsThe rows get or attend wrote into an arena, one per entry sharing the key or per query, read in place as Value.5
DenseIndex.swift:698 ↗
struct DenseVectors<Value> where Value : ScalarValue

Conforms to

RandomAccessCollection, BidirectionalCollection, Collection, Sequence
endIndexOne past the last row the call wrote.
DenseIndex.swift:704 ↗
let endIndex: Int
startIndexThe first row's position, which a collection walk starts from.
DenseIndex.swift:707 ↗
var startIndex: Int { get }
strideThe bytes between consecutive rows, as the library reported them.
DenseIndex.swift:710 ↗
var stride: Int { get }
columnsThe logical values in each row, as the library reported them.
DenseIndex.swift:713 ↗
var columns: Int { get }
subscriptOne row's stored values, packed at their natural width.
DenseIndex.swift:716 ↗
subscript(row: Int) -> Row { get }
StructDenseVectors.RowOne row of the arena's values, read in place.3
DenseIndex.swift:727 ↗
struct Row

Conforms to

RandomAccessCollection, BidirectionalCollection, Collection, Sequence
endIndexOne past the last value in the row.
DenseIndex.swift:733 ↗
let endIndex: Int
startIndexThe first value's position, which a collection walk starts from.
DenseIndex.swift:736 ↗
var startIndex: Int { get }
subscriptThe value at a position.
DenseIndex.swift:739 ↗
subscript(position: Int) -> Value { get }
StructDenseKeysThe keys a cursor pull wrote into an arena, in walk order.3
DenseIndex.swift:747 ↗
struct DenseKeys

Conforms to

RandomAccessCollection, BidirectionalCollection, Collection, Sequence
endIndexOne past the last key the call wrote.
DenseIndex.swift:753 ↗
let endIndex: Int
startIndexThe first key's position, which a collection walk starts from.
DenseIndex.swift:756 ↗
var startIndex: Int { get }
subscriptThe key at a position.
DenseIndex.swift:759 ↗
subscript(position: Int) -> Key { get }
StructDenseDrawsThe draws sample wrote into an arena, one per draw: its key, which of that key's entries it is, the shown or earlier draw it hangs from, and where it is placed, read in place.5
DenseIndex.swift:769 ↗
struct DenseDraws
keysThe drawn keys.
DenseIndex.swift:771 ↗
let keys: DenseKeys
parentsEach draw's parent and its distance, one column per draw, a root holding none.
DenseIndex.swift:773 ↗
let parents: Matches
coordinatesWhere each draw is placed, one row of three values apiece.
DenseIndex.swift:775 ↗
let coordinates: DenseVectors<Float>
countThe draws written; fewer than wanted only once the region is exhausted.
DenseIndex.swift:782 ↗
var count: Int { get }
entryOrdinalWhich of its key's entries one draw is, in the order get writes them.
DenseIndex.swift:785 ↗
func entryOrdinal(_ draw: Int) -> Int
ClassDenseCursorA walk over an index's contents; see DenseIndex.cursor().2
DenseIndex.swift:2370 ↗
final class DenseCursor

The walk is sequential, so it stays a batch operation - export, migration, and visualization all move whole batches. Every pull blocks on the executor of the receiver that opened it, and the cursor keeps its index alive.

nextCopies up to wanted next keys into arena, walking the key column alone; an empty batch means the walk is exhausted.
DenseIndex.swift:2389 ↗
func next(wanted: Int, arena: Arena) throws(USearchError) -> DenseKeys
ClassDenseSearchStreamA resumable nearest-neighbor stream over one query; see DenseIndex.searchStream.1
DenseIndex.swift:2439 ↗
final class DenseSearchStream

Best-first traversal is naturally pausable, so the stream keeps a live frontier and visited set and yields matches lazily. Ordering beyond the expansion horizon is best-effort nearest rather than globally exact, so pulling far past that breadth degrades the ranking rather than ending the stream. Every pull blocks on the executor of the receiver that opened it.

nextPulls up to wanted more matches into arena, as the one query of a search, nearest first; an empty query means the traversal is drained.
DenseIndex.swift:2459 ↗
func next(wanted: Int, arena: Arena) throws(USearchError) -> Matches