Skip to main content

Data Sources

Whoosh-NG provides a flexible data source layer for indexing documents from SQL databases, REST APIs, GraphQL APIs, file-based formats, and custom data providers.

DataSource Protocol​

All data sources implement the DataSource protocol, which defines the interface for querying, schema discovery, and metadata retrieval.

from whoosh_modern.data_sources import DataSource

class DataSource(Protocol):
@property
def name(self) -> str: ...

def discover_schema(self) -> Schema: ...
def iter_documents(self) -> Iterator[Document]: ...
def stream_batches(self, batch_size: int = 1000) -> Iterator[list[dict[str, Any]]]: ...
def health_check(self) -> bool: ...

Capability Protocols​

ProtocolDescription
DataSourceBase protocol: name, schema, iteration, metadata
IncrementalDataSourceSupports iter_changes(since)
AsyncDataSourceAsync document streaming via aiter_documents()
RefreshableDataSourcerefresh() support
CountableDataSourcedocument_count()
MetadataDataSourcemetadata()
ObservableDataSourceObserver callbacks for document changes

SQLSource​

SQLSource connects to SQL databases and yields documents from query results with automatic connection pooling.

Basic Usage​

from whoosh_modern.data_sources.sql import SQLSource
import sqlite3

conn = sqlite3.connect("mydb.db")
source = SQLSource(
connection=conn,
query="SELECT * FROM products",
)

# Discover schema from result-set metadata
schema = source.discover_schema()

# Iterate documents
for doc in source.iter_documents():
print(doc["title"], doc["price"])

# Get metadata
meta = source.metadata()
# {"type": "sql", "query": "SELECT * FROM products", ...}

# Get document count
count = source.document_count()

Connection Pooling (SQLSource)​

Connection pooling is supported via pool_size for long-running processes:

from whoosh_modern.data_sources.sql import SQLSource

source = SQLSource(
connection="sqlite:///mydb.db", # URL or connection object
query="SELECT * FROM products",
pool_size=10, # Max connections in pool
)

GROUP BY Aggregation​

source = SQLSource(
connection=conn,
query="""
SELECT category, COUNT(*) as doc_count,
AVG(price) as avg_price
FROM products
GROUP BY category
""",
)

# Each aggregated row becomes a document
for doc in source.iter_documents():
print(doc["category"], doc["doc_count"], doc["avg_price"])

JOINs with Column Aliases​

SQLSource uses result-set metadata for schema discovery. Always use column aliases in JOINs.

source = SQLSource(
connection=conn,
query="""
SELECT
p.id AS product_id,
p.name AS product_name,
c.name AS category_name
FROM products p
JOIN categories c ON p.category_id = c.id
""",
)

Incremental Sync​

from datetime import datetime

source = SQLSource(
connection=conn,
query="SELECT * FROM articles",
incremental_field="updated_at",
id_field="id",
)

# Get documents changed since a timestamp
for doc in source.iter_changes(since=datetime(2025, 1, 1)):
print(doc["id"], doc["updated_at"])

SQLAlchemySource​

For SQLAlchemy users, use SQLAlchemySource which supports engine-based connection management:

from whoosh_modern.data_sources.sqlalchemy_ds import SQLAlchemySource
from sqlalchemy import create_engine

engine = create_engine("postgresql://user:pass@localhost/mydb")
source = SQLAlchemySource(
engine=engine,
query="SELECT * FROM articles",
incremental_field="updated_at",
id_field="id",
)

schema = source.discover_schema()

PeeweeSource​

For Peewee ORM users:

from whoosh_modern.data_sources.peewee_ds import PeeweeSource
from peewee import SqliteDatabase

db = SqliteDatabase("mydb.db")
source = PeeweeSource(
database=db,
model=MyArticleModel,
fields=["id", "title", "content"],
)

schema = source.discover_schema()

TortoiseSource​

For Tortoise ORM users (async):

from whoosh_modern.data_sources.tortoise_ds import TortoiseSource

source = TortoiseSource(
model="myapp.models.Article",
fields=["id", "title", "content"],
)

schema = source.discover_schema()
count = source.document_count()

RESTSource​

RESTSource fetches documents from REST APIs with pagination and authentication.

Basic Usage​

from whoosh_modern.data_sources.rest import RESTSource

source = RESTSource(
url="https://api.example.com/v2/products",
method="GET",
headers={"Authorization": "Bearer your_token"},
pagination="page",
page_size=50,
)

# Discover schema from first page
schema = source.discover_schema()

# Iterate all documents (handles pagination automatically)
for doc in source.iter_documents():
print(doc["name"], doc["price"])

# Get document count
count = source.document_count()

Pagination Strategies​

StrategyParametersBehavior
page?page=N&size=MFetches page N with M items per page
offset?offset=N&limit=MFetches M items starting at N
cursor?cursor=XYZ&size=MFollows next_cursor in response
# Page-based pagination
source = RESTSource(
url="https://api.example.com/articles",
pagination="page",
page_size=50,
)

# Offset-based pagination
source = RESTSource(
url="https://api.example.com/records",
pagination="offset",
page_size=100,
)

# Cursor-based pagination
source = RESTSource(
url="https://api.example.com/feed",
pagination="cursor",
page_size=100,
)

Authentication​

# Bearer token via headers
source = RESTSource(
url="https://api.example.com/data",
headers={"Authorization": "Bearer your_token"},
)

# API key via headers
source = RESTSource(
url="https://api.example.com/data",
headers={"X-API-Key": "your_api_key"},
)

# Basic auth via headers
import base64
creds = base64.b64encode(b"user:pass").decode()
source = RESTSource(
url="https://api.example.com/data",
headers={"Authorization": f"Basic {creds}"},
)

Document Path​

For nested API responses, use document_path to extract documents:

# API returns: {"data": {"results": [...]}}
source = RESTSource(
url="https://api.example.com/api/v2/products",
document_path="data.results",
pagination="page",
)

GraphQLSource​

GraphQLSource fetches documents from a GraphQL API endpoint:

from whoosh_modern.data_sources.graphql import GraphQLSource

source = GraphQLSource(
url="https://api.example.com/graphql",
query="""
query GetProducts($limit: Int!, $offset: Int!) {
products(limit: $limit, offset: $offset) {
id
name
price
description
}
}
""",
pagination="offset",
page_size=100,
headers={"Authorization": "Bearer your_token"},
)

schema = source.discover_schema()
for doc in source.iter_documents():
print(doc["id"], doc["name"])

File-Based Data Sources​

Whoosh-NG supports indexing from various file formats with optimized readers.

FastCSVSource​

High-performance CSV reader with configurable encoding and delimiter:

from whoosh_modern.data_sources.fast_csv import FastCSVSource

source = FastCSVSource(
file_path="data/products.csv",
id_field="id",
incremental_field="updated_at",
delimiter=",",
encoding="utf-8",
)

schema = source.discover_schema()
count = source.document_count()
for doc in source.iter_documents():
print(doc)

JSONSource​

Index from JSON files or JSON Lines (.jsonl) files:

from whoosh_modern.data_sources.json import JSONSource

# JSON array file
source = JSONSource(file_path="data/products.json")

# JSON Lines file (one JSON object per line)
source = JSONSource(
file_path="data/logs.jsonl",
format="jsonl",
)

schema = source.discover_schema()

ParquetSource​

Index from Parquet files using pyarrow or pandas backend:

from whoosh_modern.data_sources.parquet_ds import ParquetSource

source = ParquetSource(
file_path="data/large_dataset.parquet",
engine="pyarrow", # or "pandas"
batch_size=1000,
)

schema = source.discover_schema()

PandasSource​

Index directly from a pandas DataFrame:

from whoosh_modern.data_sources.pandas_ds import PandasSource
import pandas as pd

df = pd.read_csv("data/products.csv")
source = PandasSource(dataframe=df)

schema = source.discover_schema()

PolarsSource​

Index from a Polars DataFrame (faster, lazy evaluation):

from whoosh_modern.data_sources.polars_ds import PolarsSource
import polars as pl

df = pl.read_csv("data/products.csv")
source = PolarsSource(dataframe=df)

schema = source.discover_schema()

DataSourceConfig​

For programmatic configuration, use DataSourceConfig to define data source properties:

from whoosh_modern.data_sources.config import DataSourceConfig

config = DataSourceConfig(
type="sql",
connection=conn,
query="SELECT * FROM products",
id_field="id",
incremental_field="updated_at",
)

source = config.create()
schema = source.discover_schema()

Config File Support​

Data source configurations can be loaded from dictionaries:

from whoosh_modern.data_sources.config import DataSourceConfig

# From dict
config = DataSourceConfig.from_dict({
"type": "rest",
"url": "https://api.example.com/v2/products",
"pagination": "page",
"page_size": 50,
})
source = config.create()

Supported type values: sql, sqlalchemy, rest, csv, json, graphql, pydantic, pandas, polars, parquet, peewee, tortoise.

Available Data Sources​

ClassSource TypeDependencies
SQLSourceSQLite, PostgreSQL, MySQLsqlite3 (stdlib)
SQLAlchemySourceAny SQLAlchemy-supported DBsqlalchemy
RESTSourceREST APIsnone (stdlib urllib)
GraphQLSourceGraphQL APIsnone (stdlib urllib)
FastCSVSourceCSV filesnone
JSONSourceJSON/JSONL filesnone
ParquetSourceParquet filespyarrow or pandas
PandasSourcepandas DataFramespandas
PolarsSourcePolars DataFramespolars
PeeweeSourcePeewee ORMpeewee
TortoiseSourceTortoise ORMtortoise-orm
PydanticSourcePydantic modelspydantic