Advanced Features
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
Copy
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}');
}
Copy
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 result = await db.batchCreateDocuments('books', newBooks);
console.log(`Created ${result.created} books`);
result.documents.forEach(book => {
console.log(`- ${book.id}: ${book.data.title}`);
});
Copy
# 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"]
}
Copy
// 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
Copy
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}');
Copy
const updates = [
{ id: 'doc-1', price: 19.99 },
{ id: 'doc-2', price: 24.99 },
{ id: 'doc-3', price: 34.99 },
];
const result = await db.batchUpdateDocuments('books', updates);
console.log(`Updated: ${result.updated}`);
console.log(`Failed: ${result.failed}`);
Copy
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
Copy
final ids = ['doc-1', 'doc-2', 'doc-3'];
final result = await db.batchDeleteDocuments("books", ids);
print('Deleted: ${result.deleted}');
print('Failed: ${result.failed}');
Copy
const ids = ['doc-1', 'doc-2', 'doc-3'];
const result = await db.batchDeleteDocuments('books', ids);
console.log(`Deleted: ${result.deleted}`);
console.log(`Failed: ${result.failed}`);
Copy
def main():
ids = ['doc-1', 'doc-2', 'doc-3']
result = db.bulk_delete_documents("books", ids)
return {"deleted": result["count"]}
Copy
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:Copy
// 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');
}
}
}
Copy
// JavaScript example - handle partial failures
async function updateMultipleBooks(updates) {
const result = await db.batchUpdateDocuments('books', updates);
console.log(`Success: ${result.updated}`);
if (result.failed > 0) {
console.error(`Failed to update: ${result.errorIds}`);
// Retry failed updates
}
}
Aggregations
Calculate statistics across your data efficiently.Sum
Calculate total of a field:- Flutter
- Python
Copy
final result = await db.aggregateDocuments(
"orders",
field: 'total',
operation: 'sum',
);
print('Total revenue: \$${result.value}');
Copy
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
- Python
Copy
final result = await db.aggregateDocuments(
"products",
field: 'price',
operation: 'avg',
);
print('Average price: \$${result.value}');
Copy
def main():
result = db.aggregate_documents(
"products",
field="price",
operation="avg"
)
return {"average_price": result["value"]}
Min/Max
Find minimum and maximum values:Copy
// 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}');
Aggregations with Filters
Calculate statistics on filtered data:Copy
// 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:Copy
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,
};
}
Copy
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.Basic Grouping
- Flutter
- Python
Copy
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
Copy
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
Copy
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:Copy
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;
}
Copy
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
Copy
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();
}
Performance Optimization
1. Use Indexes
Create indexes on frequently queried fields:Copy
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);
- Index fields used in
whereclauses - Index fields used for sorting
- Don’t over-index (impacts write performance)
- Avoid indexing large text fields
2. Pagination
Always paginate large datasets:Copy
// 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;
}
Copy
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:Copy
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:Copy
// Bad - fetches all fields
final docs = await db.listDocuments("books");
// 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:Copy
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
Copy
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)
Copy
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
whereclauses - 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
Copy
# 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
Copy
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
Copy
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
Copy
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
Copy
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
Query Performance Measurement
Copy
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
Copy
// Enable debug logging in Dio
final dio = Dio();
dio.interceptors.add(LogInterceptor(
requestBody: true,
responseBody: true,
));
Next Steps
- Best Practices - Security and optimization tips
- Troubleshooting - Common issues and solutions
- Cloud Functions - Build serverless functions
- Real-time - Add live data synchronization
