2025-09-01 14:50:26 +02:00
|
|
|
|
#!/usr/bin/env python3
|
|
|
|
|
|
"""
|
2025-09-01 14:56:07 +02:00
|
|
|
|
Erzeugt Qdrant-Collections für das mindnet-Projekt:
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
- mindnet_chunks : semantische Suche über Markdown-Text-Chunks (384/Cosine)
|
|
|
|
|
|
- mindnet_notes : 1 Punkt pro Notiz (Metadaten, optional Titel-Embedding)
|
|
|
|
|
|
- mindnet_edges : explizite Link-Kanten (Dummy-Vektor size=1; Filter über Payload)
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
Idempotent: legt nur an, wenn nicht vorhanden. Legt sinnvolle Payload-Indizes an.
|
2025-09-01 14:50:26 +02:00
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
|
import sys
|
|
|
|
|
|
import json
|
|
|
|
|
|
import argparse
|
|
|
|
|
|
import requests
|
|
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
DEFAULT_QDRANT_URL = os.environ.get("QDRANT_URL", "http://127.0.0.1:6333")
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
def rq(method: str, path: str, **kwargs) -> requests.Response:
|
|
|
|
|
|
url = DEFAULT_QDRANT_URL.rstrip("/") + path
|
2025-09-01 14:50:26 +02:00
|
|
|
|
r = requests.request(method, url, timeout=15, **kwargs)
|
|
|
|
|
|
if not r.ok:
|
|
|
|
|
|
raise RuntimeError(f"{method} {url} -> {r.status_code} {r.text}")
|
|
|
|
|
|
return r
|
|
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
def collection_exists(name: str) -> bool:
|
|
|
|
|
|
r = rq("GET", f"/collections/{name}")
|
|
|
|
|
|
data = r.json()
|
|
|
|
|
|
return data.get("result", {}).get("status") == "green"
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
def create_collection(name: str, size: int, distance: str = "Cosine") -> None:
|
|
|
|
|
|
if collection_exists(name):
|
|
|
|
|
|
print(f"[=] Collection '{name}' existiert bereits – überspringe Anlage.")
|
2025-09-01 14:50:26 +02:00
|
|
|
|
return
|
|
|
|
|
|
payload = {"vectors": {"size": size, "distance": distance}}
|
2025-09-01 14:56:07 +02:00
|
|
|
|
rq("PUT", f"/collections/{name}", json=payload)
|
|
|
|
|
|
print(f"[+] Collection '{name}' angelegt (size={size}, distance={distance}).")
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
def create_keyword_index(collection: str, field: str) -> None:
|
|
|
|
|
|
payload = {"field_name": field, "field_schema": "keyword"}
|
|
|
|
|
|
rq("PUT", f"/collections/{collection}/index", json=payload)
|
|
|
|
|
|
print(f"[+] Index keyword on {collection}.{field}")
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
def create_text_index(collection: str, field: str = "text") -> None:
|
|
|
|
|
|
payload = {"field_name": field, "field_schema": {"type": "text"}}
|
|
|
|
|
|
rq("PUT", f"/collections/{collection}/index", json=payload)
|
|
|
|
|
|
print(f"[+] Index text on {collection}.{field}")
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
|
|
|
|
|
def main():
|
2025-09-01 14:56:07 +02:00
|
|
|
|
ap = argparse.ArgumentParser()
|
|
|
|
|
|
ap.add_argument("--qdrant-url", default=DEFAULT_QDRANT_URL, help="z.B. http://127.0.0.1:6333")
|
|
|
|
|
|
ap.add_argument("--prefix", default="mindnet", help="Collection-Präfix (default: mindnet)")
|
|
|
|
|
|
ap.add_argument("--dim", type=int, default=384, help="Embedding-Dimension (384 für all-MiniLM-L6-v2)")
|
|
|
|
|
|
ap.add_argument("--distance", default="Cosine", choices=["Cosine", "Euclid", "Dot"], help="Distanzmetrik")
|
|
|
|
|
|
args = ap.parse_args()
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
global DEFAULT_QDRANT_URL
|
|
|
|
|
|
DEFAULT_QDRANT_URL = args.qdrant_url
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
|
|
|
|
|
chunks = f"{args.prefix}_chunks"
|
|
|
|
|
|
notes = f"{args.prefix}_notes"
|
|
|
|
|
|
edges = f"{args.prefix}_edges"
|
|
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
# 1) Collections anlegen
|
|
|
|
|
|
create_collection(chunks, size=args.dim, distance=args.distance)
|
|
|
|
|
|
create_collection(notes, size=args.dim, distance=args.distance)
|
|
|
|
|
|
create_collection(edges, size=1, distance=args.distance) # Dummy-Vektor
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
# 2) Indizes definieren
|
|
|
|
|
|
# mindnet_chunks: häufige Filter + Volltext
|
2025-09-01 14:50:26 +02:00
|
|
|
|
for f in ["note_id", "Status", "Typ", "title", "path"]:
|
2025-09-01 14:56:07 +02:00
|
|
|
|
create_keyword_index(chunks, f)
|
2025-09-01 14:50:26 +02:00
|
|
|
|
for f in ["tags", "Rolle", "links"]:
|
2025-09-01 14:56:07 +02:00
|
|
|
|
create_keyword_index(chunks, f)
|
|
|
|
|
|
create_text_index(chunks, "text") # Wort-/Phrasensuche
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
# mindnet_notes: Metadaten der Notizen
|
2025-09-01 14:50:26 +02:00
|
|
|
|
for f in ["note_id", "title", "path", "Typ", "Status"]:
|
2025-09-01 14:56:07 +02:00
|
|
|
|
create_keyword_index(notes, f)
|
2025-09-01 14:50:26 +02:00
|
|
|
|
for f in ["tags", "Rolle"]:
|
2025-09-01 14:56:07 +02:00
|
|
|
|
create_keyword_index(notes, f)
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
# mindnet_edges: Graph/Kanten (Filter-only)
|
2025-09-01 14:50:26 +02:00
|
|
|
|
for f in ["src_note_id", "dst_note_id", "src_chunk_id", "dst_chunk_id", "link_text", "relation"]:
|
2025-09-01 14:56:07 +02:00
|
|
|
|
create_keyword_index(edges, f)
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
2025-09-01 14:56:07 +02:00
|
|
|
|
# 3) Ausgabe
|
|
|
|
|
|
r = rq("GET", "/collections")
|
2025-09-01 14:50:26 +02:00
|
|
|
|
print("\n[Info] Collections vorhanden:")
|
2025-09-01 14:56:07 +02:00
|
|
|
|
print(json.dumps(r.json().get("result", {}).get("collections", []), indent=2, ensure_ascii=False))
|
2025-09-01 14:50:26 +02:00
|
|
|
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
|
|
try:
|
|
|
|
|
|
main()
|
|
|
|
|
|
except Exception as e:
|
|
|
|
|
|
print(f"[ERROR] {e}", file=sys.stderr)
|
|
|
|
|
|
sys.exit(1)
|