Compare commits

...

15 Commits

Author SHA1 Message Date
Lance Release
23d30dfc78 Bump version: 0.3.8 → 0.3.9 2023-12-04 17:02:35 +00:00
Rob Meng
94c8c50f96 fix: fix passing prefilter flag to remote client (#677)
was passing this at the wrong position
2023-12-04 12:01:16 -05:00
Rob Meng
72765d8e1a feat: enable prefilter in node js (#675)
enable prefiltering in node js, both native and remote
2023-12-01 16:49:10 -05:00
Rob Meng
a2a8f9615e chore: expose prefilter in lancedb rust (#674)
expose prefilter flag in vectordb rust code.
2023-12-01 00:44:14 -05:00
James
b085d9aaa1 (docs):Add CLIP image embedding example (#660)
In this PR, I add a guide that lets you use Roboflow Inference to
calculate CLIP embeddings for use in LanceDB. This post was reviewed by
@AyushExel.
2023-11-27 20:39:01 +05:30
Bert
6eb662de9b fix: python remote correct open_table error message (#659) 2023-11-24 19:28:33 -05:00
Lance Release
2bb2bb581a Updating package-lock.json 2023-11-19 00:45:51 +00:00
Lance Release
38321fa226 [python] Bump version: 0.3.3 → 0.3.4 2023-11-19 00:24:01 +00:00
Lance Release
22749c3fa2 Updating package-lock.json 2023-11-19 00:04:08 +00:00
Lance Release
123a49df77 Bump version: 0.3.7 → 0.3.8 2023-11-19 00:03:58 +00:00
Will Jones
a57aa4b142 chore: upgrade lance to v0.8.17 (#656)
Readying for the next Lance release.
2023-11-18 15:57:23 -08:00
Rok Mihevc
d8e3e54226 feat(python): expose index cache size (#655)
This is to enable https://github.com/lancedb/lancedb/issues/641.
Should be merged after https://github.com/lancedb/lance/pull/1587 is
released.
2023-11-18 14:17:40 -08:00
Ayush Chaurasia
ccfdf4853a [Docs]: Add Instructor embeddings and rate limit handler docs (#651) 2023-11-18 06:08:26 +05:30
Ayush Chaurasia
87e5d86e90 [Docs][SEO] Add sitemap and robots.txt (#645)
Sitemap improves SEO by ranking pages and tracking updates.
2023-11-18 06:08:13 +05:30
Aidan
1cf8a3e4e0 SaaS create_index API (#649) 2023-11-15 19:12:52 -05:00
28 changed files with 446 additions and 87 deletions

View File

@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.3.7
current_version = 0.3.9
commit = True
message = Bump version: {current_version} → {new_version}
tag = True

View File

@@ -5,9 +5,10 @@ exclude = ["python"]
resolver = "2"
[workspace.dependencies]
lance = { "version" = "=0.8.14", "features" = ["dynamodb"] }
lance-linalg = { "version" = "=0.8.14" }
lance-testing = { "version" = "=0.8.14" }
lance = { "version" = "=0.8.17", "features" = ["dynamodb"] }
lance-index = { "version" = "=0.8.17" }
lance-linalg = { "version" = "=0.8.17" }
lance-testing = { "version" = "=0.8.17" }
# Note that this one does not include pyarrow
arrow = { version = "47.0.0", optional = false }
arrow-array = "47.0"

View File

@@ -1,4 +1,5 @@
site_name: LanceDB Docs
site_url: https://lancedb.github.io/lancedb/
repo_url: https://github.com/lancedb/lancedb
edit_uri: https://github.com/lancedb/lancedb/tree/main/docs/src
repo_name: lancedb/lancedb
@@ -79,6 +80,7 @@ nav:
- Ingest Embedding Functions: embeddings/embedding_functions.md
- Available Functions: embeddings/default_embedding_functions.md
- Create Custom Embedding Functions: embeddings/api.md
- Example - Calculate CLIP Embeddings with Roboflow Inference: examples/image_embeddings_roboflow.md
- Example - Multi-lingual semantic search: notebooks/multi_lingual_example.ipynb
- Example - MultiModal CLIP Embeddings: notebooks/DisappearingEmbeddingFunction.ipynb
- 🔍 Python full-text search: fts.md

View File

@@ -1,7 +1,9 @@
There are various Embedding functions available out of the box with lancedb. We're working on supporting other popular embedding APIs.
## Text Embedding Functions
Here are the text embedding functions registered by default
Here are the text embedding functions registered by default.
Embedding functions have inbuilt rate limit handler wrapper for source and query embedding function calls that retry with exponential standoff.
Each `EmbeddingFunction` implementation automatically takes `max_retries` as an argument which has the deafult value of 7.
### Sentence Transformers
Here are the parameters that you can set when registering a `sentence-transformers` object, and their default values:
@@ -66,6 +68,56 @@ actual = table.search(query).limit(1).to_pydantic(Words)[0]
print(actual.text)
```
### Instructor Embeddings
Instructor is an instruction-finetuned text embedding model that can generate text embeddings tailored to any task (e.g., classification, retrieval, clustering, text evaluation, etc.) and domains (e.g., science, finance, etc.) by simply providing the task instruction, without any finetuning
If you want to calculate customized embeddings for specific sentences, you may follow the unified template to write instructions:
Represent the `domain` `text_type` for `task_objective`:
* `domain` is optional, and it specifies the domain of the text, e.g., science, finance, medicine, etc.
* `text_type` is required, and it specifies the encoding unit, e.g., sentence, document, paragraph, etc.
* `task_objective` is optional, and it specifies the objective of embedding, e.g., retrieve a document, classify the sentence, etc.
More information about the model can be found here - https://github.com/xlang-ai/instructor-embedding
| Argument | Type | Default | Description |
|---|---|---|---|
| `name` | `str` | "hkunlp/instructor-base" | The name of the model to use |
| `batch_size` | `int` | `32` | The batch size to use when generating embeddings |
| `device` | `str` | `"cpu"` | The device to use when generating embeddings |
| `show_progress_bar` | `bool` | `True` | Whether to show a progress bar when generating embeddings |
| `normalize_embeddings` | `bool` | `True` | Whether to normalize the embeddings |
| `quantize` | `bool` | `False` | Whether to quantize the model |
| `source_instruction` | `str` | `"represent the docuement for retreival"` | The instruction for the source column |
| `query_instruction` | `str` | `"represent the document for retreiving the most similar documents"` | The instruction for the query |
```python
import lancedb
from lancedb.pydantic import LanceModel, Vector
from lancedb.embeddings import get_registry, InstuctorEmbeddingFunction
instructor = get_registry().get("instructor").create(
source_instruction="represent the docuement for retreival",
query_instruction="represent the document for retreiving the most similar documents"
)
class Schema(LanceModel):
vector: Vector(instructor.ndims()) = instructor.VectorField()
text: str = instructor.SourceField()
db = lancedb.connect("~/.lancedb")
tbl = db.create_table("test", schema=Schema, mode="overwrite")
texts = [{"text": "Capitalism has been dominant in the Western world since the end of feudalism, but most feel[who?] that..."},
{"text": "The disparate impact theory is especially controversial under the Fair Housing Act because the Act..."},
{"text": "Disparate impact in United States labor law refers to practices in employment, housing, and other areas that.."}]
tbl.add(texts)
```
## Multi-modal embedding functions
Multi-modal embedding functions allow you query your table using both images and text.

View File

@@ -57,6 +57,19 @@ query_image = Image.open(p)
table.search(query_image)
```
### Rate limit Handling
`EmbeddingFunction` class wraps the calls for source and query embedding generation inside a rate limit handler that retries the requests with exponential backoff after successive failures. By default the maximum retires is set to 7. You can tune it by setting it to a different number or disable it by setting it to 0.
Example
----
```python
clip = registry.get("open-clip").create() # Defaults to 7 max retries
clip = registry.get("open-clip").create(max_retries=10) # Increase max retries to 10
clip = registry.get("open-clip").create(max_retries=0) # Retries disabled
````
NOTE:
Embedding functions can also fail due to other errors that have nothing to do with rate limits. This is why the error is also logged.
### A little fun with PyDantic
LanceDB is integrated with PyDantic. Infact we've used the integration in the above example to define the schema. It is also being used behing the scene by the embdding function API to ingest useful information as table metadata.

View File

@@ -0,0 +1,165 @@
# How to Load Image Embeddings into LanceDB
With the rise of Large Multimodal Models (LMMs) such as [GPT-4 Vision](https://blog.roboflow.com/gpt-4-vision/), the need for storing image embeddings is growing. The most effective way to store text and image embeddings is in a vector database such as LanceDB. Vector databases are a special kind of data store that enables efficient search over stored embeddings.
[CLIP](https://blog.roboflow.com/openai-clip/), a multimodal model developed by OpenAI, is commonly used to calculate image embeddings. These embeddings can then be used with a vector database to build a semantic search engine that you can query using images or text. For example, you could use LanceDB and CLIP embeddings to build a search engine for a database of folders.
In this guide, we are going to show you how to use Roboflow Inference to load image embeddings into LanceDB. Without further ado, lets get started!
## Step #1: Install Roboflow Inference
[Roboflow Inference](https://inference.roboflow.com) enables you to run state-of-the-art computer vision models with minimal configuration. Inference supports a range of models, from fine-tuned object detection, classification, and segmentation models to foundation models like CLIP. We will use Inference to calculate CLIP image embeddings.
Inference provides a HTTP API through which you can run vision models.
Inference powers the Roboflow hosted API, and is available as an open source utility. In this guide, we are going to run Inference locally, which enables you to calculate CLIP embeddings on your own hardware. We will also show you how to use the hosted Roboflow CLIP API, which is ideal if you need to scale and do not want to manage a system for calculating embeddings.
To get started, first install the Inference CLI:
```
pip install inference-cli
```
Next, install Docker. Refer to the official Docker installation instructions for your operating system to get Docker set up. Once Docker is ready, you can start Inference using the following command:
```
inference server start
```
An Inference server will start running at http://localhost:9001.
## Step #2: Set Up a LanceDB Vector Database
Now that we have Inference running, we can set up a LanceDB vector database. You can run LanceDB in JavaScript and Python. For this guide, we will use the Python API. But, you can take the HTTP requests we make below and change them to JavaScript if required.
For this guide, we are going to search the [COCO 128 dataset](https://universe.roboflow.com/team-roboflow/coco-128), which contains a wide range of objects. The variability in objects present in this dataset makes it a good dataset to demonstrate the capabilities of vector search. If you want to use this dataset, you can download [COCO 128 from Roboflow Universe](https://universe.roboflow.com/team-roboflow/coco-128). With that said, you can search whatever folder of images you want.
Once you have a dataset ready, install LanceDB with the following command:
```
pip install lancedb
```
We also need to install a specific commit of `tantivy`, a dependency of the LanceDB full text search engine we will use later in this guide:
```
pip install tantivy@git+https://github.com/quickwit-oss/tantivy-py#164adc87e1a033117001cf70e38c82a53014d985
```
Create a new Python file and add the following code:
```python
import cv2
import supervision as sv
import requests
import lancedb
db = lancedb.connect("./embeddings")
IMAGE_DIR = "images/"
API_KEY = os.environ.get("ROBOFLOW_API_KEY")
SERVER_URL = "http://localhost:9001"
results = []
for i, image in enumerate(os.listdir(IMAGE_DIR)):
infer_clip_payload = {
#Images can be provided as urls or as base64 encoded strings
"image": {
"type": "base64",
"value": base64.b64encode(open(IMAGE_DIR + image, "rb").read()).decode("utf-8"),
},
}
res = requests.post(
f"{SERVER_URL}/clip/embed_image?api_key={API_KEY}",
json=infer_clip_payload,
)
embeddings = res.json()['embeddings']
print("Calculated embedding for image: ", image)
image = {"vector": embeddings[0], "name": os.path.join(IMAGE_DIR, image)}
results.append(image)
tbl = db.create_table("images", data=results)
tbl.create_fts_index("name")
```
To use the code above, you will need a Roboflow API key. [Learn how to retrieve a Roboflow API key](https://docs.roboflow.com/api-reference/authentication#retrieve-an-api-key). Run the following command to set up your API key in your environment:
```
export ROBOFLOW_API_KEY=""
```
Replace the `IMAGE_DIR` value with the folder in which you are storing the images for which you want to calculate embeddings. If you want to use the Roboflow CLIP API to calculate embeddings, replace the `SERVER_URL` value with `https://infer.roboflow.com`.
Run the script above to create a new LanceDB database. This database will be stored on your local machine. The database will be called `embeddings` and the table will be called `images`.
The script above calculates all embeddings for a folder then creates a new table. To add additional images, use the following code:
```python
def make_batches():
for i in range(5):
yield [
{"vector": [3.1, 4.1], "name": "image1.png"},
{"vector": [5.9, 26.5], "name": "image2.png"}
]
tbl = db.open_table("images")
tbl.add(make_batches())
```
Replacing the `make_batches()` function with code to load embeddings for images.
## Step #3: Run a Search Query
We are now ready to run a search query. To run a search query, we need a text embedding that represents a text query. We can use this embedding to search our LanceDB database for an entry.
Lets calculate a text embedding for the query “cat”, then run a search query:
```python
infer_clip_payload = {
"text": "cat",
}
res = requests.post(
f"{SERVER_URL}/clip/embed_text?api_key={API_KEY}",
json=infer_clip_payload,
)
embeddings = res.json()['embeddings']
df = tbl.search(embeddings[0]).limit(3).to_list()
print("Results:")
for i in df:
print(i["name"])
```
This code will search for the three images most closely related to the prompt “cat”. The names of the most similar three images will be printed to the console. Here are the three top results:
```
dataset/images/train/000000000650_jpg.rf.1b74ba165c5a3513a3211d4a80b69e1c.jpg
dataset/images/train/000000000138_jpg.rf.af439ef1c55dd8a4e4b142d186b9c957.jpg
dataset/images/train/000000000165_jpg.rf.eae14d5509bf0c9ceccddbb53a5f0c66.jpg
```
Lets open the top image:
![Cat](https://media.roboflow.com/cat_lancedb.jpg)
The top image was a cat. Our search was successful.
## Conclusion
LanceDB is a vector database that you can use to store and efficiently search your image embeddings. You can use Roboflow Inference, a scalable computer vision inference server, to calculate CLIP embeddings that you can store in LanceDB.
You can use Inference and LanceDB together to build a range of applications with image embeddings, from a media search engine to a retrieval-augmented generation pipeline for use with LMMs.
To learn more about Inference and its capabilities, refer to the Inference documentation.

1
docs/src/robots.txt Normal file
View File

@@ -0,0 +1 @@
User-agent: *

80
node/package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "vectordb",
"version": "0.3.7",
"version": "0.3.8",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "vectordb",
"version": "0.3.7",
"version": "0.3.8",
"cpu": [
"x64",
"arm64"
@@ -53,11 +53,11 @@
"uuid": "^9.0.0"
},
"optionalDependencies": {
"@lancedb/vectordb-darwin-arm64": "0.3.7",
"@lancedb/vectordb-darwin-x64": "0.3.7",
"@lancedb/vectordb-linux-arm64-gnu": "0.3.7",
"@lancedb/vectordb-linux-x64-gnu": "0.3.7",
"@lancedb/vectordb-win32-x64-msvc": "0.3.7"
"@lancedb/vectordb-darwin-arm64": "0.3.8",
"@lancedb/vectordb-darwin-x64": "0.3.8",
"@lancedb/vectordb-linux-arm64-gnu": "0.3.8",
"@lancedb/vectordb-linux-x64-gnu": "0.3.8",
"@lancedb/vectordb-win32-x64-msvc": "0.3.8"
}
},
"node_modules/@apache-arrow/ts": {
@@ -316,22 +316,10 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"node_modules/@lancedb/vectordb-darwin-arm64": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-darwin-arm64/-/vectordb-darwin-arm64-0.3.7.tgz",
"integrity": "sha512-QsDxcbhrumJg+Cyflpnj8EY+bZojbco5K7VSeKvguqeXUGb62ksyOZuUTCn2sqJaCgy1KZ1qC5U8jBqfgZHc2w==",
"cpu": [
"arm64"
],
"optional": true,
"os": [
"darwin"
]
},
"node_modules/@lancedb/vectordb-darwin-x64": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-darwin-x64/-/vectordb-darwin-x64-0.3.7.tgz",
"integrity": "sha512-fgv10kI04UycgpmhJLUcCswgvSdgsGuj65o+W5usmVdxYZiWpoXBBXRkWYMjUX5RNe3mY1Ff6QPBbToR0WkSUA==",
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-darwin-x64/-/vectordb-darwin-x64-0.3.8.tgz",
"integrity": "sha512-PCJwJ2oV0yTq0XJryMMjLad14i9s6xRolFZ1M4EZtgN16X/n/m0xTZjU8Y95Fj28tPFMgd4Pmgtc/TWuEBxW8A==",
"cpu": [
"x64"
],
@@ -341,9 +329,9 @@
]
},
"node_modules/@lancedb/vectordb-linux-arm64-gnu": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-linux-arm64-gnu/-/vectordb-linux-arm64-gnu-0.3.7.tgz",
"integrity": "sha512-pvw+31+VKEH3YmS/GLKzEGt/Y2+c/IaE6JL6tIjXi2KY+ZcWuyyXpYnYiHHDw2EP7ubKj6+fKIG1P9tlxMcGMQ==",
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-linux-arm64-gnu/-/vectordb-linux-arm64-gnu-0.3.8.tgz",
"integrity": "sha512-P+ZvI9+g8MDjPvz5+HPNFCCPNAvCDpCfIvKqEiTGEm2Sk5I0meIxRX4VGEnLGcQZmF1LUnVzhKV9+Rkiqd4JIQ==",
"cpu": [
"arm64"
],
@@ -353,9 +341,9 @@
]
},
"node_modules/@lancedb/vectordb-linux-x64-gnu": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-linux-x64-gnu/-/vectordb-linux-x64-gnu-0.3.7.tgz",
"integrity": "sha512-kHFURhfhJRqw4k1auseqQgOzAHB4oYpyzLCX3TCR3uTxqRQ7gFxxlO0TnIcwNRqLcGb9GmWxWWoR8k1CdCXrMw==",
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-linux-x64-gnu/-/vectordb-linux-x64-gnu-0.3.8.tgz",
"integrity": "sha512-E7opS6JuNpyvej0JZ+DJxplnnFp543dlPW0hNxoxsndflo9NeeAa1AIsNQSCIABWlfsQbGxXPYrvsOKHbzAIdw==",
"cpu": [
"x64"
],
@@ -365,9 +353,9 @@
]
},
"node_modules/@lancedb/vectordb-win32-x64-msvc": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-win32-x64-msvc/-/vectordb-win32-x64-msvc-0.3.7.tgz",
"integrity": "sha512-zWfZ557v2Y+93dVrmqqnbiLeTOb0ptunAG0zGjyE+3oyi8j/4+bL56Fdv94k+dfNF4KrcqcULEcZhKik3/FQ9w==",
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-win32-x64-msvc/-/vectordb-win32-x64-msvc-0.3.8.tgz",
"integrity": "sha512-RAL8U46UE12ksO3VAnnLlfxDd4wxZpJNFYtXjkacKL4ud9PkAJC4FBJpD7EFP9c7LEY3IlJPtvAp5Ax9LGWFeA==",
"cpu": [
"x64"
],
@@ -4868,34 +4856,28 @@
"@jridgewell/sourcemap-codec": "^1.4.10"
}
},
"@lancedb/vectordb-darwin-arm64": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-darwin-arm64/-/vectordb-darwin-arm64-0.3.7.tgz",
"integrity": "sha512-QsDxcbhrumJg+Cyflpnj8EY+bZojbco5K7VSeKvguqeXUGb62ksyOZuUTCn2sqJaCgy1KZ1qC5U8jBqfgZHc2w==",
"optional": true
},
"@lancedb/vectordb-darwin-x64": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-darwin-x64/-/vectordb-darwin-x64-0.3.7.tgz",
"integrity": "sha512-fgv10kI04UycgpmhJLUcCswgvSdgsGuj65o+W5usmVdxYZiWpoXBBXRkWYMjUX5RNe3mY1Ff6QPBbToR0WkSUA==",
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-darwin-x64/-/vectordb-darwin-x64-0.3.8.tgz",
"integrity": "sha512-PCJwJ2oV0yTq0XJryMMjLad14i9s6xRolFZ1M4EZtgN16X/n/m0xTZjU8Y95Fj28tPFMgd4Pmgtc/TWuEBxW8A==",
"optional": true
},
"@lancedb/vectordb-linux-arm64-gnu": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-linux-arm64-gnu/-/vectordb-linux-arm64-gnu-0.3.7.tgz",
"integrity": "sha512-pvw+31+VKEH3YmS/GLKzEGt/Y2+c/IaE6JL6tIjXi2KY+ZcWuyyXpYnYiHHDw2EP7ubKj6+fKIG1P9tlxMcGMQ==",
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-linux-arm64-gnu/-/vectordb-linux-arm64-gnu-0.3.8.tgz",
"integrity": "sha512-P+ZvI9+g8MDjPvz5+HPNFCCPNAvCDpCfIvKqEiTGEm2Sk5I0meIxRX4VGEnLGcQZmF1LUnVzhKV9+Rkiqd4JIQ==",
"optional": true
},
"@lancedb/vectordb-linux-x64-gnu": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-linux-x64-gnu/-/vectordb-linux-x64-gnu-0.3.7.tgz",
"integrity": "sha512-kHFURhfhJRqw4k1auseqQgOzAHB4oYpyzLCX3TCR3uTxqRQ7gFxxlO0TnIcwNRqLcGb9GmWxWWoR8k1CdCXrMw==",
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-linux-x64-gnu/-/vectordb-linux-x64-gnu-0.3.8.tgz",
"integrity": "sha512-E7opS6JuNpyvej0JZ+DJxplnnFp543dlPW0hNxoxsndflo9NeeAa1AIsNQSCIABWlfsQbGxXPYrvsOKHbzAIdw==",
"optional": true
},
"@lancedb/vectordb-win32-x64-msvc": {
"version": "0.3.7",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-win32-x64-msvc/-/vectordb-win32-x64-msvc-0.3.7.tgz",
"integrity": "sha512-zWfZ557v2Y+93dVrmqqnbiLeTOb0ptunAG0zGjyE+3oyi8j/4+bL56Fdv94k+dfNF4KrcqcULEcZhKik3/FQ9w==",
"version": "0.3.8",
"resolved": "https://registry.npmjs.org/@lancedb/vectordb-win32-x64-msvc/-/vectordb-win32-x64-msvc-0.3.8.tgz",
"integrity": "sha512-RAL8U46UE12ksO3VAnnLlfxDd4wxZpJNFYtXjkacKL4ud9PkAJC4FBJpD7EFP9c7LEY3IlJPtvAp5Ax9LGWFeA==",
"optional": true
},
"@neon-rs/cli": {

View File

@@ -1,6 +1,6 @@
{
"name": "vectordb",
"version": "0.3.7",
"version": "0.3.9",
"description": " Serverless, low-latency vector database for AI applications",
"main": "dist/index.js",
"types": "dist/index.d.ts",
@@ -81,10 +81,10 @@
}
},
"optionalDependencies": {
"@lancedb/vectordb-darwin-arm64": "0.3.7",
"@lancedb/vectordb-darwin-x64": "0.3.7",
"@lancedb/vectordb-linux-arm64-gnu": "0.3.7",
"@lancedb/vectordb-linux-x64-gnu": "0.3.7",
"@lancedb/vectordb-win32-x64-msvc": "0.3.7"
"@lancedb/vectordb-darwin-arm64": "0.3.9",
"@lancedb/vectordb-darwin-x64": "0.3.9",
"@lancedb/vectordb-linux-arm64-gnu": "0.3.9",
"@lancedb/vectordb-linux-x64-gnu": "0.3.9",
"@lancedb/vectordb-win32-x64-msvc": "0.3.9"
}
}

View File

@@ -32,6 +32,7 @@ export class Query<T = number[]> {
private _select?: string[]
private _filter?: string
private _metricType?: MetricType
private _prefilter: boolean
protected readonly _embeddings?: EmbeddingFunction<T>
constructor (query: T, tbl?: any, embeddings?: EmbeddingFunction<T>) {
@@ -44,6 +45,7 @@ export class Query<T = number[]> {
this._filter = undefined
this._metricType = undefined
this._embeddings = embeddings
this._prefilter = false
}
/***
@@ -102,6 +104,11 @@ export class Query<T = number[]> {
return this
}
prefilter (value: boolean): Query<T> {
this._prefilter = value
return this
}
/**
* Execute the query and return the results as an Array of Objects
*/

View File

@@ -38,6 +38,7 @@ export class HttpLancedbClient {
vector: number[],
k: number,
nprobes: number,
prefilter: boolean,
refineFactor?: number,
columns?: string[],
filter?: string
@@ -50,7 +51,8 @@ export class HttpLancedbClient {
nprobes,
refineFactor,
columns,
filter
filter,
prefilter
},
{
headers: {

View File

@@ -154,6 +154,7 @@ export class RemoteQuery<T = number[]> extends Query<T> {
queryVector,
(this as any)._limit,
(this as any)._nprobes,
(this as any)._prefilter,
(this as any)._refineFactor,
(this as any)._select,
(this as any)._filter

View File

@@ -102,6 +102,20 @@ describe('LanceDB client', function () {
assertResults(results)
})
it('should correctly process prefilter/postfilter', async function () {
const uri = await createTestDB(16, 300)
const con = await lancedb.connect(uri)
const table = await con.openTable('vectors')
await table.createIndex({ type: 'ivf_pq', column: 'vector', num_partitions: 2, max_iters: 2, num_sub_vectors: 2 })
// post filter should return less than the limit
let results = await table.search(new Array(16).fill(0.1)).limit(10).filter('id >= 10').prefilter(false).execute()
assert.isTrue(results.length < 10)
// pre filter should return exactly the limit
results = await table.search(new Array(16).fill(0.1)).limit(10).filter('id >= 10').prefilter(true).execute()
assert.isTrue(results.length === 10)
})
it('select only a subset of columns', async function () {
const uri = await createTestDB()
const con = await lancedb.connect(uri)
@@ -282,7 +296,8 @@ describe('LanceDB client', function () {
)
const table = await con.createTable({ name: 'vectors', schema })
await table.add([{ vector: Array(128).fill(0.1) }])
await table.delete('vector IS NOT NULL')
// https://github.com/lancedb/lance/issues/1635
await table.delete('true')
const result = await table.search(Array(128).fill(0.1)).execute()
assert.isEmpty(result)
})

View File

@@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.3.3
current_version = 0.3.4
commit = True
message = [python] Bump version: {current_version} → {new_version}
tag = True

View File

@@ -28,6 +28,7 @@ from ..pydantic import LanceModel
from ..table import Table, _sanitize_data
from .arrow import to_ipc_binary
from .client import ARROW_STREAM_CONTENT_TYPE, RestfulLanceDBClient
from .errors import LanceDBClientError
class RemoteDBConnection(DBConnection):
@@ -101,11 +102,12 @@ class RemoteDBConnection(DBConnection):
self._loop.run_until_complete(
self._client.post(f"/v1/table/{name}/describe/")
)
except Exception:
logging.error(
"Table {name} does not exist."
"Please first call db.create_table({name}, data)"
)
except LanceDBClientError as err:
if str(err).startswith("Not found"):
logging.error(
f"Table {name} does not exist. "
f"Please first call db.create_table({name}, data)"
)
return RemoteTable(self, name)
@override

View File

@@ -71,8 +71,62 @@ class RemoteTable(Table):
vector_column_name: str = VECTOR_COLUMN_NAME,
replace: bool = True,
accelerator: Optional[str] = None,
index_cache_size: Optional[int] = None,
):
raise NotImplementedError
"""Create an index on the table.
Currently, the only parameters that matter are
the metric and the vector column name.
Parameters
----------
metric : str
The metric to use for the index. Default is "L2".
num_partitions : int
The number of partitions to use for the index. Default is 256.
num_sub_vectors : int
The number of sub-vectors to use for the index. Default is 96.
vector_column_name : str
The name of the vector column. Default is "vector".
replace : bool
Whether to replace the existing index. Default is True.
accelerator : str, optional
If set, use the given accelerator to create the index.
Default is None. Currently not supported.
index_cache_size : int, optional
The size of the index cache in number of entries. Default value is 256.
Examples
--------
import lancedb
import uuid
from lancedb.schema import vector
conn = lancedb.connect("db://...", api_key="...", region="...")
table_name = uuid.uuid4().hex
schema = pa.schema(
[
pa.field("id", pa.uint32(), False),
pa.field("vector", vector(128), False),
pa.field("s", pa.string(), False),
]
)
table = conn.create_table(
table_name,
schema=schema,
)
table.create_index()
"""
index_type = "vector"
data = {
"column": vector_column_name,
"index_type": index_type,
"metric_type": metric,
"index_cache_size": index_cache_size,
}
resp = self._conn._loop.run_until_complete(
self._conn._client.post(f"/v1/table/{self._name}/create_index/", data=data)
)
return resp
def add(
self,

View File

@@ -188,6 +188,7 @@ class Table(ABC):
vector_column_name: str = VECTOR_COLUMN_NAME,
replace: bool = True,
accelerator: Optional[str] = None,
index_cache_size: Optional[int] = None,
):
"""Create an index on the table.
@@ -212,6 +213,8 @@ class Table(ABC):
accelerator: str, default None
If set, use the given accelerator to create the index.
Only support "cuda" for now.
index_cache_size : int, optional
The size of the index cache in number of entries. Default value is 256.
"""
raise NotImplementedError
@@ -556,6 +559,7 @@ class LanceTable(Table):
vector_column_name=VECTOR_COLUMN_NAME,
replace: bool = True,
accelerator: Optional[str] = None,
index_cache_size: Optional[int] = None,
):
"""Create an index on the table."""
self._dataset.create_index(
@@ -566,6 +570,7 @@ class LanceTable(Table):
num_sub_vectors=num_sub_vectors,
replace=replace,
accelerator=accelerator,
index_cache_size=index_cache_size,
)
self._reset_dataset()
register_event("create_index")

View File

@@ -1,9 +1,9 @@
[project]
name = "lancedb"
version = "0.3.3"
version = "0.3.4"
dependencies = [
"deprecation",
"pylance==0.8.10",
"pylance==0.8.17",
"ratelimiter~=1.0",
"retry>=0.9.2",
"tqdm>=4.1.0",

View File

@@ -301,6 +301,7 @@ def test_replace_index(tmp_path):
num_partitions=2,
num_sub_vectors=4,
replace=True,
index_cache_size=10,
)

View File

@@ -26,6 +26,9 @@ class FakeLanceDBClient:
t = pa.schema([]).empty_table()
return VectorQueryResult(t)
async def post(self, path: str):
pass
def test_remote_db():
conn = lancedb.connect("db://client-will-be-injected", api_key="fake")

View File

@@ -213,6 +213,7 @@ def test_create_index_method():
num_sub_vectors=96,
vector_column_name="vector",
replace=True,
index_cache_size=256,
)
# Check that the _dataset.create_index method was called
@@ -225,6 +226,7 @@ def test_create_index_method():
num_sub_vectors=96,
replace=True,
accelerator=None,
index_cache_size=256,
)

View File

@@ -1,6 +1,6 @@
[package]
name = "vectordb-node"
version = "0.3.7"
version = "0.3.9"
description = "Serverless, low-latency vector database for AI applications"
license = "Apache-2.0"
edition = "2018"
@@ -19,6 +19,7 @@ once_cell = "1"
futures = "0.3"
half = { workspace = true }
lance = { workspace = true }
lance-index = { workspace = true }
lance-linalg = { workspace = true }
vectordb = { path = "../../vectordb" }
tokio = { version = "1.23", features = ["rt-multi-thread"] }

View File

@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use lance::index::vector::{ivf::IvfBuildParams, pq::PQBuildParams};
use lance_index::vector::{ivf::IvfBuildParams, pq::PQBuildParams};
use lance_linalg::distance::MetricType;
use neon::context::FunctionContext;
use neon::prelude::*;

View File

@@ -48,6 +48,8 @@ impl JsQuery {
.map(|s| s.value(&mut cx))
.map(|s| MetricType::try_from(s.as_str()).unwrap());
let prefilter = query_obj.get::<JsBoolean, _, _>(&mut cx, "_prefilter")?.value(&mut cx);
let is_electron = cx
.argument::<JsBoolean>(1)
.or_throw(&mut cx)?
@@ -69,7 +71,8 @@ impl JsQuery {
.nprobes(nprobes)
.filter(filter)
.metric_type(metric_type)
.select(select);
.select(select)
.prefilter(prefilter);
let record_batch_stream = builder.execute();
let results = record_batch_stream
.and_then(|stream| {

View File

@@ -1,6 +1,6 @@
[package]
name = "vectordb"
version = "0.3.7"
version = "0.3.9"
edition = "2021"
description = "LanceDB: A serverless, low-latency vector database for AI applications"
license = "Apache-2.0"
@@ -21,6 +21,7 @@ object_store = { workspace = true }
snafu = { workspace = true }
half = { workspace = true }
lance = { workspace = true }
lance-index = { workspace = true }
lance-linalg = { workspace = true }
lance-testing = { workspace = true }
tokio = { version = "1.23", features = ["rt-multi-thread"] }

View File

@@ -13,9 +13,9 @@
// limitations under the License.
use lance::format::{Index, Manifest};
use lance::index::vector::ivf::IvfBuildParams;
use lance::index::vector::pq::PQBuildParams;
use lance::index::vector::VectorIndexParams;
use lance_index::vector::ivf::IvfBuildParams;
use lance_linalg::distance::MetricType;
pub trait VectorIndexBuilder {
@@ -136,9 +136,9 @@ impl VectorIndex {
mod tests {
use super::*;
use lance::index::vector::ivf::IvfBuildParams;
use lance::index::vector::pq::PQBuildParams;
use lance::index::vector::StageParams;
use lance_index::vector::ivf::IvfBuildParams;
use lance_index::vector::pq::PQBuildParams;
use crate::index::vector::{IvfPQIndexBuilder, VectorIndexBuilder};

View File

@@ -32,6 +32,7 @@ pub struct Query {
pub refine_factor: Option<u32>,
pub metric_type: Option<MetricType>,
pub use_index: bool,
pub prefilter: bool,
}
impl Query {
@@ -56,6 +57,7 @@ impl Query {
use_index: true,
filter: None,
select: None,
prefilter: false,
}
}
@@ -74,6 +76,8 @@ impl Query {
)?;
scanner.nprobs(self.nprobes);
scanner.use_index(self.use_index);
scanner.prefilter(self.prefilter);
self.select.as_ref().map(|p| scanner.project(p.as_slice()));
self.filter.as_ref().map(|f| scanner.filter(f));
self.refine_factor.map(|rf| scanner.refine(rf));
@@ -158,6 +162,11 @@ impl Query {
self.select = columns;
self
}
pub fn prefilter(mut self, prefilter: bool) -> Query {
self.prefilter = prefilter;
self
}
}
#[cfg(test)]
@@ -167,7 +176,9 @@ mod tests {
use super::*;
use arrow_array::{Float32Array, RecordBatch, RecordBatchIterator, RecordBatchReader};
use arrow_schema::{DataType, Field as ArrowField, Schema as ArrowSchema};
use futures::StreamExt;
use lance::dataset::Dataset;
use lance_testing::datagen::{BatchGenerator, IncrementingInt32, RandomVector};
use crate::query::Query;
@@ -200,13 +211,43 @@ mod tests {
#[tokio::test]
async fn test_execute() {
let batches = make_test_batches();
let ds = Dataset::write(batches, "memory://foo", None).await.unwrap();
let batches = make_non_empty_batches();
let ds = Arc::new(Dataset::write(batches, "memory://foo", None).await.unwrap());
let vector = Float32Array::from_iter_values([0.1; 128]);
let query = Query::new(Arc::new(ds), vector.clone());
let result = query.execute().await;
assert_eq!(result.is_ok(), true);
let vector = Float32Array::from_iter_values([0.1; 4]);
let query = Query::new(ds.clone(), vector.clone());
let result = query
.limit(10)
.filter(Some("id % 2 == 0".to_string()))
.execute()
.await;
let mut stream = result.expect("should have result");
// should only have one batch
while let Some(batch) = stream.next().await {
// post filter should have removed some rows
assert!(batch.expect("should be Ok").num_rows() < 10);
}
let query = Query::new(ds, vector.clone());
let result = query
.limit(10)
.filter(Some("id % 2 == 0".to_string()))
.prefilter(true)
.execute()
.await;
let mut stream = result.expect("should have result");
// should only have one batch
while let Some(batch) = stream.next().await {
// pre filter should return 10 rows
assert!(batch.expect("should be Ok").num_rows() == 10);
}
}
fn make_non_empty_batches() -> impl RecordBatchReader + Send + 'static {
let vec = Box::new(RandomVector::new().named("vector".to_string()));
let id = Box::new(IncrementingInt32::new().named("id".to_string()));
BatchGenerator::new().col(vec).col(id).batch(512)
}
fn make_test_batches() -> impl RecordBatchReader + Send + 'static {

View File

@@ -13,6 +13,8 @@
// limitations under the License.
use chrono::Duration;
use lance::dataset::builder::DatasetBuilder;
use lance_index::IndexType;
use std::sync::Arc;
use arrow_array::{Float32Array, RecordBatchReader};
@@ -22,7 +24,7 @@ use lance::dataset::optimize::{
compact_files, CompactionMetrics, CompactionOptions, IndexRemapperOptions,
};
use lance::dataset::{Dataset, WriteParams};
use lance::index::{DatasetIndexExt, IndexType};
use lance::index::DatasetIndexExt;
use lance::io::object_store::WrappingObjectStore;
use std::path::Path;
@@ -96,7 +98,10 @@ impl Table {
Some(wrapper) => params.patch_with_store_wrapper(wrapper)?,
None => params,
};
let dataset = Dataset::open_with_params(uri, &params)
let dataset = DatasetBuilder::from_uri(uri)
.with_read_params(params)
.load()
.await
.map_err(|e| match e {
lance::Error::DatasetNotFound { .. } => Error::TableNotFound {
@@ -414,9 +419,9 @@ mod tests {
use arrow_data::ArrayDataBuilder;
use arrow_schema::{DataType, Field, Schema};
use lance::dataset::{Dataset, WriteMode};
use lance::index::vector::ivf::IvfBuildParams;
use lance::index::vector::pq::PQBuildParams;
use lance::io::object_store::{ObjectStoreParams, WrappingObjectStore};
use lance_index::vector::ivf::IvfBuildParams;
use rand::Rng;
use tempfile::tempdir;