sim_LAR finds documents that are similar to a query — by keyword, by meaning, or by both at once — and returns a single ranked list no matter which signals you use. You supply the documents (and, for semantic search, embedding vectors from whatever model you already use); sim_LAR handles the indexing, scoring, and ranking.
This guide covers what you need to install and how to run semantic and hybrid search with a few lines of code.
01Installation
sim_LAR ships as two packages: simlar, the SDK with the public API, index classes, and framework integrations, and simlar-engine, which powers semantic vector search. Both are on public PyPI, and sim_LAR requires Python 3.11 or later.
pip install simlar
pip install simlar-engineIf you plan to use a specific framework integration, install the matching extra instead of the bare package:
pip install simlar[langchain] # LangChain vector store
pip install simlar[haystack] # Haystack integration
pip install simlar[llama_index] # LlamaIndex integration02Semantic Search
SimlarEngine finds documents that mean the same thing as your query, even if the wording is different. It runs on pre-computed embedding vectors, so you'll need simlar-engine installed and an embedding model of your choice. This example embeds 500 real questions from the BeIR/quora dataset with sentence-transformers' all-MiniLM-L6-v2 (a small, fast, 384-dimension model):
pip install datasets sentence-transformersimport numpy as np
from datasets import load_dataset
from sentence_transformers import SentenceTransformer
from simlar import SimlarEngine
ds = load_dataset("BeIR/quora", "corpus", split="corpus[:500]")
corpus = ds["text"]
ids = ds["_id"]
model = SentenceTransformer("all-MiniLM-L6-v2")
vectors = model.encode(corpus, normalize_embeddings=True).astype(np.float32) # (500, 384)
id_to_text = dict(zip(ids, corpus))
idx = SimlarEngine()
idx.add(ids=list(ids), vectors=vectors)
print(f"Indexed {idx.size} questions | embedding dim: {vectors.shape[1]}")
query_text = "What is the best programming language to learn first?"
query_vec = model.encode([query_text], normalize_embeddings=True).astype(np.float32) # (1, 384)
print(f"Query: '{query_text}'\n")
for r in idx.search(query_vec, k=5):
print(f" rank={r.rank} score={r.score:.4f} {id_to_text[r.id]}")SimlarEngine also supports incremental updates — idx.update(ids=..., vectors=...) replaces an embedding in place, and idx.delete(ids=...) removes documents, both without rebuilding the index.
03Hybrid Search
For most real-world queries, you want both signals: exact-word precision and semantic depth. HelixIndex runs keyword and vector search in parallel and merges the two ranked lists into one, using a fusion strategy like ReciprocalRankFusion. Continuing with the same Quora corpus, ids, vectors, and model from the semantic search example above:
from simlar import HelixIndex, RelevanceIndex, SimlarEngine, ReciprocalRankFusion
index = HelixIndex(
text_index= RelevanceIndex(),
vector_index= SimlarEngine(),
fusion=ReciprocalRankFusion(),
top_k=5,
)
index.add(ids=list(ids), texts=list(corpus), vectors=vectors)
print(f"Index size: {index.size}")
query_text = "What is the best programming language to learn first?"
query_vec = model.encode([query_text], normalize_embeddings=True).astype(np.float32)
# Pass query_text, query_vector, or both — HelixIndex fuses whatever signals it gets
print("Hybrid:")
for r in index.search(query_text=query_text, query_vector=query_vec, k=5):
print(f" rank={r.rank} score={r.score:.4f} {id_to_text[r.id][:80]}")
print("\nText only:")
for r in index.search(query_text=query_text, k=5):
print(f" rank={r.rank} score={r.score:.4f} {id_to_text[r.id][:80]}")
print("\nVector only:")
for r in index.search(query_vector=query_vec, k=5):
print(f" rank={r.rank} score={r.score:.4f} {id_to_text[r.id][:80]}")If your corpus is too large to fit comfortably in memory, StreamingHybridIndex gives you the same hybrid approach but adds documents in batches, storing each batch as an independent shard that gets searched and merged at query time.
04Producing Embeddings
sim_LAR is model-agnostic: it accepts any (n, dim) float32 NumPy array, so you're not locked into all-MiniLM-L6-v2 from the examples above — swap in whichever embedding model or API already fits your stack (OpenAI, Cohere, a larger sentence-transformers model, your own fine-tuned model). The only requirement is that the same model produces both your corpus vectors and your query vectors, so they land in the same embedding space.
05Saving, Loading, and Extending
Every index type — keyword, semantic, or hybrid — shares the same save and load interface, and the loader detects the index type automatically. Continuing the hybrid example above:
import tempfile
from simlar import load_from_directory
with tempfile.TemporaryDirectory() as tmp:
index.save(tmp)
reloaded = load_from_directory(tmp)
print(f"Reloaded index size: {reloaded.size}")
# Confirm the reloaded index returns the same top result
orig_top = index.search(query_text=query_text, query_vector=query_vec, k=1)[0]
reloaded_top = reloaded.search(query_text=query_text, query_vector=query_vec, k=1)[0]
print(f"Top result matches original: {orig_top.id == reloaded_top.id}")You can also register your own index implementation so it behaves like a built-in index, including full save/load support:
from simlar import VectorIndex, register
@register("my_index")
class MyIndex(VectorIndex):
...06Choosing the Right Index
| You have… | You need… | Use |
|---|---|---|
| Text only | Keyword matching | RelevanceIndex |
| Embeddings | Semantic similarity | SimlarEngine |
| Text + embeddings | Both signals, fits in memory | HelixIndex |
| Text + embeddings, massive scale | Both signals, streamed in batches | StreamingHybridIndex |
That's enough to go from pip install to a working search index — keyword, semantic, or hybrid. For architecture details and design rationale, see the Concepts guide; for the full public API surface, see the API Reference.
Building retrieval into your own product?
If you're evaluating sim_LAR for a production RAG pipeline or need help with framework integrations, we'd like to hear about your workload.
Talk to the TekDatum team →