Documentation Index
Fetch the complete documentation index at: https://docs.cocobase.buzz/llms.txt
Use this file to discover all available pages before exploring further.
Advanced Operations
Unlock the full power of CocoBase with batch operations, aggregations, transactions, and advanced data processing techniques.
Batch Operations
Handle multiple documents efficiently with batch operations. Available across all SDKs.
Batch Create
Create multiple documents in a single request:
Flutter
JavaScript
Python
Go
final newBooks = [
{'title': 'Flutter Guide', 'author': 'John Doe', 'price': 29.99},
{'title': 'Dart Essentials', 'author': 'Jane Smith', 'price': 39.99},
{'title': 'Clean Code', 'author': 'Robert Martin', 'price': 49.99},
];
final result = await db.batchCreateDocuments<Book>(
"books",
newBooks,
);
print('Created ${result.created} books');
for (var book in result.documents) {
print('- ${book.id}: ${book.data.title}');
}
const newBooks = [
{ title: 'Flutter Guide', author: 'John Doe', price: 29.99 },
{ title: 'Dart Essentials', author: 'Jane Smith', price: 39.99 },
{ title: 'Clean Code', author: 'Robert Martin', price: 49.99 },
];
const documents = await db.createDocuments('books', newBooks);
console.log(`Created ${documents.length} books`);
documents.forEach(book => {
console.log(`- ${book.id}: ${book.data.title}`);
});
# Cloud Functions
def main():
new_books = [
{'title': 'Flutter Guide', 'author': 'John Doe', 'price': 29.99},
{'title': 'Dart Essentials', 'author': 'Jane Smith', 'price': 39.99},
{'title': 'Clean Code', 'author': 'Robert Martin', 'price': 49.99},
]
result = db.bulk_create_documents("books", new_books)
return {
"created": result["count"],
"documents": result["data"]
}
// Go SDK
newBooks := []map[string]interface{}{
{"title": "Flutter Guide", "author": "John Doe", "price": 29.99},
{"title": "Dart Essentials", "author": "Jane Smith", "price": 39.99},
{"title": "Clean Code", "author": "Robert Martin", "price": 49.99},
}
result, err := client.BulkCreateDocuments(ctx, "books", newBooks)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Created %d books\n", len(result))
Batch Update
Update multiple documents at once:
Flutter
JavaScript
Python
final updates = [
{'id': 'doc-1', 'price': 19.99},
{'id': 'doc-2', 'price': 24.99},
{'id': 'doc-3', 'price': 34.99},
];
final result = await db.batchUpdateDocuments("books", updates);
print('Updated: ${result.updated}');
print('Failed: ${result.failed}');
const updates = [
{ id: 'doc-1', price: 19.99 },
{ id: 'doc-2', price: 24.99 },
{ id: 'doc-3', price: 34.99 },
];
const result = await db.updateDocuments('books', updates);
console.log(`Updated: ${result.updated}`);
console.log(`Failed: ${result.failed ?? 0}`);
def main():
updates = [
{'id': 'doc-1', 'price': 19.99},
{'id': 'doc-2', 'price': 24.99},
{'id': 'doc-3', 'price': 34.99},
]
result = db.bulk_update_documents("books", updates)
return {
"updated": result["count"],
"failed": result.get("failed", 0)
}
Batch Delete
Delete multiple documents efficiently:
Flutter
JavaScript
Python
Go
final ids = ['doc-1', 'doc-2', 'doc-3'];
final result = await db.batchDeleteDocuments("books", ids);
print('Deleted: ${result.deleted}');
print('Failed: ${result.failed}');
const ids = ['doc-1', 'doc-2', 'doc-3'];
const result = await db.deleteDocuments('books', ids);
console.log(`Deleted: ${result.deleted}`);
console.log(`Failed: ${result.failed ?? 0}`);
def main():
ids = ['doc-1', 'doc-2', 'doc-3']
result = db.bulk_delete_documents("books", ids)
return {"deleted": result["count"]}
ids := []string{"doc-1", "doc-2", "doc-3"}
count, err := client.BulkDeleteDocuments(ctx, "books", ids)
if err != nil {
log.Fatal(err)
}
fmt.Printf("Deleted %d documents\n", count)
Best Practices for Batch Operations
Process Large Datasets in Chunks:
// Flutter example - process in batches of 100
Future<void> importBooks(List<Map<String, dynamic>> bookData) async {
const batchSize = 100;
for (int i = 0; i < bookData.length; i += batchSize) {
final batch = bookData.sublist(
i,
(i + batchSize).clamp(0, bookData.length),
);
try {
final result = await db.batchCreateDocuments<Book>("books", batch);
print('Created batch: ${result.created}');
} catch (e) {
print('Batch failed: $e');
}
}
}
Handle Errors Gracefully:
// JavaScript example - handle partial failures
async function updateMultipleBooks(updates) {
const result = await db.updateDocuments('books', updates);
console.log(`Success: ${result.updated}`);
if (result.failed > 0) {
console.error(`Failed to update: ${result.errorIds}`);
// Retry failed updates
}
}
Count Documents
Efficiently retrieve the number of documents in a collection matching a filter.
JavaScript
Flutter
Python
// Total count for a collection
const result = await db.countDocuments("users");
console.log(result.count);
// With filters
const active = await db.countDocuments("users", {
filters: { status: "active" }
});
console.log(`Active users: ${active.count}`);
final count = await db.countDocuments("users",
filters: {"status": "active"},
);
print('Total: ${count.count}');
# Cloud Functions
def main():
result = db.count_documents("users", filters={"status": "active"})
return {"count": result["count"]}
Aggregations
Calculate statistics across your data efficiently.
Sum
Calculate total of a field:
Flutter
JavaScript
Python
final result = await db.aggregateDocuments(
"orders",
field: 'total',
operation: 'sum',
);
print('Total revenue: \$${result.value}');
const result = await db.aggregateDocuments("sales", {
field: "amount",
operation: "sum",
query: { filters: { status: "completed" } }
});
console.log(`Total Sales: ${result.result}`);
def main():
# Sum all order totals
result = db.aggregate_documents(
"orders",
field="total",
operation="sum"
)
return {"total_revenue": result["value"]}
Average
Calculate average value:
Flutter
JavaScript
Python
final result = await db.aggregateDocuments(
"products",
field: 'price',
operation: 'avg',
);
print('Average price: \$${result.value}');
const result = await db.aggregateDocuments("products", {
field: "price",
operation: "avg"
});
console.log(`Average Price: ${result.result}`);
def main():
result = db.aggregate_documents(
"products",
field="price",
operation="avg"
)
return {"average_price": result["value"]}
Min/Max
Find minimum and maximum values:
JavaScript
Flutter
Python
// Minimum price
const min = await db.aggregateDocuments("books", {
field: "price",
operation: "min"
});
// Maximum price
const max = await db.aggregateDocuments("books", {
field: "price",
operation: "max"
});
console.log(`Price range: ${min.result} - ${max.result}`);
// Minimum price
final min = await db.aggregateDocuments(
"books",
field: 'price',
operation: 'min',
);
// Maximum price
final max = await db.aggregateDocuments(
"books",
field: 'price',
operation: 'max',
);
print('Price range: \$${min.value} - \$${max.value}');
def main():
min_price = db.aggregate_documents("books", field="price", operation="min")
max_price = db.aggregate_documents("books", field="price", operation="max")
return {"range": [min_price["value"], max_price["value"]]}
Aggregations with Filters
Calculate statistics on filtered data:
// Total revenue from completed orders in 2024
final result = await db.aggregateDocuments(
"orders",
field: 'total',
operation: 'sum',
filters: {
'status': 'completed',
'createdAt__gte': '2024-01-01',
},
);
print('2024 revenue: \$${result.value}');
Real-World Aggregation Examples
Statistics Dashboard:
Future<Map<String, dynamic>> getStorageStats() async {
final totalSize = await db.aggregateDocuments(
"files",
field: 'size',
operation: 'sum',
);
final avgSize = await db.aggregateDocuments(
"files",
field: 'size',
operation: 'avg',
);
return {
'totalSize': totalSize.value,
'averageSize': avgSize.value,
'totalDocuments': totalSize.count,
};
}
Price Analysis:
def main():
# Get price statistics
min_price = db.aggregate_documents(
"products",
field="price",
operation="min",
filters={"active": True}
)
max_price = db.aggregate_documents(
"products",
field="price",
operation="max",
filters={"active": True}
)
avg_price = db.aggregate_documents(
"products",
field="price",
operation="avg",
filters={"active": True}
)
return {
"minPrice": min_price["value"],
"maxPrice": max_price["value"],
"avgPrice": avg_price["value"],
"range": max_price["value"] - min_price["value"]
}
Group By
Group documents by field values and get counts.
Note: groupByField is available in Flutter and Python SDKs only. It is not available in the JavaScript SDK.
Basic Grouping
final result = await db.groupByField(
"orders",
field: 'status',
);
for (var group in result.groups) {
print('${group.key}: ${group.count} orders');
}
// Output:
// pending: 15 orders
// processing: 8 orders
// completed: 102 orders
// cancelled: 3 orders
def main():
result = db.group_by_field("orders", field="status")
groups = {}
for item in result["groups"]:
groups[item["key"]] = item["count"]
return {"groups": groups}
Group With Filters
final result = await db.groupByField(
"users",
field: 'country',
filters: {'active': true},
);
print('Active users by country:');
for (var group in result.groups) {
print('${group.key}: ${group.count}');
}
Grouping Patterns
Dashboard Statistics:
Future<Map<String, int>> getUserStatistics() async {
final byRole = await db.groupByField("users", field: 'role');
final stats = <String, int>{};
for (var group in byRole.groups) {
stats[group.key.toString()] = group.count;
}
return stats;
}
Activity Report:
def main():
by_type = db.group_by_field("activities", field="type")
by_status = db.group_by_field("activities", field="status")
return {
"byType": [{"type": g["key"], "count": g["count"]} for g in by_type["groups"]],
"byStatus": [{"status": g["key"], "count": g["count"]} for g in by_status["groups"]]
}
Transactions
Handle multiple operations atomically (availability depends on your backend).
Client-Side Transaction Pattern
class Transaction {
final Cocobase db;
final List<Function> operations = [];
Transaction(this.db);
void add(Function operation) {
operations.add(operation);
}
Future<void> commit() async {
for (var op in operations) {
try {
await op();
} catch (e) {
print('Transaction failed: $e');
rethrow;
}
}
}
}
// Use it
Future<void> transferFunds(
String fromAccount,
String toAccount,
double amount,
) async {
final tx = Transaction(db);
tx.add(() => db.updateDocument(
"accounts",
fromAccount,
{'balance': FieldValue.increment(-amount)},
));
tx.add(() => db.updateDocument(
"accounts",
toAccount,
{'balance': FieldValue.increment(amount)},
));
tx.add(() => db.createDocument(
"transactions",
{
'from': fromAccount,
'to': toAccount,
'amount': amount,
'timestamp': DateTime.now(),
},
));
await tx.commit();
}
1. Use Indexes
Create indexes on frequently queried fields:
final collection = Collection(
name: 'orders',
fields: {
'customerId': {'type': 'string', 'indexed': true}, // Index frequently queried
'createdAt': {'type': 'datetime', 'indexed': true},
'status': {'type': 'string', 'indexed': true},
'notes': {'type': 'string'}, // Don't index large text
},
);
await db.createCollection(collection);
Guidelines:
- Index fields used in
where clauses
- Index fields used for sorting
- Don’t over-index (impacts write performance)
- Avoid indexing large text fields
Always paginate large datasets:
// Good - Paginate large datasets
const pageSize = 50;
int page = 0;
Future<List<Document<Book>>> getNextPage() async {
final offset = page * pageSize;
final docs = await db.listDocuments<Book>(
"books",
filters: {
'limit': pageSize,
'offset': offset,
},
converter: Book.fromJson,
);
page++;
return docs;
}
Infinite Scroll Example:
class PaginatedBookList extends StatefulWidget {
@override
State<PaginatedBookList> createState() => _PaginatedBookListState();
}
class _PaginatedBookListState extends State<PaginatedBookList> {
final books = <Document<Book>>[];
bool hasMore = true;
bool isLoading = false;
Future<void> loadNextPage() async {
if (isLoading || !hasMore) return;
setState(() => isLoading = true);
try {
final newBooks = await getNextPage();
setState(() {
books.addAll(newBooks);
if (newBooks.length < 50) {
hasMore = false;
}
});
} catch (e) {
print('Error loading page: $e');
} finally {
setState(() => isLoading = false);
}
}
@override
Widget build(BuildContext context) {
return ListView.builder(
itemCount: books.length + (hasMore ? 1 : 0),
itemBuilder: (context, index) {
if (index == books.length) {
loadNextPage();
return const Center(child: CircularProgressIndicator());
}
return ListTile(title: Text(books[index].data.title));
},
);
}
}
3. Caching
Implement client-side caching to reduce API calls:
class DocumentCache {
final Map<String, Document> _cache = {};
static const cacheDuration = Duration(minutes: 5);
final Map<String, DateTime> _timestamps = {};
Document? get(String key) {
if (_isCacheValid(key)) {
return _cache[key];
}
_cache.remove(key);
_timestamps.remove(key);
return null;
}
void set(String key, Document value) {
_cache[key] = value;
_timestamps[key] = DateTime.now();
}
bool _isCacheValid(String key) {
final timestamp = _timestamps[key];
if (timestamp == null) return false;
return DateTime.now().difference(timestamp) < cacheDuration;
}
void clear() {
_cache.clear();
_timestamps.clear();
}
}
// Use cache
final cache = DocumentCache();
Future<Document<Book>> getBook(String id) async {
// Try cache first
final cached = cache.get('book_$id');
if (cached != null) {
return cached;
}
// Fetch from server
final doc = await db.getDocument<Book>("books", id);
cache.set('book_$id', doc);
return doc;
}
4. Select Specific Fields
Only fetch needed fields to reduce bandwidth:
// Select specific fields using query options
const docs = await db.listDocuments("books", {
select: ["id", "title", "price"],
limit: 100
});
// Good - select specific fields only
final docs = await db.listDocuments(
"books",
queryBuilder: QueryBuilder()
.select('id')
.select('title')
.select('price')
.limit(100),
);
5. Lazy Loading
Load data on demand:
class LazyLoadingList extends StatefulWidget {
@override
State<LazyLoadingList> createState() => _LazyLoadingListState();
}
class _LazyLoadingListState extends State<LazyLoadingList> {
final items = <Document<Item>>[];
bool hasMore = true;
ScrollController? _scrollController;
@override
void initState() {
super.initState();
_scrollController = ScrollController();
_scrollController!.addListener(_onScroll);
_loadMore();
}
void _onScroll() {
if (_scrollController!.position.pixels ==
_scrollController!.position.maxScrollExtent) {
_loadMore();
}
}
Future<void> _loadMore() async {
if (!hasMore) return;
final newItems = await db.listDocuments<Item>(
"items",
filters: {
'limit': 20,
'offset': items.length,
},
converter: Item.fromJson,
);
setState(() {
items.addAll(newItems);
if (newItems.length < 20) {
hasMore = false;
}
});
}
@override
Widget build(BuildContext context) {
return ListView.builder(
controller: _scrollController,
itemCount: items.length,
itemBuilder: (context, index) {
return ListTile(title: Text(items[index].data.name));
},
);
}
@override
void dispose() {
_scrollController?.dispose();
super.dispose();
}
}
Caching Strategies
Memory Cache
class MemoryCache {
constructor(ttl = 5 * 60 * 1000) { // 5 minutes default
this.cache = new Map();
this.ttl = ttl;
}
set(key, value) {
this.cache.set(key, {
value,
timestamp: Date.now()
});
}
get(key) {
const item = this.cache.get(key);
if (!item) return null;
const age = Date.now() - item.timestamp;
if (age > this.ttl) {
this.cache.delete(key);
return null;
}
return item.value;
}
clear() {
this.cache.clear();
}
}
// Use it
const cache = new MemoryCache();
async function getBook(id) {
// Try cache first
const cached = cache.get(`book_${id}`);
if (cached) return cached;
// Fetch from server
const book = await db.getDocument('books', id);
cache.set(`book_${id}`, book);
return book;
}
Local Storage Cache (Browser/Flutter)
import 'package:shared_preferences/shared_preferences.dart';
class PersistentCache {
static const _prefix = 'cache_';
static const _ttl = Duration(hours: 1);
Future<void> set(String key, String value) async {
final prefs = await SharedPreferences.getInstance();
await prefs.setString('$_prefix$key', value);
await prefs.setInt('${_prefix}${key}_timestamp',
DateTime.now().millisecondsSinceEpoch);
}
Future<String?> get(String key) async {
final prefs = await SharedPreferences.getInstance();
final value = prefs.getString('$_prefix$key');
final timestamp = prefs.getInt('${_prefix}${key}_timestamp');
if (value == null || timestamp == null) return null;
final age = DateTime.now().millisecondsSinceEpoch - timestamp;
if (age > _ttl.inMilliseconds) {
await prefs.remove('$_prefix$key');
await prefs.remove('${_prefix}${key}_timestamp');
return null;
}
return value;
}
}
Indexing Best Practices
When to Index
- Fields used frequently in
where clauses
- Fields used for sorting
- Fields used in relationships
- Fields with high cardinality (many unique values)
When NOT to Index
- Large text fields
- Fields that change frequently
- Low cardinality fields (few unique values like boolean)
- Fields rarely queried
Example Index Strategy
# Good indexing strategy
collection_schema = {
"name": "products",
"indexes": [
# Frequently queried
{"field": "status", "type": "btree"},
{"field": "category_id", "type": "btree"},
# Used for sorting
{"field": "created_at", "type": "btree"},
{"field": "price", "type": "btree"},
# Compound index for common query
{"fields": ["category_id", "status"], "type": "btree"}
]
}
Type Conversion and Type Safety
Flutter Type-Safe Models
class Book {
final String title;
final String author;
final double price;
Book({
required this.title,
required this.author,
required this.price,
});
factory Book.fromJson(Map<String, dynamic> json) {
return Book(
title: json['title'] as String,
author: json['author'] as String,
price: (json['price'] as num).toDouble(),
);
}
Map<String, dynamic> toJson() {
return {
'title': title,
'author': author,
'price': price,
};
}
}
// Register converter
CocobaseConverters.register<Book>(Book.fromJson);
// Use with full type safety
final books = await db.listDocuments<Book>("books");
print(books[0].data.title); // Fully typed!
JavaScript Type Safety with TypeScript
interface Book {
title: string;
author: string;
price: number;
isbn?: string;
}
// Type-safe operations
const books = await db.listDocuments<Book>('books');
books.forEach(doc => {
console.log(doc.data.title); // TypeScript knows the type
});
const book: Book = {
title: 'Clean Code',
author: 'Robert Martin',
price: 45.99
};
await db.createDocument<Book>('books', book);
Custom Data Models
Nested Objects
class Order {
final String id;
final List<Item> items;
final Address shippingAddress;
final double total;
Order({
required this.id,
required this.items,
required this.shippingAddress,
required this.total,
});
factory Order.fromJson(Map<String, dynamic> json) {
return Order(
id: json['id'] as String,
items: (json['items'] as List<dynamic>)
.map((item) => Item.fromJson(item as Map<String, dynamic>))
.toList(),
shippingAddress: Address.fromJson(
json['shippingAddress'] as Map<String, dynamic>
),
total: (json['total'] as num).toDouble(),
);
}
}
Polymorphic Types
abstract class Content {
String get title;
factory Content.fromJson(Map<String, dynamic> json) {
final type = json['contentType'] as String;
switch (type) {
case 'article':
return Article.fromJson(json);
case 'video':
return Video.fromJson(json);
case 'podcast':
return Podcast.fromJson(json);
default:
throw ArgumentError('Unknown content type: $type');
}
}
}
Monitoring and Debugging
Future<T> measureQueryTime<T>(
Future<T> Function() query,
) async {
final sw = Stopwatch()..start();
final result = await query();
sw.stop();
print('Query took ${sw.elapsedMilliseconds}ms');
return result;
}
// Use it
final books = await measureQueryTime(() =>
db.listDocuments<Book>("books")
);
Request Logging
// Enable debug logging in Dio
final dio = Dio();
dio.interceptors.add(LogInterceptor(
requestBody: true,
responseBody: true,
));
Deprecated Methods
[!WARNING]
The followings methods called directly on the db instance are deprecated. Use the db.auth namespace instead to ensure compatibility with future SDK versions and to access the latest security features.
db.login() → Use db.auth.login()
db.register() → Use db.auth.register()
db.logout() → Use db.auth.logout()
db.isAuthenticated() → Use db.auth.isAuthenticated()
db.getCurrentUser() → Use db.auth.getCurrentUser()
Next Steps