Skip to content
Merged
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
27 changes: 26 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,29 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [2.0.0] - 2025-01-30

### Changed
- **BREAKING:** Simplified `update_many()` - removed complex grouping logic (58% code reduction)
- **BREAKING:** Renamed `get_by_id()` → `get()` for cleaner API
- **BREAKING:** Renamed `delete_by_id()` → `delete()` for cleaner API

### Removed
- **BREAKING:** Removed `get_by_text()` - use SQL queries or `get_all()` with filtering
- **BREAKING:** Removed `get_by_metadata()` - use SQL queries or `get_all()` with filtering
- **BREAKING:** Removed `list_results()` - use `get_all()` generator instead

### Added
- Kept `count()` method for convenience (user request)

### Improved
- 28% smaller codebase (650 → 467 lines)
- 15% fewer methods (20 → 17)
- Test coverage increased 89% → 92%
- Cleaner, more intuitive API
- Better code maintainability
- All core CRUD and bulk operations preserved

## [1.2.0] - 2025-01-29

### Added
Expand Down Expand Up @@ -120,7 +143,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## Version History

- **1.0.0** - First stable release with comprehensive features, bulk operations, logging, security improvements, and CI/CD
- **2.0.0** - Major refactor: simplified API, removed niche methods, cleaner naming
- **1.2.0** - Added benchmarks module
- **1.0.0** - First stable release
- **0.1.0** - Initial release

---
Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -219,8 +219,9 @@ Edit [benchmarks/config.yaml](benchmarks/config.yaml) to customize:
- [TESTING.md](TESTING.md) - Testing documentation
- [Examples](examples/) - Usage examples
- [basic_usage.py](examples/basic_usage.py) - Basic CRUD operations
- [logging_example.py](examples/logging_example.py) - Logging configuration
- [transaction_example.py](examples/transaction_example.py) - Transaction management with all CRUD operations
- [batch_operations.py](examples/batch_operations.py) - Bulk operations
- [logging_example.py](examples/logging_example.py) - Logging configuration
- [Benchmarks](benchmarks/) - Performance benchmarks

## Contributing
Expand Down
2 changes: 1 addition & 1 deletion examples/basic_usage.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ def main():
print(f" [{rowid}] {text[:50]}... (distance: {distance:.4f})")

# Get record by ID
record = client.get_by_id(rowids[0])
record = client.get(rowids[0])
if record:
rowid, text, metadata, embedding = record
print(f"\nRecord {rowid}: {text}")
Expand Down
15 changes: 5 additions & 10 deletions examples/batch_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,17 +26,12 @@ def main():
rowids = client.add(texts=texts, embeddings=embeddings, metadata=metadata)
print(f"Inserted {len(rowids)} products")

# Pagination - list first page
# Pagination using get_all with batch_size
page_size = 10
page_1 = client.list_results(limit=page_size, offset=0, order="asc")
print(f"\nPage 1 ({len(page_1)} items):")
for rowid, text, meta, _ in page_1[:3]:
print(f" [{rowid}] {text} - ${meta['price']}")

# Pagination - list second page
page_2 = client.list_results(limit=page_size, offset=page_size, order="asc")
print(f"\nPage 2 ({len(page_2)} items):")
for rowid, text, meta, _ in page_2[:3]:
print(f"\nFirst {page_size} items:")
for i, (rowid, text, meta, _) in enumerate(client.get_all(batch_size=page_size)):
if i >= 3:
break
print(f" [{rowid}] {text} - ${meta['price']}")

# Batch retrieval
Expand Down
25 changes: 12 additions & 13 deletions examples/metadata_filtering.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,7 @@

Demonstrates:
- Adding records with metadata
- Filtering by metadata
- Filtering by text
- Querying records with get_all
- Updating metadata
"""

Expand Down Expand Up @@ -36,17 +35,17 @@ def main():
rowids = client.add(texts=texts, embeddings=embeddings, metadata=metadata)
print(f"Added {len(rowids)} articles")

# Filter by exact metadata match
alice_articles = client.get_by_metadata(
{"category": "programming", "author": "Alice", "year": 2023}
)
print(f"\nAlice's programming articles: {len(alice_articles)}")
for rowid, text, meta, _ in alice_articles:
print(f" [{rowid}] {text} - {meta}")
# Query all articles and filter by author
print("\nAlice's articles:")
for rowid, text, meta, _ in client.get_all():
if meta.get("author") == "Alice":
print(f" [{rowid}] {text} - {meta}")

# Filter by text
python_articles = client.get_by_text("Python for data science")
print(f"\nPython articles: {len(python_articles)}")
# Query all articles and filter by text
print("\nPython-related articles:")
for rowid, text, meta, _ in client.get_all():
if "Python" in text:
print(f" [{rowid}] {text}")

# Update metadata
if rowids:
Expand All @@ -59,7 +58,7 @@ def main():
"updated": True,
},
)
updated = client.get_by_id(rowids[0])
updated = client.get(rowids[0])
if updated:
print(f"\nUpdated metadata: {updated[2]}")

Expand Down
26 changes: 15 additions & 11 deletions examples/real_world_scenario.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,21 +81,22 @@ def main():

print("Top 3 results:")
for i, (rowid, text, distance) in enumerate(results, 1):
record = client.get_by_id(rowid)
record = client.get(rowid)
if record:
_, _, meta, _ = record
print(f" {i}. [{meta['category']}] {text[:60]}...")
print(
f" Distance: {distance:.4f}, Difficulty: {meta['difficulty']}\n"
)

# Filter by category
print("All AI-related documents:")
ai_docs = client.get_by_metadata(
{"category": "ai", "language": "general", "difficulty": "intermediate"}
)
for rowid, text, meta, _ in ai_docs:
print(f" • {text[:60]}...")
# Filter by category using get_all
print("All AI-related documents (intermediate):")
for rowid, text, meta, _ in client.get_all():
if (
meta.get("category") == "ai"
and meta.get("difficulty") == "intermediate"
):
print(f" • {text[:60]}...")

# Update document
if rowids:
Expand All @@ -114,9 +115,12 @@ def main():
print("\nKnowledge base statistics:")
print(f" Total documents: {client.count()}")

# List recent documents
recent = client.list_results(limit=3, offset=0, order="desc")
print(f" Most recent: {len(recent)} documents")
# List first 3 documents
print(" First 3 documents:")
for i, (rowid, text, _, _) in enumerate(client.get_all()):
if i >= 3:
break
print(f" [{rowid}] {text[:50]}...")


if __name__ == "__main__":
Expand Down
159 changes: 159 additions & 0 deletions examples/transaction_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
"""Transaction example for sqlite-vec-client.

Demonstrates:
- Atomic transactions with context manager
- All CRUD operations in a single transaction
- Rollback on error
- Transaction isolation
"""

from sqlite_vec_client import SQLiteVecClient


def main():
client = SQLiteVecClient(table="products", db_path=":memory:")
client.create_table(dim=64, distance="cosine")

# Example 1: Successful transaction with all CRUD operations
print("Example 1: Successful transaction")
print("-" * 50)

with client.transaction():
# CREATE: Add initial products
texts = [f"Product {i}" for i in range(5)]
embeddings = [[float(i)] * 64 for i in range(5)]
metadata = [{"price": i * 10, "stock": 100} for i in range(5)]
rowids = client.add(texts=texts, embeddings=embeddings, metadata=metadata)
print(f"[+] Added {len(rowids)} products: {rowids}")

# READ: Get a product
product = client.get(rowids[0])
if product:
print(f"[+] Retrieved product {product[0]}: {product[1]}")

# UPDATE: Update single product
updated = client.update(
rowids[0], text="Updated Product 0", metadata={"price": 99}
)
print(f"[+] Updated product {rowids[0]}: {updated}")

# UPDATE: Bulk update
updates = [
(rowids[1], "Bulk Updated 1", {"price": 150}, None),
(rowids[2], None, {"price": 200, "stock": 50}, None),
]
count = client.update_many(updates)
print(f"[+] Bulk updated {count} products")

# DELETE: Delete single product
deleted = client.delete(rowids[3])
print(f"[+] Deleted product {rowids[3]}: {deleted}")

# DELETE: Bulk delete
deleted_count = client.delete_many([rowids[4]])
print(f"[+] Bulk deleted {deleted_count} products")

# Transaction committed - verify results
print("\n[+] Transaction committed successfully")
print(f" Total products remaining: {client.count()}")

# Example 2: Failed transaction with rollback
print("\n\nExample 2: Failed transaction (rollback)")
print("-" * 50)

initial_count = client.count()
print(f"Initial count: {initial_count}")

try:
with client.transaction():
# Add more products
new_texts = ["New Product 1", "New Product 2"]
new_embeddings = [[1.0] * 64, [2.0] * 64]
new_rowids = client.add(texts=new_texts, embeddings=new_embeddings)
print(f"[+] Added {len(new_rowids)} products: {new_rowids}")

# Update existing
client.update(rowids[0], text="This will be rolled back")
print(f"[+] Updated product {rowids[0]}")

# Simulate error
raise ValueError("Simulated error - transaction will rollback")

except ValueError as e:
print(f"\n[-] Error occurred: {e}")
print("[+] Transaction rolled back automatically")

# Verify rollback
final_count = client.count()
print(f" Final count: {final_count}")
print(f" Count unchanged: {initial_count == final_count}")

# Verify data not changed
product = client.get(rowids[0])
if product:
print(f" Product {rowids[0]} text: {product[1]}")

# Example 3: Nested operations with similarity search
print("\n\nExample 3: Complex transaction with search")
print("-" * 50)

with client.transaction():
# Add products with similar embeddings
similar_texts = ["Red Apple", "Green Apple", "Orange"]
similar_embeddings = [
[0.9, 0.1] + [0.0] * 62,
[0.85, 0.15] + [0.0] * 62,
[0.5, 0.5] + [0.0] * 62,
]
similar_rowids = client.add(texts=similar_texts, embeddings=similar_embeddings)
print(f"[+] Added {len(similar_rowids)} products")

# Search within transaction
query_emb = [0.9, 0.1] + [0.0] * 62
results = client.similarity_search(embedding=query_emb, top_k=2)
print(f"[+] Found {len(results)} similar products:")
for rowid, text, distance in results:
dist_str = f"{distance:.4f}" if distance is not None else "N/A"
print(f" [{rowid}] {text} (distance: {dist_str})")

# Update based on search results
for rowid, text, distance in results:
if distance is not None and distance < 0.1:
client.update(rowid, metadata={"featured": True})
print(f"[+] Marked product {rowid} as featured")

print("\n[+] Complex transaction completed")
print(f" Total products: {client.count()}")

# Example 4: Batch operations in transaction
print("\n\nExample 4: Large batch operations")
print("-" * 50)

with client.transaction():
# Bulk insert
batch_size = 20
batch_texts = [f"Batch Product {i}" for i in range(batch_size)]
batch_embeddings = [[float(i % 10)] * 64 for i in range(batch_size)]
batch_rowids = client.add(texts=batch_texts, embeddings=batch_embeddings)
print(f"[+] Bulk inserted {len(batch_rowids)} products")

# Bulk update all
bulk_updates = [
(rid, None, {"batch": True, "processed": True}, None)
for rid in batch_rowids[:10]
]
updated = client.update_many(bulk_updates)
print(f"[+] Bulk updated {updated} products")

# Bulk delete some
deleted = client.delete_many(batch_rowids[10:15])
print(f"[+] Bulk deleted {deleted} products")

print("\n[+] Batch operations completed")
print(f" Final total: {client.count()}")

client.close()


if __name__ == "__main__":
main()
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "sqlite-vec-client"
version = "1.2.0"
version = "2.0.0"
description = "A tiny Python client around sqlite-vec for CRUD and similarity search."
readme = "README.md"
requires-python = ">=3.9"
Expand Down
Loading