Advanced Operations
This guide covers advanced features available in ekoDB client libraries, including search operations, chat functionality, and real-time data handling.
For complete, runnable examples, visit the ekoDB Examples Repository. It contains 93 examples (56 client library + 37 direct API examples).
Options Structs
Cleaner method signatures using builder pattern for operation options (Rust, TypeScript, Kotlin).
Overview
Instead of long parameter lists, use options structs for cleaner, more maintainable code:
Before (v0.7.x):
await client.insert("users", record, "1h", true, "tx_123", false);
// What do these parameters mean? 🤔
After (v0.8.0):
await client.insert("users", record, {
ttl: "1h",
bypassRipple: true,
transactionId: "tx_123",
bypassCache: false
});
// Much clearer! ✨
Insert with Options
- 🦀 Rust
- 📘 TypeScript
- 🟣 Kotlin
- 🐍 Python
- 🔷 Go
use ekodb_client::{Client, Record, InsertOptions};
let mut record = Record::new();
record.insert("name", "Alice");
record.insert("email", "alice@example.com");
// Use builder pattern for options
let options = InsertOptions::new()
.ttl("1h")
.bypass_ripple(true)
.transaction_id("tx_123")
.bypass_cache(false);
let result = client.insert("users", record, Some(options)).await?;
import { EkoDBClient, InsertOptions } from "@ekodb/ekodb-client";
const record = {
name: "Alice",
email: "alice@example.com"
};
// Use options object
const options: InsertOptions = {
ttl: "1h",
bypassRipple: true,
transactionId: "tx_123",
bypassCache: false
};
const result = await client.insert("users", record, options);
import io.ekodb.client.EkoDBClient
import io.ekodb.client.types.Record
import io.ekodb.client.InsertOptions
val record = Record.new()
.insert("name", "Alice")
.insert("email", "alice@example.com")
// Use options object
val options = InsertOptions(
ttl = "1h",
bypassRipple = true,
transactionId = "tx_123",
bypassCache = false
)
val result = client.insert("users", record, options)
# Python uses optional keyword arguments (Pythonic approach)
record = {
"name": "Alice",
"email": "alice@example.com"
}
result = await client.insert(
"users",
record,
ttl="1h",
bypass_ripple=True,
transaction_id="tx_123",
bypass_cache=False
)
// Go uses variadic options (idiomatic approach)
record := map[string]interface{}{
"name": "Alice",
"email": "alice@example.com",
}
result, err := client.Insert("users", record, InsertOptions{
TTL: stringPtr("1h"),
BypassRipple: boolPtr(true),
TransactionID: stringPtr("tx_123"),
BypassCache: boolPtr(false),
})
Update with Options
- 🦀 Rust
- 📘 TypeScript
- 🟣 Kotlin
- 🐍 Python
- 🔷 Go
use ekodb_client::UpdateOptions;
let options = UpdateOptions::new()
.bypass_ripple(true)
.transaction_id("tx_456");
let updated = client.update("users", "user-123", updates, Some(options)).await?;
const options: UpdateOptions = {
bypassRipple: true,
transactionId: "tx_456"
};
const updated = await client.update("users", "user-123", updates, options);
val options = UpdateOptions(
bypassRipple = true,
transactionId = "tx_456"
)
val updated = client.update("users", "user-123", updates, options)
# Keyword arguments
updated = await client.update(
"users",
"user-123",
updates,
bypass_ripple=True,
transaction_id="tx_456"
)
updated, err := client.Update("users", "user-123", updates, UpdateOptions{
BypassRipple: boolPtr(true),
TransactionID: stringPtr("tx_456"),
})
Available Options Structs
| Struct | Available Fields | Languages |
|---|---|---|
| InsertOptions | ttl, bypass_ripple, transaction_id, bypass_cache | All |
| UpdateOptions | bypass_ripple, transaction_id, bypass_cache | All |
| UpsertOptions | bypass_ripple, transaction_id | All |
| DeleteOptions | bypass_ripple, transaction_id | All |
| FindOptions | bypass_cache, transaction_id | All |
- Rust/TypeScript/Kotlin: Dedicated options structs with builder pattern
- Python: Optional keyword arguments (Pythonic)
- Go: Variadic options with pointers (idiomatic)
All approaches provide the same functionality with language-appropriate ergonomics.
Search Operations
ekoDB provides powerful search capabilities including full-text search, fuzzy search, and vector search.
Full-Text Search
Search across all fields in your documents:
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
use ekodb_client::{Client, SearchQuery};
let search = SearchQuery::builder()
.query("database")
.min_score(0.1)
.limit(10)
.build();
let results = client.search("articles", search).await?;
for result in results.results {
println!("Score: {:.4} - {:?}", result.score, result.record);
}
from ekodb_client import Client, SearchQuery
search = SearchQuery(
query="database",
min_score=0.1,
limit=10
)
results = client.search("articles", search)
for result in results.results:
print(f"Score: {result.score:.4f} - {result.record}")
import { EkoDBClient, SearchQuery } from "@ekodb/ekodb-client";
const search: SearchQuery = {
query: "database",
minScore: 0.1,
limit: 10
};
const results = await client.search("articles", search);
for (const result of results.results) {
console.log(`Score: ${result.score.toFixed(4)} - ${JSON.stringify(result.record)}`);
}
const results = await client.search("articles", {
query: "database",
minScore: 0.1,
limit: 10
});
for (const result of results.results) {
console.log(`Score: ${result.score.toFixed(4)} - ${JSON.stringify(result.record)}`);
}
import io.ekodb.client.EkoDBClient
val results = client.search("articles") {
query = "database"
minScore = 0.1
limit = 10
}
results.results.forEach { result ->
println("Score: ${"%.4f".format(result.score)} - ${result.record}")
}
import ekodb "github.com/ekoDB/ekodb-client-go"
searchQuery := ekodb.SearchQuery{
Query: "database",
MinScore: 0.1,
Limit: 10,
}
results, err := client.Search("articles", searchQuery)
if err != nil {
log.Fatal(err)
}
for _, result := range results.Results {
fmt.Printf("Score: %.4f - %+v\n", result.Score, result.Record)
}
Field-Weighted Search
Search with custom field weights to prioritize certain fields:
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
use std::collections::HashMap;
let mut weights = HashMap::new();
weights.insert("title".to_string(), 2.0);
weights.insert("description".to_string(), 1.0);
let search = SearchQuery::builder()
.query("rust database")
.fields(vec!["title".to_string(), "description".to_string()])
.weights(weights)
.limit(5)
.build();
let results = client.search("articles", search).await?;
search = SearchQuery(
query="rust database",
fields=["title", "description"],
weights={"title": 2.0, "description": 1.0},
limit=5
)
results = client.search("articles", search)
const search: SearchQuery = {
query: "rust database",
fields: ["title", "description"],
weights: { title: 2.0, description: 1.0 },
limit: 5
};
const results = await client.search("articles", search);
const results = await client.search("articles", {
query: "rust database",
fields: ["title", "description"],
weights: { title: 2.0, description: 1.0 },
limit: 5
});
val results = client.search("articles") {
query = "rust database"
fields = listOf("title", "description")
weights = mapOf("title" to 2.0, "description" to 1.0)
limit = 5
}
searchQuery := ekodb.SearchQuery{
Query: "rust database",
Fields: []string{"title", "description"},
Weights: map[string]float64{"title": 2.0, "description": 1.0},
Limit: 5,
}
results, err := client.Search("articles", searchQuery)
Fuzzy Search
Enable typo tolerance with fuzzy matching:
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
let search = SearchQuery::builder()
.query("databse") // Typo: "databse" instead of "database"
.fuzzy(true)
.fuzziness(2) // Allow up to 2 character differences
.limit(10)
.build();
let results = client.search("articles", search).await?;
search = SearchQuery(
query="databse", # Typo
fuzzy=True,
fuzziness=2,
limit=10
)
results = client.search("articles", search)
const search: SearchQuery = {
query: "databse", // Typo
fuzzy: true,
fuzziness: 2,
limit: 10
};
const results = await client.search("articles", search);
const results = await client.search("articles", {
query: "databse", // Typo
fuzzy: true,
fuzziness: 2,
limit: 10
});
val results = client.search("articles") {
query = "databse" // Typo
fuzzy = true
fuzziness = 2
limit = 10
}
searchQuery := ekodb.SearchQuery{
Query: "databse", // Typo
Fuzzy: true,
Fuzziness: 2,
Limit: 10,
}
results, err := client.Search("articles", searchQuery)
Vector Search
Perform semantic similarity search using embeddings:
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
// First, create a collection with vector index
let schema = Schema::builder()
.add_field("content", FieldType::String)
.add_field("embedding", FieldType::Vector(384)) // 384-dimensional vector
.build();
client.create_collection("documents", schema).await?;
// Insert document with embedding
let mut doc = Record::new();
doc.insert("content", "ekoDB is a high-performance database");
doc.insert("embedding", vec![0.1, 0.2, 0.3, /* ... 384 dimensions */]);
client.insert("documents", doc).await?;
// Search by vector similarity
let query_vector = vec![0.1, 0.2, 0.3, /* ... */];
let search = SearchQuery::builder()
.vector(query_vector)
.limit(10)
.build();
let results = client.search("documents", search).await?;
# Create collection with vector index
schema = Schema(
fields={
"content": FieldType.String(),
"embedding": FieldType.Vector(384)
}
)
client.create_collection("documents", schema)
# Insert with embedding
doc = {
"content": "ekoDB is a high-performance database",
"embedding": [0.1, 0.2, 0.3, ...] # 384 dimensions
}
client.insert("documents", doc)
# Vector search
query_vector = [0.1, 0.2, 0.3, ...]
search = SearchQuery(vector=query_vector, limit=10)
results = client.search("documents", search)
// Create collection with vector index
const schema = {
fields: {
content: { type: "String" },
embedding: { type: "Vector", dimensions: 384 }
}
};
await client.createCollection("documents", schema);
// Insert with embedding
await client.insert("documents", {
content: "ekoDB is a high-performance database",
embedding: [0.1, 0.2, 0.3, /* ... 384 dimensions */]
});
// Vector search
const queryVector = [0.1, 0.2, 0.3, /* ... */];
const results = await client.search("documents", {
vector: queryVector,
limit: 10
});
// Insert with embedding
await client.insert("documents", {
content: "ekoDB is a high-performance database",
embedding: [0.1, 0.2, 0.3, /* ... 384 dimensions */]
});
// Vector search
const queryVector = [0.1, 0.2, 0.3, /* ... */];
const results = await client.search("documents", {
vector: queryVector,
limit: 10
});
// Insert with embedding
val doc = Record.new()
.insert("content", "ekoDB is a high-performance database")
.insert("embedding", listOf(0.1, 0.2, 0.3, /* ... 384 dimensions */))
client.insert("documents", doc)
// Vector search
val queryVector = listOf(0.1, 0.2, 0.3, /* ... */)
val results = client.search("documents") {
vector = queryVector
limit = 10
}
// Create collection with vector index
schema := ekodb.NewSchemaBuilder().
AddField("content", ekodb.NewFieldTypeSchemaBuilder("String").Build()).
AddField("embedding", ekodb.NewFieldTypeSchemaBuilder("Vector").
Dimensions(384).
Build()).
Build()
client.CreateCollection("documents", schema)
// Insert with embedding
doc := ekodb.Record{
"content": "ekoDB is a high-performance database",
"embedding": []float64{0.1, 0.2, 0.3, /* ... 384 dimensions */},
}
client.Insert("documents", doc)
// Vector search
queryVector := []float64{0.1, 0.2, 0.3, /* ... */}
searchQuery := ekodb.SearchQuery{
Vector: queryVector,
Limit: 10,
}
results, err := client.Search("documents", searchQuery)
Hybrid Search
Combine text and vector search for best results:
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
let search = SearchQuery::builder()
.query("database performance") // Text query
.vector(query_vector) // Vector query
.limit(10)
.build();
let results = client.search("documents", search).await?;
search = SearchQuery(
query="database performance", # Text query
vector=query_vector, # Vector query
limit=10
)
results = client.search("documents", search)
const search: SearchQuery = {
query: "database performance", // Text query
vector: queryVector, // Vector query
limit: 10
};
const results = await client.search("documents", search);
const results = await client.search("documents", {
query: "database performance", // Text query
vector: queryVector, // Vector query
limit: 10
});
val results = client.search("documents") {
query = "database performance" // Text query
vector = queryVector // Vector query
limit = 10
}
searchQuery := ekodb.SearchQuery{
Query: "database performance", // Text query
Vector: queryVector, // Vector query
Limit: 10,
}
results, err := client.Search("documents", searchQuery)
Chat Operations
Build AI-powered chat applications with built-in context management and session handling.
Basic Chat
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
use ekodb_client::chat::{ChatClient, ChatMessage};
// Create chat client
let chat = ChatClient::new(client, "products");
// Send a message
let response = chat.send_message(
"What products do you have?",
"gpt-4"
).await?;
println!("AI: {}", response.content);
from ekodb_client import ChatClient
# Create chat client
chat = ChatClient(client, "products")
# Send a message
response = chat.send_message(
"What products do you have?",
model="gpt-4"
)
print(f"AI: {response.content}")
import { ChatClient } from "@ekodb/ekodb-client";
// Create chat client
const chat = new ChatClient(client, "products");
// Send a message
const response = await chat.sendMessage(
"What products do you have?",
"gpt-4"
);
console.log(`AI: ${response.content}`);
const { ChatClient } = require("@ekodb/ekodb-client");
// Create chat client
const chat = new ChatClient(client, "products");
// Send a message
const response = await chat.sendMessage(
"What products do you have?",
"gpt-4"
);
console.log(`AI: ${response.content}`);
import io.ekodb.client.ChatClient
// Create chat client
val chat = ChatClient(client, "products")
// Send a message
val response = chat.sendMessage(
"What products do you have?",
model = "gpt-4"
)
println("AI: ${response.content}")
import "github.com/ekoDB/ekodb-client-go"
// Create chat client
chat := ekodb.NewChatClient(client, "products")
// Send a message
response, err := chat.SendMessage(
"What products do you have?",
"gpt-4",
)
fmt.Printf("AI: %s\n", response.Content)
Chat Sessions
Manage conversation history with sessions:
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
// Create a new session
let session = chat.create_session("Customer Support Chat").await?;
// Send messages in the session
let response1 = chat.send_message_in_session(
&session.id,
"What's the price of ekoDB Pro?",
"gpt-4"
).await?;
let response2 = chat.send_message_in_session(
&session.id,
"What features does it include?",
"gpt-4"
).await?;
// Get session history
let messages = chat.get_session_messages(&session.id).await?;
println!("Total messages: {}", messages.len());
# Create a new session
session = chat.create_session("Customer Support Chat")
# Send messages in the session
response1 = chat.send_message_in_session(
session.id,
"What's the price of ekoDB Pro?",
model="gpt-4"
)
response2 = chat.send_message_in_session(
session.id,
"What features does it include?",
model="gpt-4"
)
# Get session history
messages = chat.get_session_messages(session.id)
print(f"Total messages: {len(messages)}")
// Create a new session
const session = await chat.createSession("Customer Support Chat");
// Send messages in the session
const response1 = await chat.sendMessageInSession(
session.id,
"What's the price of ekoDB Pro?",
"gpt-4"
);
const response2 = await chat.sendMessageInSession(
session.id,
"What features does it include?",
"gpt-4"
);
// Get session history
const messages = await chat.getSessionMessages(session.id);
console.log(`Total messages: ${messages.length}`);
// Create a new session
const session = await chat.createSession("Customer Support Chat");
// Send messages in the session
const response1 = await chat.sendMessageInSession(
session.id,
"What's the price of ekoDB Pro?",
"gpt-4"
);
const response2 = await chat.sendMessageInSession(
session.id,
"What features does it include?",
"gpt-4"
);
// Get session history
const messages = await chat.getSessionMessages(session.id);
console.log(`Total messages: ${messages.length}`);
// Create a new session
val session = chat.createSession("Customer Support Chat")
// Send messages in the session
val response1 = chat.sendMessageInSession(
session.id,
"What's the price of ekoDB Pro?",
model = "gpt-4"
)
val response2 = chat.sendMessageInSession(
session.id,
"What features does it include?",
model = "gpt-4"
)
// Get session history
val messages = chat.getSessionMessages(session.id)
println("Total messages: ${messages.size}")
// Create a new session
session, err := chat.CreateSession("Customer Support Chat")
// Send messages in the session
response1, err := chat.SendMessageInSession(
session.ID,
"What's the price of ekoDB Pro?",
"gpt-4",
)
response2, err := chat.SendMessageInSession(
session.ID,
"What features does it include?",
"gpt-4",
)
// Get session history
messages, err := chat.GetSessionMessages(session.ID)
fmt.Printf("Total messages: %d\n", len(messages))
ekoDB's chat system features intelligent query understanding that works with your natural data structure:
- Field Name Matching: Queries like "What is the price?" automatically find records with a
pricefield, even if it contains numeric data (not text-searchable) - Multi-Turn Context: Follow-up questions use conversation history to enhance search relevance
- No Denormalization Required: Works with structured data as you'd naturally model it
Example:
// Your data structure
{
"product": "ekoDB",
"description": "High-performance database",
"price": 99 // Numeric field
}
When a user asks "What is the price?", ekoDB:
- Checks the collection schema for fields matching "price"
- Finds records with that field name
- Provides the full record to the LLM
- LLM responds: "The price is $99"
See complete Chat Session examples in all languages:
- Rust:
client_chat_sessions.rs - Python:
client_chat_sessions.py - TypeScript:
client_chat_sessions.ts - Go:
client_chat_sessions.go - Kotlin:
ClientChatSessions.kt
Real-Time Operations
WebSocket Queries
Subscribe to real-time data changes:
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
use ekodb_client::WebSocketClient;
// Connect to WebSocket
let ws_url = "wss://your-subdomain.production.google.ekodb.net/ws";
let mut ws_client = client.websocket(ws_url).await?;
// Subscribe to collection changes
let results = ws_client.find_all("users").await?;
// Process real-time updates
for record in results {
println!("New/Updated record: {:?}", record);
}
ws_client.close().await?;
# Connect to WebSocket
ws_url = "wss://your-subdomain.production.google.ekodb.net/ws"
ws_client = client.websocket(ws_url)
# Subscribe to collection changes
results = ws_client.find_all("users")
# Process real-time updates
for record in results:
print(f"New/Updated record: {record}")
ws_client.close()
// Connect to WebSocket
const wsUrl = "wss://your-subdomain.production.google.ekodb.net/ws";
const wsClient = await client.websocket(wsUrl);
// Subscribe to collection changes
const results = await wsClient.findAll("users");
// Process real-time updates
for (const record of results) {
console.log("New/Updated record:", record);
}
await wsClient.close();
// Connect to WebSocket
const wsUrl = "wss://your-subdomain.production.google.ekodb.net/ws";
const wsClient = await client.websocket(wsUrl);
// Subscribe to collection changes
const results = await wsClient.findAll("users");
// Process real-time updates
for (const record of results) {
console.log("New/Updated record:", record);
}
await wsClient.close();
// Connect to WebSocket
val wsUrl = "wss://your-subdomain.production.google.ekodb.net/ws"
val wsClient = client.websocket(wsUrl)
// Subscribe to collection changes
val results = wsClient.findAll("users")
// Process real-time updates
results.forEach { record ->
println("New/Updated record: $record")
}
wsClient.close()
// Connect to WebSocket
wsURL := "wss://your-subdomain.production.google.ekodb.net/ws"
wsClient, err := client.WebSocket(wsURL)
// Subscribe to collection changes
results, err := wsClient.FindAll("users")
// Process real-time updates
for _, record := range results {
fmt.Printf("New/Updated record: %+v\n", record)
}
wsClient.Close()
Joins
ekoDB supports cross-collection joins to combine data from multiple collections in a single query.
Single Collection Join
Join users with their department data:
- 🦀 Rust
- 📘 TypeScript
- 🐍 Python
- 🔷 Go
use ekodb_client::{Client, QueryBuilder, JoinBuilder};
// Join users with departments
let join = JoinBuilder::single(
"departments", // Target collection
"department_id", // Local field (in users)
"id", // Foreign field (in departments)
"department" // Output field name
);
let query = QueryBuilder::new()
.join(join)
.limit(10)
.build();
let users = client.find("users", query).await?;
for user in users {
println!("User: {:?}, Department: {:?}", user["name"], user["department"]);
}
import { EkoDBClient, QueryBuilder, JoinBuilder } from "@ekodb/ekodb-client";
// Join users with departments
const join = JoinBuilder.single(
"departments", // Target collection
"department_id", // Local field (in users)
"id", // Foreign field (in departments)
"department" // Output field name
);
const query = new QueryBuilder()
.join(join)
.limit(10)
.build();
const users = await client.find("users", query);
users.forEach(user => {
console.log(`User: ${user.name}, Department: ${user.department[0]?.name}`);
});
from ekodb_client import Client, QueryBuilder, JoinBuilder
# Join users with departments
join = JoinBuilder.single(
"departments", # Target collection
"department_id", # Local field (in users)
"id", # Foreign field (in departments)
"department" # Output field name
)
query = QueryBuilder() \
.join(join) \
.limit(10) \
.build()
users = await client.find("users", query)
for user in users:
print(f"User: {user['name']}, Department: {user['department'][0]['name']}")
import ekodb "github.com/ekoDB/ekodb-client-go"
// Join users with departments
join := ekodb.JoinBuilder.Single(
"departments", // Target collection
"department_id", // Local field (in users)
"id", // Foreign field (in departments)
"department", // Output field name
)
query := ekodb.NewQueryBuilder().
Join(join).
Limit(10).
Build()
users, _ := client.Find("users", query)
for _, user := range users {
fmt.Printf("User: %v, Department: %v\n", user["name"], user["department"])
}
Join examples - Single and multi-collection joins with filtering:
- Rust:
client_joins.rs - Python:
client_joins.py - TypeScript:
client_joins.ts - JavaScript:
client_joins.js - Go:
client_joins.go - Kotlin:
ClientJoins.kt
TTL (Time-To-Live)
Set automatic expiration for documents:
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
- 📦 JavaScript
- 🟣 Kotlin
- 🔷 Go
// Insert with 1 hour TTL
let mut session = Record::new();
session.insert("user_id", "user-123");
session.insert("token", "abc123");
let result = client.insert_with_ttl(
"sessions",
session,
"1h" // Expires in 1 hour
).await?;
# Insert with 1 hour TTL
session = {
"user_id": "user-123",
"token": "abc123"
}
result = client.insert_with_ttl(
"sessions",
session,
ttl="1h" # Expires in 1 hour
)
// Insert with 1 hour TTL
const session = {
userId: "user-123",
token: "abc123"
};
const result = await client.insertWithTTL(
"sessions",
session,
"1h" // Expires in 1 hour
);
// Insert with 1 hour TTL
const session = {
userId: "user-123",
token: "abc123"
};
const result = await client.insertWithTTL(
"sessions",
session,
"1h" // Expires in 1 hour
);
// Insert with 1 hour TTL
val session = Record.new()
.insert("user_id", "user-123")
.insert("token", "abc123")
val result = client.insertWithTTL(
"sessions",
session,
ttl = "1h" // Expires in 1 hour
)
// Insert with 1 hour TTL
session := ekodb.Record{
"user_id": "user-123",
"token": "abc123",
}
result, err := client.Insert("sessions", session, "1h") // Expires in 1 hour
Document TTL examples - Insert with expiration, verify expiration works:
- Rust:
client_document_ttl.rs - Python:
client_document_ttl.py - TypeScript:
client_document_ttl.ts - JavaScript:
client_document_ttl.js - Go:
client_document_ttl.go - Kotlin:
ClientDocumentTtl.kt
WebSocket TTL examples - TTL with real-time connections:
- Rust:
client_websocket_ttl.rs - Python:
client_websocket_ttl.py - TypeScript:
client_websocket_ttl.ts - JavaScript:
client_websocket_ttl.js - Go:
client_websocket_ttl.go - Kotlin:
ClientWebsocketTtl.kt
Transactions
ekoDB supports ACID transactions with multiple isolation levels. Transactions ensure data consistency when performing multiple operations that must succeed or fail together.
Isolation Levels
| Level | Description |
|---|---|
READ_UNCOMMITTED | Can read uncommitted changes from other transactions |
READ_COMMITTED | Only reads committed data (default) |
REPEATABLE_READ | Same data on re-read within transaction |
SERIALIZABLE | Highest isolation, transactions appear sequential |
Basic Transaction
- 🔷 Go
- 🦀 Rust
- 🐍 Python
- 📘 TypeScript
// Start a transaction
txID, err := client.BeginTransaction("SERIALIZABLE")
if err != nil {
log.Fatal(err)
}
// Perform operations within transaction context
// (Pass txID in headers for transactional operations)
// Check transaction status
status, err := client.GetTransactionStatus(txID)
fmt.Printf("Status: %v\n", status)
// Commit when all operations succeed
err = client.CommitTransaction(txID)
if err != nil {
// Rollback on failure
client.RollbackTransaction(txID)
log.Fatal(err)
}
// Start a transaction
let tx_id = client.begin_transaction("SERIALIZABLE").await?;
// Perform operations within transaction context
// Commit when done
client.commit_transaction(&tx_id).await?;
// Or rollback on failure
// client.rollback_transaction(&tx_id).await?;
# Start a transaction
tx_id = client.begin_transaction("SERIALIZABLE")
# Perform operations within transaction context
try:
# ... your operations ...
client.commit_transaction(tx_id)
except Exception as e:
client.rollback_transaction(tx_id)
raise e
// Start a transaction
const txId = await client.beginTransaction("SERIALIZABLE");
try {
// Perform operations within transaction context
// Commit when done
await client.commitTransaction(txId);
} catch (error) {
// Rollback on failure
await client.rollbackTransaction(txId);
throw error;
}
- Use the lowest isolation level that meets your consistency requirements
- Keep transactions short to minimize lock contention
- Always handle rollback in error cases
- For detailed transaction patterns, see Transactions
RAG Helpers
The Go client includes convenience methods for RAG (Retrieval-Augmented Generation) workflows:
Generate Embeddings
Generate embedding vectors from text using ekoDB's native Functions:
// Generate embedding for text
embedding, err := client.Embed("Hello world", "text-embedding-3-small")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Generated %d dimensions\n", len(embedding))
Text Search
Perform full-text search with stemming and fuzzy matching:
// Search for documents by text
results, err := client.TextSearch("documents", "database performance", 10)
if err != nil {
log.Fatal(err)
}
for _, doc := range results {
fmt.Printf("Found: %v\n", doc["title"])
}
Hybrid Search
Combine semantic similarity (vector) with keyword matching (text):
// Generate embedding for query
embedding, _ := client.Embed("How to optimize queries?", "text-embedding-3-small")
// Perform hybrid search
results, err := client.HybridSearch("documents", "optimize queries", embedding, 5)
if err != nil {
log.Fatal(err)
}
Find All Records
Simple method to retrieve all records from a collection:
// Get all messages (up to limit)
allMessages, err := client.FindAll("messages", 1000)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Found %d messages\n", len(allMessages))
Functions
Functions are ekoDB's stored procedures system that runs on the server. They can be called from any client library or via REST API to execute complex business logic, queries, CRUD operations, AI workflows, and batch processing.
For comprehensive architecture details, operation types, and advanced patterns, see Functions Architecture.
Functions let you create, store, and execute complete business logic as composable operations. Define your data logic once in ekoDB, then call it like puzzle pieces from any client.
What You Can Do
- ✅ Complete business logic - Queries, CRUD, AI operations in one place
- ✅ Parameterize everything - Dynamic values via
{{param_name}} - ✅ Version control - Track function versions
- ✅ Compose like puzzles - Chain operations together
- ✅ Call from anywhere - REST API or any client library
Function Capabilities
- Query Operations: Find, filter, search, vector search, hybrid search
- CRUD Operations: Insert, update, delete (single and batch)
- Transformations: Group, project, count
- AI Operations: Chat completions, embeddings generation
- Conditional Logic: If/then/else, foreach loops
- External Integrations: HTTP requests to any REST API
Basic Example
Create a function to query active users:
POST /api/functions
Content-Type: application/json
{
"label": "get_active_users",
"name": "Get Active Users",
"description": "Returns active users with a limit",
"parameters": {
"limit": {
"default": 10,
"required": false
}
},
"functions": [
{
"type": "Query",
"collection": "users",
"filter": {
"type": "Condition",
"field": "status",
"operator": "Eq",
"value": "active"
},
"limit": "{{limit}}"
}
]
}
Call a Function
Execute via REST API:
POST /api/functions/get_active_users
Content-Type: application/json
{
"limit": 20
}
Managing Functions
# List all functions
GET /api/functions
# Get a specific function
GET /api/functions/get_active_users
# Update a function
PUT /api/functions/{id}
# Delete a function
DELETE /api/functions/get_active_users
Parameters
Make functions dynamic with parameters:
{
"parameters": {
"status": {
"default": "active",
"required": false,
"description": "Filter by status"
},
"min_amount": {
"required": true,
"description": "Minimum amount"
}
}
}
Reference parameters in your function definitions using {{param_name}}:
{
"type": "Query",
"collection": "orders",
"filter": {
"type": "Condition",
"field": "status",
"operator": "Eq",
"value": "{{status}}"
}
}
Available Operations
Functions support these operation types:
Query Operations
FindAll- Retrieve all recordsQuery- Advanced filtering, sorting, paginationVectorSearch- Semantic similarity searchHybridSearch- Combine text + vector searchTextSearch- Full-text searchFindById- Get specific record by IDFindOne- Find one by key/value
CRUD Operations
Insert- Insert single recordBatchInsert- Insert multiple recordsUpdate- Update with filterUpdateById- Update specific recordDelete- Delete with filterDeleteById- Delete specific recordBatchDelete- Delete multiple records
Transformations
Group- Group and aggregateProject- Select/exclude fieldsCount- Count records
AI Operations
Chat- AI chat completionsEmbed- Generate embeddings
Logic & Control
If- Conditional executionForEach- Loop over recordsCallFunction- Call another function
External Integrations
HttpRequest- Call external APIs (Stripe, SendGrid, etc.)
Example: AI-Powered Search
Combine embeddings with search:
{
"label": "smart_search",
"name": "Smart Product Search",
"parameters": {
"query": { "required": true }
},
"functions": [
{
"type": "Embed",
"input_field": "query",
"output_field": "query_embedding"
},
{
"type": "HybridSearch",
"collection": "products",
"query_text": "{{query}}",
"query_vector": "{{query_embedding}}",
"limit": 10
}
]
}
Example: Batch Processing
Process multiple records with AI:
{
"label": "enrich_articles",
"name": "AI Content Enrichment",
"functions": [
{
"type": "Query",
"collection": "articles",
"filter": {
"type": "Condition",
"field": "embedding",
"operator": "Eq",
"value": null
},
"limit": 100
},
{
"type": "ForEach",
"functions": [
{
"type": "Embed",
"input_field": "content",
"output_field": "embedding"
},
{
"type": "UpdateById",
"collection": "articles",
"record_id": "{{id}}",
"updates": {
"embedding": "{{embedding}}"
}
}
]
}
]
}
Best Practices
- Keep it simple - Start with single operations, build up
- Use parameters - Make functions reusable with dynamic values
- Filter early - Reduce data before expensive operations
- Add descriptions - Document what each function does
- Tag for organization - Use tags like
analytics,users,ai - Version your functions - Track changes with version field
- Test thoroughly - Validate with edge cases
Storage
Functions are stored in a dedicated collection: functions_{db_name} (configurable)
Complete Documentation
For complete details including:
- All operation types and parameters
- Advanced parameter resolution
- Conditional logic patterns
- External API integration examples
- Error handling
See complete Function examples in all languages:
- Rust:
client_function_composition.rs - Python:
client_function_composition.py - TypeScript:
client_function_composition.ts - JavaScript:
client_function_composition.js - Go:
client_functions.go - Kotlin:
ClientFunctionComposition.kt
Next Steps
- GitHub Examples - 129 (client library & direct HTTP examples)
- API Reference - Direct HTTP API documentation
Need Help?
- 🐛 Issues: GitHub Issues
- 💬 Support: app.ekodb.io/support
- 📧 Email: support@ekodb.io