Verwendung Ihrer Vektordatenbank als JSON- (oder relationale) Datenspeicher
TL;DR: Vektordatenbanken unterstützen CRUD für „traditionelle“ Datenformate wie JSON. Wenn du ein Solo-Entwickler oder ein kleines Team bist und nicht viele verschiedene Komponenten der Dateninfrastruktur verwalten möchtest, kannst du Milvus oder Zilliz Cloud (das verwaltete Milvus) als deinen einzigen Datenspeicher verwenden und vektorlose Collections bei der Skalierung einfach in andere Datenbanken migrieren.
Angetrieben durch die Popularität von ChatGPT und anderen autoregressiven Sprachmodellen hat Vektorsuche im vergangenen Jahr enorm an Beliebtheit gewonnen. Infolgedessen haben wir gesehen, wie viele Unternehmen und Organisationen auf den Zug der Vektorsuche aufgesprungen sind, von NoSQL-Datenbankanbietern wie MongoDB (über Atlas Vector Search) bis hin zu traditionellen relationalen Datenbanken wie Postgres (über pgvector). Die allgemeine Botschaft, die ich rund um diese Vektorsuch-Plugins höre, ist weitgehend dieselbe und geht ungefähr so: Entwickler sollten bei uns bleiben, da ihr zusätzlich zu Vektoren auch Tabellen/JSON speichern könnt, sodass keine Notwendigkeit besteht, mehrere Infrastrukturkomponenten zu verwalten!
Solche Aussagen bringen mich immer zum Lachen, da sie eindeutig von wenig anspruchsvollen Marketingteams formuliert wurden. Nicht nur unterscheidet sich die Technologie hinter der Vektorsuche grundlegend von Speicher- und Abfragestrategien in relationalen & NoSQL-Datenbanken, sondern inzwischen ist auch ziemlich bekannt, dass Vektordatenbanken Relationen, JSON-Dokumente und andere strukturierte Datenquellen speichern können. Der erste Punkt ist ohne tiefes Vorwissen über Datenbankmanagementsysteme schwer prägnant zu veranschaulichen, aber der zweite Punkt lässt sich recht einfach anhand einiger kurzer Beispiel-Codeausschnitte zeigen. Genau diesem Thema widmet sich dieser Blogbeitrag.
Einrichtung
Milvus speichert Daten in Einheiten, die als Collections bezeichnet werden, analog zu Tabellen in relationalen Datenbanken. Jede Collection kann ihr eigenes Schema haben, und Schemata haben ein Vektorfeld mit fester Dimensionalität, z. B. 768 für Vektor-Embeddings auf Basis von e5-base. Erstellen wir eine Collection, um JSON-Dokumente statt Vektordaten zu speichern. Um diesen Punkt besser zu veranschaulichen, habe ich einige der früheren und späteren Schritte weggelassen, wie etwa den Aufruf von collections.connect:
from pymilvus import Collection, CollectionSchema, DataType, FieldSchema
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True, max_length=100),
FieldSchema(name="_unused", dtype=DataType.FLOAT_VECTOR, dim=1)
]
schema = CollectionSchema(fields, "NoSQL data", enable_dynamic_field=True)
collection = Collection("json_data", schema)
collection.load()
Hier haben wir festgelegt, dass diese Collection die Funktionen des dynamischen Schemas von Milvus nutzt, sodass die Collection JSON-Payloads als „zusätzliche“ Daten akzeptieren kann, die jeder Zeile zugeordnet sind.
Ich hoffe, du hast nicht mehr erwartet, denn das war’s – es ist wirklich so einfach.
CRUD-Operationen
Du kannst mit der oben erstellten Collection interagieren, wie du es mit jeder anderen Datenbank tun würdest. Laden wir als Beispiel die Liste der aktuellen US-Senatoren (im JSON-Format) von govtrack herunter:
import requests
r = requests.get("https://www.govtrack.us/api/v2/role?current=true&role_type=senator")
data = r.json()["objects"]
len(data)
100
Wir können diese Dokumente mit nur ein klein wenig Datenaufbereitung direkt in Milvus speichern:
rows = [{"_unused": [0]} | d for d in data]
collection.insert(rows)
(insert count: 100, delete count: 0, upsert count: 0, ...
Von hier aus können wir Abfragen direkt über diese Daten ausführen:
collection.query(
expr="party like 'Dem%'",
limit=1,
output_fields=["person"]
)
[{'person': {'bioguideid': 'C000127',
'birthday': '1958-10-13',
'cspanid': 26137,
'fediverse_webfinger': None,
'firstname': 'Maria',
'gender': 'female',
'gender_label': 'Female',
'lastname': 'Cantwell',
'link': 'https://www.govtrack.us/congress/members/maria_cantwell/300018',
'middlename': '',
'name': 'Sen. Maria Cantwell [D-WA]',
'namemod': '',
'nickname': '',
'osid': 'N00007836',
'pvsid': None,
'sortname': 'Cantwell, Maria (Sen.) [D-WA]',
'twitterid': 'SenatorCantwell',
'youtubeid': 'SenatorCantwell'},
'id': 447376465724036249}]
Ohne ein Vektorfeld anzugeben, haben wir unsere Datenbank nach dem ersten Ergebnis abgefragt, bei dem das Feld party in der zugehörigen JSON-Payload „Dem“ (Demokrat) als Präfix hat.
Dieser Schritt demonstriert hoffentlich die Fähigkeit, strukturierte Datensuchen in Milvus durchzuführen, aber es ist keine besonders nützliche Abfrage. Suchen wir alle Senatoren aus meinem Heimatstaat Oregon:
collection.query(
expr="state in ['OR']",
limit=10,
output_fields=["person"]
)
[{'person': {'bioguideid': 'M001176',
'birthday': '1956-10-24',
'cspanid': 1029842,
'fediverse_webfinger': None,
'firstname': 'Jeff',
'gender': 'male',
'gender_label': 'Male',
'lastname': 'Merkley',
'link': 'https://www.govtrack.us/congress/members/jeff_merkley/412325',
'middlename': '',
'name': 'Sen. Jeff Merkley [D-OR]',
'namemod': '',
'nickname': '',
'osid': 'N00029303',
'pvsid': None,
'sortname': 'Merkley, Jeff (Sen.) [D-OR]',
'twitterid': 'SenJeffMerkley',
'youtubeid': 'SenatorJeffMerkley'},
'id': 447376465724036286},
{'person': {'bioguideid': 'W000779',
'birthday': '1949-05-03',
'cspanid': 1962,
'fediverse_webfinger': None,
'firstname': 'Ron',
'gender': 'male',
'gender_label': 'Male',
'lastname': 'Wyden',
'link': 'https://www.govtrack.us/congress/members/ron_wyden/300100',
'middlename': '',
'name': 'Sen. Ron Wyden [D-OR]',
'namemod': '',
'nickname': '',
'osid': 'N00007724',
'pvsid': None,
'sortname': 'Wyden, Ron (Sen.) [D-OR]',
'twitterid': 'RonWyden',
'youtubeid': 'senronwyden'},
'id': 447376465724036331}]
Obwohl wir limit=10 angegeben haben, wurden nur zwei Dokumente zurückgegeben (da jeder Staat nur zwei Senatoren hat). Grenzen wir unsere Abfrage noch weiter ein, um nur den dienstälteren Senator zu erhalten:
collection.query(
expr="state in ['OR'] and senator_rank in ['senior']",
limit=10,
output_fields=["person"]
)
[{'person': {'bioguideid': 'W000779',
'birthday': '1949-05-03',
'cspanid': 1962,
'fediverse_webfinger': None,
'firstname': 'Ron',
'gender': 'male',
'gender_label': 'Male',
'lastname': 'Wyden',
'link': 'https://www.govtrack.us/congress/members/ron_wyden/300100',
'middlename': '',
'name': 'Sen. Ron Wyden [D-OR]',
'namemod': '',
'nickname': '',
'osid': 'N00007724',
'pvsid': None,
'sortname': 'Wyden, Ron (Sen.) [D-OR]',
'twitterid': 'RonWyden',
'youtubeid': 'senronwyden'},
'id': 447376465724036331}]
Vielleicht möchte ich das Profil von Senator Ron Wyden mit etwas mehr Informationen aktualisieren. Das können wir ganz einfach tun, indem wir das gesamte Dokument mit output_fields=["*"] abrufen, das resultierende Dokument aktualisieren und es ohne den alten Primärschlüssel wieder in unsere Datenbank einfügen:
expr = "state in ['OR'] and senator_rank in ['senior']"
res = collection.query(
expr=expr,
limit=10,
output_fields=["*"]
)
res[0].update({
"elected_in": "1996",
"college": "Stanford University",
"college_major": "Political Science"
})
del res[0]["id"]
(insert count: 1, delete count: 0, upsert count: 0, ...
collection.delete(expr)
collection.insert(res)
Sehen wir nach, ob dies wie erwartet funktioniert hat.
collection.query(
expr=expr,
limit=10,
output_fields=["elected_in", "college", "college_major"]
)
[{'elected_in': '1996',
'college': 'Stanford University',
'college_major': 'Political Science',
'id': 447376465724036353}]
Die Daten entsprechen tatsächlich den Aktualisierungen, die wir vorgenommen haben.
Das vollständige Skript
Hier ist der Übersichtlichkeit halber das gesamte Skript von Anfang bis Ende ohne die überflüssigen Abfragen. Ich habe unseren Zilliz Cloud Free Tier anstelle von milvus-lite verwendet:
from milvus import default_server
from pymilvus import connections
from pymilvus import Collection, CollectionSchema, DataType, FieldSchema
import requests
# Uncomment this if you're using `milvus-lite`
#default_server.start()
#connections.connect(host="127.0.0.1", port=default_server.listen_port)
# Uncomment this if you're using Zilliz Cloud
connections.connect(
uri=os.environ["ZILLIZ_URI"],
token=os.environ["ZILLIZ_TOKEN"]
)
# Create the schema for our new collection. We set turn on Milvus' dynamic
# schema capability in order to store arbitratily large (or small) JSON blogs
# in each row.
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True, max_length=100),
FieldSchema(name="_unused", dtype=DataType.FLOAT_VECTOR, dim=1)
]
schema = CollectionSchema(fields, "Milvus as a JSON datastore", enable_dynamic_field=True)
# Now let's creat the collection.
collection = Collection("json_data", schema)
index_params = {
"index_type": "AUTOINDEX",
"metric_type": "L2",
"params": {}
}
collection.create_index(
field_name="_unused",
index_params=index_params
)
collection.load()
# Insert US senator JSON data into our newly formed collection.
r = requests.get("https://www.govtrack.us/api/v2/role?current=true&role_type=senator")
data = r.json()["objects"]
rows = [{"_unused": [0]} | d for d in data]
collection.insert(rows)
# Fetch the first Democrat in the database
top_k = 1
collection.query(
expr="state in ['OR'] and senator_rank in ['senior']",
limit=top_k,
output_fields=["person"]
)
[{'person': {'bioguideid': 'W000779',
'birthday': '1949-05-03',
'cspanid': 1962,
'fediverse_webfinger': None,
'firstname': 'Ron',
'gender': 'male',
'gender_label': 'Male',
'lastname': 'Wyden',
'link': 'https://www.govtrack.us/congress/members/ron_wyden/300100',
'middlename': '',
'name': 'Sen. Ron Wyden [D-OR]',
'namemod': '',
'nickname': '',
'osid': 'N00007724',
'pvsid': None,
'sortname': 'Wyden, Ron (Sen.) [D-OR]',
'twitterid': 'RonWyden',
'youtubeid': 'senronwyden'},
'id': 447376465724036331}]
Probieren Sie es selbst aus!
pymongo -> milvusmongo
Eine letzte Anmerkung, bevor wir zum Abschluss kommen: Ich habe ein kleines Python-Paket namens milvusmongo erstellt, das die grundlegendste CRUD-Funktionalität von pymongo über Collections hinweg implementiert. Es verwendet Milvus als zugrunde liegende Datenbank statt MongoDB. Wie pymongo unterstützt milvusmongo sowohl den Zugriff im Dictionary- als auch im Attributstil und abstrahiert zusätzliche Logik, die für CRUD-Aufrufe benötigt wird (d. h. insert_one, update_one und delete_one). Sie können es mit pip install milvusmongo installieren:
% pip install milvus
% pip install milvusmongo
Sobald das erledigt ist, können Sie eine eingebettete Milvus-Instanz starten und sie zusammen mit milvusmongo verwenden, um Abfragen über JSON-Daten auszuführen. Zum Beispiel:
from milvus import default_server
default_server.start()
from milvusmongo import MongoClient
client = MongoClient("127.0.0.1", 19530)
client.insert_one(my_document)
Bitte beachten Sie, dass diese Bibliothek dazu gedacht ist, die Flexibilität von Milvus als Datenspeicher zu demonstrieren, und nicht als etwas, das Sie in groß angelegten Produktionsumgebungen verwenden sollten.
Schlussworte
Milvus ist nicht dazu da, NoSQL-Datenbanken oder lexikalische Textsuchmaschinen zu ersetzen; wir sind hier, um Ihnen die bestmögliche Erfahrung bei der Vektor-/gefilterten Suche zu bieten. Noch wichtiger: Wir sind hier, um die Einführung der Vektorsuche als Technologie zu beschleunigen – deshalb ist Open Source ein so zentraler Bestandteil unseres Ethos.
Das bedeutet jedoch nicht, dass wir andere Datentypen nicht unterstützen. Als Einzelentwickler oder kleines Startup steht es Ihnen frei, Milvus als Ihren einzigen Datenspeicher zu nutzen. Sie können Ihre Infrastrukturnutzung später immer noch optimieren, wenn Sie wachsen. Milvus bietet Ihnen erstklassige Vektorsuchfunktionen, während andere Datenbanken dazu da sind, andere Formen von Daten zu speichern, zu indexieren und zu durchsuchen. Sobald Ihre Anwendung komplexere Workloads erfordert (wie Joins oder Aggregationen), sollten Sie in Betracht ziehen, verschiedene Datenspeicher zu verwenden.
Weiterlesen

Vector Lakebase: End the AI Data Silo
Learn how Vector Lakebase unifies vector search, data lakes, and AI data operations so teams can serve RAG and agents without copy-and-sync pipelines.

Introducing Business Critical Plan: Enterprise-Grade Security and Compliance for Mission-Critical AI Applications
Discover Zilliz Cloud’s Business Critical Plan—offering advanced security, compliance, and uptime for mission-critical AI and vector database workloads.

Zilliz Cloud Introduces Advanced BYOC-I Solution for Ultimate Enterprise Data Sovereignty
Explore Zilliz Cloud BYOC-I, the solution that balances AI innovation with data control, enabling secure deployments in finance, healthcare, and education sectors.



