Skip to content

Commit 749e8d9

Browse files
committed
First commit
0 parents  commit 749e8d9

14 files changed

Lines changed: 379 additions & 0 deletions

File tree

.github/workflows/build.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
name: build
2+
on: [push, pull_request]
3+
jobs:
4+
build:
5+
if: "!contains(github.event.head_commit.message, '[skip ci]')"
6+
runs-on: ubuntu-latest
7+
steps:
8+
- uses: actions/checkout@v2
9+
- uses: actions/setup-python@v2
10+
- run: pip install -r requirements.txt
11+
- uses: ankane/setup-postgres@v1
12+
with:
13+
database: pgvector_python_test
14+
- run: |
15+
sudo apt-get update && sudo apt-get install postgresql-server-dev-13
16+
git clone --branch v0.1.6 https://github.com/ankane/pgvector.git
17+
cd pgvector
18+
make
19+
sudo make install
20+
- run: pytest

.gitignore

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
build/
2+
dist/
3+
*.egg-info/
4+
venv/
5+
.cache/
6+
*.pyc
7+
__pycache__

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
## 0.1.0 (2021-06-11)
2+
3+
- First release

LICENSE.txt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2021 Andrew Kane
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in
13+
all copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
THE SOFTWARE.

Makefile

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
lint:
2+
pycodestyle . --ignore=E501
3+
4+
publish:
5+
python3 setup.py bdist_wheel --universal
6+
ls dist
7+
# twine upload dist/*
8+
rm -fr build dist pgvector.egg-info

README.md

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# pgvector-python
2+
3+
[pgvector](https://github.com/ankane/pgvector) support for Python
4+
5+
Supports [SQLAlchemy](https://github.com/sqlalchemy/sqlalchemy) and [Psycopg 2](https://github.com/psycopg/psycopg2)
6+
7+
[![Build Status](https://github.com/ankane/pgvector-python/workflows/build/badge.svg?branch=master)](https://github.com/ankane/pgvector-python/actions)
8+
9+
## Installation
10+
11+
Run:
12+
13+
```sh
14+
pip install pgvector
15+
```
16+
17+
And follow the instructions for your database library:
18+
19+
- [SQLAlchemy](#sqlalchemy)
20+
- [Psycopg 2](#psycopg-2)
21+
22+
## SQLAlchemy
23+
24+
Use the vector type
25+
26+
```python
27+
from pgvector.sqlalchemy import Vector
28+
29+
# Core
30+
item_table = Table(
31+
'item',
32+
metadata,
33+
Column('factors', Vector(3))
34+
)
35+
36+
# ORM
37+
class Item(Base):
38+
factors = Column(Vector(3))
39+
```
40+
41+
Add an index
42+
43+
```python
44+
index = Index(
45+
'my_index',
46+
item_table.c.factors, # or Item.factors for ORM
47+
postgresql_using='ivfflat',
48+
postgresql_with={'lists': 100}
49+
)
50+
index.create(engine)
51+
```
52+
53+
## Psycopg 2
54+
55+
Register the vector type with your connection or cursor
56+
57+
```python
58+
from pgvector.psycopg2 import register_vector
59+
60+
register_vector(conn)
61+
```
62+
63+
Insert a Numpy array as a vector
64+
65+
```python
66+
factors = np.array([1, 2, 3])
67+
cur.execute('INSERT INTO item (factors) VALUES (%s)', (factors,))
68+
```
69+
70+
Get the nearest neighbors
71+
72+
```python
73+
cur.execute('SELECT * FROM item ORDER BY factors <-> %s LIMIT 5', (factors,))
74+
cur.fetchall()
75+
```
76+
77+
## History
78+
79+
View the [changelog](https://github.com/ankane/pgvector-python/blob/master/CHANGELOG.md)
80+
81+
## Contributing
82+
83+
Everyone is encouraged to help improve this project. Here are a few ways you can help:
84+
85+
- [Report bugs](https://github.com/ankane/pgvector-python/issues)
86+
- Fix bugs and [submit pull requests](https://github.com/ankane/pgvector-python/pulls)
87+
- Write, clarify, or fix documentation
88+
- Suggest or add new features
89+
90+
To get started with development:
91+
92+
```sh
93+
git clone https://github.com/ankane/pgvector-python.git
94+
cd pgvector-python
95+
pip install -r requirements.txt
96+
pytest
97+
```

pgvector/psycopg2/__init__.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import numpy as np
2+
import psycopg2
3+
from psycopg2.extensions import adapt, new_type, register_adapter, register_type
4+
from ..utils import cast_vector, quote_vector
5+
6+
7+
__all__ = ['register_vector']
8+
9+
10+
class VectorAdapter(object):
11+
def __init__(self, vector):
12+
self._vector = vector
13+
14+
def getquoted(self):
15+
return adapt(quote_vector(self._vector)).getquoted()
16+
17+
18+
def cast_vector_type(value, cur):
19+
return cast_vector(value)
20+
21+
22+
def register_vector(conn_or_curs=None):
23+
cur = conn_or_curs.cursor() if hasattr(conn_or_curs, 'cursor') else conn_or_curs
24+
25+
try:
26+
cur.execute('SELECT NULL::vector')
27+
oid = cur.description[0][1]
28+
except psycopg2.errors.UndefinedObject:
29+
raise psycopg2.ProgrammingError('vector type not found in the database')
30+
31+
vector = new_type((oid,), 'VECTOR', cast_vector_type)
32+
register_type(vector)
33+
register_adapter(np.ndarray, VectorAdapter)

pgvector/sqlalchemy/__init__.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
from sqlalchemy.types import UserDefinedType
2+
from ..utils import cast_vector, quote_vector
3+
4+
5+
class Vector(UserDefinedType):
6+
def __init__(self, dim=None):
7+
super(UserDefinedType, self).__init__()
8+
self.dim = dim
9+
10+
def get_col_spec(self, **kw):
11+
if self.dim is None:
12+
return "VECTOR"
13+
return "VECTOR(%d)" % self.dim
14+
15+
def bind_processor(self, dialect):
16+
def process(value):
17+
return quote_vector(value, self.dim)
18+
return process
19+
20+
def result_processor(self, dialect, coltype):
21+
def process(value):
22+
return cast_vector(value)
23+
return process

pgvector/utils/__init__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import numpy as np
2+
3+
4+
def cast_vector(value):
5+
# could be ndarray if already cast by lower-level driver
6+
if value is None or isinstance(value, np.ndarray):
7+
return value
8+
9+
return np.array(value[1:-1].split(','), dtype=np.float32)
10+
11+
12+
def quote_vector(value, dim=None):
13+
if value is None:
14+
return value
15+
16+
if isinstance(value, np.ndarray):
17+
if value.ndim != 1:
18+
raise ValueError('expected ndim to be 1')
19+
20+
if not np.issubdtype(value.dtype, np.integer) and not np.issubdtype(value.dtype, np.floating):
21+
raise ValueError('dtype must be numeric')
22+
23+
value = value.tolist()
24+
25+
if dim is not None and len(value) != dim:
26+
raise ValueError('expected %d dimensions, not %d' % (dim, len(value)))
27+
28+
return '[' + ','.join([str(float(v)) for v in value]) + ']'

requirements.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
numpy
2+
psycopg2
3+
pytest
4+
SQLAlchemy

0 commit comments

Comments
 (0)