linkbay-search 0.1.0
Advanced product search and filtering library for LinkBay Platform
License: MIT
Requires Python: >=3.8
Keywords: search, fastapi, postgresql, semantic-search, vector-search, pgvector, ecommerce, product-search

# LinkBay Search

🔍 Lightweight, reusable search utilities for PostgreSQL with full-text and vector search support.

## Features

- **Full-Text Search**: PostgreSQL tsvector-based search
- **Semantic Search**: Vector similarity using pgvector (optional)
- **Generic & Reusable**: Works with any table structure
- **Minimal Dependencies**: Core library has only SQLAlchemy and Pydantic
- **Multi-language**: Supports different text search configurations

## Installation

```bash
# Core library (full-text search only)
pip install linkbay-search

# With PostgreSQL async support
pip install linkbay-search[postgres]

# With vector search support
pip install linkbay-search[vector]

# Everything
pip install linkbay-search[all]
```

## Quick Start

### 1. Setup Your Table

Create a `search_vector` column in your table:

```sql
-- Add tsvector column
ALTER TABLE products ADD COLUMN search_vector tsvector;

-- Create trigger to auto-update
CREATE OR REPLACE FUNCTION update_search_vector() RETURNS trigger AS $$
BEGIN
    NEW.search_vector := 
        setweight(to_tsvector('italian', COALESCE(NEW.name, '')), 'A') ||
        setweight(to_tsvector('italian', COALESCE(NEW.description, '')), 'B');
    RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER products_search_update 
    BEFORE INSERT OR UPDATE ON products
    FOR EACH ROW EXECUTE FUNCTION update_search_vector();

-- Create GIN index
CREATE INDEX products_search_idx ON products USING GIN(search_vector);
```

### 2. Use Full-Text Search

```python
from linkbay_search import FullTextSearchService
from sqlalchemy.ext.asyncio import create_async_engine, AsyncSession

engine = create_async_engine("postgresql+asyncpg://user:pass@localhost/db")

async with AsyncSession(engine) as session:
    search = FullTextSearchService(session, table_name="products")
    
    results, total = await search.search(
        query="scarpe running",
        language="italian",
        limit=20
    )
    
    for product in results:
        print(f"{product['name']} - Score: {product['relevance_score']}")
```

### 3. Use Semantic Search (Optional)

First, create embeddings table:

```sql
CREATE EXTENSION vector;

CREATE TABLE product_embeddings (
    product_id INTEGER PRIMARY KEY,
    embedding vector(384),
    metadata JSONB,
    updated_at TIMESTAMP DEFAULT NOW()
);

CREATE INDEX ON product_embeddings USING ivfflat (embedding vector_cosine_ops);
```

Then use it:

```python
from linkbay_search import SemanticSearchService

# Provide your own embedding function
async def my_embedding_generator(text: str):
    # Use your preferred model (OpenAI, Sentence Transformers, etc.)
    from sentence_transformers import SentenceTransformer
    model = SentenceTransformer('paraphrase-multilingual-MiniLM-L12-v2')
    return model.encode(text).tolist()

async with AsyncSession(engine) as session:
    search = SemanticSearchService(
        session,
        table_name="products",
        embeddings_table="product_embeddings",
        embedding_generator=my_embedding_generator
    )
    
    results, total = await search.search(
        query="scarpe impermeabili per correre",
        limit=20
    )
```

## Configuration

```python
from linkbay_search import SearchConfig, set_config

config = SearchConfig(
    default_language="italian",
    fuzzy_threshold=0.7,
    max_results=100,
    enable_semantic_search=True,
    semantic_weight=0.5,
)

set_config(config)
```

## Core Services

### FullTextSearchService

Generic full-text search using PostgreSQL tsvector.

**Methods:**
- `search(query, filters, limit, offset)` - Perform search
- `autocomplete(partial_query, column, limit)` - Get suggestions
- `get_aggregations(query, group_by_columns)` - Get facets/counts

### SemanticSearchService

Vector similarity search using pgvector.

**Methods:**
- `search(query, filters, limit, offset)` - Semantic search
- `upsert_embedding(record_id, text, metadata)` - Create/update embedding
- `combine_scores(fulltext_results, semantic_results, weight)` - Merge results

## Why This Library?

✅ **Lightweight**: Minimal dependencies, no unnecessary features  
✅ **Flexible**: Works with YOUR table structure  
✅ **Reusable**: Generic services, not opinionated  
✅ **No Magic**: You control models, tables, and logic  
✅ **Optional Features**: Install only what you need  

## What It's NOT

❌ Not a complete search API (you build your own endpoints)  
❌ Not an ORM (use your own models)  
❌ Not opinionated about your schema  
❌ Not a database migration tool  

## Examples

### Custom Filters

```python
filters = {
    "category_id": 5,
    "price_min": 50.0,
    "in_stock": True,
}

results, total = await search.search(
    query="running shoes",
    filters=filters,
    limit=20
)
```

### Autocomplete

```python
suggestions = await search.autocomplete(
    partial_query="scar",
    column="name",
    limit=10
)
```

### Aggregations/Facets

```python
facets = await search.get_aggregations(
    query="shoes",
    group_by_columns=["category_id", "brand_id"]
)

# Returns: {"category_id": [{value: 1, count: 45}, ...], "brand_id": [...]}
```

## Requirements

- Python >= 3.8
- PostgreSQL >= 12
- (Optional) pgvector extension for semantic search

## License

MIT License

## Support

Issues: https://github.com/linkbay/linkbay-search/issues