pgvector 0.3.6__tar.gz → 0.4.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pgvector-0.3.6 → pgvector-0.4.1}/LICENSE.txt +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/PKG-INFO +258 -7
- {pgvector-0.3.6 → pgvector-0.4.1}/README.md +254 -4
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/asyncpg/__init__.py +3 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/asyncpg/register.py +1 -1
- pgvector-0.4.1/pgvector/bit.py +75 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/django/__init__.py +3 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/django/functions.py +5 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/django/halfvec.py +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/django/sparsevec.py +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/django/vector.py +1 -1
- {pgvector-0.3.6/pgvector/utils → pgvector-0.4.1/pgvector}/halfvec.py +5 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/peewee/__init__.py +3 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/peewee/halfvec.py +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/peewee/sparsevec.py +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/peewee/vector.py +1 -1
- pgvector-0.4.1/pgvector/pg8000/__init__.py +5 -0
- pgvector-0.4.1/pgvector/pg8000/register.py +23 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/psycopg/__init__.py +3 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/psycopg/bit.py +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/psycopg/halfvec.py +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/psycopg/sparsevec.py +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/psycopg/vector.py +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/psycopg2/__init__.py +3 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/psycopg2/halfvec.py +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/psycopg2/register.py +1 -3
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/psycopg2/sparsevec.py +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/psycopg2/vector.py +2 -1
- {pgvector-0.3.6/pgvector/utils → pgvector-0.4.1/pgvector}/sparsevec.py +7 -2
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/sqlalchemy/__init__.py +3 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/sqlalchemy/halfvec.py +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/sqlalchemy/sparsevec.py +1 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/sqlalchemy/vector.py +1 -1
- pgvector-0.4.1/pgvector/utils/__init__.py +9 -0
- {pgvector-0.3.6/pgvector/utils → pgvector-0.4.1/pgvector}/vector.py +5 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector.egg-info/PKG-INFO +258 -7
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector.egg-info/SOURCES.txt +8 -4
- {pgvector-0.3.6 → pgvector-0.4.1}/pyproject.toml +2 -2
- {pgvector-0.3.6 → pgvector-0.4.1}/tests/test_asyncpg.py +32 -19
- pgvector-0.4.1/tests/test_bit.py +63 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/tests/test_django.py +40 -19
- {pgvector-0.3.6 → pgvector-0.4.1}/tests/test_half_vector.py +18 -1
- {pgvector-0.3.6 → pgvector-0.4.1}/tests/test_peewee.py +12 -11
- pgvector-0.4.1/tests/test_pg8000.py +60 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/tests/test_psycopg.py +45 -19
- {pgvector-0.3.6 → pgvector-0.4.1}/tests/test_psycopg2.py +51 -11
- {pgvector-0.3.6 → pgvector-0.4.1}/tests/test_sparse_vector.py +47 -4
- pgvector-0.4.1/tests/test_sqlalchemy.py +649 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/tests/test_sqlmodel.py +44 -44
- {pgvector-0.3.6 → pgvector-0.4.1}/tests/test_vector.py +18 -1
- pgvector-0.3.6/pgvector/utils/bit.py +0 -61
- pgvector-0.3.6/tests/test_bit.py +0 -39
- pgvector-0.3.6/tests/test_sqlalchemy.py +0 -454
- {pgvector-0.3.6/pgvector/utils → pgvector-0.4.1/pgvector}/__init__.py +0 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/django/bit.py +0 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/django/extensions.py +0 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/django/indexes.py +0 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/peewee/bit.py +0 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/psycopg/register.py +0 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/sqlalchemy/bit.py +0 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector/sqlalchemy/functions.py +0 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector.egg-info/dependency_links.txt +0 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector.egg-info/requires.txt +0 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/pgvector.egg-info/top_level.txt +0 -0
- {pgvector-0.3.6 → pgvector-0.4.1}/setup.cfg +0 -0
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: pgvector
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.4.1
|
|
4
4
|
Summary: pgvector support for Python
|
|
5
5
|
Author-email: Andrew Kane <andrew@ankane.org>
|
|
6
6
|
License: MIT
|
|
7
7
|
Project-URL: Homepage, https://github.com/pgvector/pgvector-python
|
|
8
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.9
|
|
9
9
|
Description-Content-Type: text/markdown
|
|
10
10
|
License-File: LICENSE.txt
|
|
11
11
|
Requires-Dist: numpy
|
|
12
|
+
Dynamic: license-file
|
|
12
13
|
|
|
13
14
|
# pgvector-python
|
|
14
15
|
|
|
15
16
|
[pgvector](https://github.com/pgvector/pgvector) support for Python
|
|
16
17
|
|
|
17
|
-
Supports [Django](https://github.com/django/django), [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy), [SQLModel](https://github.com/tiangolo/sqlmodel), [Psycopg 3](https://github.com/psycopg/psycopg), [Psycopg 2](https://github.com/psycopg/psycopg2), [asyncpg](https://github.com/MagicStack/asyncpg), and [Peewee](https://github.com/coleifer/peewee)
|
|
18
|
+
Supports [Django](https://github.com/django/django), [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy), [SQLModel](https://github.com/tiangolo/sqlmodel), [Psycopg 3](https://github.com/psycopg/psycopg), [Psycopg 2](https://github.com/psycopg/psycopg2), [asyncpg](https://github.com/MagicStack/asyncpg), [pg8000](https://github.com/tlocke/pg8000), and [Peewee](https://github.com/coleifer/peewee)
|
|
18
19
|
|
|
19
20
|
[](https://github.com/pgvector/pgvector-python/actions)
|
|
20
21
|
|
|
@@ -34,10 +35,12 @@ And follow the instructions for your database library:
|
|
|
34
35
|
- [Psycopg 3](#psycopg-3)
|
|
35
36
|
- [Psycopg 2](#psycopg-2)
|
|
36
37
|
- [asyncpg](#asyncpg)
|
|
38
|
+
- [pg8000](#pg8000)
|
|
37
39
|
- [Peewee](#peewee)
|
|
38
40
|
|
|
39
41
|
Or check out some examples:
|
|
40
42
|
|
|
43
|
+
- [Retrieval-augmented generation](https://github.com/pgvector/pgvector-python/blob/master/examples/rag/example.py) with Ollama
|
|
41
44
|
- [Embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/openai/example.py) with OpenAI
|
|
42
45
|
- [Binary embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/cohere/example.py) with Cohere
|
|
43
46
|
- [Sentence embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/sentence_transformers/example.py) with SentenceTransformers
|
|
@@ -45,6 +48,7 @@ Or check out some examples:
|
|
|
45
48
|
- [Hybrid search](https://github.com/pgvector/pgvector-python/blob/master/examples/hybrid_search/cross_encoder.py) with SentenceTransformers (cross-encoder)
|
|
46
49
|
- [Sparse search](https://github.com/pgvector/pgvector-python/blob/master/examples/sparse_search/example.py) with Transformers
|
|
47
50
|
- [Late interaction search](https://github.com/pgvector/pgvector-python/blob/master/examples/colbert/exact.py) with ColBERT
|
|
51
|
+
- [Visual document retrieval](https://github.com/pgvector/pgvector-python/blob/master/examples/colpali/exact.py) with ColPali
|
|
48
52
|
- [Image search](https://github.com/pgvector/pgvector-python/blob/master/examples/image_search/example.py) with PyTorch
|
|
49
53
|
- [Image search](https://github.com/pgvector/pgvector-python/blob/master/examples/imagehash/example.py) with perceptual hashing
|
|
50
54
|
- [Morgan fingerprints](https://github.com/pgvector/pgvector-python/blob/master/examples/rdkit/example.py) with RDKit
|
|
@@ -145,6 +149,36 @@ class Item(models.Model):
|
|
|
145
149
|
|
|
146
150
|
Use `vector_ip_ops` for inner product and `vector_cosine_ops` for cosine distance
|
|
147
151
|
|
|
152
|
+
#### Half-Precision Indexing
|
|
153
|
+
|
|
154
|
+
Index vectors at half-precision
|
|
155
|
+
|
|
156
|
+
```python
|
|
157
|
+
from django.contrib.postgres.indexes import OpClass
|
|
158
|
+
from django.db.models.functions import Cast
|
|
159
|
+
from pgvector.django import HnswIndex, HalfVectorField
|
|
160
|
+
|
|
161
|
+
class Item(models.Model):
|
|
162
|
+
class Meta:
|
|
163
|
+
indexes = [
|
|
164
|
+
HnswIndex(
|
|
165
|
+
OpClass(Cast('embedding', HalfVectorField(dimensions=3)), name='halfvec_l2_ops'),
|
|
166
|
+
name='my_index',
|
|
167
|
+
m=16,
|
|
168
|
+
ef_construction=64
|
|
169
|
+
)
|
|
170
|
+
]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Note: Add `'django.contrib.postgres'` to `INSTALLED_APPS` to use `OpClass`
|
|
174
|
+
|
|
175
|
+
Get the nearest neighbors
|
|
176
|
+
|
|
177
|
+
```python
|
|
178
|
+
distance = L2Distance(Cast('embedding', HalfVectorField(dimensions=3)), [3, 1, 2])
|
|
179
|
+
Item.objects.order_by(distance)[:5]
|
|
180
|
+
```
|
|
181
|
+
|
|
148
182
|
## SQLAlchemy
|
|
149
183
|
|
|
150
184
|
Enable the extension
|
|
@@ -226,6 +260,77 @@ index.create(engine)
|
|
|
226
260
|
|
|
227
261
|
Use `vector_ip_ops` for inner product and `vector_cosine_ops` for cosine distance
|
|
228
262
|
|
|
263
|
+
#### Half-Precision Indexing
|
|
264
|
+
|
|
265
|
+
Index vectors at half-precision
|
|
266
|
+
|
|
267
|
+
```python
|
|
268
|
+
from pgvector.sqlalchemy import HALFVEC
|
|
269
|
+
from sqlalchemy.sql import func
|
|
270
|
+
|
|
271
|
+
index = Index(
|
|
272
|
+
'my_index',
|
|
273
|
+
func.cast(Item.embedding, HALFVEC(3)).label('embedding'),
|
|
274
|
+
postgresql_using='hnsw',
|
|
275
|
+
postgresql_with={'m': 16, 'ef_construction': 64},
|
|
276
|
+
postgresql_ops={'embedding': 'halfvec_l2_ops'}
|
|
277
|
+
)
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Get the nearest neighbors
|
|
281
|
+
|
|
282
|
+
```python
|
|
283
|
+
order = func.cast(Item.embedding, HALFVEC(3)).l2_distance([3, 1, 2])
|
|
284
|
+
session.scalars(select(Item).order_by(order).limit(5))
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
#### Arrays
|
|
288
|
+
|
|
289
|
+
Add an array column
|
|
290
|
+
|
|
291
|
+
```python
|
|
292
|
+
from pgvector.sqlalchemy import Vector
|
|
293
|
+
from sqlalchemy import ARRAY
|
|
294
|
+
|
|
295
|
+
class Item(Base):
|
|
296
|
+
embeddings = mapped_column(ARRAY(Vector(3)))
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
And register the types with the underlying driver
|
|
300
|
+
|
|
301
|
+
For Psycopg 3, use
|
|
302
|
+
|
|
303
|
+
```python
|
|
304
|
+
from pgvector.psycopg import register_vector
|
|
305
|
+
from sqlalchemy import event
|
|
306
|
+
|
|
307
|
+
@event.listens_for(engine, "connect")
|
|
308
|
+
def connect(dbapi_connection, connection_record):
|
|
309
|
+
register_vector(dbapi_connection)
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
For [async connections](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html) with Psycopg 3, use
|
|
313
|
+
|
|
314
|
+
```python
|
|
315
|
+
from pgvector.psycopg import register_vector_async
|
|
316
|
+
from sqlalchemy import event
|
|
317
|
+
|
|
318
|
+
@event.listens_for(engine.sync_engine, "connect")
|
|
319
|
+
def connect(dbapi_connection, connection_record):
|
|
320
|
+
dbapi_connection.run_async(register_vector_async)
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
For Psycopg 2, use
|
|
324
|
+
|
|
325
|
+
```python
|
|
326
|
+
from pgvector.psycopg2 import register_vector
|
|
327
|
+
from sqlalchemy import event
|
|
328
|
+
|
|
329
|
+
@event.listens_for(engine, "connect")
|
|
330
|
+
def connect(dbapi_connection, connection_record):
|
|
331
|
+
register_vector(dbapi_connection, arrays=True)
|
|
332
|
+
```
|
|
333
|
+
|
|
229
334
|
## SQLModel
|
|
230
335
|
|
|
231
336
|
Enable the extension
|
|
@@ -238,10 +343,9 @@ Add a vector column
|
|
|
238
343
|
|
|
239
344
|
```python
|
|
240
345
|
from pgvector.sqlalchemy import Vector
|
|
241
|
-
from sqlalchemy import Column
|
|
242
346
|
|
|
243
347
|
class Item(SQLModel, table=True):
|
|
244
|
-
embedding: Any = Field(
|
|
348
|
+
embedding: Any = Field(sa_type=Vector(3))
|
|
245
349
|
```
|
|
246
350
|
|
|
247
351
|
Also supports `HALFVEC`, `BIT`, and `SPARSEVEC`
|
|
@@ -287,7 +391,7 @@ Also supports `sum`
|
|
|
287
391
|
Add an approximate index
|
|
288
392
|
|
|
289
393
|
```python
|
|
290
|
-
from
|
|
394
|
+
from sqlmodel import Index
|
|
291
395
|
|
|
292
396
|
index = Index(
|
|
293
397
|
'my_index',
|
|
@@ -326,6 +430,15 @@ from pgvector.psycopg import register_vector
|
|
|
326
430
|
register_vector(conn)
|
|
327
431
|
```
|
|
328
432
|
|
|
433
|
+
For [connection pools](https://www.psycopg.org/psycopg3/docs/advanced/pool.html), use
|
|
434
|
+
|
|
435
|
+
```python
|
|
436
|
+
def configure(conn):
|
|
437
|
+
register_vector(conn)
|
|
438
|
+
|
|
439
|
+
pool = ConnectionPool(..., configure=configure)
|
|
440
|
+
```
|
|
441
|
+
|
|
329
442
|
For [async connections](https://www.psycopg.org/psycopg3/docs/advanced/async.html), use
|
|
330
443
|
|
|
331
444
|
```python
|
|
@@ -464,6 +577,51 @@ await conn.execute('CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops
|
|
|
464
577
|
|
|
465
578
|
Use `vector_ip_ops` for inner product and `vector_cosine_ops` for cosine distance
|
|
466
579
|
|
|
580
|
+
## pg8000
|
|
581
|
+
|
|
582
|
+
Enable the extension
|
|
583
|
+
|
|
584
|
+
```python
|
|
585
|
+
conn.run('CREATE EXTENSION IF NOT EXISTS vector')
|
|
586
|
+
```
|
|
587
|
+
|
|
588
|
+
Register the vector type with your connection
|
|
589
|
+
|
|
590
|
+
```python
|
|
591
|
+
from pgvector.pg8000 import register_vector
|
|
592
|
+
|
|
593
|
+
register_vector(conn)
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
Create a table
|
|
597
|
+
|
|
598
|
+
```python
|
|
599
|
+
conn.run('CREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(3))')
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
Insert a vector
|
|
603
|
+
|
|
604
|
+
```python
|
|
605
|
+
embedding = np.array([1, 2, 3])
|
|
606
|
+
conn.run('INSERT INTO items (embedding) VALUES (:embedding)', embedding=embedding)
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
Get the nearest neighbors to a vector
|
|
610
|
+
|
|
611
|
+
```python
|
|
612
|
+
conn.run('SELECT * FROM items ORDER BY embedding <-> :embedding LIMIT 5', embedding=embedding)
|
|
613
|
+
```
|
|
614
|
+
|
|
615
|
+
Add an approximate index
|
|
616
|
+
|
|
617
|
+
```python
|
|
618
|
+
conn.run('CREATE INDEX ON items USING hnsw (embedding vector_l2_ops)')
|
|
619
|
+
# or
|
|
620
|
+
conn.run('CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100)')
|
|
621
|
+
```
|
|
622
|
+
|
|
623
|
+
Use `vector_ip_ops` for inner product and `vector_cosine_ops` for cosine distance
|
|
624
|
+
|
|
467
625
|
## Peewee
|
|
468
626
|
|
|
469
627
|
Add a vector column
|
|
@@ -521,6 +679,99 @@ Item.add_index('embedding vector_l2_ops', using='hnsw')
|
|
|
521
679
|
|
|
522
680
|
Use `vector_ip_ops` for inner product and `vector_cosine_ops` for cosine distance
|
|
523
681
|
|
|
682
|
+
## Reference
|
|
683
|
+
|
|
684
|
+
### Half Vectors
|
|
685
|
+
|
|
686
|
+
Create a half vector from a list
|
|
687
|
+
|
|
688
|
+
```python
|
|
689
|
+
vec = HalfVector([1, 2, 3])
|
|
690
|
+
```
|
|
691
|
+
|
|
692
|
+
Or a NumPy array
|
|
693
|
+
|
|
694
|
+
```python
|
|
695
|
+
vec = HalfVector(np.array([1, 2, 3]))
|
|
696
|
+
```
|
|
697
|
+
|
|
698
|
+
Get a list
|
|
699
|
+
|
|
700
|
+
```python
|
|
701
|
+
lst = vec.to_list()
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
Get a NumPy array
|
|
705
|
+
|
|
706
|
+
```python
|
|
707
|
+
arr = vec.to_numpy()
|
|
708
|
+
```
|
|
709
|
+
|
|
710
|
+
### Sparse Vectors
|
|
711
|
+
|
|
712
|
+
Create a sparse vector from a list
|
|
713
|
+
|
|
714
|
+
```python
|
|
715
|
+
vec = SparseVector([1, 0, 2, 0, 3, 0])
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
Or a NumPy array
|
|
719
|
+
|
|
720
|
+
```python
|
|
721
|
+
vec = SparseVector(np.array([1, 0, 2, 0, 3, 0]))
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
Or a SciPy sparse array
|
|
725
|
+
|
|
726
|
+
```python
|
|
727
|
+
arr = coo_array(([1, 2, 3], ([0, 2, 4],)), shape=(6,))
|
|
728
|
+
vec = SparseVector(arr)
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
Or a dictionary of non-zero elements
|
|
732
|
+
|
|
733
|
+
```python
|
|
734
|
+
vec = SparseVector({0: 1, 2: 2, 4: 3}, 6)
|
|
735
|
+
```
|
|
736
|
+
|
|
737
|
+
Note: Indices start at 0
|
|
738
|
+
|
|
739
|
+
Get the number of dimensions
|
|
740
|
+
|
|
741
|
+
```python
|
|
742
|
+
dim = vec.dimensions()
|
|
743
|
+
```
|
|
744
|
+
|
|
745
|
+
Get the indices of non-zero elements
|
|
746
|
+
|
|
747
|
+
```python
|
|
748
|
+
indices = vec.indices()
|
|
749
|
+
```
|
|
750
|
+
|
|
751
|
+
Get the values of non-zero elements
|
|
752
|
+
|
|
753
|
+
```python
|
|
754
|
+
values = vec.values()
|
|
755
|
+
```
|
|
756
|
+
|
|
757
|
+
Get a list
|
|
758
|
+
|
|
759
|
+
```python
|
|
760
|
+
lst = vec.to_list()
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
Get a NumPy array
|
|
764
|
+
|
|
765
|
+
```python
|
|
766
|
+
arr = vec.to_numpy()
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
Get a SciPy sparse array
|
|
770
|
+
|
|
771
|
+
```python
|
|
772
|
+
arr = vec.to_coo()
|
|
773
|
+
```
|
|
774
|
+
|
|
524
775
|
## History
|
|
525
776
|
|
|
526
777
|
View the [changelog](https://github.com/pgvector/pgvector-python/blob/master/CHANGELOG.md)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[pgvector](https://github.com/pgvector/pgvector) support for Python
|
|
4
4
|
|
|
5
|
-
Supports [Django](https://github.com/django/django), [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy), [SQLModel](https://github.com/tiangolo/sqlmodel), [Psycopg 3](https://github.com/psycopg/psycopg), [Psycopg 2](https://github.com/psycopg/psycopg2), [asyncpg](https://github.com/MagicStack/asyncpg), and [Peewee](https://github.com/coleifer/peewee)
|
|
5
|
+
Supports [Django](https://github.com/django/django), [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy), [SQLModel](https://github.com/tiangolo/sqlmodel), [Psycopg 3](https://github.com/psycopg/psycopg), [Psycopg 2](https://github.com/psycopg/psycopg2), [asyncpg](https://github.com/MagicStack/asyncpg), [pg8000](https://github.com/tlocke/pg8000), and [Peewee](https://github.com/coleifer/peewee)
|
|
6
6
|
|
|
7
7
|
[](https://github.com/pgvector/pgvector-python/actions)
|
|
8
8
|
|
|
@@ -22,10 +22,12 @@ And follow the instructions for your database library:
|
|
|
22
22
|
- [Psycopg 3](#psycopg-3)
|
|
23
23
|
- [Psycopg 2](#psycopg-2)
|
|
24
24
|
- [asyncpg](#asyncpg)
|
|
25
|
+
- [pg8000](#pg8000)
|
|
25
26
|
- [Peewee](#peewee)
|
|
26
27
|
|
|
27
28
|
Or check out some examples:
|
|
28
29
|
|
|
30
|
+
- [Retrieval-augmented generation](https://github.com/pgvector/pgvector-python/blob/master/examples/rag/example.py) with Ollama
|
|
29
31
|
- [Embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/openai/example.py) with OpenAI
|
|
30
32
|
- [Binary embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/cohere/example.py) with Cohere
|
|
31
33
|
- [Sentence embeddings](https://github.com/pgvector/pgvector-python/blob/master/examples/sentence_transformers/example.py) with SentenceTransformers
|
|
@@ -33,6 +35,7 @@ Or check out some examples:
|
|
|
33
35
|
- [Hybrid search](https://github.com/pgvector/pgvector-python/blob/master/examples/hybrid_search/cross_encoder.py) with SentenceTransformers (cross-encoder)
|
|
34
36
|
- [Sparse search](https://github.com/pgvector/pgvector-python/blob/master/examples/sparse_search/example.py) with Transformers
|
|
35
37
|
- [Late interaction search](https://github.com/pgvector/pgvector-python/blob/master/examples/colbert/exact.py) with ColBERT
|
|
38
|
+
- [Visual document retrieval](https://github.com/pgvector/pgvector-python/blob/master/examples/colpali/exact.py) with ColPali
|
|
36
39
|
- [Image search](https://github.com/pgvector/pgvector-python/blob/master/examples/image_search/example.py) with PyTorch
|
|
37
40
|
- [Image search](https://github.com/pgvector/pgvector-python/blob/master/examples/imagehash/example.py) with perceptual hashing
|
|
38
41
|
- [Morgan fingerprints](https://github.com/pgvector/pgvector-python/blob/master/examples/rdkit/example.py) with RDKit
|
|
@@ -133,6 +136,36 @@ class Item(models.Model):
|
|
|
133
136
|
|
|
134
137
|
Use `vector_ip_ops` for inner product and `vector_cosine_ops` for cosine distance
|
|
135
138
|
|
|
139
|
+
#### Half-Precision Indexing
|
|
140
|
+
|
|
141
|
+
Index vectors at half-precision
|
|
142
|
+
|
|
143
|
+
```python
|
|
144
|
+
from django.contrib.postgres.indexes import OpClass
|
|
145
|
+
from django.db.models.functions import Cast
|
|
146
|
+
from pgvector.django import HnswIndex, HalfVectorField
|
|
147
|
+
|
|
148
|
+
class Item(models.Model):
|
|
149
|
+
class Meta:
|
|
150
|
+
indexes = [
|
|
151
|
+
HnswIndex(
|
|
152
|
+
OpClass(Cast('embedding', HalfVectorField(dimensions=3)), name='halfvec_l2_ops'),
|
|
153
|
+
name='my_index',
|
|
154
|
+
m=16,
|
|
155
|
+
ef_construction=64
|
|
156
|
+
)
|
|
157
|
+
]
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
Note: Add `'django.contrib.postgres'` to `INSTALLED_APPS` to use `OpClass`
|
|
161
|
+
|
|
162
|
+
Get the nearest neighbors
|
|
163
|
+
|
|
164
|
+
```python
|
|
165
|
+
distance = L2Distance(Cast('embedding', HalfVectorField(dimensions=3)), [3, 1, 2])
|
|
166
|
+
Item.objects.order_by(distance)[:5]
|
|
167
|
+
```
|
|
168
|
+
|
|
136
169
|
## SQLAlchemy
|
|
137
170
|
|
|
138
171
|
Enable the extension
|
|
@@ -214,6 +247,77 @@ index.create(engine)
|
|
|
214
247
|
|
|
215
248
|
Use `vector_ip_ops` for inner product and `vector_cosine_ops` for cosine distance
|
|
216
249
|
|
|
250
|
+
#### Half-Precision Indexing
|
|
251
|
+
|
|
252
|
+
Index vectors at half-precision
|
|
253
|
+
|
|
254
|
+
```python
|
|
255
|
+
from pgvector.sqlalchemy import HALFVEC
|
|
256
|
+
from sqlalchemy.sql import func
|
|
257
|
+
|
|
258
|
+
index = Index(
|
|
259
|
+
'my_index',
|
|
260
|
+
func.cast(Item.embedding, HALFVEC(3)).label('embedding'),
|
|
261
|
+
postgresql_using='hnsw',
|
|
262
|
+
postgresql_with={'m': 16, 'ef_construction': 64},
|
|
263
|
+
postgresql_ops={'embedding': 'halfvec_l2_ops'}
|
|
264
|
+
)
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Get the nearest neighbors
|
|
268
|
+
|
|
269
|
+
```python
|
|
270
|
+
order = func.cast(Item.embedding, HALFVEC(3)).l2_distance([3, 1, 2])
|
|
271
|
+
session.scalars(select(Item).order_by(order).limit(5))
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
#### Arrays
|
|
275
|
+
|
|
276
|
+
Add an array column
|
|
277
|
+
|
|
278
|
+
```python
|
|
279
|
+
from pgvector.sqlalchemy import Vector
|
|
280
|
+
from sqlalchemy import ARRAY
|
|
281
|
+
|
|
282
|
+
class Item(Base):
|
|
283
|
+
embeddings = mapped_column(ARRAY(Vector(3)))
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
And register the types with the underlying driver
|
|
287
|
+
|
|
288
|
+
For Psycopg 3, use
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from pgvector.psycopg import register_vector
|
|
292
|
+
from sqlalchemy import event
|
|
293
|
+
|
|
294
|
+
@event.listens_for(engine, "connect")
|
|
295
|
+
def connect(dbapi_connection, connection_record):
|
|
296
|
+
register_vector(dbapi_connection)
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
For [async connections](https://docs.sqlalchemy.org/en/20/orm/extensions/asyncio.html) with Psycopg 3, use
|
|
300
|
+
|
|
301
|
+
```python
|
|
302
|
+
from pgvector.psycopg import register_vector_async
|
|
303
|
+
from sqlalchemy import event
|
|
304
|
+
|
|
305
|
+
@event.listens_for(engine.sync_engine, "connect")
|
|
306
|
+
def connect(dbapi_connection, connection_record):
|
|
307
|
+
dbapi_connection.run_async(register_vector_async)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
For Psycopg 2, use
|
|
311
|
+
|
|
312
|
+
```python
|
|
313
|
+
from pgvector.psycopg2 import register_vector
|
|
314
|
+
from sqlalchemy import event
|
|
315
|
+
|
|
316
|
+
@event.listens_for(engine, "connect")
|
|
317
|
+
def connect(dbapi_connection, connection_record):
|
|
318
|
+
register_vector(dbapi_connection, arrays=True)
|
|
319
|
+
```
|
|
320
|
+
|
|
217
321
|
## SQLModel
|
|
218
322
|
|
|
219
323
|
Enable the extension
|
|
@@ -226,10 +330,9 @@ Add a vector column
|
|
|
226
330
|
|
|
227
331
|
```python
|
|
228
332
|
from pgvector.sqlalchemy import Vector
|
|
229
|
-
from sqlalchemy import Column
|
|
230
333
|
|
|
231
334
|
class Item(SQLModel, table=True):
|
|
232
|
-
embedding: Any = Field(
|
|
335
|
+
embedding: Any = Field(sa_type=Vector(3))
|
|
233
336
|
```
|
|
234
337
|
|
|
235
338
|
Also supports `HALFVEC`, `BIT`, and `SPARSEVEC`
|
|
@@ -275,7 +378,7 @@ Also supports `sum`
|
|
|
275
378
|
Add an approximate index
|
|
276
379
|
|
|
277
380
|
```python
|
|
278
|
-
from
|
|
381
|
+
from sqlmodel import Index
|
|
279
382
|
|
|
280
383
|
index = Index(
|
|
281
384
|
'my_index',
|
|
@@ -314,6 +417,15 @@ from pgvector.psycopg import register_vector
|
|
|
314
417
|
register_vector(conn)
|
|
315
418
|
```
|
|
316
419
|
|
|
420
|
+
For [connection pools](https://www.psycopg.org/psycopg3/docs/advanced/pool.html), use
|
|
421
|
+
|
|
422
|
+
```python
|
|
423
|
+
def configure(conn):
|
|
424
|
+
register_vector(conn)
|
|
425
|
+
|
|
426
|
+
pool = ConnectionPool(..., configure=configure)
|
|
427
|
+
```
|
|
428
|
+
|
|
317
429
|
For [async connections](https://www.psycopg.org/psycopg3/docs/advanced/async.html), use
|
|
318
430
|
|
|
319
431
|
```python
|
|
@@ -452,6 +564,51 @@ await conn.execute('CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops
|
|
|
452
564
|
|
|
453
565
|
Use `vector_ip_ops` for inner product and `vector_cosine_ops` for cosine distance
|
|
454
566
|
|
|
567
|
+
## pg8000
|
|
568
|
+
|
|
569
|
+
Enable the extension
|
|
570
|
+
|
|
571
|
+
```python
|
|
572
|
+
conn.run('CREATE EXTENSION IF NOT EXISTS vector')
|
|
573
|
+
```
|
|
574
|
+
|
|
575
|
+
Register the vector type with your connection
|
|
576
|
+
|
|
577
|
+
```python
|
|
578
|
+
from pgvector.pg8000 import register_vector
|
|
579
|
+
|
|
580
|
+
register_vector(conn)
|
|
581
|
+
```
|
|
582
|
+
|
|
583
|
+
Create a table
|
|
584
|
+
|
|
585
|
+
```python
|
|
586
|
+
conn.run('CREATE TABLE items (id bigserial PRIMARY KEY, embedding vector(3))')
|
|
587
|
+
```
|
|
588
|
+
|
|
589
|
+
Insert a vector
|
|
590
|
+
|
|
591
|
+
```python
|
|
592
|
+
embedding = np.array([1, 2, 3])
|
|
593
|
+
conn.run('INSERT INTO items (embedding) VALUES (:embedding)', embedding=embedding)
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
Get the nearest neighbors to a vector
|
|
597
|
+
|
|
598
|
+
```python
|
|
599
|
+
conn.run('SELECT * FROM items ORDER BY embedding <-> :embedding LIMIT 5', embedding=embedding)
|
|
600
|
+
```
|
|
601
|
+
|
|
602
|
+
Add an approximate index
|
|
603
|
+
|
|
604
|
+
```python
|
|
605
|
+
conn.run('CREATE INDEX ON items USING hnsw (embedding vector_l2_ops)')
|
|
606
|
+
# or
|
|
607
|
+
conn.run('CREATE INDEX ON items USING ivfflat (embedding vector_l2_ops) WITH (lists = 100)')
|
|
608
|
+
```
|
|
609
|
+
|
|
610
|
+
Use `vector_ip_ops` for inner product and `vector_cosine_ops` for cosine distance
|
|
611
|
+
|
|
455
612
|
## Peewee
|
|
456
613
|
|
|
457
614
|
Add a vector column
|
|
@@ -509,6 +666,99 @@ Item.add_index('embedding vector_l2_ops', using='hnsw')
|
|
|
509
666
|
|
|
510
667
|
Use `vector_ip_ops` for inner product and `vector_cosine_ops` for cosine distance
|
|
511
668
|
|
|
669
|
+
## Reference
|
|
670
|
+
|
|
671
|
+
### Half Vectors
|
|
672
|
+
|
|
673
|
+
Create a half vector from a list
|
|
674
|
+
|
|
675
|
+
```python
|
|
676
|
+
vec = HalfVector([1, 2, 3])
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
Or a NumPy array
|
|
680
|
+
|
|
681
|
+
```python
|
|
682
|
+
vec = HalfVector(np.array([1, 2, 3]))
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
Get a list
|
|
686
|
+
|
|
687
|
+
```python
|
|
688
|
+
lst = vec.to_list()
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
Get a NumPy array
|
|
692
|
+
|
|
693
|
+
```python
|
|
694
|
+
arr = vec.to_numpy()
|
|
695
|
+
```
|
|
696
|
+
|
|
697
|
+
### Sparse Vectors
|
|
698
|
+
|
|
699
|
+
Create a sparse vector from a list
|
|
700
|
+
|
|
701
|
+
```python
|
|
702
|
+
vec = SparseVector([1, 0, 2, 0, 3, 0])
|
|
703
|
+
```
|
|
704
|
+
|
|
705
|
+
Or a NumPy array
|
|
706
|
+
|
|
707
|
+
```python
|
|
708
|
+
vec = SparseVector(np.array([1, 0, 2, 0, 3, 0]))
|
|
709
|
+
```
|
|
710
|
+
|
|
711
|
+
Or a SciPy sparse array
|
|
712
|
+
|
|
713
|
+
```python
|
|
714
|
+
arr = coo_array(([1, 2, 3], ([0, 2, 4],)), shape=(6,))
|
|
715
|
+
vec = SparseVector(arr)
|
|
716
|
+
```
|
|
717
|
+
|
|
718
|
+
Or a dictionary of non-zero elements
|
|
719
|
+
|
|
720
|
+
```python
|
|
721
|
+
vec = SparseVector({0: 1, 2: 2, 4: 3}, 6)
|
|
722
|
+
```
|
|
723
|
+
|
|
724
|
+
Note: Indices start at 0
|
|
725
|
+
|
|
726
|
+
Get the number of dimensions
|
|
727
|
+
|
|
728
|
+
```python
|
|
729
|
+
dim = vec.dimensions()
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
Get the indices of non-zero elements
|
|
733
|
+
|
|
734
|
+
```python
|
|
735
|
+
indices = vec.indices()
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
Get the values of non-zero elements
|
|
739
|
+
|
|
740
|
+
```python
|
|
741
|
+
values = vec.values()
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
Get a list
|
|
745
|
+
|
|
746
|
+
```python
|
|
747
|
+
lst = vec.to_list()
|
|
748
|
+
```
|
|
749
|
+
|
|
750
|
+
Get a NumPy array
|
|
751
|
+
|
|
752
|
+
```python
|
|
753
|
+
arr = vec.to_numpy()
|
|
754
|
+
```
|
|
755
|
+
|
|
756
|
+
Get a SciPy sparse array
|
|
757
|
+
|
|
758
|
+
```python
|
|
759
|
+
arr = vec.to_coo()
|
|
760
|
+
```
|
|
761
|
+
|
|
512
762
|
## History
|
|
513
763
|
|
|
514
764
|
View the [changelog](https://github.com/pgvector/pgvector-python/blob/master/CHANGELOG.md)
|