> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ditto.live/llms.txt
> Use this file to discover all available pages before exploring further.

# Rust Legacy→DQL Migration Guide

> Guide for migrating Rust apps from legacy query builder APIs to DQL (Ditto Query Language) syntax.

## Overview

This guide will help you successfully migrate your Ditto Rust application from the legacy query builder APIs to the modern DQL (Ditto Query Language). After reviewing this documentation, you'll understand how to convert method chaining patterns to DQL syntax and systematically update your data operations.

## AI Agent Prompt

Use this prompt when working with an AI coding assistant to migrate your Ditto Rust app from legacy query builder to DQL.

<Accordion title="Copy AI Migration Prompt (Click to Expand)">
  ````text theme={null}
  I need help migrating a Ditto Rust application from the legacy query builder APIs to modern DQL (Ditto Query Language). This migration involves converting method chaining patterns to SQL-like DQL syntax.

  CRITICAL RULES:
  1. All query builder method chains (.collection().find()) must be replaced with ditto.store().execute() using DQL
  2. Use parameterized queries with :paramName syntax - NEVER string formatting
  3. Counter operations must use PN_INCREMENT BY in APPLY clause - do NOT initialize counter fields
  4. Sync subscriptions must use ditto.sync().register_subscription() instead of .find().subscribe()
  5. observe_local must be replaced with register_observer

  ---

  CORE MIGRATION AREAS:

  1. QUERY SYNTAX MIGRATION

  BEFORE (Legacy Query Builder):
  ```rust
  ditto.store.collection("cars")
      .find("color == $args.color")
      .args(HashMap::from([("color", "red".into())]))
      .exec()?;
  ```

  AFTER (DQL):
  ```rust
  ditto.store().execute(
      "SELECT * FROM cars WHERE color = :color",
      Some(HashMap::from([("color", "red".into())]))
  )?;
  ```

  2. INSERT OPERATIONS

  BEFORE (Legacy Query Builder):
  ```rust
  ditto.store.collection("cars")
      .upsert(HashMap::from([("_id", id.into()), ("color", "blue".into())]))?;
  ```

  AFTER (DQL):
  ```rust
  ditto.store().execute(
      "INSERT INTO cars DOCUMENTS (:car)",
      Some(HashMap::from([("car", car_data)]))
  )?;
  ```

  3. UPDATE OPERATIONS

  BEFORE (Legacy Query Builder):
  ```rust
  ditto.store.collection("cars")
      .find_by_id(id)
      .update(|doc| {
          doc.get_mut("color")?.set("green");
          Ok(())
      })?;
  ```

  AFTER (DQL):
  ```rust
  ditto.store().execute(
      "UPDATE cars SET color = :color WHERE _id = :id",
      Some(HashMap::from([("color", "green".into()), ("id", id.into())]))
  )?;
  ```

  4. DELETE OPERATIONS

  BEFORE (Legacy Query Builder):
  ```rust
  ditto.store.collection("cars").find_by_id(id).remove()?;
  ```

  AFTER (DQL):
  ```rust
  ditto.store().execute(
      "DELETE FROM cars WHERE _id = :id",
      Some(HashMap::from([("id", id.into())]))
  )?;
  ```

  5. EVICTION OPERATIONS

  BEFORE (Legacy Query Builder):
  ```rust
  ditto.store.collection("cars").find_by_id(id).evict()?;
  ```

  AFTER (DQL):
  ```rust
  ditto.store().execute(
      "EVICT FROM cars WHERE _id = :id",
      Some(HashMap::from([("id", id.into())]))
  )?;
  ```

  6. COUNTER OPERATIONS (PN_COUNTER)

  BEFORE (Legacy Query Builder):
  ```rust
  ditto.store.collection("cars")
      .find_by_id(id)
      .update(|doc| {
          doc.get_mut("numUpdates")?.counter()?.increment(1.0);
          Ok(())
      })?;
  ```

  AFTER (DQL with PN_INCREMENT):
  ```rust
  ditto.store().execute(
      "UPDATE cars APPLY numUpdates PN_INCREMENT BY :increment WHERE _id = :id",
      Some(HashMap::from([("increment", 1.into()), ("id", id.into())]))
  )?;
  ```

  IMPORTANT: Do NOT initialize counter fields in documents:
  ```rust
  // WRONG - Creates a register, not a counter
  HashMap::from([("counter", 0.into())])

  // CORRECT - Omit counter field, it's created on first PN_INCREMENT
  HashMap::from([("_id", id.into()), ("color", "blue".into())])
  ```

  7. DOCUMENT FIELD ACCESS MIGRATION

  BEFORE (Legacy Query Builder):
  ```rust
  let document = ditto.store.collection("cars").find_by_id(id).exec()?;
  let color = document.and_then(|d| d.value.get("color"));
  ```

  AFTER (DQL):
  ```rust
  let result = ditto.store().execute(dql_string, None)?;
  let item = result.items.first();
  let color = item.and_then(|i| i.value.get("color"));
  ```

  8. OBSERVER MIGRATION (observe_local → register_observer)

  BEFORE (Legacy observe_local):
  ```rust
  let live_query = ditto.store.collection("cars")
      .find(&format!("_id.locationId == '{}'", Constants::LOCATION_ID))
      .observe_local(|docs, event| {
          match event {
              DittoLiveQueryEvent::Update(changes) => {
                  // Handle changes
              }
              DittoLiveQueryEvent::Initial => {
                  // Handle initial data
              }
          }
      })?;
  ```

  AFTER (DQL with register_observer):
  ```rust
  let observer = ditto.store().register_observer(
      "SELECT * FROM cars WHERE _id.locationId = :locationId",
      Some(HashMap::from([("locationId", Constants::LOCATION_ID.into())])),
      |result| {
          // Process result items
          let items = &result.items;

          // Update UI
          update_ui(items);
      }
  )?;

  // Don't forget to stop when done
  observer.stop();
  ```

  9. SYNC SUBSCRIPTIONS MIGRATION

  BEFORE (Legacy Query Builder):
  ```rust
  let subscription = ditto.store.collection("cars")
      .find("color == $args.color")
      .args(HashMap::from([("color", "red".into())]))
      .subscribe()?;
  ```

  AFTER (DQL):
  ```rust
  let subscription = ditto.sync().register_subscription(
      "SELECT * FROM cars WHERE color = :color",
      Some(HashMap::from([("color", "red".into())]))
  )?;
  ```

  ---

  COMMON PITFALLS TO AVOID:

  1. DQL Syntax Errors
     - Use :paramName for parameters, not $args or string formatting

  2. Missing Parameter Binding
     - NEVER use string formatting in queries
     - Always use parameterized queries with HashMap

  3. Counter Type Errors
     - Do NOT initialize counter fields with DittoCounter::new() or numbers
     - Use PN_INCREMENT BY in APPLY clause
     - Pass negative values for decrements

  4. Memory Management with Observers
     - Always store observer reference
     - Call observer.stop() when done

  5. Attachment Handling
     - Use ATTACHMENT annotation: "(image ATTACHMENT)"
     - Create attachments with ditto.store().new_attachment()

  ---

  MIGRATION CHECKLIST:

  Search for these legacy patterns and replace:
  - [ ] .collection( → ditto.store().execute("SELECT * FROM
  - [ ] .find( → Convert to DQL WHERE clause with HashMap arguments
  - [ ] .find_by_id( → Convert to DQL WHERE _id = :id
  - [ ] .upsert( → Convert to DQL INSERT INTO
  - [ ] .update( → Convert to DQL UPDATE SET
  - [ ] .remove( → Convert to DQL DELETE FROM
  - [ ] .evict( → Convert to DQL EVICT FROM
  - [ ] .counter()?.increment( → Convert to PN_INCREMENT BY in APPLY clause
  - [ ] DittoCounter::new() → Remove initialization, use PN_INCREMENT
  - [ ] .observe_local( → Convert to register_observer
  - [ ] .subscribe() → Convert to ditto.sync().register_subscription()
  - [ ] ditto.store → ditto.store()
  - [ ] ditto.sync → ditto.sync()

  ---

  Please help me convert all legacy query builder patterns in my codebase to DQL syntax. Focus on:
  1. Maintaining the same functionality
  2. Using proper parameterized queries with HashMap
  3. Handling counter operations correctly with PN_INCREMENT
  4. Implementing proper observer cleanup with stop()
  5. Converting all sync subscriptions to DQL

  Start by identifying all uses of .collection() in my codebase and systematically converting each one to the appropriate DQL pattern.
  ````
</Accordion>

***

## Syntax Change Reference

### Document Query Syntax

**Document Query All**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  ditto.store().execute(
      "SELECT * FROM cars",
      None
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  ditto.store.collection("cars").exec()?;
  ```
</CodeGroup>

**Document Query by ID**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  ditto.store().execute(
      "SELECT * FROM cars WHERE _id = '123'",
      None
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  ditto.store.collection("cars")
      .find_by_id("123")
      .exec()?;
  ```
</CodeGroup>

**Document Query with Predicate**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  ditto.store().execute(
      "SELECT * FROM cars WHERE color = 'blue'",
      None
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  ditto.store.collection("cars")
      .find("color == 'blue'")
      .exec()?;
  ```
</CodeGroup>

**Document Query with Arguments**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  use std::collections::HashMap;

  ditto.store().execute(
      "SELECT * FROM cars WHERE color = :color",
      Some(HashMap::from([("color", "red".into())]))
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  ditto.store.collection("cars")
      .find("color == $args.color")
      .args(HashMap::from([("color", "red".into())]))
      .exec()?;
  ```
</CodeGroup>

**Document Insert**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  ditto.store().execute(
      "INSERT INTO cars DOCUMENTS (:car)",
      Some(HashMap::from([("car", car_data)]))
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  ditto.store.collection("cars")
      .upsert(HashMap::from([
          ("_id", id.into()),
          ("color", "blue".into())
      ]))?;
  ```
</CodeGroup>

**Document Update**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  ditto.store().execute(
      "UPDATE cars SET color = :color WHERE _id = :id",
      Some(HashMap::from([
          ("color", "green".into()),
          ("id", id.into())
      ]))
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  // Upsert document
  ditto.store.collection("cars")
      .upsert(HashMap::from([
          ("_id", id.into()),
          ("color", "green".into())
      ]))?;

  // Update with closure
  ditto.store.collection("cars")
      .find_by_id(id)
      .update(|doc| {
          doc.get_mut("color")?.set("green");
          Ok(())
      })?;
  ```
</CodeGroup>

**Document Delete**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  ditto.store().execute(
      "DELETE FROM cars WHERE _id = :id",
      Some(HashMap::from([("id", id.into())]))
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  ditto.store.collection("cars").find_by_id(id).remove()?;
  ```
</CodeGroup>

**Document Local Eviction**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  // Evict by ID
  ditto.store().execute(
      "EVICT FROM cars WHERE _id = :id",
      Some(HashMap::from([("id", id.into())]))
  )?;

  // Evict all matching documents
  ditto.store().execute(
      "EVICT FROM cars WHERE color = :color",
      Some(HashMap::from([("color", "red".into())]))
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  // Evict by ID
  ditto.store.collection("cars").find_by_id(id).evict()?;

  // Evict all matching documents
  ditto.store.collection("cars").find_all().evict()?;
  ```
</CodeGroup>

### Query Response Handling

**Legacy Query Builder → DQL Response**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  let result = ditto.store().execute(dql_string, None)?;
  let item = result.items.first();
  let color = item.and_then(|i| i.value.get("color"));
  ```

  ```rust RUST LEGACY (v4) theme={null}
  let document = ditto.store.collection("cars").find_by_id(id).exec()?;
  let color = document.and_then(|d| d.value.get("color"));
  ```
</CodeGroup>

**Legacy Query Builder → Modern Document Conversion**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  fn document_to_car(item: &DittoQueryResultItem) -> Car {
      Car {
          id: item.value["_id"].as_str().unwrap().to_string(),
          color: item.value["color"].as_str().unwrap().to_string(),
      }
  }
  ```

  ```rust RUST LEGACY (v4) theme={null}
  fn document_to_car(doc: &DittoDocument) -> Car {
      Car {
          id: doc.value["_id"].as_str().unwrap().to_string(),
          color: doc.value["color"].as_str().unwrap().to_string(),
      }
  }
  ```
</CodeGroup>

### Observer Migration

**Legacy Query Builder → DQL Store Observer Migration**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  let observer = ditto.store().register_observer(
      "SELECT * FROM cars WHERE _id.locationId = :locationId",
      Some(HashMap::from([("locationId", Constants::LOCATION_ID.into())])),
      |result| {
          // Process result items
          let items = &result.items;

          // Update UI
          update_ui(items);
      }
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  let live_query = ditto.store.collection("cars")
      .find(&format!("_id.locationId == '{}'", Constants::LOCATION_ID))
      .observe_local(|docs, event| {
          println!("Live query handler called. Event: {:?} Docs count: {}", event, docs.len());
          match event {
              DittoLiveQueryEvent::Update(changes) => {
                  // Handle deletions, insertions, updates
              }
              DittoLiveQueryEvent::Initial => {
                  // Handle initial data
              }
          }
      })?;
  ```
</CodeGroup>

<Warning>
  **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.
</Warning>

**Best Practice: Create Indexes for Observer Queries**

```rust theme={null}
// Create index on frequently queried fields
ditto.store().execute(
    "CREATE INDEX idx_cars_locationId ON cars (_id.locationId)",
    None,
)?;

// Then register observer - queries will use the index
let observer = ditto.store().register_observer(
    "SELECT * FROM cars WHERE _id.locationId = :locationId",
    Some(HashMap::from([("locationId", Constants::LOCATION_ID.into())])),
    |result| {
        // Process results
    }
)?;
```

For more information on creating and managing indexes, see the [DQL Indexing documentation](/dql/indexing).

### Sync Subscriptions Migration

**Legacy Query Builder → DQL Sync Subscriptions**

**Subscribe with Query**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  let subscription = ditto.sync().register_subscription(
      "SELECT * FROM cars WHERE color = :color",
      Some(HashMap::from([("color", "red".into())]))
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  let subscription = ditto.store.collection("cars")
      .find("color == $args.color")
      .args(HashMap::from([("color", "red".into())]))
      .subscribe()?;
  ```
</CodeGroup>

**Subscribe with Parameters**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  let subscription = ditto.sync().register_subscription(
      "SELECT * FROM cars WHERE _id.locationId = :locationId",
      Some(HashMap::from([("locationId", Constants::LOCATION_ID.into())]))
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  let subscription = ditto.store.collection("cars")
      .find(&format!("_id.locationId == '{}'", Constants::LOCATION_ID))
      .subscribe()?;
  ```
</CodeGroup>

**Multiple Subscriptions**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  let mut subscriptions = Vec::new();
  subscriptions.push(
      ditto.sync().register_subscription(
          "SELECT * FROM cars WHERE color = :color",
          Some(HashMap::from([("color", "red".into())]))
      )?
  );
  subscriptions.push(
      ditto.sync().register_subscription(
          "SELECT * FROM cars WHERE year > :year",
          Some(HashMap::from([("year", 2020.into())]))
      )?
  );
  ```

  ```rust RUST LEGACY (v4) theme={null}
  let mut subscriptions = Vec::new();
  subscriptions.push(
      ditto.store.collection("cars")
          .find("color == 'red'")
          .subscribe()?
  );
  subscriptions.push(
      ditto.store.collection("cars")
          .find("year > 2020")
          .subscribe()?
  );
  ```
</CodeGroup>

**Cancel Subscription**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  subscription.cancel();
  ```

  ```rust RUST LEGACY (v4) theme={null}
  subscription.cancel();
  ```
</CodeGroup>

**Subscribe to All Documents**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  let subscription = ditto.sync().register_subscription(
      "SELECT * FROM cars",
      None
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  let subscription = ditto.store.collection("cars")
      .find_all()
      .subscribe()?;
  ```
</CodeGroup>

### Counter Type Migration

<Note>
  **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`.
</Note>

**Counter Increment**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  ditto.store().execute(
      "UPDATE cars APPLY numUpdates PN_INCREMENT BY :increment WHERE _id = :id",
      Some(HashMap::from([
          ("increment", 1.into()),
          ("id", id.into())
      ]))
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  ditto.store.collection("cars")
      .find_by_id(id)
      .update(|doc| {
          doc.get_mut("numUpdates")?.counter()?.increment(1.0);
          Ok(())
      })?;
  ```
</CodeGroup>

**Counter Decrement**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  ditto.store().execute(
      "UPDATE cars APPLY viewCount PN_INCREMENT BY :decrement WHERE _id = :id",
      Some(HashMap::from([
          ("decrement", (-1).into()),
          ("id", id.into())
      ]))
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  ditto.store.collection("cars")
      .find_by_id(id)
      .update(|doc| {
          doc.get_mut("viewCount")?.counter()?.increment(-1.0);
          Ok(())
      })?;
  ```
</CodeGroup>

**Initialize Counter in Document**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  // Counter fields are automatically created on first PN_INCREMENT use
  ditto.store().execute(
      "INSERT INTO cars DOCUMENTS (:car)",
      Some(HashMap::from([
          ("car", HashMap::from([
              ("_id", id.into()),
              ("color", "blue".into())
              // Do NOT initialize counter fields - they are created on first PN_INCREMENT
          ]).into())
      ]))
  )?;

  // Then use PN_INCREMENT with APPLY clause to create and increment the counter
  ditto.store().execute(
      "UPDATE cars APPLY numUpdates PN_INCREMENT BY 1 WHERE _id = :id",
      Some(HashMap::from([("id", id.into())]))
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  let car_data = HashMap::from([
      ("_id", id.into()),
      ("color", "blue".into()),
      ("numUpdates", DittoCounter::new().into())
  ]);
  ditto.store.collection("cars").upsert(car_data)?;
  ```
</CodeGroup>

**Multiple Counter Operations**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  ditto.store().execute(
      r#"UPDATE cars
         APPLY likes PN_INCREMENT BY :likeIncrement,
               dislikes PN_INCREMENT BY :dislikeDecrement,
               views PN_INCREMENT BY :viewIncrement
         WHERE _id = :id"#,
      Some(HashMap::from([
          ("likeIncrement", 1.into()),
          ("dislikeDecrement", (-1).into()),
          ("viewIncrement", 1.into()),
          ("id", id.into())
      ]))
  )?;
  ```

  ```rust RUST LEGACY (v4) theme={null}
  ditto.store.collection("cars")
      .find_by_id(id)
      .update(|doc| {
          doc.get_mut("likes")?.counter()?.increment(1.0);
          doc.get_mut("dislikes")?.counter()?.increment(-1.0);
          doc.get_mut("views")?.counter()?.increment(1.0);
          Ok(())
      })?;
  ```
</CodeGroup>

### Attachment Operations with DQL

**Attachment Creation and Storage**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  // Create attachment using store
  let attachment = ditto.store().new_attachment(
      file_path,
      metadata
  )?;

  // Store attachment with DQL
  ditto.store().execute(
      "INSERT INTO COLLECTION cars (image ATTACHMENT) DOCUMENTS (:doc)",
      Some(HashMap::from([("doc", doc_with_attachment)]))
  )?;
  ```
</CodeGroup>

**Attachment Fetching**

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  // Fetch attachment with progress callback
  let fetch_result = ditto.store().fetch_attachment(
      attachment_token,
      |event| {
          if let DittoAttachmentFetchEvent::Progress { downloaded_bytes, total_bytes } = event {
              update_progress(downloaded_bytes, total_bytes);
          }
      }
  )?;
  ```
</CodeGroup>

***

## 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:**

```rust theme={null}
// Create index on single field
ditto.store().execute(
    "CREATE INDEX idx_cars_color ON cars (color)",
    None,
)?;

// Create compound index on multiple fields
ditto.store().execute(
    "CREATE INDEX idx_cars_color_year ON cars (color, year)",
    None,
)?;

// Create index on nested field
ditto.store().execute(
    "CREATE INDEX idx_cars_location ON cars (_id.locationId)",
    None,
)?;
```

**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](/dql/indexing).

***

## Common Pitfalls to Avoid

### 1. DQL Syntax Errors

Use `:paramName` for parameters, not `$args.paramName` or string formatting.

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  // ❌ Wrong: String formatting
  let color = "red";
  ditto.store().execute(
      &format!("SELECT * FROM cars WHERE color = '{}'", color),
      None
  )?;

  // ✅ Correct: Using :paramName with HashMap
  ditto.store().execute(
      "SELECT * FROM cars WHERE color = :color",
      Some(HashMap::from([("color", "red".into())]))
  )?;
  ```
</CodeGroup>

### 2. Missing Parameter Binding

**NEVER** use string formatting in queries. Always use parameterized queries with `HashMap<&str, Value>`.

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  // ❌ Wrong: String formatting
  let location_id = "loc_123";
  ditto.store().execute(
      &format!("SELECT * FROM cars WHERE _id.locationId = '{}'", location_id),
      None
  )?;

  // ✅ Correct: Parameterized query
  ditto.store().execute(
      "SELECT * FROM cars WHERE _id.locationId = :locationId",
      Some(HashMap::from([("locationId", location_id.into())]))
  )?;
  ```
</CodeGroup>

### 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.

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  // ❌ Wrong: Initializing counter with a number (creates REGISTER, not COUNTER)
  let doc = HashMap::from([
      ("_id", id.into()),
      ("counter", 0.into())
  ]);
  ditto.store().execute(
      "INSERT INTO items DOCUMENTS (:doc)",
      Some(HashMap::from([("doc", doc.into())]))
  )?;

  // ❌ Wrong: Using SET on counter field
  ditto.store().execute(
      "UPDATE items SET counter = 5 WHERE _id = :id",
      Some(HashMap::from([("id", id.into())]))
  )?;

  // ✅ Correct: Use PN_INCREMENT BY with APPLY clause (creates counter on first use)
  ditto.store().execute(
      "UPDATE COLLECTION items (counter COUNTER) APPLY counter PN_INCREMENT BY :value WHERE _id = :id",
      Some(HashMap::from([("value", 1.into()), ("id", id.into())]))
  )?;

  // ✅ Correct: Decrement by passing negative value
  ditto.store().execute(
      "UPDATE items APPLY counter PN_INCREMENT BY :value WHERE _id = :id",
      Some(HashMap::from([("value", (-1).into()), ("id", id.into())]))
  )?;
  ```
</CodeGroup>

### 4. Memory Management with Observers

Always call `observer.stop()` when done. Be mindful of ownership and lifetimes. Use indexes for improved memory and performance.

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  // ❌ Wrong: Not stopping observer
  {
      let observer = ditto.store().register_observer(
          "SELECT * FROM cars",
          None,
          |result| {
              // Process results
          }
      )?;
      // Observer dropped without stopping
  }

  // ✅ Correct: Always stop observer when done
  struct CarRepository {
      observer: Option<StoreObserver>,
  }

  impl CarRepository {
      fn start_observing(&mut self, ditto: &Ditto) -> Result<(), DittoError> {
          let observer = ditto.store().register_observer(
              "SELECT * FROM cars",
              None,
              |result| {
                  // Extract data immediately
                  let cars: Vec<String> = result.items()
                      .iter()
                      .filter_map(|item| item.value().get("_id")?.as_str())
                      .map(String::from)
                      .collect();

                  // Update UI with extracted data
              }
          )?;

          self.observer = Some(observer);
          Ok(())
      }

      fn stop_observing(&mut self) {
          if let Some(observer) = self.observer.take() {
              observer.stop();  // Always stop observer
          }
      }
  }
  ```
</CodeGroup>

### 5. Attachment Handling

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

<CodeGroup>
  ```rust RUST DQL (v5) theme={null}
  // ❌ Wrong: Missing ATTACHMENT annotation
  ditto.store().execute(
      "INSERT INTO cars DOCUMENTS (:doc)",
      Some(HashMap::from([("doc", doc_with_attachment)]))
  )?;

  // ✅ Correct: Use ATTACHMENT annotation in COLLECTION definition
  let attachment = ditto.store().new_attachment(
      file_path,
      metadata
  )?;

  let doc = HashMap::from([
      ("_id", id.into()),
      ("image", attachment.into())
  ]);

  ditto.store().execute(
      "INSERT INTO COLLECTION cars (image ATTACHMENT) DOCUMENTS (:doc)",
      Some(HashMap::from([("doc", doc.into())]))
  )?;
  ```
</CodeGroup>
