https://github.com/activeloopai/vector_search_utility
https://github.com/activeloopai/vector_search_utility
Last synced: about 2 months ago
JSON representation
- Host: GitHub
- URL: https://github.com/activeloopai/vector_search_utility
- Owner: activeloopai
- License: apache-2.0
- Created: 2025-10-13T09:31:51.000Z (10 months ago)
- Default Branch: main
- Last Pushed: 2025-12-02T10:04:21.000Z (8 months ago)
- Last Synced: 2026-03-05T04:33:32.960Z (5 months ago)
- Language: Python
- Size: 75.2 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# Vector Search Utility - Parquet to DeepLake Converter
This utility converts Parquet files to DeepLake format with embedding support, automatic schema parsing, and data validation.
## Features
- **Automatic Schema Detection**: Parses Spark-style schema files to configure column types
- **Embedding Validation**: Automatically filters out rows with invalid/missing embeddings
- **Efficient Processing**: Batch processing with memory optimization
- **Flexible Input**: Support for both folder-based and file-list based ingestion
- **Index Creation**: Automatic index creation for vector search and inverted text search
## Installation
```bash
pip install deeplake pyarrow numpy tqdm
```
## Usage
### Option 1: Convert All Files from a Folder
Process all `.parquet` files in a directory:
```bash
python main.py \
--schema schema.txt \
--source /path/to/parquet/folder \
--output s3://bucket/dataset \
--org-id= --ds-name=
```
```bash
python main.py \
--schema schema.txt \
--source s3:///path \
--output s3://bucket/dataset \
--org-id= --ds-name=
```
### Option 2: Convert Specific Files
Process a specific list of parquet files (space-separated):
```bash
python main.py \
--schema schema.txt \
--files /path/file1.parquet /path/file2.parquet /path/file3.parquet \
--output s3://bucket/dataset \
--org-id= --ds-name=
```
**Important**: Files must be **space-separated**, not comma-separated.
### Full Command with All Options
```bash
python main.py \
--schema schema.txt \
--source /path/to/parquet/folder \
--output s3://bucket/dataset \
--embedding title_emb \
--embedding-size 768 \
--inverted-column title1 \
--unique-id-column video_id \
--org-id= --ds-name=
```
## Command-Line Arguments
| Argument | Required | Default | Description |
| -------------------- | ------------ | ----------- | ------------------------------------------------ |
| `--schema` | Yes | - | Path to schema configuration file (Spark format) |
| `--source` | Conditional* | - | Source folder containing parquet files |
| `--files` | Conditional* | - | List of parquet file paths (space-separated) |
| `--output` | Yes | - | Output DeepLake dataset path (local or S3) |
| `--embedding` | No | `title_emb` | Name of the embedding column |
| `--embedding-size` | No | `768` | Dimension size of embedding vectors |
| `--inverted-column` | No | `title1` | String column to index with inverted index |
| `--unique-id-column` | No | `video_id` | Column to use as unique identifier |
\* Either `--source` or `--files` must be provided (not both)
## Schema File Format
The schema file should follow Spark's `printSchema()` output format:
```
root
|-- ucid: string (nullable = true)
|-- video_id: string (nullable = true)
|-- id: long (nullable = true)
|-- created_at: timestamp (nullable = true)
|-- title1: string (nullable = true)
|-- views: long (nullable = true)
|-- made_for_kids: boolean (nullable = true)
|-- cluster_index: integer (nullable = true)
|-- v7_average: float (nullable = true)
|-- performance_index: float (nullable = true)
|-- title_emb: array (nullable = true)
| |-- element: float (containsNull = true)
|-- power_keywords: array (nullable = true)
| |-- element: struct (containsNull = true)
```
### Supported Column Types
- **String**: `string`
- **Integer**: `integer` (32-bit)
- **Long**: `long` (64-bit)
- **Float**: `float` (32-bit)
- **Double**: `double` (64-bit)
- **Boolean**: `boolean`
- **Timestamp**: `timestamp`
- **Array**: `array` (simple arrays like embeddings)
- **Struct/Dict**: `array` with nested `struct` (complex nested data)
## Data Validation
The converter automatically:
1. **Validates Embeddings**: Rows with `None`, empty, or invalid embeddings are filtered out
2. **Maintains Alignment**: All columns remain aligned after filtering
3. **Logs Filtering**: Prints number of rows filtered per batch
4. **Skips Empty Batches**: If all rows in a batch have invalid embeddings, the batch is skipped
## Processing Flow
1. **Schema Parsing**: Reads and parses the schema file
2. **Dataset Creation**: Creates DeepLake dataset with proper column types
3. **Data Ingestion**:
- Processes embedding column first
- Identifies invalid rows
- Filters all columns to maintain alignment
- Appends valid data in batches
4. **Index Creation**: Enables indexing mode and commits (separate step)
## Examples
### Example 1: Basic Usage with Folder
```bash
python main.py \
--schema schema.txt \
--source /data/parquet_files \
--output ./local_dataset
```
### Example 2: S3 Output with Custom Settings
```bash
python main.py \
--schema schema.txt \
--source /data/parquet_files \
--output s3://my-bucket/my-dataset \
--embedding video_embedding \
--embedding-size 1024 \
--inverted-column description \
--unique-id-column content_id
```
### Example 3: Specific Files Only
```bash
python main.py \
--schema schema.txt \
--files \
/data/batch1/part-00001.parquet \
/data/batch1/part-00002.parquet \
/data/batch2/part-00003.parquet \
--output s3://my-bucket/incremental-dataset
```
## Output
The converter will display:
1. **Schema Summary**: Column counts by type
2. **Processing Progress**: File-by-file progress with tqdm
3. **Filtering Stats**: Number of rows filtered due to invalid embeddings
4. **Batch Information**: Size of each batch being appended
5. **Index Creation**: Status of index building
Example output:
```
Parsed Schema Configuration:
String columns: 15
Integer columns: 2
Long columns: 5
Float columns: 3
Double columns: 1
Boolean columns: 1
Timestamp columns: 2
Array columns (simple): 1
Dict columns (complex): 1
Embedding column: title_emb
Embedding size: 768
Total columns: 31
Processing 3 parquet files
Converting files: 100%|████████████| 3/3 [01:23<00:00, 27.8s/it]
Indexing dataset...
Conversion completed successfully!
```
## Performance Optimization
- **Batch Size**: Default 200,000 rows per batch (configurable in `SchemaConfig`)
- **Memory Management**: Automatic garbage collection after each batch
- **Contiguous Arrays**: Ensures numpy arrays are memory-contiguous for optimal performance
- **Zero-Copy**: Uses PyArrow zero-copy conversion when possible
## Programmatic Usage
You can also use the converter programmatically:
```python
from parquet_to_deeplake import ParquetToDeepLakeConverter
from schema_parser import parse_schema_file
# Parse schema
config = parse_schema_file(
"schema.txt",
embedding_column="title_emb",
embedding_size=768,
inverted_column="title1",
unique_id_column="video_id"
)
# Create converter
converter = ParquetToDeepLakeConverter("s3://bucket/dataset", config)
# Option 1: Convert folder
converter.convert_folder("/path/to/folder")
# Option 2: Convert specific files
file_list = [
"/path/file1.parquet",
"/path/file2.parquet"
]
converter.convert_files(file_list)
```
## Troubleshooting
### Issue: "Either --source or --files must be provided"
**Solution**: You must specify either `--source` (for folder) or `--files` (for file list), but not both.
### Issue: Only processing one file when using --files
**Solution**: Ensure files are **space-separated**, not comma-separated:
```bash
# Wrong
--files file1.parquet,file2.parquet
# Correct
--files file1.parquet file2.parquet
```
### Issue: "Embedding column not found"
**Solution**: Ensure the `--embedding` argument matches the column name in your schema file.
### Issue: All rows filtered out
**Solution**: Check that your embedding column contains valid non-null vectors. Rows with None, empty, or invalid embeddings are automatically filtered.
## Notes
- The converter commits after **every batch** for incremental progress tracking
- Indexing is enabled in a **separate commit** after all data is ingested
- Invalid embeddings (None, empty, or wrong type) cause the entire row to be filtered
- All numeric arrays are converted to contiguous memory layout for performance
- The unique ID column is indexed with exact matching for fast lookups
- The inverted column supports full-text search capabilities
## Architecture
```
main.py
├─ ParquetToDeepLakeConverter
│ ├─ DeepLakeManager (dataset creation/schema)
│ └─ ParquetProcessor (data processing)
│ ├─ _convert_arrow_array (type conversion + filtering)
│ ├─ _transform_batch_data (batch transformation)
│ └─ process_file (file iteration)
└─ parse_schema_file (schema parsing)
```
## Data Flow Examples
### 1. Initial Ingest (Create New Dataset)
Creates a new DeepLake dataset from a folder of parquet files:
```bash
python3 main.py \
--schema=schema.txt \
--source=s3://path/to/short \
--output=s3://path/to/scripts/ds \
--org-id=scripts \
--ds-name=ds \
--num-workers=6
```
**When to use**: First-time data load into a new dataset.
### 2. Append (Add New Data)
Adds additional data to an existing dataset without modifying existing records:
```bash
python3 main.py \
--schema=schema.txt \
--output=s3://path/to/scripts/ds \
--org-id=scripts \
--ds-name=ds \
--files \
/path/to/new/part-00001.parquet \
/path/to/new/part-00002.parquet \
/path/to/new/part-00003.parquet
```
**When to use**: Adding new records to an existing dataset (no duplicates expected). Does not check for existing records.
### 3. Upsert (Update or Insert)
Updates existing records or inserts new ones based on the unique ID column:
#### Local Files
```bash
python3 main.py \
--schema=schema.txt \
--output=s3://path/to/scripts/ds \
--org-id=scripts \
--ds-name=ds \
--files \
/path/to/upsert_data/part-6fb66e36-5e67-42bc-89d4-7147c139a20d-1993-1-c000.snappy.parquet \
/path/to/upsert_data/part-6fb66e36-5e67-42bc-89d4-7147c139a20d-2003-1-c000.snappy.parquet \
/path/to/upsert_data/part-6fb66e36-5e67-42bc-89d4-7147c139a20d-2007-1-c000.snappy.parquet \
--upsert
```
#### S3 Files
```bash
python3 main.py \
--schema=schema.txt \
--output=s3://path/to/scripts/ds \
--org-id=scripts \
--ds-name=ds \
--files \
s3://data/short/part-00000-6fb66e36-5e67-42bc-89d4-7147c139a20d-1375-1-c000.snappy.parquet \
s3://data/short/part-00004-6fb66e36-5e67-42bc-89d4-7147c139a20d-1399-1-c000.snappy.parquet \
--upsert
```
**When to use**: Updating existing records or handling incremental updates with potential duplicates.