Skip to main content

Overview

This guide covers migrating from Ditto’s legacy query builder API to DQL (Ditto Query Language) in C++. DQL provides a SQL-like syntax that’s more powerful and intuitive for querying and manipulating data. Note: This migration is required alongside the V4→V5 API Migration for a complete upgrade.

AI Agent Prompt

Use this prompt when working with an AI coding assistant to migrate your Ditto C++ app to DQL.

Document Query Syntax

Query All Documents

Query Document by ID

Query with Predicate

Query with Parameterized Arguments


Insert, Update, Delete, Eviction

Insert Document

Update Document

Delete Document

Evict All Documents Matching Condition


Query Response Handling

Working with Query Results


Observer Migration

Observing Query Results

Key Differences:
  • DQL observers use register_observer(query, args, callback) instead of .observe()
  • Results are std::vector<CborValue> instead of std::vector<DittoDocument>
  • Same cleanup pattern with .stop()
Performance Consideration: DQL observers provide more advanced return results including aggregates and projections. This requires more database full scans to ensure consistent results compared to the legacy query builder.Use indexes on query fields to maintain and improve observer performance. Indexes ensure your observers remain functional with optimal query performance.
Best Practice: Create Indexes for Observer Queries
For more information on creating and managing indexes, see the DQL Indexing documentation.

Sync Subscriptions Migration

Creating Sync Subscriptions

Key Differences:
  • DQL subscriptions use register_subscription(query, args) instead of .subscribe()
  • Cleanup now uses .cancel() instead of .close()

Counter Type Migration

Updating Counter Values

Critical Change: Counters are now modified using the PN_INCREMENT BY operation in the APPLY clause, not by setting values directly.
PN_COUNTER is the DQL equivalent of the legacy DittoCounter type. When migrating counter operations from the legacy query builder’s counter methods, use PN_INCREMENT BY in the APPLY clause. This maintains full compatibility with existing counter data created by DittoCounter.
Important Notes:
  • Never initialize counter fields in INSERT statements - they start at 0 automatically when incremented
  • Use PN_INCREMENT BY in the APPLY clause for counter operations
  • Positive values increment, negative values decrement
  • Counter values are read as integers in SELECT queries

Attachment Operations

Fetching Attachments


Performance Enhancements

Indexes for Improved Query Performance

DQL observers and queries benefit significantly from proper indexing. When migrating from the legacy query builder to DQL, creating indexes on frequently queried fields is essential for maintaining optimal performance. Why Indexes Matter for DQL:
  • DQL observers support advanced features like aggregates and projections
  • These advanced features require full database scans to ensure consistent results
  • Indexes dramatically reduce query execution time by avoiding full scans
  • Combining indexes with observers provides better performance than legacy query builder
Creating Indexes:
Best Practices:
  1. Create indexes on fields used in WHERE clauses
  2. Create indexes before registering observers for those queries
  3. Use compound indexes for queries with multiple filter conditions
  4. Monitor query performance and add indexes as needed
For comprehensive information on indexing strategies, syntax, and best practices, see the DQL Indexing documentation.

Common Pitfalls to Avoid

1. DQL Syntax Errors

Use :paramName for parameters, not string literals or concatenation.

2. Missing Parameter Binding

NEVER use string concatenation in queries. Always use parameterized queries with std::map<std::string, CborValue>.

3. Counter Type Errors

Use COUNTER annotation in collection definitions. Do NOT use SET with COUNTER fields. Use APPLY with PN_INCREMENT BY. Pass negative values for decrements.

4. Memory Management with Observers

Call item->close() after extracting data from QueryResultItems. Always close observers when done. Use indexes for improved memory and performance.

5. Attachment Handling

Use ATTACHMENT annotation in collection definitions. Create attachments with ditto->store().new_attachment().

Migration Checklist

  • Replace all .collection().find() with DQL SELECT queries
  • Replace all .collection().find_by_id() with DQL SELECT with _id filter
  • Replace all .collection().upsert() with DQL INSERT statements
  • Replace all .update() lambdas with DQL UPDATE statements
  • Replace all .remove() with DQL EVICT statements
  • Update all observers to use register_observer(query, args, callback)
  • Update all subscriptions to use register_subscription(query, args)
  • Convert counter operations to use PN_INCREMENT BY in APPLY clause
  • Remove counter field initialization from INSERT statements
  • Use empty maps {} for queries without parameters
  • Update result handling from DittoDocument to CborValue
  • Verify attachment fetching uses tokens from query results
  • Use proper type conversion methods (.as_string(), .as_int(), etc.)