Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 53 additions & 0 deletions .github/workflows/paradedb.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
name: LocalStack ParadeDB Extension Tests

on:
push:
paths:
- paradedb/**
branches:
- main
pull_request:
paths:
- .github/workflows/paradedb.yml
- paradedb/**
workflow_dispatch:

env:
LOCALSTACK_DISABLE_EVENTS: "1"
LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }}

jobs:
integration-tests:
name: Run Integration Tests
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Setup LocalStack and extension
run: |
cd paradedb

docker pull localstack/localstack-pro &
docker pull paradedb/paradedb &
pip install localstack

make install
make lint
make dist
localstack extensions -v install file://$(ls ./dist/localstack_extension_paradedb-*.tar.gz)

DEBUG=1 localstack start -d
localstack wait

- name: Run integration tests
run: |
cd paradedb
make test

- name: Print logs
if: always()
run: |
localstack logs
localstack stop
22 changes: 22 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
description = "localstack-extensions";

inputs = {
nixpkgs.url = "nixpkgs/nixpkgs-unstable";
};

outputs = { self, nixpkgs }@inputs:
(
let
forAllSystems = nixpkgs.lib.genAttrs nixpkgs.lib.platforms.all;
in
{
devShell = forAllSystems (system:
let pkgs = import nixpkgs { inherit system; }; in
pkgs.mkShell {
buildInputs = with pkgs; [ uv python311 python311Packages.pip ty ];
}
);
}
);
}
5 changes: 5 additions & 0 deletions paradedb/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.venv
dist
build
**/*.egg-info
.eggs
48 changes: 48 additions & 0 deletions paradedb/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
VENV_BIN = python3 -m venv
VENV_DIR ?= .venv
VENV_ACTIVATE = $(VENV_DIR)/bin/activate
VENV_RUN = . $(VENV_ACTIVATE)
TEST_PATH ?= tests

usage: ## Shows usage for this Makefile
@cat Makefile | grep -E '^[a-zA-Z_-]+:.*?## .*$$' | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}'

venv: $(VENV_ACTIVATE)

$(VENV_ACTIVATE): pyproject.toml
test -d .venv || $(VENV_BIN) .venv
$(VENV_RUN); pip install --upgrade pip setuptools plux
$(VENV_RUN); pip install -e .[dev]
touch $(VENV_DIR)/bin/activate

clean:
rm -rf .venv/
rm -rf build/
rm -rf .eggs/
rm -rf *.egg-info/

install: venv ## Install dependencies
$(VENV_RUN); python -m plux entrypoints

dist: venv ## Create distribution
$(VENV_RUN); python -m build

publish: clean-dist venv dist ## Publish extension to pypi
$(VENV_RUN); pip install --upgrade twine; twine upload dist/*

entrypoints: venv ## Generate plugin entrypoints for Python package
$(VENV_RUN); python -m plux entrypoints

format: ## Run ruff to format the codebase
$(VENV_RUN); python -m ruff format .; make lint

lint: ## Run ruff to lint the codebase
$(VENV_RUN); python -m ruff check --output-format=full .

test: ## Run integration tests (requires LocalStack running with the Extension installed)
$(VENV_RUN); pytest $(PYTEST_ARGS) $(TEST_PATH)

clean-dist: clean
rm -rf dist/

.PHONY: clean clean-dist dist install publish usage venv format test
97 changes: 97 additions & 0 deletions paradedb/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
ParadeDB on LocalStack
======================

This repo contains a [LocalStack Extension](https://github.com/localstack/localstack-extensions) that facilitates developing [ParadeDB](https://www.paradedb.com)-based applications locally.

ParadeDB is an Elasticsearch alternative built on Postgres. It provides full-text search with BM25 scoring, hybrid search combining semantic and keyword search, and real-time analytics capabilities.

After installing the extension, a ParadeDB server instance will become available and can be accessed using standard PostgreSQL clients.

## Connection Details

Once the extension is running, you can connect to ParadeDB using any PostgreSQL client with the following default credentials:

- **Host**: `localhost` (or the Docker host if running in a container)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd use different values than postgres here. See: https://docs.paradedb.com/documentation/getting-started/install

- **Port**: `5432` (mapped from the container)
- **Database**: `mydatabase`
- **Username**: `myuser`
- **Password**: `mypassword`

Example connection using `psql`:
```bash
psql -h localhost -p 5432 -U myuser -d mydatabase
```

Example connection using Python:
```python
import psycopg2

conn = psycopg2.connect(
host="localhost",
port=5432,
database="mydatabase",
user="myuser",
password="mypassword"
)
```

## ParadeDB Features

ParadeDB includes the **pg_search** extension, for both search and
analytics workloads.

Example using pg_search:
```sql
-- Create a table with search index
CREATE TABLE products (
id SERIAL PRIMARY KEY,
name TEXT,
description TEXT
);

-- Create a BM25 search index
CALL paradedb.create_bm25(

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is super wrong too. We have agents skills: https://docs.paradedb.com/welcome/ai-agents

I'd recommend importing this to your Claude and trying again.

index_name => 'products_idx',
table_name => 'products',
key_field => 'id',
text_fields => paradedb.field('name') || paradedb.field('description')
);

-- Search with BM25 scoring
SELECT * FROM products.search('description:electronics');

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is wrong

```

## Configuration

The following environment variables can be passed to the LocalStack container to configure the extension:

* `PARADEDB_POSTGRES_USER`: PostgreSQL username (default: `myuser`)
* `PARADEDB_POSTGRES_PASSWORD`: PostgreSQL password (default: `mypassword`)
* `PARADEDB_POSTGRES_DB`: Default database name (default: `mydatabase`)

## Prerequisites

* Docker
* LocalStack Pro (free trial available)
* `localstack` CLI
* `make`

## Install from GitHub repository

This extension can be installed directly from this Github repo via:

```bash
localstack extensions install "git+https://github.com/localstack/localstack-extensions.git#egg=localstack-extension-paradedb&subdirectory=paradedb"
```

## Install local development version

Please refer to the docs [here](https://github.com/localstack/localstack-extensions?tab=readme-ov-file#start-localstack-with-the-extension) for instructions on how to start the extension in developer mode.

## Change Log

* `0.1.0`: Initial version of the extension

## License

The code in this repo is available under the Apache 2.0 license.
1 change: 1 addition & 0 deletions paradedb/localstack_paradedb/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
name = "localstack_paradedb"
66 changes: 66 additions & 0 deletions paradedb/localstack_paradedb/extension.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import os
import logging

from localstack_paradedb.utils.docker import DatabaseDockerContainerExtension

LOG = logging.getLogger(__name__)

# Environment variables for configuration
ENV_POSTGRES_USER = "PARADEDB_POSTGRES_USER"
ENV_POSTGRES_PASSWORD = "PARADEDB_POSTGRES_PASSWORD"
ENV_POSTGRES_DB = "PARADEDB_POSTGRES_DB"
ENV_POSTGRES_PORT = "PARADEDB_POSTGRES_PORT"

# Default values
DEFAULT_POSTGRES_USER = "myuser"
DEFAULT_POSTGRES_PASSWORD = "mypassword"
DEFAULT_POSTGRES_DB = "mydatabase"
DEFAULT_POSTGRES_PORT = 5432


class ParadeDbExtension(DatabaseDockerContainerExtension):
name = "paradedb"

# Name of the Docker image to spin up
DOCKER_IMAGE = "paradedb/paradedb"

def __init__(self):
# Get configuration from environment variables
postgres_user = os.environ.get(ENV_POSTGRES_USER, DEFAULT_POSTGRES_USER)
postgres_password = os.environ.get(ENV_POSTGRES_PASSWORD, DEFAULT_POSTGRES_PASSWORD)
postgres_db = os.environ.get(ENV_POSTGRES_DB, DEFAULT_POSTGRES_DB)
postgres_port = int(os.environ.get(ENV_POSTGRES_PORT, DEFAULT_POSTGRES_PORT))

# Environment variables to pass to the container
env_vars = {
"POSTGRES_USER": postgres_user,
"POSTGRES_PASSWORD": postgres_password,
"POSTGRES_DB": postgres_db,
}

super().__init__(
image_name=self.DOCKER_IMAGE,
container_ports=[postgres_port],
env_vars=env_vars,
)

# Store configuration for connection info
self.postgres_user = postgres_user
self.postgres_password = postgres_password
self.postgres_db = postgres_db
self.postgres_port = postgres_port

def get_connection_info(self) -> dict:
"""Return connection information for ParadeDB."""
info = super().get_connection_info()
info.update({
"database": self.postgres_db,
"user": self.postgres_user,
"password": self.postgres_password,
"port": self.postgres_port,
"connection_string": (
f"postgresql://{self.postgres_user}:{self.postgres_password}"
f"@{self.container_host}:{self.postgres_port}/{self.postgres_db}"
),
})
return info
Empty file.
Loading