mirror of
https://github.com/lancedb/lancedb.git
synced 2025-12-23 13:29:57 +00:00
Compare commits
7 Commits
python-v0.
...
v0.3.9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
23d30dfc78 | ||
|
|
94c8c50f96 | ||
|
|
72765d8e1a | ||
|
|
a2a8f9615e | ||
|
|
b085d9aaa1 | ||
|
|
6eb662de9b | ||
|
|
2bb2bb581a |
@@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 0.3.8
|
||||
current_version = 0.3.9
|
||||
commit = True
|
||||
message = Bump version: {current_version} → {new_version}
|
||||
tag = True
|
||||
|
||||
@@ -80,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
|
||||
|
||||
165
docs/src/examples/image_embeddings_roboflow.md
Normal file
165
docs/src/examples/image_embeddings_roboflow.md
Normal 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, let’s 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.
|
||||
|
||||
Let’s 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
|
||||
```
|
||||
|
||||
Let’s open the top image:
|
||||
|
||||

|
||||
|
||||
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.
|
||||
72
node/package-lock.json
generated
72
node/package-lock.json
generated
@@ -316,6 +316,54 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"node_modules/@lancedb/vectordb-darwin-x64": {
|
||||
"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"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
]
|
||||
},
|
||||
"node_modules/@lancedb/vectordb-linux-arm64-gnu": {
|
||||
"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"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@lancedb/vectordb-linux-x64-gnu": {
|
||||
"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"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"linux"
|
||||
]
|
||||
},
|
||||
"node_modules/@lancedb/vectordb-win32-x64-msvc": {
|
||||
"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"
|
||||
],
|
||||
"optional": true,
|
||||
"os": [
|
||||
"win32"
|
||||
]
|
||||
},
|
||||
"node_modules/@neon-rs/cli": {
|
||||
"version": "0.0.160",
|
||||
"resolved": "https://registry.npmjs.org/@neon-rs/cli/-/cli-0.0.160.tgz",
|
||||
@@ -4808,6 +4856,30 @@
|
||||
"@jridgewell/sourcemap-codec": "^1.4.10"
|
||||
}
|
||||
},
|
||||
"@lancedb/vectordb-darwin-x64": {
|
||||
"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.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.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.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": {
|
||||
"version": "0.0.160",
|
||||
"resolved": "https://registry.npmjs.org/@neon-rs/cli/-/cli-0.0.160.tgz",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "vectordb",
|
||||
"version": "0.3.8",
|
||||
"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.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"
|
||||
"@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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "vectordb-node"
|
||||
version = "0.3.8"
|
||||
version = "0.3.9"
|
||||
description = "Serverless, low-latency vector database for AI applications"
|
||||
license = "Apache-2.0"
|
||||
edition = "2018"
|
||||
|
||||
@@ -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| {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "vectordb"
|
||||
version = "0.3.8"
|
||||
version = "0.3.9"
|
||||
edition = "2021"
|
||||
description = "LanceDB: A serverless, low-latency vector database for AI applications"
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -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 {
|
||||
|
||||
Reference in New Issue
Block a user