Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions client/python/USAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,12 @@ The client supports usage as a context manager, which automatically closes the u
Example available in:
```examples/context_manager_usage.py```

### Batch Insertion and Search Support

The client now supports batch insertion and batch search queries.
Methods of usage and examples available in:
```examples/batch_insert_usage.py``` & ```examples/search_query_usage.py```

---

## Client API
Expand Down Expand Up @@ -78,6 +84,22 @@ Raises

---

#### **Batch Insert**

Insert multiple vectors with payloads in a single request
```
batch_insert(*, items: list[tuple[DenseVector, Payload]]) -> list[str]
```

Returns
- List of `point_id` (UUID string)

Raises
- `TypeError` if input structure is invalid
- gRPC-mapped errors (see Error Handling)

---

#### **Get**

Fetch a point by its ID
Expand Down Expand Up @@ -112,6 +134,32 @@ Raises

---

#### **Batch Search**

Search for nearest neighbours for multiple queries in a single request
```
batch_search(
*,
queries,
similarity: Similarity | None = None,
limit: int | None = None,
) -> list[list[str]]
```

Returns
- `TypeError` for invalid query formats
- `ValueError` if required parameters are missing

Supported Input Formats:
The `queries` parameter is flexible and supports multiple formats:
- List of `SearchQuery` objects
- List of `(DenseVector, Similarity, Limit)` tuples
- List of `(DenseVector, Similarity)` tuples with a global `Limit`
- List of `(DenseVector, Limit)` tuples with a global `Similarity`
- List of `DenseVector` with global `Similarity` and `Limit`

---

#### **Delete**

Delete a point by its ID
Expand Down Expand Up @@ -177,6 +225,19 @@ All fields are directly accessible:

---

### `SearchQuery`

```
SearchQuery(
vector: DenseVector,
similarity: Similarity,
limit: int,
)
```
Structured representation of a search request

---

### `Similarity`

Enum representing distance functions:
Expand Down
39 changes: 39 additions & 0 deletions client/python/examples/batch_insert_usage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
from vortexdb import VortexDB
from vortexdb import DenseVector, Payload, to_dense_vectors


def main():
db = VortexDB(
grpc_url="localhost:50051",
api_key="my-secret-password",
)

raw_vectors = [
[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6],
[0.7, 0.8, 0.9],
]
vectors = to_dense_vectors(raw_vectors)

p1 = Payload.text("hello world")
p2 = Payload.image("/img/a.png")
p3 = Payload.text("foo bar")

items = [
(vectors[0], p1),
(vectors[1], p2),
(vectors[2], p3),
]

# Batch Insert
point_ids = db.batch_insert(items=items)
print("Inserted ids:\n", point_ids)

for pid in point_ids:
db.delete(point_id=pid)

db.close()


if __name__ == "__main__":
main()
69 changes: 69 additions & 0 deletions client/python/examples/search_query_usage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from vortexdb import VortexDB
from vortexdb import DenseVector, Similarity, SearchQuery, to_dense_vectors


def main():
db = VortexDB(
grpc_url="localhost:50051",
api_key="my-secret-password",
)

raw_vectors = [
[0.1, 0.2, 0.3],
[0.4, 0.5, 0.6],
[0.7, 0.8, 0.9],
]
vectors = to_dense_vectors(raw_vectors)

q = SearchQuery(
vector=vectors[0],
similarity=Similarity.COSINE,
limit=3,
)
res = db.search(query=q)
print("Single SearchQuery:\n", res)

# List of SearchQuery
queries = [
SearchQuery(vectors[0], Similarity.HAMMING, 3),
SearchQuery(vectors[1], Similarity.EUCLIDEAN, 2),
q,
]
res = db.batch_search(queries=queries)
print("\nBatch SearchQuery:\n", res)

# List of vectors with global Similarity and Limit
res = db.batch_search(
queries=vectors,
similarity=Similarity.COSINE,
limit=3,
)
print("\nList of DenseVectors:\n", res)

# List of tuple (DenseVector, Similarity) with global Limit
queries = [
(vectors[0], Similarity.COSINE),
(vectors[1], Similarity.MANHATTAN),
]
res = db.batch_search(
queries=queries,
limit=3,
)
print("\nList of (DenseVector, Similarity):\n", res)

# List of tuple (DenseVector, Limit) with global Similarity
queries = [
(vectors[0], 2),
(vectors[1], 4),
]
res = db.batch_search(
queries=queries,
similarity=Similarity.COSINE,
)
print("\nList of (DenseVector, Limit):\n", res)

db.close()


if __name__ == "__main__":
main()
111 changes: 110 additions & 1 deletion client/python/tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
from vortexdb.connection import GRPCConnection
from vortexdb.models import DenseVector, Payload, Similarity, ContentType, Point
from vortexdb.exceptions import InvalidArgumentError
from vortexdb.models import SearchQuery



Expand Down Expand Up @@ -45,7 +46,6 @@ def test_insert_success(client, mock_connection):
assert point_id == "point-123"



def test_insert_rejects_invalid_vector(client):
with pytest.raises(TypeError):
client.insert(
Expand All @@ -54,6 +54,41 @@ def test_insert_rejects_invalid_vector(client):
)


# Batch Insert

def test_batch_insert_success(client, mock_connection):
response = Mock()
response.ids = [
Mock(id=Mock(value="p1")),
Mock(id=Mock(value="p2")),
]
mock_connection.call.return_value = response
items = [
(DenseVector([1, 2, 3]), Payload.text("a")),
(DenseVector([4, 5, 6]), Payload.text("b")),
]
result = client.batch_insert(items=items)
assert result == ["p1", "p2"]

def test_batch_insert_invalid_items_type(client):
with pytest.raises(TypeError):
client.batch_insert(items="not-a-list")

def test_batch_insert_invalid_tuple_structure(client):
items = [
(DenseVector([1, 2, 3]),), # only one element
]
with pytest.raises(TypeError):
client.batch_insert(items=items)

def test_batch_insert_invalid_vector(client):
items = [
([1, 2, 3], Payload.text("a")), # not DenseVector
]
with pytest.raises(TypeError):
client.batch_insert(items=items)


# Get

def test_get_point_success(client, mock_connection):
Expand Down Expand Up @@ -118,6 +153,80 @@ def test_search_invalid_vector(client):
)


# Batch Search

def test_batch_search_full_tuple(client, mock_connection):
mock_connection.call.return_value = Mock(
results=[
Mock(result_point_ids=[Mock(id=Mock(value="p1"))]),
Mock(result_point_ids=[Mock(id=Mock(value="p2"))]),
]
)
queries = [
(DenseVector([1, 2, 3]), Similarity.COSINE, 2),
(DenseVector([4, 5, 6]), Similarity.EUCLIDEAN, 1),
]
result = client.batch_search(queries=queries)
assert result == [["p1"], ["p2"]]

def test_batch_search_vectors_with_global_params(client, mock_connection):
mock_connection.call.return_value = Mock(
results=[
Mock(result_point_ids=[Mock(id=Mock(value="p1"))]),
]
)
queries = [DenseVector([1, 2, 3])]
result = client.batch_search(
queries=queries,
similarity=Similarity.MANHATTAN,
limit=2,
)
assert result == [["p1"]]

def test_batch_search_vector_similarity_with_global_limit(client, mock_connection):
mock_connection.call.return_value = Mock(
results=[
Mock(result_point_ids=[Mock(id=Mock(value="p1"))]),
]
)
queries = [
(DenseVector([1, 2, 3]), Similarity.COSINE),
]
result = client.batch_search(
queries=queries,
limit=2,
)
assert result == [["p1"]]

def test_batch_search_searchquery_objects(client, mock_connection):
mock_connection.call.return_value = Mock(
results=[
Mock(result_point_ids=[Mock(id=Mock(value="p1"))]),
]
)
queries = [
SearchQuery(DenseVector([1, 2, 3]), Similarity.COSINE, 2),
]
result = client.batch_search(queries=queries)
assert result == [["p1"]]

def test_batch_search_missing_globals_for_vector(client):
queries = [DenseVector([1, 2, 3])]
with pytest.raises(ValueError):
client.batch_search(queries=queries)

def test_batch_search_missing_limit(client):
queries = [
(DenseVector([1, 2, 3]), Similarity.COSINE),
]
with pytest.raises(ValueError):
client.batch_search(queries=queries)

def test_batch_search_invalid_format(client):
queries = ["invalid"]
with pytest.raises(TypeError):
client.batch_search(queries=queries)

# Close

def test_close_closes_connection(client, mock_connection):
Expand Down
3 changes: 3 additions & 0 deletions client/python/vortexdb/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
Payload,
Point,
Similarity,
SearchQuery,
to_dense_vectors,
)
from vortexdb.exceptions import (
VortexDBError,
Expand All @@ -23,6 +25,7 @@
"Payload",
"Point",
"Similarity",
"SearchQuery",
"VortexDBError",
"AuthenticationError",
"NotFoundError",
Expand Down
Loading
Loading