pgvector 0.3.5__py3-none-any.whl
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/asyncpg/__init__.py +9 -0
- pgvector/asyncpg/register.py +31 -0
- pgvector/django/__init__.py +26 -0
- pgvector/django/bit.py +32 -0
- pgvector/django/extensions.py +6 -0
- pgvector/django/functions.py +55 -0
- pgvector/django/halfvec.py +60 -0
- pgvector/django/indexes.py +46 -0
- pgvector/django/sparsevec.py +55 -0
- pgvector/django/vector.py +73 -0
- pgvector/peewee/__init__.py +14 -0
- pgvector/peewee/bit.py +21 -0
- pgvector/peewee/halfvec.py +34 -0
- pgvector/peewee/sparsevec.py +34 -0
- pgvector/peewee/vector.py +34 -0
- pgvector/psycopg/__init__.py +11 -0
- pgvector/psycopg/bit.py +31 -0
- pgvector/psycopg/halfvec.py +53 -0
- pgvector/psycopg/register.py +37 -0
- pgvector/psycopg/sparsevec.py +53 -0
- pgvector/psycopg/vector.py +58 -0
- pgvector/psycopg2/__init__.py +8 -0
- pgvector/psycopg2/halfvec.py +20 -0
- pgvector/psycopg2/register.py +28 -0
- pgvector/psycopg2/sparsevec.py +20 -0
- pgvector/psycopg2/vector.py +21 -0
- pgvector/sqlalchemy/__init__.py +19 -0
- pgvector/sqlalchemy/bit.py +26 -0
- pgvector/sqlalchemy/functions.py +14 -0
- pgvector/sqlalchemy/halfvec.py +51 -0
- pgvector/sqlalchemy/sparsevec.py +51 -0
- pgvector/sqlalchemy/vector.py +51 -0
- pgvector/utils/__init__.py +11 -0
- pgvector/utils/bit.py +61 -0
- pgvector/utils/halfvec.py +78 -0
- pgvector/utils/sparsevec.py +156 -0
- pgvector/utils/vector.py +78 -0
- pgvector-0.3.5.dist-info/LICENSE.txt +21 -0
- pgvector-0.3.5.dist-info/METADATA +554 -0
- pgvector-0.3.5.dist-info/RECORD +42 -0
- pgvector-0.3.5.dist-info/WHEEL +5 -0
- pgvector-0.3.5.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from ..utils import Vector, HalfVector, SparseVector
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
async def register_vector(conn, schema='public'):
|
|
5
|
+
await conn.set_type_codec(
|
|
6
|
+
'vector',
|
|
7
|
+
schema=schema,
|
|
8
|
+
encoder=Vector._to_db_binary,
|
|
9
|
+
decoder=Vector._from_db_binary,
|
|
10
|
+
format='binary'
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
try:
|
|
14
|
+
await conn.set_type_codec(
|
|
15
|
+
'halfvec',
|
|
16
|
+
schema=schema,
|
|
17
|
+
encoder=HalfVector._to_db_binary,
|
|
18
|
+
decoder=HalfVector._from_db_binary,
|
|
19
|
+
format='binary'
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
await conn.set_type_codec(
|
|
23
|
+
'sparsevec',
|
|
24
|
+
schema=schema,
|
|
25
|
+
encoder=SparseVector._to_db_binary,
|
|
26
|
+
decoder=SparseVector._from_db_binary,
|
|
27
|
+
format='binary'
|
|
28
|
+
)
|
|
29
|
+
except ValueError as e:
|
|
30
|
+
if not str(e).startswith('unknown type:'):
|
|
31
|
+
raise e
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
from .bit import BitField
|
|
2
|
+
from .extensions import VectorExtension
|
|
3
|
+
from .functions import L2Distance, MaxInnerProduct, CosineDistance, L1Distance, HammingDistance, JaccardDistance
|
|
4
|
+
from .halfvec import HalfVectorField
|
|
5
|
+
from .indexes import IvfflatIndex, HnswIndex
|
|
6
|
+
from .sparsevec import SparseVectorField
|
|
7
|
+
from .vector import VectorField
|
|
8
|
+
from ..utils import HalfVector, SparseVector
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
'VectorExtension',
|
|
12
|
+
'VectorField',
|
|
13
|
+
'HalfVectorField',
|
|
14
|
+
'BitField',
|
|
15
|
+
'SparseVectorField',
|
|
16
|
+
'IvfflatIndex',
|
|
17
|
+
'HnswIndex',
|
|
18
|
+
'L2Distance',
|
|
19
|
+
'MaxInnerProduct',
|
|
20
|
+
'CosineDistance',
|
|
21
|
+
'L1Distance',
|
|
22
|
+
'HammingDistance',
|
|
23
|
+
'JaccardDistance',
|
|
24
|
+
'HalfVector',
|
|
25
|
+
'SparseVector'
|
|
26
|
+
]
|
pgvector/django/bit.py
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
from django import forms
|
|
2
|
+
from django.db.models import Field
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
# https://docs.djangoproject.com/en/5.0/howto/custom-model-fields/
|
|
6
|
+
class BitField(Field):
|
|
7
|
+
description = 'Bit string'
|
|
8
|
+
|
|
9
|
+
def __init__(self, *args, length=None, **kwargs):
|
|
10
|
+
self.length = length
|
|
11
|
+
super().__init__(*args, **kwargs)
|
|
12
|
+
|
|
13
|
+
def deconstruct(self):
|
|
14
|
+
name, path, args, kwargs = super().deconstruct()
|
|
15
|
+
if self.length is not None:
|
|
16
|
+
kwargs['length'] = self.length
|
|
17
|
+
return name, path, args, kwargs
|
|
18
|
+
|
|
19
|
+
def db_type(self, connection):
|
|
20
|
+
if self.length is None:
|
|
21
|
+
return 'bit'
|
|
22
|
+
return 'bit(%d)' % self.length
|
|
23
|
+
|
|
24
|
+
def formfield(self, **kwargs):
|
|
25
|
+
return super().formfield(form_class=BitFormField, **kwargs)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class BitFormField(forms.CharField):
|
|
29
|
+
def to_python(self, value):
|
|
30
|
+
if isinstance(value, str) and value == '':
|
|
31
|
+
return None
|
|
32
|
+
return super().to_python(value)
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from django.db.models import FloatField, Func, Value
|
|
2
|
+
from ..utils import Vector, HalfVector, SparseVector
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class DistanceBase(Func):
|
|
6
|
+
output_field = FloatField()
|
|
7
|
+
|
|
8
|
+
def __init__(self, expression, vector, **extra):
|
|
9
|
+
if not hasattr(vector, 'resolve_expression'):
|
|
10
|
+
if isinstance(vector, HalfVector):
|
|
11
|
+
vector = Value(HalfVector._to_db(vector))
|
|
12
|
+
elif isinstance(vector, SparseVector):
|
|
13
|
+
vector = Value(SparseVector._to_db(vector))
|
|
14
|
+
else:
|
|
15
|
+
vector = Value(Vector._to_db(vector))
|
|
16
|
+
super().__init__(expression, vector, **extra)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class BitDistanceBase(Func):
|
|
20
|
+
output_field = FloatField()
|
|
21
|
+
|
|
22
|
+
def __init__(self, expression, vector, **extra):
|
|
23
|
+
if not hasattr(vector, 'resolve_expression'):
|
|
24
|
+
vector = Value(vector)
|
|
25
|
+
super().__init__(expression, vector, **extra)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class L2Distance(DistanceBase):
|
|
29
|
+
function = ''
|
|
30
|
+
arg_joiner = ' <-> '
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class MaxInnerProduct(DistanceBase):
|
|
34
|
+
function = ''
|
|
35
|
+
arg_joiner = ' <#> '
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class CosineDistance(DistanceBase):
|
|
39
|
+
function = ''
|
|
40
|
+
arg_joiner = ' <=> '
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class L1Distance(DistanceBase):
|
|
44
|
+
function = ''
|
|
45
|
+
arg_joiner = ' <+> '
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class HammingDistance(BitDistanceBase):
|
|
49
|
+
function = ''
|
|
50
|
+
arg_joiner = ' <~> '
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class JaccardDistance(BitDistanceBase):
|
|
54
|
+
function = ''
|
|
55
|
+
arg_joiner = ' <%%> '
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
from django import forms
|
|
2
|
+
from django.db.models import Field
|
|
3
|
+
from ..utils import HalfVector
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# https://docs.djangoproject.com/en/5.0/howto/custom-model-fields/
|
|
7
|
+
class HalfVectorField(Field):
|
|
8
|
+
description = 'Half vector'
|
|
9
|
+
empty_strings_allowed = False
|
|
10
|
+
|
|
11
|
+
def __init__(self, *args, dimensions=None, **kwargs):
|
|
12
|
+
self.dimensions = dimensions
|
|
13
|
+
super().__init__(*args, **kwargs)
|
|
14
|
+
|
|
15
|
+
def deconstruct(self):
|
|
16
|
+
name, path, args, kwargs = super().deconstruct()
|
|
17
|
+
if self.dimensions is not None:
|
|
18
|
+
kwargs['dimensions'] = self.dimensions
|
|
19
|
+
return name, path, args, kwargs
|
|
20
|
+
|
|
21
|
+
def db_type(self, connection):
|
|
22
|
+
if self.dimensions is None:
|
|
23
|
+
return 'halfvec'
|
|
24
|
+
return 'halfvec(%d)' % self.dimensions
|
|
25
|
+
|
|
26
|
+
def from_db_value(self, value, expression, connection):
|
|
27
|
+
return HalfVector._from_db(value)
|
|
28
|
+
|
|
29
|
+
def to_python(self, value):
|
|
30
|
+
if value is None or isinstance(value, HalfVector):
|
|
31
|
+
return value
|
|
32
|
+
elif isinstance(value, str):
|
|
33
|
+
return HalfVector._from_db(value)
|
|
34
|
+
else:
|
|
35
|
+
return HalfVector(value)
|
|
36
|
+
|
|
37
|
+
def get_prep_value(self, value):
|
|
38
|
+
return HalfVector._to_db(value)
|
|
39
|
+
|
|
40
|
+
def value_to_string(self, obj):
|
|
41
|
+
return self.get_prep_value(self.value_from_object(obj))
|
|
42
|
+
|
|
43
|
+
def formfield(self, **kwargs):
|
|
44
|
+
return super().formfield(form_class=HalfVectorFormField, **kwargs)
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
class HalfVectorWidget(forms.TextInput):
|
|
48
|
+
def format_value(self, value):
|
|
49
|
+
if isinstance(value, HalfVector):
|
|
50
|
+
value = value.to_list()
|
|
51
|
+
return super().format_value(value)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class HalfVectorFormField(forms.CharField):
|
|
55
|
+
widget = HalfVectorWidget
|
|
56
|
+
|
|
57
|
+
def to_python(self, value):
|
|
58
|
+
if isinstance(value, str) and value == '':
|
|
59
|
+
return None
|
|
60
|
+
return super().to_python(value)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
from django.contrib.postgres.indexes import PostgresIndex
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class IvfflatIndex(PostgresIndex):
|
|
5
|
+
suffix = 'ivfflat'
|
|
6
|
+
|
|
7
|
+
def __init__(self, *expressions, lists=None, **kwargs):
|
|
8
|
+
self.lists = lists
|
|
9
|
+
super().__init__(*expressions, **kwargs)
|
|
10
|
+
|
|
11
|
+
def deconstruct(self):
|
|
12
|
+
path, args, kwargs = super().deconstruct()
|
|
13
|
+
if self.lists is not None:
|
|
14
|
+
kwargs['lists'] = self.lists
|
|
15
|
+
return path, args, kwargs
|
|
16
|
+
|
|
17
|
+
def get_with_params(self):
|
|
18
|
+
with_params = []
|
|
19
|
+
if self.lists is not None:
|
|
20
|
+
with_params.append('lists = %d' % self.lists)
|
|
21
|
+
return with_params
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class HnswIndex(PostgresIndex):
|
|
25
|
+
suffix = 'hnsw'
|
|
26
|
+
|
|
27
|
+
def __init__(self, *expressions, m=None, ef_construction=None, **kwargs):
|
|
28
|
+
self.m = m
|
|
29
|
+
self.ef_construction = ef_construction
|
|
30
|
+
super().__init__(*expressions, **kwargs)
|
|
31
|
+
|
|
32
|
+
def deconstruct(self):
|
|
33
|
+
path, args, kwargs = super().deconstruct()
|
|
34
|
+
if self.m is not None:
|
|
35
|
+
kwargs['m'] = self.m
|
|
36
|
+
if self.ef_construction is not None:
|
|
37
|
+
kwargs['ef_construction'] = self.ef_construction
|
|
38
|
+
return path, args, kwargs
|
|
39
|
+
|
|
40
|
+
def get_with_params(self):
|
|
41
|
+
with_params = []
|
|
42
|
+
if self.m is not None:
|
|
43
|
+
with_params.append('m = %d' % self.m)
|
|
44
|
+
if self.ef_construction is not None:
|
|
45
|
+
with_params.append('ef_construction = %d' % self.ef_construction)
|
|
46
|
+
return with_params
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
from django import forms
|
|
2
|
+
from django.db.models import Field
|
|
3
|
+
from ..utils import SparseVector
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# https://docs.djangoproject.com/en/5.0/howto/custom-model-fields/
|
|
7
|
+
class SparseVectorField(Field):
|
|
8
|
+
description = 'Sparse vector'
|
|
9
|
+
empty_strings_allowed = False
|
|
10
|
+
|
|
11
|
+
def __init__(self, *args, dimensions=None, **kwargs):
|
|
12
|
+
self.dimensions = dimensions
|
|
13
|
+
super().__init__(*args, **kwargs)
|
|
14
|
+
|
|
15
|
+
def deconstruct(self):
|
|
16
|
+
name, path, args, kwargs = super().deconstruct()
|
|
17
|
+
if self.dimensions is not None:
|
|
18
|
+
kwargs['dimensions'] = self.dimensions
|
|
19
|
+
return name, path, args, kwargs
|
|
20
|
+
|
|
21
|
+
def db_type(self, connection):
|
|
22
|
+
if self.dimensions is None:
|
|
23
|
+
return 'sparsevec'
|
|
24
|
+
return 'sparsevec(%d)' % self.dimensions
|
|
25
|
+
|
|
26
|
+
def from_db_value(self, value, expression, connection):
|
|
27
|
+
return SparseVector._from_db(value)
|
|
28
|
+
|
|
29
|
+
def to_python(self, value):
|
|
30
|
+
return SparseVector._from_db(value)
|
|
31
|
+
|
|
32
|
+
def get_prep_value(self, value):
|
|
33
|
+
return SparseVector._to_db(value)
|
|
34
|
+
|
|
35
|
+
def value_to_string(self, obj):
|
|
36
|
+
return self.get_prep_value(self.value_from_object(obj))
|
|
37
|
+
|
|
38
|
+
def formfield(self, **kwargs):
|
|
39
|
+
return super().formfield(form_class=SparseVectorFormField, **kwargs)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class SparseVectorWidget(forms.TextInput):
|
|
43
|
+
def format_value(self, value):
|
|
44
|
+
if isinstance(value, SparseVector):
|
|
45
|
+
value = value.to_text()
|
|
46
|
+
return super().format_value(value)
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class SparseVectorFormField(forms.CharField):
|
|
50
|
+
widget = SparseVectorWidget
|
|
51
|
+
|
|
52
|
+
def to_python(self, value):
|
|
53
|
+
if isinstance(value, str) and value == '':
|
|
54
|
+
return None
|
|
55
|
+
return super().to_python(value)
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from django import forms
|
|
2
|
+
from django.db.models import Field
|
|
3
|
+
import numpy as np
|
|
4
|
+
from ..utils import Vector
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# https://docs.djangoproject.com/en/5.0/howto/custom-model-fields/
|
|
8
|
+
class VectorField(Field):
|
|
9
|
+
description = 'Vector'
|
|
10
|
+
empty_strings_allowed = False
|
|
11
|
+
|
|
12
|
+
def __init__(self, *args, dimensions=None, **kwargs):
|
|
13
|
+
self.dimensions = dimensions
|
|
14
|
+
super().__init__(*args, **kwargs)
|
|
15
|
+
|
|
16
|
+
def deconstruct(self):
|
|
17
|
+
name, path, args, kwargs = super().deconstruct()
|
|
18
|
+
if self.dimensions is not None:
|
|
19
|
+
kwargs['dimensions'] = self.dimensions
|
|
20
|
+
return name, path, args, kwargs
|
|
21
|
+
|
|
22
|
+
def db_type(self, connection):
|
|
23
|
+
if self.dimensions is None:
|
|
24
|
+
return 'vector'
|
|
25
|
+
return 'vector(%d)' % self.dimensions
|
|
26
|
+
|
|
27
|
+
def from_db_value(self, value, expression, connection):
|
|
28
|
+
return Vector._from_db(value)
|
|
29
|
+
|
|
30
|
+
def to_python(self, value):
|
|
31
|
+
if isinstance(value, list):
|
|
32
|
+
return np.array(value, dtype=np.float32)
|
|
33
|
+
return Vector._from_db(value)
|
|
34
|
+
|
|
35
|
+
def get_prep_value(self, value):
|
|
36
|
+
return Vector._to_db(value)
|
|
37
|
+
|
|
38
|
+
def value_to_string(self, obj):
|
|
39
|
+
return self.get_prep_value(self.value_from_object(obj))
|
|
40
|
+
|
|
41
|
+
def validate(self, value, model_instance):
|
|
42
|
+
if isinstance(value, np.ndarray):
|
|
43
|
+
value = value.tolist()
|
|
44
|
+
super().validate(value, model_instance)
|
|
45
|
+
|
|
46
|
+
def run_validators(self, value):
|
|
47
|
+
if isinstance(value, np.ndarray):
|
|
48
|
+
value = value.tolist()
|
|
49
|
+
super().run_validators(value)
|
|
50
|
+
|
|
51
|
+
def formfield(self, **kwargs):
|
|
52
|
+
return super().formfield(form_class=VectorFormField, **kwargs)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class VectorWidget(forms.TextInput):
|
|
56
|
+
def format_value(self, value):
|
|
57
|
+
if isinstance(value, np.ndarray):
|
|
58
|
+
value = value.tolist()
|
|
59
|
+
return super().format_value(value)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class VectorFormField(forms.CharField):
|
|
63
|
+
widget = VectorWidget
|
|
64
|
+
|
|
65
|
+
def has_changed(self, initial, data):
|
|
66
|
+
if isinstance(initial, np.ndarray):
|
|
67
|
+
initial = initial.tolist()
|
|
68
|
+
return super().has_changed(initial, data)
|
|
69
|
+
|
|
70
|
+
def to_python(self, value):
|
|
71
|
+
if isinstance(value, str) and value == '':
|
|
72
|
+
return None
|
|
73
|
+
return super().to_python(value)
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from .bit import FixedBitField
|
|
2
|
+
from .halfvec import HalfVectorField
|
|
3
|
+
from .sparsevec import SparseVectorField
|
|
4
|
+
from .vector import VectorField
|
|
5
|
+
from ..utils import HalfVector, SparseVector
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
'VectorField',
|
|
9
|
+
'HalfVectorField',
|
|
10
|
+
'FixedBitField',
|
|
11
|
+
'SparseVectorField',
|
|
12
|
+
'HalfVector',
|
|
13
|
+
'SparseVector'
|
|
14
|
+
]
|
pgvector/peewee/bit.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from peewee import Expression, Field
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class FixedBitField(Field):
|
|
5
|
+
field_type = 'bit'
|
|
6
|
+
|
|
7
|
+
def __init__(self, max_length=None, *args, **kwargs):
|
|
8
|
+
self.max_length = max_length
|
|
9
|
+
super(FixedBitField, self).__init__(*args, **kwargs)
|
|
10
|
+
|
|
11
|
+
def get_modifiers(self):
|
|
12
|
+
return self.max_length and [self.max_length] or None
|
|
13
|
+
|
|
14
|
+
def _distance(self, op, vector):
|
|
15
|
+
return Expression(lhs=self, op=op, rhs=self.to_value(vector))
|
|
16
|
+
|
|
17
|
+
def hamming_distance(self, vector):
|
|
18
|
+
return self._distance('<~>', vector)
|
|
19
|
+
|
|
20
|
+
def jaccard_distance(self, vector):
|
|
21
|
+
return self._distance('<%%>', vector)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from peewee import Expression, Field
|
|
2
|
+
from ..utils import HalfVector
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class HalfVectorField(Field):
|
|
6
|
+
field_type = 'halfvec'
|
|
7
|
+
|
|
8
|
+
def __init__(self, dimensions=None, *args, **kwargs):
|
|
9
|
+
self.dimensions = dimensions
|
|
10
|
+
super(HalfVectorField, self).__init__(*args, **kwargs)
|
|
11
|
+
|
|
12
|
+
def get_modifiers(self):
|
|
13
|
+
return self.dimensions and [self.dimensions] or None
|
|
14
|
+
|
|
15
|
+
def db_value(self, value):
|
|
16
|
+
return HalfVector._to_db(value)
|
|
17
|
+
|
|
18
|
+
def python_value(self, value):
|
|
19
|
+
return HalfVector._from_db(value)
|
|
20
|
+
|
|
21
|
+
def _distance(self, op, vector):
|
|
22
|
+
return Expression(lhs=self, op=op, rhs=self.to_value(vector))
|
|
23
|
+
|
|
24
|
+
def l2_distance(self, vector):
|
|
25
|
+
return self._distance('<->', vector)
|
|
26
|
+
|
|
27
|
+
def max_inner_product(self, vector):
|
|
28
|
+
return self._distance('<#>', vector)
|
|
29
|
+
|
|
30
|
+
def cosine_distance(self, vector):
|
|
31
|
+
return self._distance('<=>', vector)
|
|
32
|
+
|
|
33
|
+
def l1_distance(self, vector):
|
|
34
|
+
return self._distance('<+>', vector)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from peewee import Expression, Field
|
|
2
|
+
from ..utils import SparseVector
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SparseVectorField(Field):
|
|
6
|
+
field_type = 'sparsevec'
|
|
7
|
+
|
|
8
|
+
def __init__(self, dimensions=None, *args, **kwargs):
|
|
9
|
+
self.dimensions = dimensions
|
|
10
|
+
super(SparseVectorField, self).__init__(*args, **kwargs)
|
|
11
|
+
|
|
12
|
+
def get_modifiers(self):
|
|
13
|
+
return self.dimensions and [self.dimensions] or None
|
|
14
|
+
|
|
15
|
+
def db_value(self, value):
|
|
16
|
+
return SparseVector._to_db(value)
|
|
17
|
+
|
|
18
|
+
def python_value(self, value):
|
|
19
|
+
return SparseVector._from_db(value)
|
|
20
|
+
|
|
21
|
+
def _distance(self, op, vector):
|
|
22
|
+
return Expression(lhs=self, op=op, rhs=self.to_value(vector))
|
|
23
|
+
|
|
24
|
+
def l2_distance(self, vector):
|
|
25
|
+
return self._distance('<->', vector)
|
|
26
|
+
|
|
27
|
+
def max_inner_product(self, vector):
|
|
28
|
+
return self._distance('<#>', vector)
|
|
29
|
+
|
|
30
|
+
def cosine_distance(self, vector):
|
|
31
|
+
return self._distance('<=>', vector)
|
|
32
|
+
|
|
33
|
+
def l1_distance(self, vector):
|
|
34
|
+
return self._distance('<+>', vector)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from peewee import Expression, Field
|
|
2
|
+
from ..utils import Vector
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class VectorField(Field):
|
|
6
|
+
field_type = 'vector'
|
|
7
|
+
|
|
8
|
+
def __init__(self, dimensions=None, *args, **kwargs):
|
|
9
|
+
self.dimensions = dimensions
|
|
10
|
+
super(VectorField, self).__init__(*args, **kwargs)
|
|
11
|
+
|
|
12
|
+
def get_modifiers(self):
|
|
13
|
+
return self.dimensions and [self.dimensions] or None
|
|
14
|
+
|
|
15
|
+
def db_value(self, value):
|
|
16
|
+
return Vector._to_db(value)
|
|
17
|
+
|
|
18
|
+
def python_value(self, value):
|
|
19
|
+
return Vector._from_db(value)
|
|
20
|
+
|
|
21
|
+
def _distance(self, op, vector):
|
|
22
|
+
return Expression(lhs=self, op=op, rhs=self.to_value(vector))
|
|
23
|
+
|
|
24
|
+
def l2_distance(self, vector):
|
|
25
|
+
return self._distance('<->', vector)
|
|
26
|
+
|
|
27
|
+
def max_inner_product(self, vector):
|
|
28
|
+
return self._distance('<#>', vector)
|
|
29
|
+
|
|
30
|
+
def cosine_distance(self, vector):
|
|
31
|
+
return self._distance('<=>', vector)
|
|
32
|
+
|
|
33
|
+
def l1_distance(self, vector):
|
|
34
|
+
return self._distance('<+>', vector)
|
pgvector/psycopg/bit.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from psycopg.adapt import Dumper
|
|
2
|
+
from psycopg.pq import Format
|
|
3
|
+
from ..utils import Bit
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class BitDumper(Dumper):
|
|
7
|
+
|
|
8
|
+
format = Format.TEXT
|
|
9
|
+
|
|
10
|
+
def dump(self, obj):
|
|
11
|
+
return Bit._to_db(obj).encode('utf8')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class BitBinaryDumper(BitDumper):
|
|
15
|
+
|
|
16
|
+
format = Format.BINARY
|
|
17
|
+
|
|
18
|
+
def dump(self, obj):
|
|
19
|
+
return Bit._to_db_binary(obj)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def register_bit_info(context, info):
|
|
23
|
+
info.register(context)
|
|
24
|
+
|
|
25
|
+
# add oid to anonymous class for set_types
|
|
26
|
+
text_dumper = type('', (BitDumper,), {'oid': info.oid})
|
|
27
|
+
binary_dumper = type('', (BitBinaryDumper,), {'oid': info.oid})
|
|
28
|
+
|
|
29
|
+
adapters = context.adapters
|
|
30
|
+
adapters.register_dumper(Bit, text_dumper)
|
|
31
|
+
adapters.register_dumper(Bit, binary_dumper)
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
from psycopg.adapt import Loader, Dumper
|
|
2
|
+
from psycopg.pq import Format
|
|
3
|
+
from ..utils import HalfVector
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class HalfVectorDumper(Dumper):
|
|
7
|
+
|
|
8
|
+
format = Format.TEXT
|
|
9
|
+
|
|
10
|
+
def dump(self, obj):
|
|
11
|
+
return HalfVector._to_db(obj).encode('utf8')
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class HalfVectorBinaryDumper(HalfVectorDumper):
|
|
15
|
+
|
|
16
|
+
format = Format.BINARY
|
|
17
|
+
|
|
18
|
+
def dump(self, obj):
|
|
19
|
+
return HalfVector._to_db_binary(obj)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class HalfVectorLoader(Loader):
|
|
23
|
+
|
|
24
|
+
format = Format.TEXT
|
|
25
|
+
|
|
26
|
+
def load(self, data):
|
|
27
|
+
if isinstance(data, memoryview):
|
|
28
|
+
data = bytes(data)
|
|
29
|
+
return HalfVector._from_db(data.decode('utf8'))
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class HalfVectorBinaryLoader(HalfVectorLoader):
|
|
33
|
+
|
|
34
|
+
format = Format.BINARY
|
|
35
|
+
|
|
36
|
+
def load(self, data):
|
|
37
|
+
if isinstance(data, memoryview):
|
|
38
|
+
data = bytes(data)
|
|
39
|
+
return HalfVector._from_db_binary(data)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def register_halfvec_info(context, info):
|
|
43
|
+
info.register(context)
|
|
44
|
+
|
|
45
|
+
# add oid to anonymous class for set_types
|
|
46
|
+
text_dumper = type('', (HalfVectorDumper,), {'oid': info.oid})
|
|
47
|
+
binary_dumper = type('', (HalfVectorBinaryDumper,), {'oid': info.oid})
|
|
48
|
+
|
|
49
|
+
adapters = context.adapters
|
|
50
|
+
adapters.register_dumper(HalfVector, text_dumper)
|
|
51
|
+
adapters.register_dumper(HalfVector, binary_dumper)
|
|
52
|
+
adapters.register_loader(info.oid, HalfVectorLoader)
|
|
53
|
+
adapters.register_loader(info.oid, HalfVectorBinaryLoader)
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
from psycopg.types import TypeInfo
|
|
2
|
+
from .bit import register_bit_info
|
|
3
|
+
from .halfvec import register_halfvec_info
|
|
4
|
+
from .sparsevec import register_sparsevec_info
|
|
5
|
+
from .vector import register_vector_info
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def register_vector(context):
|
|
9
|
+
info = TypeInfo.fetch(context, 'vector')
|
|
10
|
+
register_vector_info(context, info)
|
|
11
|
+
|
|
12
|
+
info = TypeInfo.fetch(context, 'bit')
|
|
13
|
+
register_bit_info(context, info)
|
|
14
|
+
|
|
15
|
+
info = TypeInfo.fetch(context, 'halfvec')
|
|
16
|
+
if info is not None:
|
|
17
|
+
register_halfvec_info(context, info)
|
|
18
|
+
|
|
19
|
+
info = TypeInfo.fetch(context, 'sparsevec')
|
|
20
|
+
if info is not None:
|
|
21
|
+
register_sparsevec_info(context, info)
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
async def register_vector_async(context):
|
|
25
|
+
info = await TypeInfo.fetch(context, 'vector')
|
|
26
|
+
register_vector_info(context, info)
|
|
27
|
+
|
|
28
|
+
info = await TypeInfo.fetch(context, 'bit')
|
|
29
|
+
register_bit_info(context, info)
|
|
30
|
+
|
|
31
|
+
info = await TypeInfo.fetch(context, 'halfvec')
|
|
32
|
+
if info is not None:
|
|
33
|
+
register_halfvec_info(context, info)
|
|
34
|
+
|
|
35
|
+
info = await TypeInfo.fetch(context, 'sparsevec')
|
|
36
|
+
if info is not None:
|
|
37
|
+
register_sparsevec_info(context, info)
|