LanceDB: Database Vektor Serverless untuk Aplikasi AI Multimodal
Database vektor telah menjadi komponen fundamental dalam aplikasi AI modern, mulai dari pencarian semantik hingga Retrieval-Augmented Generation (RAG). Namun, banyak solusi database vektor memerlukan infrastruktur server yang kompleks dan mahal untuk di-maintain. LanceDB hadir sebagai alternatif yang ringan, serverless, dan mendukung pencarian multimodal.
LanceDB adalah database vektor open-source yang berjalan secara embedded, artinya tidak memerlukan server terpisah. Dibangun di atas format data Lance yang dioptimalkan untuk operasi vektor, LanceDB menawarkan performa tinggi dengan footprint yang minimal. Yang membuatnya istimewa adalah dukungan native untuk data multimodal, termasuk teks, gambar, audio, dan video.
Dalam tutorial ini, kita akan mempelajari cara menggunakan LanceDB mulai dari instalasi, operasi dasar, hingga membangun mesin pencari multimodal yang lengkap.
Prasyarat
Sebelum memulai, pastikan Anda memiliki:
- Python 3.9 atau lebih baru
- pip package manager
- Pemahaman dasar tentang Python dan konsep embedding vektor
- (Opsional) OpenAI API key untuk embedding functions
Instalasi
Instalasi Dasar
pip install lancedb
Instalasi dengan Embedding Functions
pip install lancedb sentence-transformers
Instalasi untuk Pencarian Multimodal
pip install lancedb open-clip-torch Pillow
Verifikasi Instalasi
import lancedb
print(f"LanceDB version: {lancedb.version}")
Membuat Database dan Tabel
LanceDB menggunakan pendekatan embedded, sehingga database cukup dibuat sebagai direktori lokal.
Membuat Database
import lancedb
Buat koneksi ke database (direktori lokal)
db = lancedb.connect("./mylancedb")
print("Database berhasil dibuat!")
print(f"Lokasi: ./mylancedb")
Membuat Tabel dengan Data
import lancedb
import numpy as np
db = lancedb.connect("./mylancedb")
Buat data dengan embedding
data = [
{
"id": 1,
"text": "Python adalah bahasa pemrograman yang populer untuk AI",
"vector": np.random.randn(128).tolist(),
"category": "programming",
},
{
"id": 2,
"text": "Machine learning menggunakan data untuk membuat prediksi",
"vector": np.random.randn(128).tolist(),
"category": "ai",
},
{
"id": 3,
"text": "Deep learning adalah subset dari machine learning",
"vector": np.random.randn(128).tolist(),
"category": "ai",
},
]
Buat tabel
table = db.createtable("articles", data=data)
print(f"Tabel 'articles' dibuat dengan {len(table)} baris")
Menggunakan Pydantic Model
import lancedb
from lancedb.pydantic import LanceModel, Vector
import numpy as np
Definisikan schema menggunakan Pydantic
class Article(LanceModel):
id: int
title: str
content: str
vector: Vector(384) # Dimensi embedding
category: str
published: bool = True
db = lancedb.connect("./mylancedb")
Buat tabel dengan schema
table = db.createtable("articlesv2", schema=Article)
Tambahkan data
articles = [
Article(
id=1,
title="Pengenalan LanceDB",
content="LanceDB adalah database vektor serverless",
vector=np.random.randn(384).tolist(),
category="database",
),
Article(
id=2,
title="Tutorial RAG",
content="RAG menggabungkan retrieval dengan generation",
vector=np.random.randn(384).tolist(),
category="ai",
),
]
table.add([a.dict() for a in articles])
print(f"Ditambahkan {len(articles)} artikel")
Menambahkan Data
Menambah Data ke Tabel yang Sudah Ada
import lancedb
import numpy as np
db = lancedb.connect("./mylancedb")
table = db.opentable("articles")
Tambah data baru
newdata = [
{
"id": 4,
"text": "Natural Language Processing memproses bahasa manusia",
"vector": np.random.randn(128).tolist(),
"category": "nlp",
},
{
"id": 5,
"text": "Computer Vision memungkinkan komputer memahami gambar",
"vector": np.random.randn(128).tolist(),
"category": "cv",
},
]
table.add(newdata)
print(f"Total baris sekarang: {len(table)}")
Update Data
import lancedb
db = lancedb.connect("./mylancedb")
table = db.opentable("articles")
Update data berdasarkan kondisi
table.update(
where="id = 1",
values={"category": "python"},
)
print("Data berhasil diupdate")
Hapus Data
import lancedb
db = lancedb.connect("./mylancedb")
table = db.opentable("articles")
Hapus data berdasarkan kondisi
table.delete("id = 5")
print(f"Total baris setelah hapus: {len(table)}")
Pencarian Vektor
Pencarian vektor adalah fitur utama LanceDB. Berikut cara melakukan berbagai jenis pencarian.
Pencarian L2 (Euclidean Distance)
import lancedb
import numpy as np
db = lancedb.connect("./mylancedb")
table = db.opentable("articles")
Query vektor
queryvector = np.random.randn(128).tolist()
Pencarian vektor (default: L2 distance)
results = (
table.search(queryvector)
.limit(5)
.topandas()
)
print("Hasil pencarian (L2):")
print(results[["id", "text", "distance"]])
Pencarian Cosine Similarity
import lancedb
import numpy as np
db = lancedb.connect("./mylancedb")
table = db.opentable("articles")
queryvector = np.random.randn(128).tolist()
Pencarian dengan cosine similarity
results = (
table.search(queryvector)
.metric("cosine")
.limit(5)
.topandas()
)
print("Hasil pencarian (Cosine):")
print(results[["id", "text", "distance"]])
Pencarian Dot Product
import lancedb
import numpy as np
db = lancedb.connect("./mylancedb")
table = db.opentable("articles")
queryvector = np.random.randn(128).tolist()
Pencarian dengan dot product
results = (
table.search(queryvector)
.metric("dot")
.limit(5)
.topandas()
)
print("Hasil pencarian (Dot Product):")
print(results[["id", "text", "distance"]])
Full-Text Search
LanceDB juga mendukung pencarian teks penuh menggunakan Tantivy.
import lancedb
db = lancedb.connect("./mylancedb")
Buat tabel dengan FTS index
data = [
{"id": 1, "text": "Python programming language for AI development"},
{"id": 2, "text": "Machine learning algorithms and deep learning"},
{"id": 3, "text": "Natural language processing with transformers"},
{"id": 4, "text": "Computer vision and image recognition"},
{"id": 5, "text": "Reinforcement learning for game AI"},
]
table = db.createtable("ftsarticles", data=data, mode="overwrite")
Buat full-text search index
table.createftsindex("text")
Pencarian teks
results = (
table.search("machine learning", querytype="fts")
.limit(3)
.topandas()
)
print("Hasil Full-Text Search:")
print(results)
Hybrid Search
Hybrid search menggabungkan pencarian vektor dan full-text search untuk hasil yang lebih akurat.
import lancedb
import numpy as np
db = lancedb.connect("./mylancedb")
Buat data dengan vektor dan teks
data = [
{
"id": i,
"text": text,
"vector": np.random.randn(128).tolist(),
}
for i, text in enumerate([
"Introduction to machine learning algorithms",
"Deep learning with neural networks",
"Natural language processing fundamentals",
"Computer vision and object detection",
"Reinforcement learning in robotics",
"Transfer learning for NLP tasks",
"Generative AI and large language models",
"Vector databases for semantic search",
])
]
table = db.createtable("hybridarticles", data=data, mode="overwrite")
Buat FTS index
table.createftsindex("text")
Hybrid search
queryvector = np.random.randn(128).tolist()
results = (
table.search(queryvector, querytype="hybrid")
.text("neural networks")
.limit(5)
.topandas()
)
print("Hasil Hybrid Search:")
print(results[["id", "text", "relevancescore"]])
Filtering
LanceDB mendukung filtering SQL-like pada pencarian vektor.
import lancedb
import numpy as np
db = lancedb.connect("./mylancedb")
data = [
{
"id": i,
"title": f"Article {i}",
"text": f"Content of article {i}",
"vector": np.random.randn(128).tolist(),
"category": ["ai", "database", "web"][i % 3],
"year": 2024 + (i % 3),
}
for i in range(20)
]
table = db.createtable("filteredarticles", data=data, mode="overwrite")
queryvector = np.random.randn(128).tolist()
Pencarian dengan filter
results = (
table.search(queryvector)
.where("category = 'ai' AND year >= 2025")
.limit(5)
.topandas()
)
print("Hasil pencarian dengan filter:")
print(results[["id", "title", "category", "year", "distance"]])
Filter dengan IN clause
resultsin = (
table.search(queryvector)
.where("category IN ('ai', 'database')")
.limit(5)
.topandas()
)
print("\nHasil dengan IN filter:")
print(resultsin[["id", "title", "category", "distance"]])
Indexing (IVFPQ)
Untuk dataset besar, LanceDB mendukung indexing IVFPQ untuk mempercepat pencarian.
import lancedb
import numpy as np
db = lancedb.connect("./mylancedb")
Buat dataset besar
largedata = [
{
"id": i,
"text": f"Document {i}",
"vector": np.random.randn(128).tolist(),
}
for i in range(10000)
]
table = db.createtable("largedataset", data=largedata, mode="overwrite")
Buat IVFPQ index
table.createindex(
metric="cosine",
numpartitions=16,
numsubvectors=8,
indextype="IVFPQ",
)
print("Index IVFPQ berhasil dibuat!")
Pencarian dengan index
queryvector = np.random.randn(128).tolist()
results = (
table.search(queryvector)
.metric("cosine")
.nprobes(8) # Jumlah partisi yang diperiksa
.limit(10)
.topandas()
)
print(f"\nTop 10 hasil:")
print(results[["id", "text", "distance"]])
Embedding Functions
LanceDB menyediakan embedding functions built-in yang memudahkan proses embedding otomatis.
Menggunakan Sentence Transformers
import lancedb
from lancedb.pydantic import LanceModel, Vector
from lancedb.embeddings import getregistry
Dapatkan embedding function
sentencetransformers = getregistry().get("sentence-transformers")
embeddingfunc = sentencetransformers.create(
name="all-MiniLM-L6-v2",
device="cpu",
)
Definisikan model dengan embedding otomatis
class Document(LanceModel):
text: str = embeddingfunc.SourceField()
vector: Vector(embeddingfunc.ndims()) = embeddingfunc.VectorField()
category: str = ""
db = lancedb.connect("./mylancedb")
table = db.createtable("autoembed", schema=Document, mode="overwrite")
Tambah data - embedding dibuat otomatis
documents = [
{"text": "Python adalah bahasa pemrograman populer", "category": "programming"},
{"text": "Machine learning untuk prediksi data", "category": "ai"},
{"text": "Database vektor untuk pencarian semantik", "category": "database"},
{"text": "Deep learning menggunakan neural network", "category": "ai"},
{"text": "API development dengan FastAPI", "category": "web"},
]
table.add(documents)
print(f"Ditambahkan {len(documents)} dokumen dengan embedding otomatis")
Pencarian - query juga di-embed otomatis
results = (
table.search("cara menggunakan artificial intelligence")
.limit(3)
.topandas()
)
print("\nHasil pencarian:")
for , row in results.iterrows():
print(f" [{row['distance']:.4f}] {row['text']}")
Menggunakan OpenAI Embeddings
import lancedb
from lancedb.pydantic import LanceModel, Vector
from lancedb.embeddings import getregistry
import os
Set API key
os.environ["OPENAIAPIKEY"] = "sk-your-api-key"
Gunakan OpenAI embedding
openaiembed = getregistry().get("openai")
embeddingfunc = openaiembed.create(
name="text-embedding-3-small",
)
class Document(LanceModel):
text: str = embeddingfunc.SourceField()
vector: Vector(embeddingfunc.ndims()) = embeddingfunc.VectorField()
db = lancedb.connect("./mylancedb")
table = db.createtable("openaiembed", schema=Document, mode="overwrite")
Data akan di-embed otomatis menggunakan OpenAI
table.add([
{"text": "Artificial intelligence is transforming industries"},
{"text": "Vector databases enable semantic search"},
{"text": "Large language models understand natural language"},
])
results = table.search("how AI changes business").limit(3).topandas()
print(results[["text", "distance"]])
Menggunakan CLIP untuk Multimodal
import lancedb
from lancedb.pydantic import LanceModel, Vector
from lancedb.embeddings import getregistry
Gunakan CLIP untuk multimodal embedding
clip = getregistry().get("open-clip")
embeddingfunc = clip.create(
name="ViT-B-32",
pretrained="laion2bs34bb79k",
)
class MultimodalItem(LanceModel):
text: str = embeddingfunc.SourceField()
imageuri: str = embeddingfunc.SourceField()
vector: Vector(embeddingfunc.ndims()) = embeddingfunc.VectorField()
label: str = ""
db = lancedb.connect("./mylancedb")
table = db.createtable(
"multimodal", schema=MultimodalItem, mode="overwrite"
)
print("Tabel multimodal siap digunakan!")
Pencarian Multimodal (Teks + Gambar)
LanceDB mendukung pencarian multimodal yang menggabungkan teks dan gambar.
import lancedb
from lancedb.pydantic import LanceModel, Vector
from lancedb.embeddings import getregistry
from pathlib import Path
from PIL import Image
import numpy as np
Setup CLIP embedding
clip = getregistry().get("open-clip")
embeddingfunc = clip.create(
name="ViT-B-32",
pretrained="laion2bs34bb79k",
)
class ImageDocument(LanceModel):
imageuri: str = embeddingfunc.SourceField()
vector: Vector(embeddingfunc.ndims()) = embeddingfunc.VectorField()
label: str
description: str = ""
db = lancedb.connect("./multimodaldb")
Asumsikan kita punya direktori gambar
imagedir = Path("images")
if imagedir.exists():
imagedata = []
for imgpath in imagedir.glob(".jpg"):
imagedata.append({
"imageuri": str(imgpath),
"label": imgpath.stem,
"description": f"Image: {imgpath.stem}",
})
table = db.createtable(
"imagesearch", data=imagedata, schema=ImageDocument, mode="overwrite"
)
# Pencarian gambar menggunakan query teks
results = (
table.search("a photo of a cat")
.limit(5)
.topandas()
)
print("Hasil pencarian multimodal:")
for , row in results.iterrows():
print(f" [{row['distance']:.4f}] {row['label']}: {row['description']}")
# Pencarian menggunakan gambar sebagai query
queryimage = "images/queryimage.jpg"
if Path(queryimage).exists():
results = (
table.search(Image.open(queryimage))
.limit(5)
.topandas()
)
print("\nHasil pencarian dengan gambar:")
for , row in results.iterrows():
print(f" [{row['distance']:.4f}] {row['label']}")
Integrasi dengan LangChain
LanceDB terintegrasi dengan LangChain sebagai vector store.
pip install langchain-community lancedb langchain-openai
from langchaincommunity.vectorstores import LanceDB
from langchain
openai import OpenAIEmbeddings, ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.schema import Document
import lancedb
Buat koneksi LanceDB
db = lancedb.connect("./langchainlancedb")
Siapkan embedding
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
Siapkan dokumen
documents = [
Document(
pagecontent="LanceDB adalah database vektor serverless yang cepat",
metadata={"source": "docs", "topic": "database"},
),
Document(
pagecontent="Vector search memungkinkan pencarian berdasarkan makna semantik",
metadata={"source": "docs", "topic": "search"},
),
Document(
pagecontent="RAG menggabungkan retrieval dan generation untuk jawaban akurat",
metadata={"source": "tutorial", "topic": "rag"},
),
Document(
pagecontent="Embedding mengubah teks menjadi representasi vektor numerik",
metadata={"source": "tutorial", "topic": "embedding"},
),
]
Buat vector store
vectorstore = LanceDB.fromdocuments(
documents,
embeddings,
connection=db,
tablename="langchaindocs",
)
Pencarian similarity
results = vectorstore.similaritysearch(
"bagaimana cara kerja pencarian vektor?", k=3
)
for doc in results:
print(f"- {doc.pagecontent}")
print(f" Metadata: {doc.metadata}\n")
RAG chain
llm = ChatOpenAI(model="gpt-4o", temperature=0)
qachain = RetrievalQA.fromchaintype(
llm=llm,
chaintype="stuff",
retriever=vectorstore.asretriever(searchkwargs={"k": 3}),
)
response = qachain.invoke({"query": "Apa itu LanceDB?"})
print(f"\nJawaban: {response['result']}")
Integrasi dengan LlamaIndex
LanceDB juga tersedia sebagai vector store untuk LlamaIndex.
pip install llama-index-vector-stores-lancedb llama-index
from llamaindex.core import VectorStoreIndex, SimpleDirectoryReader
from llamaindex.vectorstores.lancedb import LanceDBVectorStore
from llamaindex.core import StorageContext
import lancedb
Buat koneksi LanceDB
db = lancedb.connect("./llamaindexlancedb")
Konfigurasi LanceDB vector store
vectorstore = LanceDBVectorStore(
uri="./llamaindexlancedb",
tablename="llamadocs",
)
storagecontext = StorageContext.fromdefaults(
vectorstore=vectorstore
)
Load dokumen
documents = SimpleDirectoryReader("data/").loaddata()
Buat index
index = VectorStoreIndex.fromdocuments(
documents,
storagecontext=storagecontext,
)
Query
queryengine = index.asqueryengine()
response = queryengine.query("Apa topik utama dari dokumen-dokumen ini?")
print(response)
Contoh Praktis: Mesin Pencari Multimodal
Berikut adalah contoh lengkap membangun mesin pencari multimodal menggunakan LanceDB.
"""
Mesin Pencari Multimodal dengan LanceDB
Mendukung pencarian teks dan gambar dalam satu sistem
"""
import lancedb
from lancedb.pydantic import LanceModel, Vector
from lancedb.embeddings import getregistry
from pathlib import Path
from datetime import datetime
from typing import Optional
import json
class MultimodalSearchEngine:
"""Mesin pencari multimodal menggunakan LanceDB."""
def init(
self,
dbpath: str = "./multimodalsearchdb",
embeddingmodel: str = "all-MiniLM-L6-v2",
):
self.db = lancedb.connect(dbpath)
# Setup text embedding
st = getregistry().get("sentence-transformers")
self.textembedfunc = st.create(
name=embeddingmodel,
device="cpu",
)
# Definisikan schema
embedfunc = self.textembedfunc
class TextDocument(LanceModel):
text: str = embedfunc.SourceField()
vector: Vector(embedfunc.ndims()) = embedfunc.VectorField()
title: str = ""
source: str = ""
doctype: str = "text"
createdat: str = ""
metadatajson: str = "{}"
self.TextDocument = TextDocument
self.inittables()
def inittables(self):
"""Inisialisasi tabel jika belum ada."""
try:
self.texttable = self.db.opentable("textdocuments")
print("Tabel 'textdocuments' ditemukan")
except Exception:
self.texttable = self.db.createtable(
"textdocuments",
schema=self.TextDocument,
)
print("Tabel 'textdocuments' dibuat")
def adddocuments(
self,
texts: list[str],
titles: list[str] = None,
sources: list[str] = None,
metadata: list[dict] = None,
):
"""Tambahkan dokumen teks ke database."""
documents = []
for i, text in enumerate(texts):
doc = {
"text": text,
"title": titles[i] if titles else f"Document {i + 1}",
"source": sources[i] if sources else "unknown",
"doctype": "text",
"createdat": datetime.now().isoformat(),
"metadatajson": json.dumps(
metadata[i] if metadata else {}
),
}
documents.append(doc)
self.texttable.add(documents)
print(f"Ditambahkan {len(documents)} dokumen teks")
def searchtext(
self,
query: str,
limit: int = 10,
filtercondition: str = None,
metric: str = "cosine",
) -> list[dict]:
"""Pencarian semantik pada dokumen teks."""
search = (
self.texttable
.search(query)
.metric(metric)
.limit(limit)
)
if filtercondition:
search = search.where(filtercondition)
results = search.topandas()
output = []
for , row in results.iterrows():
output.append({
"title": row.get("title", ""),
"text": row.get("text", ""),
"source": row.get("source", ""),
"score": 1 - row.get("distance", 0),
"distance": row.get("distance", 0),
})
return output
def searchfulltext(
self,
query: str,
limit: int = 10,
) -> list[dict]:
"""Pencarian full-text pada dokumen."""
try:
results = (
self.texttable
.search(query, querytype="fts")
.limit(limit)
.topandas()
)
output = []
for , row in results.iterrows():
output.append({
"title": row.get("title", ""),
"text": row.get("text", ""),
"source": row.get("source", ""),
"score": row.get("score", 0),
})
return output
except Exception as e:
print(f"FTS belum di-index. Jalankan createftsindex() terlebih dahulu.")
print(f"Error: {e}")
return []
def createftsindex(self):
"""Buat full-text search index."""
self.texttable.createftsindex("text")
print("FTS index berhasil dibuat")
def createvectorindex(self, numpartitions: int = 16):
"""Buat vector index untuk pencarian yang lebih cepat."""
rowcount = len(self.texttable)
if rowcount < 256:
print(
f"Dataset terlalu kecil ({rowcount} baris) "
"untuk indexing. Minimal 256 baris."
)
return
self.texttable.createindex(
metric="cosine",
numpartitions=numpartitions,
numsubvectors=8,
indextype="IVFPQ",
)
print("Vector index IVFPQ berhasil dibuat")
def getstats(self) -> dict:
"""Dapatkan statistik database."""
return {
"totaltextdocuments": len(self.texttable),
"tables": self.db.tablenames(),
}
def deletebysource(self, source: str):
"""Hapus dokumen berdasarkan source."""
self.texttable.delete(f"source = '{source}'")
print(f"Dokumen dari source '{source}' berhasil dihapus")
Penggunaan
if name == "main":
# Inisialisasi search engine
engine = MultimodalSearchEngine(
dbpath="./mysearchengine",
embeddingmodel="all-MiniLM-L6-v2",
)
# Tambahkan dokumen
texts = [
"LanceDB adalah database vektor serverless yang ringan dan cepat",
"PostgreSQL adalah database relasional open source yang powerful",
"MongoDB adalah database NoSQL berbasis dokumen",
"Redis adalah in-memory data store untuk caching",
"Elasticsearch menyediakan pencarian full-text yang powerful",
"ChromaDB adalah database vektor untuk aplikasi AI",
"Pinecone adalah managed vector database di cloud",
"Weaviate mendukung pencarian vektor dan hybrid search",
"Milvus adalah database vektor open source untuk skala besar",
"FAISS adalah library dari Meta untuk similarity search",
]
titles = [
"LanceDB Overview", "PostgreSQL Guide", "MongoDB Basics",
"Redis Caching", "Elasticsearch Search", "ChromaDB AI",
"Pinecone Cloud", "Weaviate Search", "Milvus Scale",
"FAISS Library",
]
engine.adddocuments(
texts=texts,
titles=titles,
sources=["docs"] len(texts),
)
# Pencarian semantik
print("\n=== Pencarian Semantik ===")
results = engine.searchtext("database untuk aplikasi AI", limit=5)
for r in results:
print(f" [{r['score']:.4f}] {r['title']}: {r['text'][:80]}...")
# Buat FTS index dan cari
engine.createftsindex()
print("\n=== Pencarian Full-Text ===")
ftsresults = engine.searchfulltext("vector database", limit=5)
for r in ftsresults:
print(f" [{r['score']:.4f}] {r['title']}: {r['text'][:80]}...")
# Statistik
print(f"\n=== Statistik ===")
stats = engine.getstats()
print(f"Total dokumen: {stats['totaltextdocuments']}")
print(f"Tabel: {stats['tables']}")
Tips dan Best Practices
Kesimpulan
LanceDB menawarkan solusi database vektor yang unik dengan pendekatan serverless dan embedded. Tanpa perlu mengelola infrastruktur server, Anda dapat langsung membangun aplikasi pencarian semantik, RAG, dan pencarian multimodal dengan performa yang baik.
Keunggulan LanceDB terletak pada kemudahan penggunaan, dukungan multimodal native, dan integrasi yang mulus dengan ekosistem AI seperti LangChain dan LlamaIndex. Untuk proyek yang membutuhkan database vektor tanpa overhead operasional, LanceDB adalah pilihan yang sangat tepat.
Mulailah bereksperimen dengan LanceDB dan temukan bagaimana database vektor serverless ini dapat mempercepat pengembangan aplikasi AI Anda!