<p align="center">
<img src="assets/weaviate_ex.svg" alt="WeaviateEx Logo" width="200" height="200">
</p>
# WeaviateEx
[](https://elixir-lang.org)
[](https://hex.pm/packages/weaviate_ex)
[](https://hexdocs.pm/weaviate_ex)
[](LICENSE)
A modern Elixir client for [Weaviate](https://weaviate.io) vector database (v2.0).
WeaviateEx provides a clean, idiomatic Elixir interface with:
- ✅ **158 Tests Passing** - Comprehensive test coverage with Mox-based mocking
- ✅ **TDD Architecture** - Built test-first with protocol-based design
- ✅ **Collections API** - Full schema management (8 functions)
- ✅ **Data Operations API** - Complete CRUD with UUID generation & vectors (7 functions)
- ✅ **Filter System** - Advanced filtering with 13 operators & GraphQL conversion (26 tests)
- ✅ **Objects API** - Full CRUD operations with vectors
- ✅ **Batch Operations** - Efficient bulk imports
- ✅ **GraphQL Queries** - Complex searches with near_text, hybrid, BM25
- ✅ **Mix Tasks** - Manage Weaviate with `mix weaviate.start/stop/status/logs`
- ✅ **Auto Health Checks** - Validates connection on startup with strict mode
- ✅ **Easy Setup** - One-command Docker installation
## Quick Start
### 1. Install Weaviate
Run the installation script to set up Weaviate with Docker:
```bash
./install.sh
```
This will:
- Install Docker (if needed)
- Create a `.env` file with configuration
- Start Weaviate in Docker
- Verify the connection
For detailed installation instructions, see [INSTALL.md](INSTALL.md).
### 2. Add to Your Project
Add `weaviate_ex` to your `mix.exs` dependencies:
```elixir
def deps do
[
{:weaviate_ex, "~> 2.0"}
]
end
```
Then run:
```bash
mix deps.get
```
### 3. Configure
The library automatically reads configuration from environment variables (loaded from `.env`):
```bash
# .env file (created by install.sh)
WEAVIATE_URL=http://localhost:8080
WEAVIATE_API_KEY= # Optional, for authenticated instances
```
Alternatively, configure in `config/config.exs`:
```elixir
config :weaviate_ex,
url: "http://localhost:8080",
api_key: nil, # Optional
strict: true # Default: true - fails fast if Weaviate is unreachable
```
**Strict Mode**: By default, WeaviateEx will raise an error on startup if it cannot connect to Weaviate. Set `strict: false` to allow the application to start anyway (useful for development when Weaviate might not always be running).
### 4. Test the Connection
The library automatically performs a health check on startup. You'll see:
```
[WeaviateEx] Successfully connected to Weaviate
URL: http://localhost:8080
Version: 1.28.1
```
If configuration is missing, you'll get friendly error messages:
```
╔════════════════════════════════════════════════════════════════╗
║ WeaviateEx Configuration Error ║
╠════════════════════════════════════════════════════════════════╣
║ Missing required configuration: WEAVIATE_URL ║
║ ║
║ Please set the Weaviate URL using one of these methods: ║
║ 1. Environment variable: export WEAVIATE_URL=http://localhost:8080
║ 2. Application configuration (config/config.exs) ║
║ 3. Runtime configuration (config/runtime.exs) ║
╚════════════════════════════════════════════════════════════════╝
```
## Usage
### Health Check
```elixir
# Check if Weaviate is accessible
{:ok, meta} = WeaviateEx.health_check()
# => %{"version" => "1.28.1", "modules" => %{}}
# Check readiness
{:ok, true} = WeaviateEx.ready?()
# Check liveness
{:ok, true} = WeaviateEx.alive?()
```
### Collections
```elixir
# Create a collection
{:ok, collection} = WeaviateEx.Collections.create("Article", %{
description: "News articles",
properties: [
%{name: "title", dataType: ["text"]},
%{name: "content", dataType: ["text"]},
%{name: "publishedAt", dataType: ["date"]}
],
vectorizer: "text2vec-openai" # or "none" for custom vectors
})
# List all collections
{:ok, schema} = WeaviateEx.Collections.list()
# Get a specific collection
{:ok, collection} = WeaviateEx.Collections.get("Article")
# Add a property to existing collection
{:ok, property} = WeaviateEx.Collections.add_property("Article", %{
name: "author",
dataType: ["text"]
})
# Delete a collection
{:ok, _} = WeaviateEx.Collections.delete("Article")
```
### Objects
```elixir
# Create an object
{:ok, object} = WeaviateEx.Objects.create("Article", %{
properties: %{
title: "Hello Weaviate",
content: "This is a test article",
publishedAt: "2025-01-15T10:00:00Z"
},
vector: [0.1, 0.2, 0.3, ...] # Optional, if not using auto-vectorization
})
# Get an object
{:ok, object} = WeaviateEx.Objects.get("Article", uuid)
# List objects with pagination
{:ok, result} = WeaviateEx.Objects.list("Article", limit: 10, offset: 0)
# Update object (full replacement)
{:ok, updated} = WeaviateEx.Objects.update("Article", uuid, %{
properties: %{title: "Updated Title"}
})
# Patch object (partial update)
{:ok, patched} = WeaviateEx.Objects.patch("Article", uuid, %{
properties: %{title: "New Title"}
})
# Delete an object
{:ok, _} = WeaviateEx.Objects.delete("Article", uuid)
# Check if object exists
{:ok, true} = WeaviateEx.Objects.exists?("Article", uuid)
```
### Batch Operations
```elixir
# Batch create (efficient for large imports)
objects = [
%{class: "Article", properties: %{title: "Article 1"}},
%{class: "Article", properties: %{title: "Article 2"}},
%{class: "Article", properties: %{title: "Article 3"}}
]
{:ok, result} = WeaviateEx.Batch.create_objects(objects)
# Batch delete with criteria
{:ok, result} = WeaviateEx.Batch.delete_objects(%{
class: "Article",
where: %{
path: ["status"],
operator: "Equal",
valueText: "draft"
}
})
```
### GraphQL Queries & Vector Search
```elixir
alias WeaviateEx.Query
# Simple query
query = Query.get("Article")
|> Query.fields(["title", "content"])
|> Query.limit(10)
{:ok, results} = Query.execute(query)
# Semantic search with near_text
query = Query.get("Article")
|> Query.near_text("artificial intelligence", certainty: 0.7)
|> Query.fields(["title", "content"])
|> Query.additional(["certainty", "distance"])
|> Query.limit(5)
{:ok, results} = Query.execute(query)
# Vector search
query = Query.get("Article")
|> Query.near_vector([0.1, 0.2, 0.3, ...], certainty: 0.8)
|> Query.fields(["title"])
{:ok, results} = Query.execute(query)
# Hybrid search (combines keyword + vector)
query = Query.get("Article")
|> Query.hybrid("machine learning", alpha: 0.5)
|> Query.fields(["title"])
{:ok, results} = Query.execute(query)
# BM25 keyword search
query = Query.get("Article")
|> Query.bm25("elixir programming")
|> Query.fields(["title"])
{:ok, results} = Query.execute(query)
# With filters
query = Query.get("Article")
|> Query.where(%{
path: ["publishedAt"],
operator: "GreaterThan",
valueDate: "2025-01-01T00:00:00Z"
})
|> Query.fields(["title", "publishedAt"])
{:ok, results} = Query.execute(query)
```
## Testing
WeaviateEx uses **Mox** for clean, idiomatic testing with **158 tests** covering all functionality.
### Quick Test Commands
```bash
# Run all unit tests with mocks (default - no Weaviate needed)
mix test
# Run integration tests against real Weaviate
mix test --include integration
# Run specific test file
mix test test/weaviate_ex/api/collections_test.exs
# Run with coverage
mix test --cover
```
### Test Modes
**Mock Mode (Default)** - Fast, Isolated, No Dependencies:
- ✅ Uses Mox to mock HTTP/Protocol responses
- ✅ No Weaviate instance required
- ✅ Fast execution (~0.1 seconds)
- ✅ All 158 unit tests run with mocks
- ✅ Perfect for TDD and CI/CD
**Integration Mode** - Real Weaviate Testing:
- ✅ Tests against live Weaviate instance
- ✅ Validates actual API behavior
- ✅ Requires Weaviate running locally
- ✅ Run with `--include integration` flag
- ✅ 53 integration tests available
### Running Integration Tests
**Step 1: Start Weaviate**
```bash
# Start Weaviate with Docker
mix weaviate.start
# Or use Docker Compose directly
docker compose up -d
# Verify it's running
mix weaviate.status
```
**Step 2: Run Integration Tests**
```bash
# Run all integration tests
mix test --include integration
# Or use environment variable
WEAVIATE_INTEGRATION=true mix test
# Run specific integration test
mix test test/weaviate_ex/objects_test.exs:95 --include integration
```
**Step 3: Clean Up**
```bash
# Stop Weaviate
mix weaviate.stop
# Or remove all data
mix weaviate.stop --remove-volumes
```
### Test Coverage
**Current Coverage (Phase 1 - 50% Complete):**
- ✅ Collections API: 17 tests (8 functions)
- ✅ Filter System: 26 tests (13 operators/combinators)
- ✅ Data Operations: 17 tests (7 functions)
- ✅ Objects API: 15 tests
- ✅ Batch Operations: Basic tests
- ✅ Query System: Basic tests
- 🎯 **Total: 158 tests passing**
All new modules (Collections, Filter, Data) have **100% test coverage**.
## Authentication
For **authenticated Weaviate instances** (production/cloud):
### Option 1: Environment Variable
```bash
# Add to .env file (not committed to git)
WEAVIATE_API_KEY=your-api-key-here
# Or add to ~/.bash_secrets (sourced by ~/.bashrc)
export WEAVIATE_API_KEY=your-api-key-here
```
### Option 2: Application Config
```elixir
# config/runtime.exs (recommended for production)
config :weaviate_ex,
url: System.fetch_env!("WEAVIATE_URL"),
api_key: System.fetch_env!("WEAVIATE_API_KEY")
```
### Option 3: Config File (Development Only)
```elixir
# config/dev.exs (never commit production keys!)
config :weaviate_ex,
url: "http://localhost:8080",
api_key: "dev-key-here" # Only for local development
```
**Security Notes:**
- Never commit API keys to version control
- Use environment variables for production
- Add `.env` to `.gitignore` (already done)
- Consider using `~/.bash_secrets` for persistent local keys
- Use `System.fetch_env!/1` in production to fail fast on missing keys
## Development
```bash
# Get dependencies
mix deps.get
# Compile
mix compile
# Run tests (mocked - no Weaviate needed)
mix test
# Run integration tests (requires live Weaviate)
mix test --include integration
# Generate documentation
mix docs
# Run code analysis
mix credo
```
## Managing Weaviate
WeaviateEx provides convenient Mix tasks for managing your local Weaviate instance:
```bash
# Start Weaviate
mix weaviate.start
# Stop Weaviate
mix weaviate.stop
# Check status
mix weaviate.status
# View logs
mix weaviate.logs
# Follow logs in real-time
mix weaviate.logs --follow
# Stop and remove all data (WARNING: deletes everything)
mix weaviate.stop --remove-volumes
```
You can also use Docker Compose directly:
```bash
# Start Weaviate
docker compose up -d
# Stop Weaviate
docker compose down
# View logs
docker compose logs -f weaviate
# Check status
docker compose ps
```
## Environment Variables
| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `WEAVIATE_URL` | Yes | - | Full URL to Weaviate (e.g., `http://localhost:8080`) |
| `WEAVIATE_API_KEY` | No | - | API key for authentication |
## Documentation
- [Installation Guide](INSTALL.md) - Detailed setup instructions
- [API Documentation](https://hexdocs.pm/weaviate_ex) - Full API reference (coming soon)
- [Weaviate Docs](https://docs.weaviate.io) - Official Weaviate documentation
## Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
## License
MIT License. See [LICENSE](LICENSE) for details.
## Acknowledgments
- Built for [Weaviate](https://weaviate.io) vector database
- Inspired by official Python and TypeScript clients