diff --git a/docs/snippets/basic_usage.mdx b/docs/snippets/basic_usage.mdx index 2f05ded..a62a519 100644 --- a/docs/snippets/basic_usage.mdx +++ b/docs/snippets/basic_usage.mdx @@ -22,7 +22,7 @@ export const PyBasicImports = "import json\n\nimport lancedb\nimport pandas as p export const PyBasicOpenTable = "table = db.open_table(\"camelot\")\n"; -export const PyBasicVectorSearch = "query_vector = [0.03, 0.85, 0.61, 0.90]\ntable.search(query_vector).limit(5).to_polars()\n"; +export const PyBasicVectorSearch = "query_vector = [0.03, 0.85, 0.61, 0.90]\nresult = table.search(query_vector).limit(5).to_polars()\nprint(result)\n"; export const PyBasicVectorSearchQ1 = "# Who are the characters similar to \"wizard\"?\nquery_vector_1 = [0.03, 0.85, 0.61, 0.90]\nr1 = (\n table.search(query_vector_1)\n .limit(5)\n .select([\"name\", \"role\", \"description\"])\n .to_polars()\n)\nprint(r1)\n"; @@ -52,7 +52,7 @@ export const TsBasicImports = "import * as lancedb from \"@lancedb/lancedb\";\ni export const TsBasicOpenTable = "table = await db.openTable(\"camelot\");\n"; -export const TsBasicVectorSearch = "const queryVector = [0.03, 0.85, 0.61, 0.9];\nawait table.search(queryVector).limit(5).toArray();\n"; +export const TsBasicVectorSearch = "const queryVector = [0.03, 0.85, 0.61, 0.9];\nconst result = await table.search(queryVector).limit(5).toArray();\nconsole.log(result);\n"; export const TsBasicVectorSearchQ1 = "// Who are the characters similar to \"wizard\"?\nconst queryVector1 = [0.03, 0.85, 0.61, 0.9];\nconst r1 = await table\n .search(queryVector1)\n .limit(5)\n .select([\"name\", \"role\", \"description\"])\n .toArray();\nconsole.log(r1);\n"; @@ -88,7 +88,7 @@ export const RsBasicImports = "use arrow_array::types::Float32Type;\nuse arrow_a export const RsBasicOpenTable = "table = db.open_table(\"camelot\").execute().await.unwrap();\n"; -export const RsBasicVectorSearch = "let query_vector = [0.03, 0.85, 0.61, 0.90];\nlet _ = table\n .query()\n .nearest_to(&query_vector)\n .unwrap()\n .limit(5)\n .execute()\n .await\n .unwrap()\n .try_collect::>()\n .await\n .unwrap();\n"; +export const RsBasicVectorSearch = "let query_vector = [0.03, 0.85, 0.61, 0.90];\nlet result = table\n .query()\n .nearest_to(&query_vector)\n .unwrap()\n .limit(5)\n .execute()\n .await\n .unwrap()\n .try_collect::>()\n .await\n .unwrap();\nprintln!(\"{result:?}\");\n"; export const RsBasicVectorSearchQ1 = "// Who are the characters similar to \"wizard\"?\nlet query_vector_1 = [0.03, 0.85, 0.61, 0.90];\nlet r1 = table\n .query()\n .nearest_to(&query_vector_1)\n .unwrap()\n .limit(5)\n .select(Select::Columns(vec![\n \"name\".to_string(),\n \"role\".to_string(),\n \"description\".to_string(),\n ]))\n .execute()\n .await\n .unwrap()\n .try_collect::>()\n .await\n .unwrap();\nprintln!(\"{r1:?}\");\n"; diff --git a/docs/snippets/quickstart.mdx b/docs/snippets/quickstart.mdx index c2d8e58..570b8da 100644 --- a/docs/snippets/quickstart.mdx +++ b/docs/snippets/quickstart.mdx @@ -8,9 +8,9 @@ export const PyQuickstartCreateTableNoOverwrite = "table = db.create_table(\"adv export const PyQuickstartOpenTable = "table = db.open_table(\"adventurers\")\n"; -export const PyQuickstartOutputPandas = "# Ensure you run `pip install pandas` beforehand\nresult = table.search(query_vector).limit(2).to_pandas()\n"; +export const PyQuickstartOutputPandas = "# Ensure you run `pip install pandas` beforehand\nresult = table.search(query_vector).limit(2).to_pandas()\nprint(result)\n"; -export const PyQuickstartVectorSearch1 = "# Let's search for vectors similar to \"warrior\"\nquery_vector = [0.8, 0.3, 0.8]\n\n# Ensure you run `pip install polars` beforehand\nresult = table.search(query_vector).limit(2).to_polars()\n"; +export const PyQuickstartVectorSearch1 = "# Let's search for vectors similar to \"warrior\"\nquery_vector = [0.8, 0.3, 0.8]\n\n# Ensure you run `pip install polars` beforehand\nresult = table.search(query_vector).limit(2).to_polars()\nprint(result)\n"; export const PyQuickstartVectorSearch2 = "# Let's search for vectors similar to \"wizard\"\nquery_vector = [0.7, 0.3, 0.5]\n\nresults = table.search(query_vector).limit(2).to_polars()\nprint(results)\n"; @@ -22,13 +22,13 @@ export const TsQuickstartCreateTableNoOverwrite = "table = await db.createTable( export const TsQuickstartOpenTable = "table = await db.openTable(\"adventurers\");\n"; -export const TsQuickstartOutputArray = "result = await table.search(queryVector).limit(2).toArray();\n"; +export const TsQuickstartOutputArray = "result = await table.search(queryVector).limit(2).toArray();\nconsole.table(result);\n"; export const TsQuickstartOutputPandas = "result = await table.search(queryVector).limit(2).toArray();\n"; -export const TsQuickstartVectorSearch1 = "// Let's search for vectors similar to \"warrior\"\nlet queryVector = [0.8, 0.3, 0.8];\n\nlet result = await table.search(queryVector).limit(2).toArray();\n"; +export const TsQuickstartVectorSearch1 = "// Let's search for vectors similar to \"warrior\"\nlet queryVector = [0.8, 0.3, 0.8];\n\nlet result = await table.search(queryVector).limit(2).toArray();\nconsole.table(result);\n"; -export const TsQuickstartVectorSearch2 = "// Let's search for vectors similar to \"wizard\"\nqueryVector = [0.7, 0.3, 0.5];\n\nconst results = await table.search(queryVector).limit(2).toArray();\nconsole.log(results);\n"; +export const TsQuickstartVectorSearch2 = "// Let's search for vectors similar to \"wizard\"\nqueryVector = [0.7, 0.3, 0.5];\n\nconst results = await table.search(queryVector).limit(2).toArray();\nconsole.table(results);\n"; export const RsQuickstartAddData = "let more_data = vec![\n Adventurer {\n id: \"7\".to_string(),\n text: \"mage\".to_string(),\n vector: [0.6, 0.3, 0.4],\n },\n Adventurer {\n id: \"8\".to_string(),\n text: \"bard\".to_string(),\n vector: [0.3, 0.8, 0.4],\n },\n];\n\n// Add data to table\ntable\n .add(adventurers_to_reader(schema.clone(), &more_data))\n .execute()\n .await\n .unwrap();\n"; @@ -40,9 +40,9 @@ export const RsQuickstartDefineStruct = "// Define a struct representing the dat export const RsQuickstartOpenTable = "let table: Table = db.open_table(\"adventurers\").execute().await.unwrap();\n"; -export const RsQuickstartOutputArray = "let result: DataFrame = table\n .query()\n .nearest_to(&query_vector)\n .unwrap()\n .limit(2)\n .select(Select::Columns(vec![\"text\".to_string()]))\n .execute()\n .await\n .unwrap()\n .into_polars()\n .await\n .unwrap();\nlet text_col = result.column(\"text\").unwrap().str().unwrap();\nlet top_two = vec![\n text_col.get(0).unwrap().to_string(),\n text_col.get(1).unwrap().to_string(),\n];\n"; +export const RsQuickstartOutputArray = "let result: DataFrame = table\n .query()\n .nearest_to(&query_vector)\n .unwrap()\n .limit(2)\n .select(Select::Columns(vec![\"text\".to_string()]))\n .execute()\n .await\n .unwrap()\n .into_polars()\n .await\n .unwrap();\nprintln!(\"{result:?}\");\nlet text_col = result.column(\"text\").unwrap().str().unwrap();\nlet top_two = vec![\n text_col.get(0).unwrap().to_string(),\n text_col.get(1).unwrap().to_string(),\n];\n"; -export const RsQuickstartVectorSearch1 = "// Let's search for vectors similar to \"warrior\"\nlet query_vector = [0.8, 0.3, 0.8];\n\nlet result: DataFrame = table\n .query()\n .nearest_to(&query_vector)\n .unwrap()\n .limit(2)\n .select(Select::Columns(vec![\"text\".to_string()]))\n .execute()\n .await\n .unwrap()\n .into_polars()\n .await\n .unwrap();\n"; +export const RsQuickstartVectorSearch1 = "// Let's search for vectors similar to \"warrior\"\nlet query_vector = [0.8, 0.3, 0.8];\n\nlet result: DataFrame = table\n .query()\n .nearest_to(&query_vector)\n .unwrap()\n .limit(2)\n .select(Select::Columns(vec![\"text\".to_string()]))\n .execute()\n .await\n .unwrap()\n .into_polars()\n .await\n .unwrap();\nprintln!(\"{result:?}\");\n"; -export const RsQuickstartVectorSearch2 = "// Let's search for vectors similar to \"wizard\"\nlet query_vector = [0.7, 0.3, 0.5];\n\nlet result: DataFrame = table\n .query()\n .nearest_to(&query_vector)\n .unwrap()\n .limit(2)\n .select(Select::Columns(vec![\"text\".to_string()]))\n .execute()\n .await\n .unwrap()\n .into_polars()\n .await\n .unwrap();\nlet text_col = result.column(\"text\").unwrap().str().unwrap();\nlet top_two = vec![\n text_col.get(0).unwrap().to_string(),\n text_col.get(1).unwrap().to_string(),\n];\n"; +export const RsQuickstartVectorSearch2 = "// Let's search for vectors similar to \"wizard\"\nlet query_vector = [0.7, 0.3, 0.5];\n\nlet result: DataFrame = table\n .query()\n .nearest_to(&query_vector)\n .unwrap()\n .limit(2)\n .select(Select::Columns(vec![\"text\".to_string()]))\n .execute()\n .await\n .unwrap()\n .into_polars()\n .await\n .unwrap();\nprintln!(\"{result:?}\");\nlet text_col = result.column(\"text\").unwrap().str().unwrap();\nlet top_two = vec![\n text_col.get(0).unwrap().to_string(),\n text_col.get(1).unwrap().to_string(),\n];\n"; diff --git a/docs/tables/create.mdx b/docs/tables/create.mdx index f77008d..7fbd2ed 100644 --- a/docs/tables/create.mdx +++ b/docs/tables/create.mdx @@ -155,8 +155,10 @@ document: struct not null #### Validators -Note that neither Pydantic nor PyArrow automatically validates that input data -is of the correct timezone, but this is easy to add as a custom field validator: +Because `LanceModel` inherits from Pydantic's `BaseModel`, you can combine them with Pydantic's +[field validators](https://docs.pydantic.dev/latest/concepts/validators). The example +below shows how to add a validator to ensure that only valid timezone-aware datetime objects are used +for a `created_at` field. @@ -164,12 +166,7 @@ is of the correct timezone, but this is easy to add as a custom field validator: -When you run this code it should print "A ValidationError was raised." - -#### Pydantic custom types - -LanceDB does NOT yet support converting pydantic custom types. If this is something you need, -please file a feature request on the [LanceDB Github repo](https://github.com/lancedb/lancedb/issues/new). +When you run this code it, should raise the `ValidationError`. ### Using Iterators / Writing Large Datasets @@ -198,7 +195,9 @@ If you forget the name of your table, you can always get a listing of all table ## Creating empty table -You can create an empty table for scenarios where you want to add data to the table later. An example would be when you want to collect data from a stream/external file and then add it to a table in batches. +You can create an empty table for scenarios where you want to add data to the table later. +An example would be when you want to collect data from a stream/external file and then add it to a table in +batches. An empty table can be initialized via a PyArrow schema. @@ -218,7 +217,8 @@ that has been extended to support LanceDB specific types like `Vector`. -Once the empty table has been created, you can add data to it, as explained in the next section on [working with data](/tables/update). +Once the empty table has been created, you can append to it or modify its contents, +as explained in the [updating and modifying tables](/tables/update) section. ## Drop a table diff --git a/docs/tables/index.mdx b/docs/tables/index.mdx index 46f5cbd..9ae70c6 100644 --- a/docs/tables/index.mdx +++ b/docs/tables/index.mdx @@ -234,9 +234,9 @@ initial testing). - + If you want to avoid overwriting an existing table, omit the overwrite mode. - + ### From Pandas DataFrames Python Only @@ -287,14 +287,13 @@ do so by defining an Arrow schema explicitly. Once the empty table is defined, LanceDB is ready to accept new data via the `add` method, as shown in the next section. - -**Display table schema** - + LanceDB tables are type-aware, leveraging Apache Arrow under the hood. -In Python, you can display a given table's schema using the `schema` property. -For example running `print(table.schema)` would show something like the following: +You can display a given table's schema using the `schema` property or +method. For example, in Python, running `print(table.schema)` would show +something like the following: -``` +```txt expandable=true id: int64 name: string role: string @@ -307,7 +306,7 @@ stats: struct child 2, strength: int64 child 3, wisdom: int64 ``` - + ## Append data to a table @@ -378,8 +377,8 @@ the desired columns). | Queen Guinevere | Queen of Camelot | Arthur's queen, admired for he… | | Sir Galahad | Knight of the Round Table | The purest and most virtuous k… | -We have Merlin, The Lady of the Lake, and Morgan le Fay in the top results, which -makes sense. +We have Merlin, The Lady of the Lake, and Morgan le Fay in the top results, who +all have magical abilities. Next, let's try to answer a more complex question that involves filtering on a nested struct field. Filtering is done using the `where` method, where you can @@ -408,15 +407,15 @@ pass in SQL-like expressions. | Morgan le Fay | Sorceress | A powerful enchantress, Arthur… | Only three characters have magical abilities greater than 3. Merlin is -clearly the most magical of them all. +clearly the most magical of them all! ## Filtered search You can also run traditional analytics-style search queries that do not involve vectors. For example, let's find the strongest characters in the dataset. In the query below, we leave the `search` method empty to indicate -that we don't want to use any vector for similarity search (in TypeScript, use `query()` instead), -and use the `where` method to filter on the `strength` field. +that we don't want to use any vector for similarity search (in TypeScript/Rust, +use `query()` instead), and use the `where` method to filter on the `strength` field. > Q3: _Who are the strongest characters?_ @@ -471,30 +470,10 @@ print(duckdb_tbl) | Sir Percival | Knight of the Round Table | A loyal and innocent knight whose bravery and … | | Mordred | Traitor Knight | Arthur's treacherous son or nephew who ultimat… | -## Delete data - -You can delete rows from a LanceDB table using the `delete` method with -a filtering expression. - -Say we want to throw away Mordred, the traitor knight, from our table. - - - - {PyBasicDeleteRows} - - - - {TsBasicDeleteRows} - - - - {RsBasicDeleteRows} - - - -This will delete the row(s) where the `role` column matches "Traitor Knight". -You can verify that the row has been deleted by running a search query again, -and confirming that Mordred no longer appears in the results. + +Under the hood, Lance tables are Arrow-based, leveraging Arrow's type system. This is why +it's trivial to query a Lance table using DuckDB, which also natively supports Arrow tables. + ## Add column @@ -516,9 +495,9 @@ of each character's strength, courage, magic, and wisdom stats. -The example above shows how a `cast` expression can be used to ensure the -average total stats is available as a float column, that is then converted to -an Arrow float type under the hood for the Lance table. +The example above sums up the individual stats and divides by 4 to compute the average. +The resulting average total stats is cast to an Arrow float type under the hood for the +Lance table. We can display the results of this column in descending order of power. @@ -539,11 +518,11 @@ We can display the results of this column in descending order of power. Note that LanceDB's `where` only filters rows, but doesn't sort them by applying an `ORDER BY` -clause that you may be used to when working with SQL databases. In TypeScript, you can sort the -returned array in application code. +clause that you may be used to when working with SQL databases. -Python Only -You can also sort the results after converting them to a Polars DataFrame, as shown in the example above. +You can also sort the results after converting them to a Polars DataFrame. +In TypeScript/Rust, you can sort the +returned array in application code. ```python Python icon="python" # Sort Polars DataFrame by power in descending order @@ -558,9 +537,33 @@ print(r1.sort("power", descending=True).limit(5)) | Sir Lancelot | Knight of the Round Table | Arthur's most skilled knight, … | 3.5 | | Sir Gawain | Knight of the Round Table | A noble and honorable knight k… | 3.5 | -Merlin and Sir Galahad top the list of powerful characters when considering all their -abilities! Sir Lancelot and the Lady of the Lake follow closely behind. +Merlin and Sir Galahad are the most powerful characters when considering the average of +all their abilities! Sir Lancelot and the Lady of the Lake follow closely behind. + +## Delete data + +You can delete rows from a LanceDB table using the `delete` method with +a filtering expression. + +Say we want to remove Mordred, the traitor knight, from our table. + + + + {PyBasicDeleteRows} + + + + {TsBasicDeleteRows} + + + {RsBasicDeleteRows} + + + +This will delete the row(s) where the `role` value matches "Traitor Knight". +You can verify that the row has been deleted by running a search query again, +and confirming that Mordred no longer appears in the results. ## Drop column diff --git a/docs/tutorials/agents/multimodal-agent/index.mdx b/docs/tutorials/agents/multimodal-agent/index.mdx index dd90fff..5c951d7 100644 --- a/docs/tutorials/agents/multimodal-agent/index.mdx +++ b/docs/tutorials/agents/multimodal-agent/index.mdx @@ -4,56 +4,39 @@ sidebarTitle: "Multimodal agent" description: "Build an AI agent that understands both text and images to help users find recipes using LanceDB and PydanticAI" --- -In this tutorial, we show how to build a sophisticated AI agent that can understand both text and images to help users discover recipes that are relevant to them. The approach shown combines LanceDB's multimodal capabilities with [Pydantic AI](https://ai.pydantic.dev/) for the agentic workflow. -## What You'll Build - -### Colab Tutorial (Sample Data) -- **Interactive Learning**: Step-by-step notebook with sample recipes -- **Core Concepts**: Learn multimodal agent development -- **No Setup Required**: Run directly in your browser - -### Full Demo Application (Real Dataset) -- **Complete Streamlit App**: Full chat interface with image upload -- **Real Recipe Dataset**: Thousands of actual recipes with images -- **Production Features**: Error handling, logging, and deployment considerations -- **Multimodal Search**: Both text and image-based recipe discovery - -## Tutorial Overview - -### Colab Tutorial (Quick Start) -Interactive notebook covering: -1. **Data Preparation**: Work with sample recipe data -2. **Embedding Generation**: Create text and image embeddings -3. **LanceDB Setup**: Store multimodal data efficiently -4. **Agent Development**: Build a PydanticAI agent with custom tools -5. **Testing**: Try the agent with sample queries +[![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1pxavAGoXa-KSh_4HxNpvP2AjHPcIRpbq?usp=sharing) -### Full Demo (Complete Application) -Complete codebase including: -1. **Real Dataset**: Download and process thousands of recipes -2. **Streamlit Interface**: Full chat application with image upload -3. **Production Features**: Error handling, logging, and monitoring -4. **Deployment Ready**: Complete with all necessary files +Ever wanted to combine the power of text and images in a single AI agent? In this tutorial, +you'll build an agent that can understand both text and images to help users discover recipes that are relevant to them. The approach shown combines LanceDB's multimodal capabilities with [Pydantic AI](https://ai.pydantic.dev/) for the agentic workflow. -## Prerequisites +## Key Technologies -- Python 3.8+ -- Basic understanding of vector databases -- Familiarity with AI agents (helpful but not required) +- **LanceDB**: Multimodal vector database for efficient storage and retrieval +- **PydanticAI**: Modern AI agent framework with type safety +- **Sentence Transformers**: Text embeddings for semantic search +- **CLIP**: Vision-language model for image understanding +- **Streamlit**: Interactive web application framework -## Quick Start +## Tutorial Overview -### Option 1: Interactive Tutorial (Google Colab) +### Option 1: Notebook +The notebook shows how to work through the steps and prepare a small sample recipe dataset, generate both text and image +embeddings, store everything efficiently in LanceDB, and then build a PydanticAI agent with custom tools to +query it. You'll finish by testing the agent against a few example questions to see the full multimodal flow +end to end. - + +This simple tutorial provides a step-by-step workflow with a small demo dataset of 4 examples. +No local setup required - just click and start learning about multimodal agents. [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/1pxavAGoXa-KSh_4HxNpvP2AjHPcIRpbq?usp=sharing) -This Colab notebook provides a step-by-step tutorial with sample data. No setup required - just click and start learning about multimodal agents. -**Perfect for learning!** - -### Option 2: Full Demo Application (Local Setup) +### Option 2: Demo Application (Local Setup) +The demo application is the full codebase: you'll download and process a real recipe dataset with thousands +of items, run a Streamlit chat interface that supports image upload, and follow a structure that includes +production-minded touches like error handling, logging, and monitoring. Everything you need to deploy is +included. #### Download the Complete Tutorial @@ -61,7 +44,12 @@ This Colab notebook provides a step-by-step tutorial with sample data. No setup Download the tutorial files from GitHub. -#### Setup Instructions +### Dataset Information +- **Source**: [Kaggle Recipe Dataset](https://www.kaggle.com/datasets/pes12017000148/food-ingredients-and-recipe-dataset-with-images) +- **Size**: Thousands of recipes with images +- **Format**: CSV file with recipe data and image references + +### Setup ```bash bash icon="code" @@ -83,54 +71,3 @@ uv run python import.py uv run streamlit run app.py ``` - -**Complete experience!** This gives you the full Streamlit chat interface with a real recipe dataset. Requires downloading the dataset from Kaggle but provides the complete production-ready application. - -### Dataset Information -- **Source**: [Kaggle Recipe Dataset](https://www.kaggle.com/datasets/pes12017000148/food-ingredients-and-recipe-dataset-with-images) -- **Size**: Thousands of recipes with images -- **Format**: CSV file with recipe data and image references - -## Code Files - -This tutorial includes complete, runnable code: - -- **`multimodal-recipe-agent.ipynb`** - Interactive Jupyter notebook tutorial -- **`agent.py`** - Complete PydanticAI agent implementation -- **`app.py`** - Streamlit chat interface -- **`import.py`** - Data import and processing script -- **`pyproject.toml`** - Modern Python project configuration -- **`uv.lock`** - Locked dependency versions for reproducible builds -- **`README.md`** - Complete project documentation - -## Folder Structure - -When you download the tutorial, organize your files like this: - -``` -multimodal-recipe-agent/ -├── multimodal-recipe-agent.ipynb # Interactive tutorial -├── agent.py # Core agent implementation -├── app.py # Streamlit chat interface -├── import.py # Data processing script -├── pyproject.toml # Project configuration -├── uv.lock # Dependency lock file -├── README.md # Project documentation -└── data/ # Generated data (created after import) - ├── recipes.csv # Recipe dataset - ├── images/ # Recipe images - └── recipes.lance # LanceDB database -``` - -## Key Technologies - -- **LanceDB**: Multimodal vector database for efficient storage and retrieval -- **PydanticAI**: Modern AI agent framework with type safety -- **Sentence Transformers**: Text embeddings for semantic search -- **CLIP**: Vision-language model for image understanding -- **Streamlit**: Interactive web application framework - -Ready to build your first multimodal AI agent? Try out these ideas in your domain! - - - diff --git a/tests/py/test_basic_usage.py b/tests/py/test_basic_usage.py index 4512d5c..44c6f5d 100644 --- a/tests/py/test_basic_usage.py +++ b/tests/py/test_basic_usage.py @@ -97,7 +97,8 @@ def test_basic_usage(db_path_factory): # --8<-- [start:basic_vector_search] query_vector = [0.03, 0.85, 0.61, 0.90] - table.search(query_vector).limit(5).to_polars() + result = table.search(query_vector).limit(5).to_polars() + print(result) # --8<-- [end:basic_vector_search] # --8<-- [start:basic_add_columns] diff --git a/tests/py/test_quickstart.py b/tests/py/test_quickstart.py index e2c5802..0e952bb 100644 --- a/tests/py/test_quickstart.py +++ b/tests/py/test_quickstart.py @@ -31,12 +31,14 @@ def test_quickstart(db_path_factory): # Ensure you run `pip install polars` beforehand result = table.search(query_vector).limit(2).to_polars() + print(result) # --8<-- [end:quickstart_vector_search_1] assert result.head(1)["text"][0] == "knight" # --8<-- [start:quickstart_output_pandas] # Ensure you run `pip install pandas` beforehand result = table.search(query_vector).limit(2).to_pandas() + print(result) # --8<-- [end:quickstart_output_pandas] assert result.iloc[0]["text"] == "knight" @@ -66,4 +68,3 @@ def test_quickstart(db_path_factory): - diff --git a/tests/rs/basic_usage.rs b/tests/rs/basic_usage.rs index a7ce33c..04df251 100644 --- a/tests/rs/basic_usage.rs +++ b/tests/rs/basic_usage.rs @@ -222,7 +222,7 @@ async fn main() { // --8<-- [start:basic_vector_search] let query_vector = [0.03, 0.85, 0.61, 0.90]; - let _ = table + let result = table .query() .nearest_to(&query_vector) .unwrap() @@ -233,6 +233,7 @@ async fn main() { .try_collect::>() .await .unwrap(); + println!("{result:?}"); // --8<-- [end:basic_vector_search] // --8<-- [start:basic_add_columns] diff --git a/tests/rs/quickstart.rs b/tests/rs/quickstart.rs index 2edb16e..3bbdf5a 100644 --- a/tests/rs/quickstart.rs +++ b/tests/rs/quickstart.rs @@ -124,6 +124,7 @@ async fn main() { .into_polars() .await .unwrap(); + println!("{result:?}"); // --8<-- [end:quickstart_vector_search_1] let text_col = result.column("text").unwrap().str().unwrap(); assert_eq!(text_col.get(0).unwrap(), "knight"); @@ -141,6 +142,7 @@ async fn main() { .into_polars() .await .unwrap(); + println!("{result:?}"); let text_col = result.column("text").unwrap().str().unwrap(); let top_two = vec![ text_col.get(0).unwrap().to_string(), @@ -192,6 +194,7 @@ async fn main() { .into_polars() .await .unwrap(); + println!("{result:?}"); let text_col = result.column("text").unwrap().str().unwrap(); let top_two = vec![ text_col.get(0).unwrap().to_string(), diff --git a/tests/ts/basic_usage.test.ts b/tests/ts/basic_usage.test.ts index 6e98716..f00e69b 100644 --- a/tests/ts/basic_usage.test.ts +++ b/tests/ts/basic_usage.test.ts @@ -85,7 +85,8 @@ test("basic usage examples (async)", async () => { // --8<-- [start:basic_vector_search] const queryVector = [0.03, 0.85, 0.61, 0.9]; - await table.search(queryVector).limit(5).toArray(); + const result = await table.search(queryVector).limit(5).toArray(); + console.log(result); // --8<-- [end:basic_vector_search] // --8<-- [start:basic_add_columns] diff --git a/tests/ts/quickstart.test.ts b/tests/ts/quickstart.test.ts index 65349de..655ac8c 100644 --- a/tests/ts/quickstart.test.ts +++ b/tests/ts/quickstart.test.ts @@ -30,11 +30,13 @@ test("quickstart example (async)", async () => { let queryVector = [0.8, 0.3, 0.8]; let result = await table.search(queryVector).limit(2).toArray(); + console.table(result); // --8<-- [end:quickstart_vector_search_1] expect(result[0].text).toBe("knight"); // --8<-- [start:quickstart_output_array] result = await table.search(queryVector).limit(2).toArray(); + console.table(result); // --8<-- [end:quickstart_output_array] expect(result[0].text).toBe("knight"); @@ -58,7 +60,7 @@ test("quickstart example (async)", async () => { queryVector = [0.7, 0.3, 0.5]; const results = await table.search(queryVector).limit(2).toArray(); - console.log(results); + console.table(results); // --8<-- [end:quickstart_vector_search_2] expect(results[0].text).toBe("mage"); });