Una singola ricerca nella directory.
Una singola scansione contigua.
La ricerca vettoriale filtrata è di solito uno scatter-gather su un indice che non sa nulla del tuo filtro. Telys controlla invece il layout fisico: sulla chiave di partizione, nearest-neighbours-where-key-equals-x è una ricerca O(1) nella directory più una singola scansione sequenziale di un blocco contiguo. Questa pagina illustra il runtime attualmente in produzione, poi l'architettura verso cui sta convergendo — etichettata come spec.
Il motore controlla dove vive ogni vettore.
Una collection dichiara partition_by alla creazione e il layout segue la chiave. Il segmento base è raggruppato per partizione — ogni chiave mappa a una singola slice contigua — e le scritture recenti risiedono accanto in un delta mutabile a forma Arrow.
Raggruppato per partizione
Vettori memorizzati ordinati per chiave di partizione, così ogni partizione è un blocco contiguo. Una query con scope legge una singola sequenza di memoria, non righe sparse in un indice.
O(1) key → slice
Una directory mappa ogni valore di partizione al suo (offset, length) nel segmento base. Risolvere il filtro è una ricerca nel dizionario, non un attraversamento dell'indice.
Scritture a forma Arrow
add e upsert accodano qui, ogni riga marcata con un LSN di scrittura monotono. Il delta è interrogabile immediatamente e si unisce alla slice base attraverso un unico percorso di scansione.
Cosa esegue una query con scope.
col.search(qvec, where={"tenant_id": "acme"}, top_k=10)
Il filtro indica la chiave di partizione con cui è stata creata la collection.
Ricerca O(1): il valore di partizione si risolve in (offset, length) nel segmento base. Nessuna generazione di candidati, nessun punto di ingresso nel grafo.
Scoring esatto SIMD su un singolo blocco sequenziale. Il recall su questo percorso è 1.0 — è una scansione, non un'approssimazione. I valori p50/p95 misurati per questo passaggio sono pubblicati nella pagina dei benchmark, ciascuno legato alla propria soglia di recall e al proprio rig.
Le righe con la stessa chiave nel delta a forma di Arrow vengono valutate attraverso lo stesso percorso. Una scrittura è visibile alla query immediatamente successiva.
I risultati vengono filtrati all'LSN dello snapshot: le versioni con tombstone o superate non emergono mai. Top-k restituisce il payload explain allegato.
Le partizioni sovradimensionate seguono un fork dichiarato: build_ivf posiziona un IVF per partizione su qualsiasi partizione oltre la soglia di righe, e le query vi vengono instradate con un rerank esatto — calibrato rispetto a un floor di recall, e nominato nel piano.
Ogni risultato nomina il proprio piano.
explain=True restituisce il piano fisico effettivamente seguito dalla query. Sulla chiave di partizione si ottiene la scansione a slice contigua. Le partizioni sovradimensionate dichiarano il fork IVF. I filtri fuori chiave ricadono sullo scatter-gather — e il payload spiega perché.
hits = col.search(qvec, where={"tenant_id": "acme"}, top_k=10, explain=True)
hits["explain"]["plan"]
# on the partition key → "PartitionSliceExactF32" # one contiguous slice, exact, recall 1.0
# oversized partition → "PartitionIVFRerankF32" # per-partition IVF + exact rerank
# off-key filter → "ScatterGatherExact" # fallback — and it says why:
hits["explain"]["fallback_reason"]
# "path is not the physical partition key"Nulla viene sovrascritto. Le versioni vengono superate.
Il modello di scrittura è MVCC append-only. upsert scrive una nuova versione di un id logico e contrassegna la precedente come superata; delete posiziona un tombstone a un delete LSN; snapshot() fissa una vista di lettura consistente; compact() incorpora il delta nella base e scarta ciò che nessuno snapshot può vedere.
col.upsert(vectors, ids=ids, metadata=metadata) # a new version; the prior one is superseded col.delete(["doc-41"]) # tombstone at a delete LSN — no in-place erase lsn = col.snapshot() # pin a consistent read view at an LSN col.compact() # fold delta into base; drop superseded rows col.build_ivf(min_rows=20000, target_recall=0.98)
| Chiamata | Funzione |
|---|---|
| aggiungi / aggiungi_testi | Inserisce nuove righe. Gli id duplicati vengono rifiutati — una seconda riga fisica per un id logico richiede upsert, per scelta esplicita. |
| upsert / upsert_texts | Scrive una nuova versione di un id logico; la versione precedente viene superata, mai sovrascritta in loco. |
| cerca / cerca_testo | Top-k filtrato. explain=True allega il piano fisico al risultato. |
| delete | Applica un tombstone alle righe logiche a un delete LSN. |
| compact | Incorpora il delta nel segmento base; elimina le versioni con tombstone e quelle superate. |
| build_ivf | Costruisce un IVF per partizione sulle partizioni oltre la soglia di righe, calibrato su un floor di recall. |
| snapshot | Restituisce un LSN che fissa una vista di lettura consistente. |
| save / stats | Persiste la collection su disco; riporta conteggi di righe, partizioni e informazioni sul layout. |
Tutto ciò che segue è specifica, non riferimento API rilasciato. È il substrato verso cui il runtime sta convergendo. La giuntura esposta all'SDK è congelata, quindi la sostituzione sottostante è invisibile ai chiamanti.
Un planner. Un IR piatto. Un executor.
Tre front end convergono in un unico planner, che emette una rappresentazione intermedia fisica piatta, eseguita da un unico executor vettorizzato Mojo direttamente sui buffer Arrow.
Il modello di memoria (remember · recall · as_of), le API di query (point_get · scan · search · hybrid_search) e il fabric. Un unico contratto d'ingresso; nessun engine per API.
Pianificazione logica e fisica in un unico punto. La selettività è stimata dalle statistiche di segmento; il piano è esplicito e restituito insieme al risultato.
Un array piatto di operatori legati da slot interi. Il registro inter-operatore è un row set — una bitmap, row-id ordinati o un vettore di selezione.
Esecuzione vettorizzata su buffer Arrow e array di candidati FAISS. L'hot path SIMD rimane fuori da Python; le pagine Parquet compresse vengono decodificate prima in scratch buffer.
Il layer di mutazione
Un write-ahead log per durabilità e ordinamento, e un delta mutabile a forma di Arrow per visibilità immediata — il layer tabellare che Parquet da solo non offre.
Sealed, immutabili
Segmenti colonnari compressi e durevoli. Le statistiche del footer guidano il pruning di segmenti e row-group; i file sealed non vengono mai mutati.
FAISS ANN
Indici FAISS per segmento, legati al checksum e alla versione del segmento, mappati in sola lettura con durata vincolata allo snapshot di lettura.
Pruning e lessicale
Zone map, bloom filter e bitmap per il pruning; un sidecar BM25 per il recupero sparso e la fusione dei punteggi.
SCAN FILTER PROJECT POINT_GET RANGE_GET AGGREGATE TOP_K HASH_JOIN HASH_GROUP_BY SORT ANN_SEARCH SPARSE_SEARCH FUSE RERANK MATERIALIZE
Prima il WAL. Visibile immediatamente. Sealed in background.
Frame append-only con checksum e LSN monotoni. Il recovery riproduce i record oltre l'ultimo LSN sealed e tronca al primo checksum non valido.
La scrittura approda nel delta mutabile ed è immediatamente interrogabile — segmenti sealed e delta si uniscono attraverso un unico percorso di scansione.
Superata una soglia di dimensione, righe o età, il delta viene ordinato e scritto come segmento Parquet con il relativo .vidx e i file sidecar, quindi pubblicato tramite uno swap atomico del manifest.
I merge in background fondono i segmenti piccoli, eliminano le righe con tombstone e ricostruiscono i sidecar — a budget controllato, così i deployment on-device restano silenziosi.
I segmenti sealed non vengono mai mutati. Una lettura fissa (snapshot del manifest, LSN); i writer sealed nuovi segmenti e scambiano il manifest senza disturbare i reader in volo. Il garbage collection attende lo snapshot live più vecchio.
È il filtro a scegliere il piano, non il contrario.
Filtered ANN è pianificazione adattiva per selettività: il planner stima quante righe sopravvivono al filtro dalle statistiche di segmento, poi sceglie la strategia meno costosa che rispetta il contratto di recall.
Prefiltro → esatto
Prima un prefilter scalare o bitmap, poi scoring SIMD esatto sui sopravvissuti. Sotto una soglia di righe l'ANN viene saltato del tutto — scansione esatta, recall 1.0.
Scansione allow-bitmap
L'attraversamento ANN porta un allow-bitmap, così l'indice espone solo le righe ammesse dal filtro.
ANN → post-filtro
Prima ANN con over-fetch adattivo, poi post-filter. Il filtro rimuove poco, quindi guida la generazione dei candidati.
I gate di correttezza precedono qualsiasi dato di velocità.
Il runtime distribuito è verificato da suite di parità eseguite su entrambe le implementazioni del motore, e ogni query può nominare il piano fisico che ha seguito. Questo è l'ordine delle operazioni: prima i gate di correttezza, poi la misurazione.
I benchmark vengono pubblicati sotto un contratto di equità — recall corrispondente, hardware corrispondente, risultati separati per trasporto, win / tie / lose riportati. I primi risultati misurati sono ora disponibili nella pagina dei benchmark, ogni cifra accompagnata dalle proprie condizioni complete: hardware, dataset, dimensione, selettività, recall e trasporto. Ogni moltiplicatore rimane un'ipotesi al di fuori delle condizioni dichiarate accanto ad esso.