@@ -4,98 +4,203 @@ QQL can be used as a Python library without the CLI.
44
55---
66
7- ## ` run_query() ` — high-level API
7+ ## ` Connection ` — Primary API
8+
9+ ` Connection ` is the recommended way to use QQL programmatically. It opens a
10+ single connection to Qdrant once and reuses it for every ` run_query() ` call —
11+ more efficient than the legacy ` run_query() ` function, which creates a new
12+ client on every invocation.
13+
14+ ### Basic usage
815
916``` python
10- from qql import run_query
17+ from qql import Connection
18+
19+ conn = Connection(" http://localhost:6333" )
1120
1221# Insert a document (dense-only)
13- result = run_query(
14- " INSERT INTO COLLECTION notes VALUES {'text': 'hello world', 'author': 'alice', 'year': 2024}" ,
15- url = " http://localhost:6333" ,
22+ result = conn.run_query(
23+ " INSERT INTO COLLECTION notes VALUES {'text': 'hello world', 'author': 'alice', 'year': 2024}"
1624)
1725print (result.message) # "Inserted 1 point [<id>]"
18- print (result.data) # {"id": 1001 or "<uuid>", "collection": "notes"}
26+ print (result.data) # {"id": "<uuid>", "collection": "notes"}
1927
20- # Insert with hybrid vectors
21- result = run_query(
22- " INSERT INTO COLLECTION notes VALUES {'text': 'hello world'} USING HYBRID" ,
23- url = " http://localhost:6333" ,
24- )
25- print (result.message) # "Inserted 1 point [<id>] (hybrid)"
26-
27- # Dense search with WHERE filter
28- result = run_query(
29- " SEARCH notes SIMILAR TO 'hello' LIMIT 5 WHERE year >= 2023 AND author != 'bot'" ,
30- url = " http://localhost:6333" ,
28+ # Search
29+ result = conn.run_query(
30+ " SEARCH notes SIMILAR TO 'hello' LIMIT 5 WHERE year >= 2023"
3131)
3232for hit in result.data:
3333 print (hit[" score" ], hit[" payload" ])
3434
35- # Hybrid search with WHERE filter
36- result = run_query(
37- " SEARCH notes SIMILAR TO 'hello' LIMIT 5 USING HYBRID WHERE year >= 2023" ,
38- url = " http://localhost:6333" ,
39- )
40- for hit in result.data:
41- print (hit[" score" ], hit[" payload" ])
35+ conn.close()
36+ ```
4237
43- # Scroll / pagination
44- result = run_query(
45- " SCROLL FROM notes LIMIT 2" ,
46- url = " http://localhost:6333" ,
47- )
48- for point in result.data[" points" ]:
49- print (point[" id" ], point[" payload" ])
50- print (result.data[" next_offset" ])
38+ ### Context manager (preferred)
5139
52- # Bulk insert (all records embedded and upserted in one call)
53- result = run_query(
54- """ INSERT BULK INTO COLLECTION notes VALUES [
55- {'id': 1, 'text': 'first document', 'year': 2023},
56- {'id': 2, 'text': 'second document', 'year': 2024}
57- ]""" ,
58- url = " http://localhost:6333" ,
59- )
60- print (result.message) # "Inserted 2 points"
40+ The context manager guarantees the HTTP connection pool is released even if an
41+ exception occurs:
6142
62- # Recommend similar points using known IDs as positive examples
63- result = run_query(
64- " RECOMMEND FROM notes POSITIVE IDS (1, 2) NEGATIVE IDS (3) LIMIT 5" ,
65- url = " http://localhost:6333" ,
66- )
67- for hit in result.data:
68- print (hit[" score" ], hit[" payload" ])
43+ ``` python
44+ from qql import Connection
6945
70- # Retrieve a point by ID
71- result = run_query(
72- " SELECT * FROM notes WHERE id = 1" ,
73- url = " http://localhost:6333" ,
74- )
75- print (result.data) # {"id": "1", "payload": {...}}
46+ with Connection(" http://localhost:6333" ) as conn:
47+ # All queries share the same connection
48+ conn.run_query(
49+ " INSERT INTO COLLECTION notes VALUES {'text': 'hello world'} USING HYBRID"
50+ )
51+ result = conn.run_query(
52+ " SEARCH notes SIMILAR TO 'hello' LIMIT 5 USING HYBRID WHERE year >= 2023"
53+ )
54+ for hit in result.data:
55+ print (hit[" score" ], hit[" payload" ])
56+ ```
57+
58+ ### Qdrant Cloud
59+
60+ ``` python
61+ from qql import Connection
62+
63+ with Connection(" https://<your-cluster>.qdrant.io" , secret = " <your-api-key>" ) as conn:
64+ result = conn.run_query(" SHOW COLLECTIONS" )
65+ print (result.data)
66+ ```
67+
68+ ### Custom embedding model
7669
77- # Delete by filter
70+ ``` python
71+ from qql import Connection
72+
73+ with Connection(
74+ " http://localhost:6333" ,
75+ default_model = " BAAI/bge-base-en-v1.5" ,
76+ ) as conn:
77+ conn.run_query(
78+ " INSERT INTO COLLECTION articles VALUES {'text': 'Attention is all you need'}"
79+ )
80+ ```
81+
82+ ### All statement examples
83+
84+ ``` python
85+ from qql import Connection
86+
87+ with Connection(" http://localhost:6333" ) as conn:
88+
89+ # Hybrid insert
90+ conn.run_query(
91+ " INSERT INTO COLLECTION notes VALUES {'text': 'hello world'} USING HYBRID"
92+ )
93+
94+ # Dense search with WHERE filter
95+ result = conn.run_query(
96+ " SEARCH notes SIMILAR TO 'hello' LIMIT 5 WHERE year >= 2023 AND author != 'bot'"
97+ )
98+ for hit in result.data:
99+ print (hit[" score" ], hit[" payload" ])
100+
101+ # Hybrid search
102+ result = conn.run_query(
103+ " SEARCH notes SIMILAR TO 'hello' LIMIT 5 USING HYBRID WHERE year >= 2023"
104+ )
105+
106+ # Scroll / pagination
107+ result = conn.run_query(" SCROLL FROM notes LIMIT 2" )
108+ for point in result.data[" points" ]:
109+ print (point[" id" ], point[" payload" ])
110+ next_cursor = result.data[" next_offset" ] # str | int | None
111+
112+ # Continue pagination
113+ if next_cursor is not None :
114+ result = conn.run_query(f " SCROLL FROM notes AFTER ' { next_cursor} ' LIMIT 2 " )
115+
116+ # Bulk insert
117+ result = conn.run_query(
118+ """ INSERT BULK INTO COLLECTION notes VALUES [
119+ {'id': 1, 'text': 'first document', 'year': 2023},
120+ {'id': 2, 'text': 'second document', 'year': 2024}
121+ ]"""
122+ )
123+ print (result.message) # "Inserted 2 points"
124+
125+ # Recommend similar points
126+ result = conn.run_query(
127+ " RECOMMEND FROM notes POSITIVE IDS (1, 2) NEGATIVE IDS (3) LIMIT 5"
128+ )
129+ for hit in result.data:
130+ print (hit[" score" ], hit[" payload" ])
131+
132+ # Retrieve a point by ID
133+ result = conn.run_query(" SELECT * FROM notes WHERE id = 1" )
134+ print (result.data) # {"id": "1", "payload": {...}}
135+
136+ # Delete by filter
137+ conn.run_query(" DELETE FROM notes WHERE year < 2023" )
138+
139+ # Inspect collection diagnostics
140+ result = conn.run_query(" SHOW COLLECTION notes" )
141+ print (result.data[" topology" ]) # "dense" or "hybrid"
142+ print (result.data[" vectors" ]) # {"": {...}} or {"dense": {...}}
143+ print (result.data[" payload_schema" ]) # field index info, or None
144+ ```
145+
146+ ### ` Connection ` parameters
147+
148+ | Parameter | Type | Default | Description |
149+ | ---| ---| ---| ---|
150+ | ` url ` | ` str ` | ` "http://localhost:6333" ` | Qdrant instance URL |
151+ | ` secret ` | ` str \| None ` | ` None ` | API key; ` None ` for unauthenticated |
152+ | ` default_model ` | ` str \| None ` | ` None ` → ` sentence-transformers/all-MiniLM-L6-v2 ` | Dense embedding model used when no ` USING MODEL ` clause is given |
153+
154+ ### Power-user: ` executor ` property
155+
156+ For low-level access to the pipeline, use ` conn.executor ` directly:
157+
158+ ``` python
159+ from qql import Connection
160+ from qql.lexer import Lexer
161+ from qql.parser import Parser
162+
163+ with Connection(" http://localhost:6333" ) as conn:
164+ tokens = Lexer().tokenize(" SEARCH docs SIMILAR TO 'hello' LIMIT 5" )
165+ node = Parser(tokens).parse()
166+ result = conn.executor.execute(node)
167+ ```
168+
169+ ---
170+
171+ ## ` run_query() ` — Legacy one-shot API
172+
173+ > ** Note:** ` run_query() ` is kept for backward compatibility. It creates a new
174+ > ` Connection ` (and therefore a new ` QdrantClient ` ) on every call. For
175+ > workloads that issue more than one query, use ` Connection ` instead.
176+
177+ ``` python
178+ from qql import run_query
179+
180+ # Insert a document
78181result = run_query(
79- " DELETE FROM notes WHERE year < 2023 " ,
182+ " INSERT INTO COLLECTION notes VALUES {'text': 'hello world', 'author': 'alice', 'year': 2024} " ,
80183 url = " http://localhost:6333" ,
81184)
82- print (result.message) # "Deleted N point(s)"
185+ print (result.message)
83186
84- # Inspect collection diagnostics
187+ # Search
85188result = run_query(
86- " SHOW COLLECTION notes" ,
189+ " SEARCH notes SIMILAR TO 'hello' LIMIT 5 WHERE year >= 2023 " ,
87190 url = " http://localhost:6333" ,
88191)
89- print (result.data[" topology" ]) # "dense" or "hybrid"
90- print (result.data[" vectors" ]) # {"": {...}} or {"dense": {...}, ...}
91- print (result.data[" payload_schema" ]) # {"field": {"type": "keyword", ...}, ...} or None
192+ for hit in result.data:
193+ print (hit[" score" ], hit[" payload" ])
92194```
93195
196+ ` run_query() ` accepts the same ` url ` , ` secret ` , and ` default_model ` parameters
197+ as ` Connection.__init__() ` .
198+
94199---
95200
96201## Low-level pipeline API
97202
98- For more control, use the pipeline directly:
203+ For full control, use the Lexer → Parser → Executor pipeline directly:
99204
100205``` python
101206from qdrant_client import QdrantClient
@@ -117,9 +222,12 @@ for hit in result.data:
117222 print (hit[" score" ], hit[" payload" ])
118223```
119224
225+ This is equivalent to what ` Connection ` does internally, giving you full
226+ control over the client lifecycle and config.
227+
120228---
121229
122- ## ExecutionResult
230+ ## ` ExecutionResult `
123231
124232All operations return an ` ExecutionResult ` :
125233
0 commit comments