From e0f092dddaa6c8401bf047b6061555c7e92f1b34 Mon Sep 17 00:00:00 2001
From: Quantum Explorer <quantum@dash.org>
Date: Sun, 24 Nov 2024 21:00:46 +0300
Subject: [PATCH 1/6] worked on query_start_after

---
 packages/rs-drive/src/query/mod.rs            | 570 +++++++++++++-----
 packages/rs-drive/tests/query_tests.rs        | 423 ++++++++++++-
 .../withdrawals/withdrawals-contract.json     | 141 +++++
 3 files changed, 983 insertions(+), 151 deletions(-)
 create mode 100644 packages/rs-drive/tests/supporting_files/contract/withdrawals/withdrawals-contract.json

diff --git a/packages/rs-drive/src/query/mod.rs b/packages/rs-drive/src/query/mod.rs
index 7491454a64..e5e569489b 100644
--- a/packages/rs-drive/src/query/mod.rs
+++ b/packages/rs-drive/src/query/mod.rs
@@ -54,7 +54,7 @@ use dpp::document;
 use dpp::prelude::Identifier;
 #[cfg(feature = "server")]
 use {
-    crate::{drive::Drive, error::Error::GroveDB, fees::op::LowLevelDriveOperation},
+    crate::{drive::Drive, fees::op::LowLevelDriveOperation},
     dpp::block::block_info::BlockInfo,
 };
 // Crate-local unconditional imports
@@ -141,6 +141,26 @@ pub mod drive_contested_document_query;
 /// A query to get the block counts of proposers in an epoch
 pub mod proposer_block_count_query;
 
+#[cfg(any(feature = "server", feature = "verify"))]
+/// Represents a starting point for a query based on a specific document.
+///
+/// This struct encapsulates all the necessary details to define the starting
+/// conditions for a query, including the document to start from, its type,
+/// associated index property, and whether the document itself should be included
+/// in the query results.
+#[derive(Debug, Clone)]
+pub struct StartAtDocument<'a> {
+    /// The document that serves as the starting point for the query.
+    pub document: Document,
+
+    /// The type of the document, providing metadata about its schema and structure.
+    pub document_type: DocumentTypeRef<'a>,
+
+    /// Indicates whether the starting document itself should be included in the query results.
+    /// - `true`: The document is included in the results.
+    /// - `false`: The document is excluded, and the query starts from the next matching document.
+    pub included: bool,
+}
 #[cfg(any(feature = "server", feature = "verify"))]
 /// Internal clauses struct
 #[derive(Clone, Debug, PartialEq, Default)]
@@ -898,7 +918,7 @@ impl<'a> DriveDocumentQuery<'a> {
         let (starts_at_document, start_at_path_query) = match &self.start_at {
             None => Ok((None, None)),
             Some(starts_at) => {
-                // First if we have a startAt or or startsAfter we must get the element
+                // First if we have a startAt or startsAfter we must get the element
                 // from the backing store
 
                 let (start_at_document_path, start_at_document_key) =
@@ -970,7 +990,7 @@ impl<'a> DriveDocumentQuery<'a> {
                 vec![&start_at_path_query, &main_path_query],
                 &platform_version.drive.grove_version,
             )
-            .map_err(GroveDB)?;
+            .map_err(Error::GroveDB)?;
             merged.query.limit = limit.map(|a| a.saturating_add(1));
             Ok(merged)
         } else {
@@ -1252,13 +1272,16 @@ impl<'a> DriveDocumentQuery<'a> {
     #[cfg(any(feature = "server", feature = "verify"))]
     /// Returns a `Query` that either starts at or after the given document ID if given.
     fn inner_query_from_starts_at_for_id(
-        starts_at_document: &Option<(Document, DocumentTypeRef, &IndexProperty, bool)>,
+        starts_at_document: Option<&StartAtDocument>,
         left_to_right: bool,
     ) -> Query {
         // We only need items after the start at document
         let mut inner_query = Query::new_with_direction(left_to_right);
 
-        if let Some((document, _, _, included)) = starts_at_document {
+        if let Some(StartAtDocument {
+            document, included, ..
+        }) = starts_at_document
+        {
             let start_at_key = document.id().to_vec();
             if *included {
                 inner_query.insert_range_from(start_at_key..)
@@ -1313,18 +1336,19 @@ impl<'a> DriveDocumentQuery<'a> {
 
     #[cfg(any(feature = "server", feature = "verify"))]
     /// Returns a `Query` that either starts at or after the given document if given.
-    // We are passing in starts_at_document 4 parameters
-    // The document
-    // The document type (borrowed)
-    // The index property (borrowed)
-    // if the element itself should be included. ie StartAt vs StartAfter
     fn inner_query_from_starts_at(
-        starts_at_document: &Option<(Document, DocumentTypeRef, &IndexProperty, bool)>,
+        starts_at_document: Option<&StartAtDocument>,
+        indexed_property: &IndexProperty,
         left_to_right: bool,
         platform_version: &PlatformVersion,
     ) -> Result<Query, Error> {
         let mut inner_query = Query::new_with_direction(left_to_right);
-        if let Some((document, document_type, indexed_property, included)) = starts_at_document {
+        if let Some(StartAtDocument {
+            document,
+            document_type,
+            included,
+        }) = starts_at_document
+        {
             // We only need items after the start at document
             let start_at_key = document.get_raw_for_document_type(
                 indexed_property.name.as_str(),
@@ -1356,56 +1380,173 @@ impl<'a> DriveDocumentQuery<'a> {
         }
         Ok(inner_query)
     }
+    fn recursive_create_query(
+        left_over_index_properties: &[&IndexProperty],
+        unique: bool,
+        starts_at_document: Option<&StartAtDocument>, //for key level, included
+        indexed_property: &IndexProperty,
+        order_by: Option<&IndexMap<String, OrderClause>>,
+        platform_version: &PlatformVersion,
+    ) -> Result<Option<Query>, Error> {
+        match left_over_index_properties.split_first() {
+            None => Ok(None),
+            Some((first, left_over)) => {
+                let left_to_right = if let Some(order_by) = order_by {
+                    order_by
+                        .get(first.name.as_str())
+                        .map(|order_clause| order_clause.ascending)
+                        .unwrap_or(first.ascending)
+                } else {
+                    first.ascending
+                };
+
+                let mut inner_query = Self::inner_query_from_starts_at(
+                    starts_at_document,
+                    indexed_property,
+                    left_to_right,
+                    platform_version,
+                )?;
+                DriveDocumentQuery::recursive_insert_on_query(
+                    &mut inner_query,
+                    left_over,
+                    unique,
+                    starts_at_document,
+                    left_to_right,
+                    order_by,
+                    platform_version,
+                )?;
+                Ok(Some(inner_query))
+            }
+        }
+    }
 
     #[cfg(any(feature = "server", feature = "verify"))]
     /// Recursively queries as long as there are leftover index properties.
+    /// The in_start_at_document_sub_path_needing_conditional is interesting.
+    /// It indicates whether the start at document should be applied as a conditional
+    /// For example if we have a tree
+    /// Root
+    /// ├── model
+    /// │   ├── sedan
+    /// │   │   ├── brand_name
+    /// │   │   │   ├── Honda
+    /// │   │   │   │   ├── car_type
+    /// │   │   │   │   │   ├── Accord
+    /// │   │   │   │   │   │   ├── 0
+    /// │   │   │   │   │   │   │   ├── a47d2...
+    /// │   │   │   │   │   │   │   ├── e19c8...
+    /// │   │   │   │   │   │   │   └── f1a7b...
+    /// │   │   │   │   │   └── Civic
+    /// │   │   │   │   │       ├── 0
+    /// │   │   │   │   │       │   ├── b65a7...
+    /// │   │   │   │   │       │   └── c43de...
+    /// │   │   │   ├── Toyota
+    /// │   │   │   │   ├── car_type
+    /// │   │   │   │   │   ├── Camry
+    /// │   │   │   │   │   │   ├── 0
+    /// │   │   │   │   │   │   │   └── 1a9d2...
+    /// │   │   │   │   │   └── Corolla
+    /// │   │   │   │   │       ├── 0
+    /// │   │   │   │   │       │   ├── 3f7b4...
+    /// │   │   │   │   │       │   ├── 4e8fa...
+    /// │   │   │   │   │       │   └── 9b1c6...
+    /// │   ├── suv
+    /// │   │   ├── brand_name
+    /// │   │   │   ├── Ford*
+    /// │   │   │   │   ├── car_type*
+    /// │   │   │   │   │   ├── Escape*
+    /// │   │   │   │   │   │   ├── 0
+    /// │   │   │   │   │   │   │   ├── 102bc...
+    /// │   │   │   │   │   │   │   ├── 29f8e... <- Set After this document
+    /// │   │   │   │   │   │   │   └── 6b1a3...
+    /// │   │   │   │   │   └── Explorer
+    /// │   │   │   │   │       ├── 0
+    /// │   │   │   │   │       │   ├── b2a9d...
+    /// │   │   │   │   │       │   └── f4d5c...
+    /// │   │   │   ├── Nissan
+    /// │   │   │   │   ├── car_type
+    /// │   │   │   │   │   ├── Rogue
+    /// │   │   │   │   │   │   ├── 0
+    /// │   │   │   │   │   │   │   ├── 5a9c3...
+    /// │   │   │   │   │   │   │   └── 7e4b9...
+    /// │   │   │   │   │   └── Murano
+    /// │   │   │   │   │       ├── 0
+    /// │   │   │   │   │       │   ├── 8f6a2...
+    /// │   │   │   │   │       │   └── 9c7d4...
+    /// │   ├── truck
+    /// │   │   ├── brand_name
+    /// │   │   │   ├── Ford
+    /// │   │   │   │   ├── car_type
+    /// │   │   │   │   │   ├── F-150
+    /// │   │   │   │   │   │   ├── 0
+    /// │   │   │   │   │   │   │   ├── 72a3b...
+    /// │   │   │   │   │   │   │   └── 94c8e...
+    /// │   │   │   │   │   └── Ranger
+    /// │   │   │   │   │       ├── 0
+    /// │   │   │   │   │       │   ├── 3f4b1...
+    /// │   │   │   │   │       │   ├── 6e7d2...
+    /// │   │   │   │   │       │   └── 8a1f5...
+    /// │   │   │   ├── Toyota
+    /// │   │   │   │   ├── car_type
+    /// │   │   │   │   │   ├── Tundra
+    /// │   │   │   │   │   │   ├── 0
+    /// │   │   │   │   │   │   │   ├── 7c9a4...
+    /// │   │   │   │   │   │   │   └── a5d1e...
+    /// │   │   │   │   │   └── Tacoma
+    /// │   │   │   │   │       ├── 0
+    /// │   │   │   │   │       │   ├── 1e7f4...
+    /// │   │   │   │   │       │   └── 6b9d3...
+    ///
+    /// let's say we are asking for suv's after 29f8e
+    /// here the * denotes the area needing a conditional
+    /// We need a conditional subquery on Ford to say only things after Ford (with Ford included)
+    /// We need a conditional subquery on Escape to say only things after Escape (with Escape included)
+    ///
+    /// The index property used to identify the document within its type.
+    /// This helps in determining the position of the document in query results.
     fn recursive_insert_on_query(
-        query: Option<&mut Query>,
+        query: &mut Query,
         left_over_index_properties: &[&IndexProperty],
         unique: bool,
-        starts_at_document: &Option<(Document, DocumentTypeRef, &IndexProperty, bool)>, //for key level, included
+        starts_at_document: Option<&StartAtDocument>, //for key level, included
         default_left_to_right: bool,
         order_by: Option<&IndexMap<String, OrderClause>>,
         platform_version: &PlatformVersion,
     ) -> Result<Option<Query>, Error> {
         match left_over_index_properties.split_first() {
             None => {
-                if let Some(query) = query {
-                    match unique {
-                        true => {
-                            query.set_subquery_key(vec![0]);
-
-                            // In the case things are NULL we allow to have multiple values
-                            let inner_query = Self::inner_query_from_starts_at_for_id(
-                                starts_at_document,
-                                true, //for ids we always go left to right
-                            );
-                            query.add_conditional_subquery(
-                                QueryItem::Key(b"".to_vec()),
-                                Some(vec![vec![0]]),
-                                Some(inner_query),
-                            );
-                        }
-                        false => {
-                            query.set_subquery_key(vec![0]);
-                            // we just get all by document id order ascending
-                            let full_query = Self::inner_query_from_starts_at_for_id(
-                                &None,
-                                default_left_to_right,
-                            );
-                            query.set_subquery(full_query);
+                match unique {
+                    true => {
+                        query.set_subquery_key(vec![0]);
 
-                            let inner_query = Self::inner_query_from_starts_at_for_id(
-                                starts_at_document,
-                                default_left_to_right,
-                            );
+                        // In the case things are NULL we allow to have multiple values
+                        let inner_query = Self::inner_query_from_starts_at_for_id(
+                            starts_at_document,
+                            true, //for ids we always go left to right
+                        );
+                        query.add_conditional_subquery(
+                            QueryItem::Key(b"".to_vec()),
+                            Some(vec![vec![0]]),
+                            Some(inner_query),
+                        );
+                    }
+                    false => {
+                        query.set_subquery_key(vec![0]);
+                        // we just get all by document id order ascending
+                        let full_query =
+                            Self::inner_query_from_starts_at_for_id(None, default_left_to_right);
+                        query.set_subquery(full_query);
 
-                            query.add_conditional_subquery(
-                                QueryItem::Key(b"".to_vec()),
-                                Some(vec![vec![0]]),
-                                Some(inner_query),
-                            );
-                        }
+                        let inner_query = Self::inner_query_from_starts_at_for_id(
+                            starts_at_document,
+                            default_left_to_right,
+                        );
+
+                        query.add_conditional_subquery(
+                            QueryItem::Key(b"".to_vec()),
+                            Some(vec![vec![0]]),
+                            Some(inner_query),
+                        );
                     }
                 }
                 Ok(None)
@@ -1420,77 +1561,237 @@ impl<'a> DriveDocumentQuery<'a> {
                     first.ascending
                 };
 
-                match query {
-                    None => {
-                        let mut inner_query = Self::inner_query_from_starts_at(
-                            starts_at_document,
-                            left_to_right,
-                            platform_version,
-                        )?;
-                        DriveDocumentQuery::recursive_insert_on_query(
-                            Some(&mut inner_query),
-                            left_over,
-                            unique,
-                            starts_at_document,
-                            left_to_right,
-                            order_by,
+                if let Some(start_at_document_inner) = starts_at_document {
+                    let StartAtDocument {
+                        document,
+                        document_type,
+                        included,
+                    } = start_at_document_inner;
+                    let start_at_key = document
+                        .get_raw_for_document_type(
+                            first.name.as_str(),
+                            *document_type,
+                            None,
                             platform_version,
-                        )?;
-                        Ok(Some(inner_query))
+                        )
+                        .ok()
+                        .flatten();
+
+                    // We should always include if we have left_over
+                    let non_conditional_included =
+                        !left_over.is_empty() || *included || start_at_key.is_none();
+
+                    let mut non_conditional_query = Self::inner_query_starts_from_key(
+                        start_at_key.clone(),
+                        left_to_right,
+                        non_conditional_included,
+                    );
+
+                    // We place None here on purpose, this has been well-thought-out
+                    // and should not change. The reason is that the path of the start
+                    // at document is used only on the conditional subquery and not on the
+                    // main query
+                    // for example in the following
+                    // Our query will be with $ownerId == a3f9b81c4d7e6a9f5b1c3e8a2d9c4f7b
+                    // With start after 8f2d5
+                    // We want to get from 2024-11-17T12:45:00Z
+                    // withdrawal
+                    // ├── $ownerId
+                    // │   ├── a3f9b81c4d7e6a9f5b1c3e8a2d9c4f7b
+                    // │   │   ├── $updatedAt
+                    // │   │   │   ├── 2024-11-17T12:45:00Z <- conditional subquery here
+                    // │   │   │   │   ├── status
+                    // │   │   │   │   │   ├── 0
+                    // │   │   │   │   │   │   ├── 7a9f1...
+                    // │   │   │   │   │   │   └── 4b8c3...
+                    // │   │   │   │   │   ├── 1
+                    // │   │   │   │   │   │   ├── 8f2d5... <- start after
+                    // │   │   │   │   │   │   └── 5c1e4...
+                    // │   │   │   │   │   ├── 2
+                    // │   │   │   │   │   │   ├── 2e7a9...
+                    // │   │   │   │   │   │   └── 1c8b3...
+                    // │   │   │   ├── 2024-11-18T11:25:00Z <- we want all statuses here, so normal subquery, with None as start at document
+                    // │   │   │   │   ├── status
+                    // │   │   │   │   │   ├── 0
+                    // │   │   │   │   │   │   └── 1a4f2...
+                    // │   │   │   │   │   ├── 2
+                    // │   │   │   │   │   │   ├── 3e7a9...
+                    // │   │   │   │   │   │   └── 198b4...
+                    // │   ├── b6d7e9c4a5f2b3d8e1a7c9f4b1e8a3f
+                    // │   │   ├── $updatedAt
+                    // │   │   │   ├── 2024-11-17T13:30:00Z
+                    // │   │   │   │   ├── status
+                    // │   │   │   │   │   ├── 0
+                    // │   │   │   │   │   │   ├── 6d7e2...
+                    // │   │   │   │   │   │   └── 9c7f5...
+                    // │   │   │   │   │   ├── 3
+                    // │   │   │   │   │   │   ├── 3a9b7...
+                    // │   │   │   │   │   │   └── 8e5c4...
+                    // │   │   │   │   │   ├── 4
+                    // │   │   │   │   │   │   ├── 1f7a8...
+                    // │   │   │   │   │   │   └── 2c9b3...
+                    DriveDocumentQuery::recursive_insert_on_query(
+                        &mut non_conditional_query,
+                        left_over,
+                        unique,
+                        None,
+                        left_to_right,
+                        order_by,
+                        platform_version,
+                    )?;
+
+                    // DriveDocumentQuery::recursive_conditional_insert_on_query(
+                    //     &mut non_conditional_query,
+                    //     left_over,
+                    //     unique,
+                    //     start_at_document_inner,
+                    //     left_to_right,
+                    //     order_by,
+                    //     platform_version,
+                    // )?;
+
+                    query.set_subquery(non_conditional_query);
+                } else {
+                    let mut inner_query = Query::new_with_direction(first.ascending);
+                    inner_query.insert_all();
+                    DriveDocumentQuery::recursive_insert_on_query(
+                        &mut inner_query,
+                        left_over,
+                        unique,
+                        starts_at_document,
+                        left_to_right,
+                        order_by,
+                        platform_version,
+                    )?;
+                    query.set_subquery(inner_query);
+                }
+                query.set_subquery_key(first.name.as_bytes().to_vec());
+                Ok(None)
+            }
+        }
+    }
+    fn recursive_conditional_insert_on_query(
+        query: &mut Query,
+        left_over_index_properties: &[&IndexProperty],
+        unique: bool,
+        starts_at_document: &StartAtDocument,
+        default_left_to_right: bool,
+        order_by: Option<&IndexMap<String, OrderClause>>,
+        platform_version: &PlatformVersion,
+    ) -> Result<Option<Query>, Error> {
+        match left_over_index_properties.split_first() {
+            None => {
+                match unique {
+                    true => {
+                        query.set_subquery_key(vec![0]);
+
+                        // In the case things are NULL we allow to have multiple values
+                        let inner_query = Self::inner_query_from_starts_at_for_id(
+                            Some(starts_at_document),
+                            true, //for ids we always go left to right
+                        );
+                        query.add_conditional_subquery(
+                            QueryItem::Key(b"".to_vec()),
+                            Some(vec![vec![0]]),
+                            Some(inner_query),
+                        );
                     }
-                    Some(query) => {
-                        if let Some((document, document_type, _indexed_property, included)) =
-                            starts_at_document
-                        {
-                            let start_at_key = document
-                                .get_raw_for_document_type(
-                                    first.name.as_str(),
-                                    *document_type,
-                                    None,
-                                    platform_version,
-                                )
-                                .ok()
-                                .flatten();
-
-                            // We should always include if we have left_over
-                            let non_conditional_included =
-                                !left_over.is_empty() | *included | start_at_key.is_none();
-
-                            let mut non_conditional_query = Self::inner_query_starts_from_key(
-                                start_at_key,
-                                left_to_right,
-                                non_conditional_included,
-                            );
+                    false => {
+                        query.set_subquery_key(vec![0]);
+                        // we just get all by document id order ascending
+                        let full_query =
+                            Self::inner_query_from_starts_at_for_id(None, default_left_to_right);
+                        query.set_subquery(full_query);
+
+                        let inner_query = Self::inner_query_from_starts_at_for_id(
+                            Some(starts_at_document),
+                            default_left_to_right,
+                        );
+
+                        query.add_conditional_subquery(
+                            QueryItem::Key(b"".to_vec()),
+                            Some(vec![vec![0]]),
+                            Some(inner_query),
+                        );
+                    }
+                }
+                Ok(None)
+            }
+            Some((first, left_over)) => {
+                let left_to_right = if let Some(order_by) = order_by {
+                    order_by
+                        .get(first.name.as_str())
+                        .map(|order_clause| order_clause.ascending)
+                        .unwrap_or(first.ascending)
+                } else {
+                    first.ascending
+                };
 
-                            DriveDocumentQuery::recursive_insert_on_query(
-                                Some(&mut non_conditional_query),
-                                left_over,
-                                unique,
-                                starts_at_document,
-                                left_to_right,
-                                order_by,
-                                platform_version,
-                            )?;
+                let StartAtDocument {
+                    document,
+                    document_type,
+                    included,
+                    ..
+                } = starts_at_document;
+
+                let start_at_key = document
+                    .get_raw_for_document_type(
+                        first.name.as_str(),
+                        *document_type,
+                        None,
+                        platform_version,
+                    )
+                    .ok()
+                    .flatten();
 
-                            query.set_subquery(non_conditional_query);
-                        } else {
-                            let mut inner_query = Query::new_with_direction(first.ascending);
-                            inner_query.insert_all();
+                // We should always include if we have left_over
+                let non_conditional_included =
+                    !left_over.is_empty() || *included || start_at_key.is_none();
+
+                let mut non_conditional_query = Self::inner_query_starts_from_key(
+                    start_at_key.clone(),
+                    left_to_right,
+                    non_conditional_included,
+                );
+
+                DriveDocumentQuery::recursive_insert_on_query(
+                    &mut non_conditional_query,
+                    left_over,
+                    unique,
+                    None,
+                    left_to_right,
+                    order_by,
+                    platform_version,
+                )?;
+
+                if let Some(start_at_key) = start_at_key {
+                    match left_over.split_first() {
+                        None => {}
+                        Some((next, lower_left_over)) => {
+                            let mut conditional_query = non_conditional_query.clone();
                             DriveDocumentQuery::recursive_insert_on_query(
-                                Some(&mut inner_query),
-                                left_over,
+                                &mut conditional_query,
+                                lower_left_over,
                                 unique,
-                                starts_at_document,
+                                Some(starts_at_document),
                                 left_to_right,
                                 order_by,
                                 platform_version,
                             )?;
-                            query.set_subquery(inner_query);
+
+                            non_conditional_query.add_conditional_subquery(
+                                QueryItem::Key(start_at_key),
+                                Some(vec![next.name.as_bytes().to_vec()]),
+                                Some(conditional_query),
+                            );
                         }
-                        query.set_subquery_key(first.name.as_bytes().to_vec());
-                        Ok(None)
                     }
                 }
+
+                query.set_subquery(non_conditional_query);
+
+                query.set_subquery_key(first.name.as_bytes().to_vec());
+                Ok(None)
             }
         }
     }
@@ -1569,14 +1870,17 @@ impl<'a> DriveDocumentQuery<'a> {
                 let first_index = index.properties.first().ok_or(Error::Drive(
                     DriveError::CorruptedContractIndexes("index must have properties".to_string()),
                 ))?; // Index must have properties
-                Self::recursive_insert_on_query(
-                    None,
+                Self::recursive_create_query(
                     left_over_index_properties.as_slice(),
                     index.unique,
-                    &starts_at_document.map(|(document, included)| {
-                        (document, self.document_type, first_index, included)
-                    }),
-                    first_index.ascending,
+                    starts_at_document
+                        .map(|(document, included)| StartAtDocument {
+                            document,
+                            document_type: self.document_type,
+                            included,
+                        })
+                        .as_ref(),
+                    first_index,
                     None,
                     platform_version,
                 )?
@@ -1614,22 +1918,17 @@ impl<'a> DriveDocumentQuery<'a> {
 
                 match subquery_clause {
                     None => {
-                        // There is a last_clause, but no subquery_clause, we should use the index property of the last clause
-                        // We need to get the terminal indexes unused by clauses.
-                        let last_index_property = index
-                            .properties
-                            .iter()
-                            .find(|field| where_clause.field == field.name)
-                            .ok_or(Error::Drive(DriveError::CorruptedContractIndexes(
-                                "index must have last_clause field".to_string(),
-                            )))?;
                         Self::recursive_insert_on_query(
-                            Some(&mut query),
+                            &mut query,
                             left_over_index_properties.as_slice(),
                             index.unique,
-                            &starts_at_document.map(|(document, included)| {
-                                (document, self.document_type, last_index_property, included)
-                            }),
+                            starts_at_document
+                                .map(|(document, included)| StartAtDocument {
+                                    document,
+                                    document_type: self.document_type,
+                                    included,
+                                })
+                                .as_ref(),
                             left_to_right,
                             Some(&self.order_by),
                             platform_version,
@@ -1648,20 +1947,17 @@ impl<'a> DriveDocumentQuery<'a> {
                             order_clause.ascending,
                             platform_version,
                         )?;
-                        let last_index_property = index
-                            .properties
-                            .iter()
-                            .find(|field| subquery_where_clause.field == field.name)
-                            .ok_or(Error::Drive(DriveError::CorruptedContractIndexes(
-                                "index must have subquery_clause field".to_string(),
-                            )))?;
                         Self::recursive_insert_on_query(
-                            Some(&mut subquery),
+                            &mut subquery,
                             left_over_index_properties.as_slice(),
                             index.unique,
-                            &starts_at_document.map(|(document, included)| {
-                                (document, self.document_type, last_index_property, included)
-                            }),
+                            starts_at_document
+                                .map(|(document, included)| StartAtDocument {
+                                    document,
+                                    document_type: self.document_type,
+                                    included,
+                                })
+                                .as_ref(),
                             left_to_right,
                             Some(&self.order_by),
                             platform_version,
@@ -1866,6 +2162,8 @@ impl<'a> DriveDocumentQuery<'a> {
             platform_version,
         )?;
 
+        print!("{}", path_query);
+
         let query_result = drive.grove_get_path_query_serialized_results(
             &path_query,
             transaction,
diff --git a/packages/rs-drive/tests/query_tests.rs b/packages/rs-drive/tests/query_tests.rs
index 23d8491885..4dac50d89d 100644
--- a/packages/rs-drive/tests/query_tests.rs
+++ b/packages/rs-drive/tests/query_tests.rs
@@ -68,15 +68,16 @@ use dpp::document::{DocumentV0Getters, DocumentV0Setters};
 use dpp::fee::default_costs::CachedEpochIndexFeeVersions;
 use dpp::identity::TimestampMillis;
 use dpp::platform_value;
+use dpp::platform_value::string_encoding::Encoding;
 #[cfg(feature = "server")]
 use dpp::prelude::DataContract;
 use dpp::tests::json_document::json_document_to_contract;
 #[cfg(feature = "server")]
 use dpp::util::cbor_serializer;
-use once_cell::sync::Lazy;
-
 use dpp::version::fee::FeeVersion;
 use dpp::version::PlatformVersion;
+use once_cell::sync::Lazy;
+use rand::prelude::StdRng;
 
 #[cfg(feature = "server")]
 use drive::drive::contract::test_helpers::add_init_contracts_structure_operations;
@@ -434,12 +435,36 @@ struct Domain {
     subdomain_rules: SubdomainRules,
 }
 
+#[derive(Serialize, Deserialize, Debug, Clone)]
+#[serde(rename_all = "camelCase")]
+pub struct Withdrawal {
+    #[serde(rename = "$id")]
+    pub id: Identifier, // Unique identifier for the withdrawal
+
+    #[serde(rename = "$ownerId")]
+    pub owner_id: Identifier, // Identity of the withdrawal owner
+
+    #[serde(rename = "$createdAt")]
+    pub created_at: TimestampMillis,
+
+    #[serde(rename = "$updatedAt")]
+    pub updated_at: TimestampMillis,
+
+    pub transaction_index: Option<u32>, // Optional sequential index of the transaction
+    pub transaction_sign_height: Option<u32>, // Optional Core height on which the transaction was signed
+    pub amount: u64,                          // Amount to withdraw (minimum: 1000)
+    pub core_fee_per_byte: u32,               // Fee in Duffs/Byte (minimum: 1, max: 4294967295)
+    pub pooling: u8,                          // Pooling level (enum: 0, 1, 2)
+    pub output_script: Vec<u8>,               // Byte array (size: 23-25)
+    pub status: u8,                           // Status (enum: 0 - Pending, 1 - Signed, etc.)
+}
+
 #[cfg(feature = "server")]
 #[test]
 fn test_serialization_and_deserialization() {
     let platform_version = PlatformVersion::latest();
 
-    let domains = Domain::random_domains_in_parent(20, 100, "dash");
+    let domains = Domain::random_domains_in_parent(20, None, 100, "dash");
     let contract = json_document_to_contract(
         "tests/supporting_files/contract/dpns/dpns-contract.json",
         false,
@@ -566,8 +591,10 @@ fn test_serialization_and_deserialization_with_null_values() {
 #[cfg(feature = "server")]
 impl Domain {
     /// Creates `count` random names as domain names for the given parent domain
+    /// If total owners in None it will create a new owner id per domain.
     fn random_domains_in_parent(
         count: u32,
+        total_owners: Option<u32>,
         seed: u64,
         normalized_parent_domain_name: &str,
     ) -> Vec<Self> {
@@ -575,13 +602,29 @@ impl Domain {
             "tests/supporting_files/contract/family/first-names.txt",
         );
         let mut vec: Vec<Domain> = Vec::with_capacity(count as usize);
+        let mut rng = StdRng::seed_from_u64(seed);
+
+        let owners = if let Some(total_owners) = total_owners {
+            if total_owners == 0 {
+                return vec![];
+            }
+            (0..total_owners)
+                .map(|_| Identifier::random_with_rng(&mut rng))
+                .collect()
+        } else {
+            vec![]
+        };
 
-        let mut rng = rand::rngs::StdRng::seed_from_u64(seed);
         for _i in 0..count {
             let label = first_names.choose(&mut rng).unwrap();
             let domain = Domain {
                 id: Identifier::random_with_rng(&mut rng),
-                owner_id: Identifier::random_with_rng(&mut rng),
+                owner_id: if let Some(_) = total_owners {
+                    // Pick a random owner from the owners list
+                    *owners.choose(&mut rng).unwrap()
+                } else {
+                    Identifier::random_with_rng(&mut rng)
+                },
                 label: Some(label.clone()),
                 normalized_label: Some(label.to_lowercase()),
                 normalized_parent_domain_name: normalized_parent_domain_name.to_string(),
@@ -599,6 +642,75 @@ impl Domain {
     }
 }
 
+#[cfg(feature = "server")]
+impl Withdrawal {
+    /// Generate `count` random withdrawals
+    /// If `total_owners` is provided, assigns withdrawals to random owners from a predefined set.
+    pub fn random_withdrawals(count: u32, total_owners: Option<u32>, seed: u64) -> Vec<Self> {
+        let mut rng = StdRng::seed_from_u64(seed);
+
+        // Generate a list of random owners if `total_owners` is provided
+        let owners: Vec<Identifier> = if let Some(total) = total_owners {
+            (0..total)
+                .map(|_| Identifier::random_with_rng(&mut rng))
+                .collect()
+        } else {
+            vec![]
+        };
+
+        let mut next_transaction_index = 1; // Start transaction index from 1
+
+        let mut next_timestamp = 1732192259000;
+
+        (0..count)
+            .map(|_| {
+                let owner_id = if !owners.is_empty() {
+                    *owners.choose(&mut rng).unwrap()
+                } else {
+                    Identifier::random_with_rng(&mut rng)
+                };
+
+                // Determine the status randomly
+                let status = if rng.gen_bool(0.5) {
+                    0
+                } else {
+                    rng.gen_range(1..=4)
+                }; // 0 = Pending, 1-4 = other statuses
+
+                // Determine transaction index and sign height based on status
+                let (transaction_index, transaction_sign_height) = if status == 0 {
+                    (None, None) // No transaction index or sign height for Pending status
+                } else {
+                    let index = next_transaction_index;
+                    next_transaction_index += 1; // Increment index for next withdrawal
+                    (Some(index), Some(rng.gen_range(1..=500000))) // Set sign height only if transaction index is set
+                };
+
+                let output_script_length = rng.gen_range(23..=25);
+                let output_script: Vec<u8> = (0..output_script_length).map(|_| rng.gen()).collect();
+
+                let created_at = next_timestamp;
+
+                next_timestamp += rng.gen_range(0..3) * 2000;
+
+                Withdrawal {
+                    id: Identifier::random_with_rng(&mut rng),
+                    owner_id,
+                    transaction_index,
+                    transaction_sign_height,
+                    amount: rng.gen_range(1000..=1_000_000), // Example range (minimum: 1000)
+                    core_fee_per_byte: 0,                    // Always 0
+                    pooling: 0,                              // Always 0
+                    output_script,
+                    status,
+                    created_at,
+                    updated_at: created_at,
+                }
+            })
+            .collect()
+    }
+}
+
 #[cfg(feature = "server")]
 /// Adds `count` random domain names to the given contract
 pub fn add_domains_to_contract(
@@ -606,10 +718,11 @@ pub fn add_domains_to_contract(
     contract: &DataContract,
     transaction: TransactionArg,
     count: u32,
+    total_owners: Option<u32>,
     seed: u64,
 ) {
     let platform_version = PlatformVersion::latest();
-    let domains = Domain::random_domains_in_parent(count, seed, "dash");
+    let domains = Domain::random_domains_in_parent(count, total_owners, seed, "dash");
     let document_type = contract
         .document_type_for_name("domain")
         .expect("expected to get document type");
@@ -641,9 +754,56 @@ pub fn add_domains_to_contract(
     }
 }
 
+#[cfg(feature = "server")]
+/// Adds `count` random withdrawals to the given contract
+pub fn add_withdrawals_to_contract(
+    drive: &Drive,
+    contract: &DataContract,
+    transaction: TransactionArg,
+    count: u32,
+    total_owners: Option<u32>,
+    seed: u64,
+) {
+    let platform_version = PlatformVersion::latest();
+    let withdrawals = Withdrawal::random_withdrawals(count, total_owners, seed);
+    let document_type = contract
+        .document_type_for_name("withdrawal")
+        .expect("expected to get document type");
+    for domain in withdrawals {
+        let value = platform_value::to_value(domain).expect("expected value");
+        let document =
+            Document::from_platform_value(value, platform_version).expect("expected value");
+
+        let storage_flags = Some(Cow::Owned(StorageFlags::SingleEpoch(0)));
+
+        drive
+            .add_document_for_contract(
+                DocumentAndContractInfo {
+                    owned_document_info: OwnedDocumentInfo {
+                        document_info: DocumentRefInfo((&document, storage_flags)),
+                        owner_id: None,
+                    },
+                    contract,
+                    document_type,
+                },
+                true,
+                BlockInfo::genesis(),
+                true,
+                transaction,
+                platform_version,
+                None,
+            )
+            .expect("document should be inserted");
+    }
+}
+
 #[cfg(feature = "server")]
 /// Sets up and inserts random domain name data to the DPNS contract to test queries on.
-pub fn setup_dpns_tests_with_batches(count: u32, seed: u64) -> (Drive, DataContract) {
+pub fn setup_dpns_tests_with_batches(
+    count: u32,
+    total_owners: Option<u32>,
+    seed: u64,
+) -> (Drive, DataContract) {
     let drive = setup_drive(Some(DriveConfig::default()));
 
     let db_transaction = drive.grove.start_transaction();
@@ -667,7 +827,61 @@ pub fn setup_dpns_tests_with_batches(count: u32, seed: u64) -> (Drive, DataContr
         Some(&db_transaction),
     );
 
-    add_domains_to_contract(&drive, &contract, Some(&db_transaction), count, seed);
+    add_domains_to_contract(
+        &drive,
+        &contract,
+        Some(&db_transaction),
+        count,
+        total_owners,
+        seed,
+    );
+    drive
+        .grove
+        .commit_transaction(db_transaction)
+        .unwrap()
+        .expect("transaction should be committed");
+
+    (drive, contract)
+}
+
+#[cfg(feature = "server")]
+/// Sets up and inserts random withdrawal to the Withdrawal contract to test queries on.
+pub fn setup_withdrawal_tests(
+    count: u32,
+    total_owners: Option<u32>,
+    seed: u64,
+) -> (Drive, DataContract) {
+    let drive = setup_drive(Some(DriveConfig::default()));
+
+    let db_transaction = drive.grove.start_transaction();
+
+    // Create contracts tree
+    let mut batch = GroveDbOpBatch::new();
+
+    add_init_contracts_structure_operations(&mut batch);
+
+    let platform_version = PlatformVersion::latest();
+
+    drive
+        .grove_apply_batch(batch, false, Some(&db_transaction), &platform_version.drive)
+        .expect("expected to create contracts tree successfully");
+
+    // setup code
+    let contract = setup_contract(
+        &drive,
+        "tests/supporting_files/contract/withdrawals/withdrawals-contract.json",
+        None,
+        Some(&db_transaction),
+    );
+
+    add_withdrawals_to_contract(
+        &drive,
+        &contract,
+        Some(&db_transaction),
+        count,
+        total_owners,
+        seed,
+    );
     drive
         .grove
         .commit_transaction(db_transaction)
@@ -738,7 +952,7 @@ pub fn setup_dpns_tests_label_not_required(count: u32, seed: u64) -> (Drive, Dat
         Some(&db_transaction),
     );
 
-    add_domains_to_contract(&drive, &contract, Some(&db_transaction), count, seed);
+    add_domains_to_contract(&drive, &contract, Some(&db_transaction), count, None, seed);
     drive
         .grove
         .commit_transaction(db_transaction)
@@ -3078,7 +3292,7 @@ fn test_query_with_cached_contract() {
 #[cfg(feature = "server")]
 #[test]
 fn test_dpns_query_contract_verification() {
-    let (drive, contract) = setup_dpns_tests_with_batches(10, 11456);
+    let (drive, contract) = setup_dpns_tests_with_batches(10, None, 11456);
 
     let platform_version = PlatformVersion::latest();
 
@@ -3155,7 +3369,7 @@ fn test_contract_keeps_history_fetch_and_verification() {
 #[cfg(feature = "server")]
 #[test]
 fn test_dpns_query() {
-    let (drive, contract) = setup_dpns_tests_with_batches(10, 11456);
+    let (drive, contract) = setup_dpns_tests_with_batches(10, None, 11456);
 
     let platform_version = PlatformVersion::latest();
 
@@ -3707,7 +3921,7 @@ fn test_dpns_insertion_with_aliases() {
 #[test]
 fn test_dpns_query_start_at() {
     // The point of this test is to test the situation where we have a start at a certain value for the DPNS query.
-    let (drive, contract) = setup_dpns_tests_with_batches(10, 11456);
+    let (drive, contract) = setup_dpns_tests_with_batches(10, None, 11456);
 
     let platform_version = PlatformVersion::latest();
 
@@ -3801,7 +4015,7 @@ fn test_dpns_query_start_at() {
 #[test]
 fn test_dpns_query_start_after() {
     // The point of this test is to test the situation where we have a start at a certain value for the DPNS query.
-    let (drive, contract) = setup_dpns_tests_with_batches(10, 11456);
+    let (drive, contract) = setup_dpns_tests_with_batches(10, None, 11456);
 
     let platform_version = PlatformVersion::latest();
 
@@ -3895,7 +4109,7 @@ fn test_dpns_query_start_after() {
 #[test]
 fn test_dpns_query_start_at_desc() {
     // The point of this test is to test the situation where we have a start at a certain value for the DPNS query.
-    let (drive, contract) = setup_dpns_tests_with_batches(10, 11456);
+    let (drive, contract) = setup_dpns_tests_with_batches(10, None, 11456);
 
     let platform_version = PlatformVersion::latest();
 
@@ -3989,7 +4203,7 @@ fn test_dpns_query_start_at_desc() {
 #[test]
 fn test_dpns_query_start_after_desc() {
     // The point of this test is to test the situation where we have a start at a certain value for the DPNS query.
-    let (drive, contract) = setup_dpns_tests_with_batches(10, 11456);
+    let (drive, contract) = setup_dpns_tests_with_batches(10, None, 11456);
 
     let platform_version = PlatformVersion::latest();
 
@@ -4804,6 +5018,185 @@ fn test_dpns_query_start_after_with_null_id_desc() {
     assert_eq!(results, proof_results);
 }
 
+#[cfg(feature = "server")]
+#[test]
+fn test_withdrawals_query_by_owner_id() {
+    // We create 10 withdrawals owned by 2 identities
+    let (drive, contract) = setup_withdrawal_tests(10, Some(2), 11456);
+
+    let platform_version = PlatformVersion::latest();
+
+    let db_transaction = drive.grove.start_transaction();
+
+    let root_hash = drive
+        .grove
+        .root_hash(Some(&db_transaction), &platform_version.drive.grove_version)
+        .unwrap()
+        .expect("there is always a root hash");
+
+    let expected_app_hash = vec![
+        144, 177, 24, 41, 104, 174, 220, 135, 164, 0, 240, 215, 42, 60, 249, 142, 150, 169, 135,
+        72, 151, 35, 238, 131, 164, 229, 106, 83, 198, 109, 65, 211,
+    ];
+
+    assert_eq!(root_hash.as_slice(), expected_app_hash);
+
+    // Document Ids are
+    // document v0 : id:2kTB6gW4wCCnySj3UFUJQM3aUYBd6qDfLCY74BnWmFKu owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:09 updated_at:2024-11-21 12:31:09 amount:(i64)646767 coreFeePerByte:(i64)0 outputScript:bytes 00952c808390e575c8dd29fc07ccfed7b428e1ec2ffcb23e pooling:(i64)0 status:(i64)1 transactionIndex:(i64)4 transactionSignHeight:(i64)303186
+    // document v0 : id:3T4aKmidGKA4ETnWYSedm6ETzrcdkfPL2r3D6eg6CSib owner_id:CH1EHBkN5FUuQ7z8ep1abroLPzzYjagvM5XV2NYR3DEh created_at:2024-11-21 12:31:01 updated_at:2024-11-21 12:31:01 amount:(i64)971045 coreFeePerByte:(i64)0 outputScript:bytes 525dfc160c160a7a52ef3301a7e55fccf41d73857f50a55a4d pooling:(i64)0 status:(i64)1 transactionIndex:(i64)2 transactionSignHeight:(i64)248787
+    // document v0 : id:3X2QfUfR8EeVZQAKmEjcue5xDv3CZXrfPTgXkZ5vQo13 owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:11 updated_at:2024-11-21 12:31:11 amount:(i64)122155 coreFeePerByte:(i64)0 outputScript:bytes f76eb8b953ff41040d906c25a4ae42884bedb41a07fc3a pooling:(i64)0 status:(i64)3 transactionIndex:(i64)7 transactionSignHeight:(i64)310881
+    // document v0 : id:5ikeRNwvFekr6ex32B4dLEcCaSsgXXHJBx5rJ2rwuhEV owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:30:59 updated_at:2024-11-21 12:30:59 amount:(i64)725014 coreFeePerByte:(i64)0 outputScript:bytes 51f203a755a7ff25ba8645841f80403ee98134690b2c0dd5e2 pooling:(i64)0 status:(i64)3 transactionIndex:(i64)1 transactionSignHeight:(i64)4072
+    // document v0 : id:74giZJn9fNczYRsxxh3wVnktJS1vzTiRWYinKK1rRcyj owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:11 updated_at:2024-11-21 12:31:11 amount:(i64)151943 coreFeePerByte:(i64)0 outputScript:bytes 9db03f4c8a51e4e9855e008aae6121911b4831699c53ed pooling:(i64)0 status:(i64)1 transactionIndex:(i64)5 transactionSignHeight:(i64)343099
+    // document v0 : id:8iqDAFxTzHYcmUWtcNnCRoj9Fss4HE1G3GP3HhVAZJhn owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:13 updated_at:2024-11-21 12:31:13 amount:(i64)409642 coreFeePerByte:(i64)0 outputScript:bytes 19fe0a2458a47e1726191f4dc94d11bcfacf821d024043 pooling:(i64)0 status:(i64)4 transactionIndex:(i64)8 transactionSignHeight:(i64)304397
+    // document v0 : id:BdH274iP17nhquQVY4KMCAM6nwyPRc8AFJkUT91vxhbc owner_id:CH1EHBkN5FUuQ7z8ep1abroLPzzYjagvM5XV2NYR3DEh created_at:2024-11-21 12:31:03 updated_at:2024-11-21 12:31:03 amount:(i64)81005 coreFeePerByte:(i64)0 outputScript:bytes 2666e87b6cc7ddf2b63e7e52c348818c05e5562efa48f5 pooling:(i64)0 status:(i64)0
+    // document v0 : id:CCjaU67Pe79Vt51oXvQ5SkyNiypofNX9DS9PYydN9tpD owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:01 updated_at:2024-11-21 12:31:01 amount:(i64)455074 coreFeePerByte:(i64)0 outputScript:bytes acde2e1652771b50a2c68fd330ee1d4b8e115631ce72375432 pooling:(i64)0 status:(i64)3 transactionIndex:(i64)3 transactionSignHeight:(i64)261103
+    // document v0 : id:DxFzXvkb2mNQHmeVknsv3gWsc6rMtLk9AsS5zMpy6hou owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:05 updated_at:2024-11-21 12:31:05 amount:(i64)271303 coreFeePerByte:(i64)0 outputScript:bytes 0b845e8c3a4679f1913172f7fd939cc153f458519de8ed3d pooling:(i64)0 status:(i64)0
+    // document v0 : id:FDnvFN7e72LcZEojTWNmJTP7uzok3BtvbKnaa5gjqCpW owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:11 updated_at:2024-11-21 12:31:11 amount:(i64)123433 coreFeePerByte:(i64)0 outputScript:bytes 82712473b2d0fc5663afb1a08006913ccccbf38e091a8cc7 pooling:(i64)0 status:(i64)4 transactionIndex:(i64)6 transactionSignHeight:(i64)319518
+
+    let query_value = json!({
+        "where": [
+            ["$ownerId", "==", "A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ"]
+        ],
+        "limit": 2
+    });
+    let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
+        .expect("expected to serialize to cbor");
+    let domain_document_type = contract
+        .document_type_for_name("withdrawal")
+        .expect("contract should have a domain document type");
+    let query = DriveDocumentQuery::from_cbor(
+        where_cbor.as_slice(),
+        &contract,
+        domain_document_type,
+        &drive.config,
+    )
+    .expect("query should be built");
+    let (results, _, _) = query
+        .execute_raw_results_no_proof(&drive, None, Some(&db_transaction), platform_version)
+        .expect("proof should be executed");
+    let names: Vec<String> = results
+        .iter()
+        .map(|result| {
+            let document =
+                Document::from_bytes(result.as_slice(), domain_document_type, platform_version)
+                    .expect("we should be able to deserialize the document");
+            document.id().to_string(Encoding::Base58)
+        })
+        .collect();
+
+    let a_names = [
+        "5ikeRNwvFekr6ex32B4dLEcCaSsgXXHJBx5rJ2rwuhEV".to_string(),
+        "CCjaU67Pe79Vt51oXvQ5SkyNiypofNX9DS9PYydN9tpD".to_string(),
+    ];
+
+    assert_eq!(names, a_names);
+
+    let (proof_root_hash, proof_results, _) = query
+        .execute_with_proof_only_get_elements(&drive, None, None, platform_version)
+        .expect("we should be able to a proof");
+    assert_eq!(root_hash, proof_root_hash);
+    assert_eq!(results, proof_results);
+}
+
+#[cfg(feature = "server")]
+#[test]
+fn test_withdrawals_query_start_after_query_by_owner_id() {
+    // We create 10 withdrawals owned by 2 identities
+    let (drive, contract) = setup_withdrawal_tests(10, Some(2), 11456);
+
+    let platform_version = PlatformVersion::latest();
+
+    let db_transaction = drive.grove.start_transaction();
+
+    let root_hash = drive
+        .grove
+        .root_hash(Some(&db_transaction), &platform_version.drive.grove_version)
+        .unwrap()
+        .expect("there is always a root hash");
+
+    let expected_app_hash = vec![
+        144, 177, 24, 41, 104, 174, 220, 135, 164, 0, 240, 215, 42, 60, 249, 142, 150, 169, 135,
+        72, 151, 35, 238, 131, 164, 229, 106, 83, 198, 109, 65, 211,
+    ];
+
+    assert_eq!(root_hash.as_slice(), expected_app_hash);
+
+    // Document Ids are
+    // document v0 : id:2kTB6gW4wCCnySj3UFUJQM3aUYBd6qDfLCY74BnWmFKu owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:09 updated_at:2024-11-21 12:31:09 amount:(i64)646767 coreFeePerByte:(i64)0 outputScript:bytes 00952c808390e575c8dd29fc07ccfed7b428e1ec2ffcb23e pooling:(i64)0 status:(i64)1 transactionIndex:(i64)4 transactionSignHeight:(i64)303186
+    // document v0 : id:3T4aKmidGKA4ETnWYSedm6ETzrcdkfPL2r3D6eg6CSib owner_id:CH1EHBkN5FUuQ7z8ep1abroLPzzYjagvM5XV2NYR3DEh created_at:2024-11-21 12:31:01 updated_at:2024-11-21 12:31:01 amount:(i64)971045 coreFeePerByte:(i64)0 outputScript:bytes 525dfc160c160a7a52ef3301a7e55fccf41d73857f50a55a4d pooling:(i64)0 status:(i64)1 transactionIndex:(i64)2 transactionSignHeight:(i64)248787
+    // document v0 : id:3X2QfUfR8EeVZQAKmEjcue5xDv3CZXrfPTgXkZ5vQo13 owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:11 updated_at:2024-11-21 12:31:11 amount:(i64)122155 coreFeePerByte:(i64)0 outputScript:bytes f76eb8b953ff41040d906c25a4ae42884bedb41a07fc3a pooling:(i64)0 status:(i64)3 transactionIndex:(i64)7 transactionSignHeight:(i64)310881
+    // document v0 : id:5ikeRNwvFekr6ex32B4dLEcCaSsgXXHJBx5rJ2rwuhEV owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:30:59 updated_at:2024-11-21 12:30:59 amount:(i64)725014 coreFeePerByte:(i64)0 outputScript:bytes 51f203a755a7ff25ba8645841f80403ee98134690b2c0dd5e2 pooling:(i64)0 status:(i64)3 transactionIndex:(i64)1 transactionSignHeight:(i64)4072
+    // document v0 : id:74giZJn9fNczYRsxxh3wVnktJS1vzTiRWYinKK1rRcyj owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:11 updated_at:2024-11-21 12:31:11 amount:(i64)151943 coreFeePerByte:(i64)0 outputScript:bytes 9db03f4c8a51e4e9855e008aae6121911b4831699c53ed pooling:(i64)0 status:(i64)1 transactionIndex:(i64)5 transactionSignHeight:(i64)343099
+    // document v0 : id:8iqDAFxTzHYcmUWtcNnCRoj9Fss4HE1G3GP3HhVAZJhn owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:13 updated_at:2024-11-21 12:31:13 amount:(i64)409642 coreFeePerByte:(i64)0 outputScript:bytes 19fe0a2458a47e1726191f4dc94d11bcfacf821d024043 pooling:(i64)0 status:(i64)4 transactionIndex:(i64)8 transactionSignHeight:(i64)304397
+    // document v0 : id:BdH274iP17nhquQVY4KMCAM6nwyPRc8AFJkUT91vxhbc owner_id:CH1EHBkN5FUuQ7z8ep1abroLPzzYjagvM5XV2NYR3DEh created_at:2024-11-21 12:31:03 updated_at:2024-11-21 12:31:03 amount:(i64)81005 coreFeePerByte:(i64)0 outputScript:bytes 2666e87b6cc7ddf2b63e7e52c348818c05e5562efa48f5 pooling:(i64)0 status:(i64)0
+    // document v0 : id:CCjaU67Pe79Vt51oXvQ5SkyNiypofNX9DS9PYydN9tpD owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:01 updated_at:2024-11-21 12:31:01 amount:(i64)455074 coreFeePerByte:(i64)0 outputScript:bytes acde2e1652771b50a2c68fd330ee1d4b8e115631ce72375432 pooling:(i64)0 status:(i64)3 transactionIndex:(i64)3 transactionSignHeight:(i64)261103
+    // document v0 : id:DxFzXvkb2mNQHmeVknsv3gWsc6rMtLk9AsS5zMpy6hou owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:05 updated_at:2024-11-21 12:31:05 amount:(i64)271303 coreFeePerByte:(i64)0 outputScript:bytes 0b845e8c3a4679f1913172f7fd939cc153f458519de8ed3d pooling:(i64)0 status:(i64)0
+    // document v0 : id:FDnvFN7e72LcZEojTWNmJTP7uzok3BtvbKnaa5gjqCpW owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:11 updated_at:2024-11-21 12:31:11 amount:(i64)123433 coreFeePerByte:(i64)0 outputScript:bytes 82712473b2d0fc5663afb1a08006913ccccbf38e091a8cc7 pooling:(i64)0 status:(i64)4 transactionIndex:(i64)6 transactionSignHeight:(i64)319518
+
+    let query_value = json!({
+        "where": [
+            ["$ownerId", "==", "A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ"]
+        ],
+        "startAfter":  "CCjaU67Pe79Vt51oXvQ5SkyNiypofNX9DS9PYydN9tpD",
+        "limit": 2,
+    });
+
+    // This will use the identity recent index
+    // {
+    //     "name": "identityRecent",
+    //     "properties": [
+    //     {
+    //         "$ownerId": "asc"
+    //     },
+    //     {
+    //         "$updatedAt": "asc"
+    //     },
+    //     {
+    //         "status": "asc"
+    //     }
+    //     ],
+    //     "unique": false
+    // },
+
+    let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
+        .expect("expected to serialize to cbor");
+    let domain_document_type = contract
+        .document_type_for_name("withdrawal")
+        .expect("contract should have a domain document type");
+    let query = DriveDocumentQuery::from_cbor(
+        where_cbor.as_slice(),
+        &contract,
+        domain_document_type,
+        &drive.config,
+    )
+    .expect("query should be built");
+    let (results, _, _) = query
+        .execute_raw_results_no_proof(&drive, None, Some(&db_transaction), platform_version)
+        .expect("proof should be executed");
+    let names: Vec<String> = results
+        .iter()
+        .map(|result| {
+            let document =
+                Document::from_bytes(result.as_slice(), domain_document_type, platform_version)
+                    .expect("we should be able to deserialize the document");
+            document.id().to_string(Encoding::Base58)
+        })
+        .collect();
+
+    let a_names = [
+        "DxFzXvkb2mNQHmeVknsv3gWsc6rMtLk9AsS5zMpy6hou".to_string(),
+        "2kTB6gW4wCCnySj3UFUJQM3aUYBd6qDfLCY74BnWmFKu".to_string(),
+    ];
+
+    assert_eq!(names, a_names);
+
+    let (proof_root_hash, proof_results, _) = query
+        .execute_with_proof_only_get_elements(&drive, None, None, platform_version)
+        .expect("we should be able to a proof");
+    assert_eq!(root_hash, proof_root_hash);
+    assert_eq!(results, proof_results);
+}
+
 #[cfg(feature = "server")]
 #[test]
 fn test_query_a_b_c_d_e_contract() {
diff --git a/packages/rs-drive/tests/supporting_files/contract/withdrawals/withdrawals-contract.json b/packages/rs-drive/tests/supporting_files/contract/withdrawals/withdrawals-contract.json
new file mode 100644
index 0000000000..5e12831bef
--- /dev/null
+++ b/packages/rs-drive/tests/supporting_files/contract/withdrawals/withdrawals-contract.json
@@ -0,0 +1,141 @@
+{
+  "$format_version": "0",
+  "id": "A6Z7WkPjzp8Qe77Av5PNxY2E8JFCYpSVdJ8tZE94PErh",
+  "ownerId": "B1XbULsStFtFhJoc6qmMKx8a3nH4YCsotupSWoBiFaKr",
+  "version": 1,
+  "documentSchemas": {
+    "withdrawal": {
+      "description": "Withdrawal document to track underlying withdrawal transactions. Withdrawals should be created with IdentityWithdrawalTransition",
+      "creationRestrictionMode": 2,
+      "type": "object",
+      "indices": [
+        {
+          "name": "identityStatus",
+          "properties": [
+            {
+              "$ownerId": "asc"
+            },
+            {
+              "status": "asc"
+            },
+            {
+              "$createdAt": "asc"
+            }
+          ],
+          "unique": false
+        },
+        {
+          "name": "identityRecent",
+          "properties": [
+            {
+              "$ownerId": "asc"
+            },
+            {
+              "$updatedAt": "asc"
+            },
+            {
+              "status": "asc"
+            }
+          ],
+          "unique": false
+        },
+        {
+          "name": "pooling",
+          "properties": [
+            {
+              "status": "asc"
+            },
+            {
+              "pooling": "asc"
+            },
+            {
+              "coreFeePerByte": "asc"
+            },
+            {
+              "$updatedAt": "asc"
+            }
+          ],
+          "unique": false
+        },
+        {
+          "name": "transaction",
+          "properties": [
+            {
+              "status": "asc"
+            },
+            {
+              "transactionIndex": "asc"
+            }
+          ],
+          "unique": false
+        }
+      ],
+      "properties": {
+        "transactionIndex": {
+          "type": "integer",
+          "description": "Sequential index of asset unlock (withdrawal) transaction. Populated when a withdrawal pooled into withdrawal transaction",
+          "minimum": 1,
+          "position": 0
+        },
+        "transactionSignHeight": {
+          "type": "integer",
+          "description": "The Core height on which transaction was signed",
+          "minimum": 1,
+          "position": 1
+        },
+        "amount": {
+          "type": "integer",
+          "description": "The amount to be withdrawn",
+          "minimum": 1000,
+          "position": 2
+        },
+        "coreFeePerByte": {
+          "type": "integer",
+          "description": "This is the fee that you are willing to spend for this transaction in Duffs/Byte",
+          "minimum": 1,
+          "maximum": 4294967295,
+          "position": 3
+        },
+        "pooling": {
+          "type": "integer",
+          "description": "This indicated the level at which Platform should try to pool this transaction",
+          "enum": [
+            0,
+            1,
+            2
+          ],
+          "position": 4
+        },
+        "outputScript": {
+          "type": "array",
+          "byteArray": true,
+          "minItems": 23,
+          "maxItems": 25,
+          "position": 5
+        },
+        "status": {
+          "type": "integer",
+          "enum": [
+            0,
+            1,
+            2,
+            3,
+            4
+          ],
+          "description": "0 - Pending, 1 - Signed, 2 - Broadcasted, 3 - Complete, 4 - Expired",
+          "position": 6
+        }
+      },
+      "additionalProperties": false,
+      "required": [
+        "$createdAt",
+        "$updatedAt",
+        "amount",
+        "coreFeePerByte",
+        "pooling",
+        "outputScript",
+        "status"
+      ]
+    }
+  }
+}
\ No newline at end of file

From 6635cf6bde71b0e0a174b2fa586a5b663ec20a99 Mon Sep 17 00:00:00 2001
From: Quantum Explorer <quantum@dash.org>
Date: Mon, 25 Nov 2024 03:00:01 +0300
Subject: [PATCH 2/6] fixed

---
 packages/rs-drive/src/query/mod.rs     | 89 +++++++++++---------------
 packages/rs-drive/tests/query_tests.rs |  7 +-
 2 files changed, 41 insertions(+), 55 deletions(-)

diff --git a/packages/rs-drive/src/query/mod.rs b/packages/rs-drive/src/query/mod.rs
index e5e569489b..0ec6e97902 100644
--- a/packages/rs-drive/src/query/mod.rs
+++ b/packages/rs-drive/src/query/mod.rs
@@ -1567,6 +1567,7 @@ impl<'a> DriveDocumentQuery<'a> {
                         document_type,
                         included,
                     } = start_at_document_inner;
+                    println!("document is {}", document);
                     let start_at_key = document
                         .get_raw_for_document_type(
                             first.name.as_str(),
@@ -1630,6 +1631,7 @@ impl<'a> DriveDocumentQuery<'a> {
                     // │   │   │   │   │   ├── 4
                     // │   │   │   │   │   │   ├── 1f7a8...
                     // │   │   │   │   │   │   └── 2c9b3...
+                    println!("going to call recursive_insert_on_query on non_conditional_query {} with left_over {:?}", non_conditional_query, left_over);
                     DriveDocumentQuery::recursive_insert_on_query(
                         &mut non_conditional_query,
                         left_over,
@@ -1640,15 +1642,16 @@ impl<'a> DriveDocumentQuery<'a> {
                         platform_version,
                     )?;
 
-                    // DriveDocumentQuery::recursive_conditional_insert_on_query(
-                    //     &mut non_conditional_query,
-                    //     left_over,
-                    //     unique,
-                    //     start_at_document_inner,
-                    //     left_to_right,
-                    //     order_by,
-                    //     platform_version,
-                    // )?;
+                    DriveDocumentQuery::recursive_conditional_insert_on_query(
+                        &mut non_conditional_query,
+                        start_at_key,
+                        left_over,
+                        unique,
+                        start_at_document_inner,
+                        left_to_right,
+                        order_by,
+                        platform_version,
+                    )?;
 
                     query.set_subquery(non_conditional_query);
                 } else {
@@ -1672,19 +1675,18 @@ impl<'a> DriveDocumentQuery<'a> {
     }
     fn recursive_conditional_insert_on_query(
         query: &mut Query,
+        conditional_value: Option<Vec<u8>>,
         left_over_index_properties: &[&IndexProperty],
         unique: bool,
         starts_at_document: &StartAtDocument,
         default_left_to_right: bool,
         order_by: Option<&IndexMap<String, OrderClause>>,
         platform_version: &PlatformVersion,
-    ) -> Result<Option<Query>, Error> {
+    ) -> Result<(), Error> {
         match left_over_index_properties.split_first() {
             None => {
                 match unique {
                     true => {
-                        query.set_subquery_key(vec![0]);
-
                         // In the case things are NULL we allow to have multiple values
                         let inner_query = Self::inner_query_from_starts_at_for_id(
                             Some(starts_at_document),
@@ -1697,25 +1699,18 @@ impl<'a> DriveDocumentQuery<'a> {
                         );
                     }
                     false => {
-                        query.set_subquery_key(vec![0]);
-                        // we just get all by document id order ascending
-                        let full_query =
-                            Self::inner_query_from_starts_at_for_id(None, default_left_to_right);
-                        query.set_subquery(full_query);
-
                         let inner_query = Self::inner_query_from_starts_at_for_id(
                             Some(starts_at_document),
                             default_left_to_right,
                         );
 
                         query.add_conditional_subquery(
-                            QueryItem::Key(b"".to_vec()),
+                            QueryItem::Key(conditional_value.unwrap_or_default()),
                             Some(vec![vec![0]]),
                             Some(inner_query),
                         );
                     }
                 }
-                Ok(None)
             }
             Some((first, left_over)) => {
                 let left_to_right = if let Some(order_by) = order_by {
@@ -1730,11 +1725,10 @@ impl<'a> DriveDocumentQuery<'a> {
                 let StartAtDocument {
                     document,
                     document_type,
-                    included,
                     ..
                 } = starts_at_document;
 
-                let start_at_key = document
+                let lower_start_at_key = document
                     .get_raw_for_document_type(
                         first.name.as_str(),
                         *document_type,
@@ -1744,12 +1738,12 @@ impl<'a> DriveDocumentQuery<'a> {
                     .ok()
                     .flatten();
 
-                // We should always include if we have left_over
-                let non_conditional_included =
-                    !left_over.is_empty() || *included || start_at_key.is_none();
+                // We include it if we are not unique,
+                // or if we are unique but the value is empty
+                let non_conditional_included = !unique || lower_start_at_key.is_none();
 
                 let mut non_conditional_query = Self::inner_query_starts_from_key(
-                    start_at_key.clone(),
+                    lower_start_at_key.clone(),
                     left_to_right,
                     non_conditional_included,
                 );
@@ -1764,36 +1758,25 @@ impl<'a> DriveDocumentQuery<'a> {
                     platform_version,
                 )?;
 
-                if let Some(start_at_key) = start_at_key {
-                    match left_over.split_first() {
-                        None => {}
-                        Some((next, lower_left_over)) => {
-                            let mut conditional_query = non_conditional_query.clone();
-                            DriveDocumentQuery::recursive_insert_on_query(
-                                &mut conditional_query,
-                                lower_left_over,
-                                unique,
-                                Some(starts_at_document),
-                                left_to_right,
-                                order_by,
-                                platform_version,
-                            )?;
-
-                            non_conditional_query.add_conditional_subquery(
-                                QueryItem::Key(start_at_key),
-                                Some(vec![next.name.as_bytes().to_vec()]),
-                                Some(conditional_query),
-                            );
-                        }
-                    }
-                }
-
-                query.set_subquery(non_conditional_query);
+                DriveDocumentQuery::recursive_conditional_insert_on_query(
+                    &mut non_conditional_query,
+                    lower_start_at_key, //todo
+                    left_over,
+                    unique,
+                    starts_at_document,
+                    left_to_right,
+                    order_by,
+                    platform_version,
+                )?;
 
-                query.set_subquery_key(first.name.as_bytes().to_vec());
-                Ok(None)
+                query.add_conditional_subquery(
+                    QueryItem::Key(conditional_value.unwrap_or_default()),
+                    Some(vec![first.name.as_bytes().to_vec()]),
+                    Some(non_conditional_query),
+                );
             }
         }
+        Ok(())
     }
 
     #[cfg(any(feature = "server", feature = "verify"))]
diff --git a/packages/rs-drive/tests/query_tests.rs b/packages/rs-drive/tests/query_tests.rs
index 4dac50d89d..9501206f5d 100644
--- a/packages/rs-drive/tests/query_tests.rs
+++ b/packages/rs-drive/tests/query_tests.rs
@@ -4679,7 +4679,8 @@ fn test_dpns_query_start_after_with_null_id() {
                     .expect("we should be able to deserialize the document");
             let normalized_label_value = document
                 .get("normalizedLabel")
-                .expect("we should be able to get the first name");
+                .cloned()
+                .unwrap_or(Value::Null);
             if normalized_label_value.is_null() {
                 String::from("")
             } else {
@@ -5138,7 +5139,7 @@ fn test_withdrawals_query_start_after_query_by_owner_id() {
             ["$ownerId", "==", "A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ"]
         ],
         "startAfter":  "CCjaU67Pe79Vt51oXvQ5SkyNiypofNX9DS9PYydN9tpD",
-        "limit": 2,
+        "limit": 3,
     });
 
     // This will use the identity recent index
@@ -5183,6 +5184,8 @@ fn test_withdrawals_query_start_after_query_by_owner_id() {
         })
         .collect();
 
+    // We only get back 2 values, even though we put limit 3 because the time with status 0 is an
+    // empty tree and consumes a limit
     let a_names = [
         "DxFzXvkb2mNQHmeVknsv3gWsc6rMtLk9AsS5zMpy6hou".to_string(),
         "2kTB6gW4wCCnySj3UFUJQM3aUYBd6qDfLCY74BnWmFKu".to_string(),

From 48ac37c5aa106f1cf9bcb89d248a8cf983a5f29a Mon Sep 17 00:00:00 2001
From: Quantum Explorer <quantum@dash.org>
Date: Mon, 25 Nov 2024 03:01:53 +0300
Subject: [PATCH 3/6] clean up

---
 packages/rs-drive/src/query/mod.rs | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/packages/rs-drive/src/query/mod.rs b/packages/rs-drive/src/query/mod.rs
index 0ec6e97902..af32e78d4b 100644
--- a/packages/rs-drive/src/query/mod.rs
+++ b/packages/rs-drive/src/query/mod.rs
@@ -1380,6 +1380,8 @@ impl<'a> DriveDocumentQuery<'a> {
         }
         Ok(inner_query)
     }
+
+    #[cfg(any(feature = "server", feature = "verify"))]
     fn recursive_create_query(
         left_over_index_properties: &[&IndexProperty],
         unique: bool,
@@ -1567,7 +1569,6 @@ impl<'a> DriveDocumentQuery<'a> {
                         document_type,
                         included,
                     } = start_at_document_inner;
-                    println!("document is {}", document);
                     let start_at_key = document
                         .get_raw_for_document_type(
                             first.name.as_str(),
@@ -1631,7 +1632,7 @@ impl<'a> DriveDocumentQuery<'a> {
                     // │   │   │   │   │   ├── 4
                     // │   │   │   │   │   │   ├── 1f7a8...
                     // │   │   │   │   │   │   └── 2c9b3...
-                    println!("going to call recursive_insert_on_query on non_conditional_query {} with left_over {:?}", non_conditional_query, left_over);
+                    // println!("going to call recursive_insert_on_query on non_conditional_query {} with left_over {:?}", non_conditional_query, left_over);
                     DriveDocumentQuery::recursive_insert_on_query(
                         &mut non_conditional_query,
                         left_over,
@@ -1673,6 +1674,8 @@ impl<'a> DriveDocumentQuery<'a> {
             }
         }
     }
+
+    #[cfg(any(feature = "server", feature = "verify"))]
     fn recursive_conditional_insert_on_query(
         query: &mut Query,
         conditional_value: Option<Vec<u8>>,

From 31a1f9b9c284a5d5e37ea962a06f0bc1dc8f11f1 Mon Sep 17 00:00:00 2001
From: Quantum Explorer <quantum@dash.org>
Date: Mon, 25 Nov 2024 04:21:31 +0300
Subject: [PATCH 4/6] another test

---
 packages/rs-drive/src/query/mod.rs     |   2 -
 packages/rs-drive/tests/query_tests.rs | 104 +++++++++++++++++++++++++
 2 files changed, 104 insertions(+), 2 deletions(-)

diff --git a/packages/rs-drive/src/query/mod.rs b/packages/rs-drive/src/query/mod.rs
index af32e78d4b..fe35e85987 100644
--- a/packages/rs-drive/src/query/mod.rs
+++ b/packages/rs-drive/src/query/mod.rs
@@ -2148,8 +2148,6 @@ impl<'a> DriveDocumentQuery<'a> {
             platform_version,
         )?;
 
-        print!("{}", path_query);
-
         let query_result = drive.grove_get_path_query_serialized_results(
             &path_query,
             transaction,
diff --git a/packages/rs-drive/tests/query_tests.rs b/packages/rs-drive/tests/query_tests.rs
index 9501206f5d..6bad5144f9 100644
--- a/packages/rs-drive/tests/query_tests.rs
+++ b/packages/rs-drive/tests/query_tests.rs
@@ -5200,6 +5200,110 @@ fn test_withdrawals_query_start_after_query_by_owner_id() {
     assert_eq!(results, proof_results);
 }
 
+#[cfg(feature = "server")]
+#[test]
+fn test_withdrawals_query_start_after_query_by_owner_id_desc() {
+    // We create 10 withdrawals owned by 2 identities
+    let (drive, contract) = setup_withdrawal_tests(10, Some(2), 11456);
+
+    let platform_version = PlatformVersion::latest();
+
+    let db_transaction = drive.grove.start_transaction();
+
+    let root_hash = drive
+        .grove
+        .root_hash(Some(&db_transaction), &platform_version.drive.grove_version)
+        .unwrap()
+        .expect("there is always a root hash");
+
+    let expected_app_hash = vec![
+        144, 177, 24, 41, 104, 174, 220, 135, 164, 0, 240, 215, 42, 60, 249, 142, 150, 169, 135,
+        72, 151, 35, 238, 131, 164, 229, 106, 83, 198, 109, 65, 211,
+    ];
+
+    assert_eq!(root_hash.as_slice(), expected_app_hash);
+
+    // Document Ids are
+    // document v0 : id:2kTB6gW4wCCnySj3UFUJQM3aUYBd6qDfLCY74BnWmFKu owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:09 updated_at:2024-11-21 12:31:09 amount:(i64)646767 coreFeePerByte:(i64)0 outputScript:bytes 00952c808390e575c8dd29fc07ccfed7b428e1ec2ffcb23e pooling:(i64)0 status:(i64)1 transactionIndex:(i64)4 transactionSignHeight:(i64)303186
+    // document v0 : id:3T4aKmidGKA4ETnWYSedm6ETzrcdkfPL2r3D6eg6CSib owner_id:CH1EHBkN5FUuQ7z8ep1abroLPzzYjagvM5XV2NYR3DEh created_at:2024-11-21 12:31:01 updated_at:2024-11-21 12:31:01 amount:(i64)971045 coreFeePerByte:(i64)0 outputScript:bytes 525dfc160c160a7a52ef3301a7e55fccf41d73857f50a55a4d pooling:(i64)0 status:(i64)1 transactionIndex:(i64)2 transactionSignHeight:(i64)248787
+    // document v0 : id:3X2QfUfR8EeVZQAKmEjcue5xDv3CZXrfPTgXkZ5vQo13 owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:11 updated_at:2024-11-21 12:31:11 amount:(i64)122155 coreFeePerByte:(i64)0 outputScript:bytes f76eb8b953ff41040d906c25a4ae42884bedb41a07fc3a pooling:(i64)0 status:(i64)3 transactionIndex:(i64)7 transactionSignHeight:(i64)310881
+    // document v0 : id:5ikeRNwvFekr6ex32B4dLEcCaSsgXXHJBx5rJ2rwuhEV owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:30:59 updated_at:2024-11-21 12:30:59 amount:(i64)725014 coreFeePerByte:(i64)0 outputScript:bytes 51f203a755a7ff25ba8645841f80403ee98134690b2c0dd5e2 pooling:(i64)0 status:(i64)3 transactionIndex:(i64)1 transactionSignHeight:(i64)4072
+    // document v0 : id:74giZJn9fNczYRsxxh3wVnktJS1vzTiRWYinKK1rRcyj owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:11 updated_at:2024-11-21 12:31:11 amount:(i64)151943 coreFeePerByte:(i64)0 outputScript:bytes 9db03f4c8a51e4e9855e008aae6121911b4831699c53ed pooling:(i64)0 status:(i64)1 transactionIndex:(i64)5 transactionSignHeight:(i64)343099
+    // document v0 : id:8iqDAFxTzHYcmUWtcNnCRoj9Fss4HE1G3GP3HhVAZJhn owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:13 updated_at:2024-11-21 12:31:13 amount:(i64)409642 coreFeePerByte:(i64)0 outputScript:bytes 19fe0a2458a47e1726191f4dc94d11bcfacf821d024043 pooling:(i64)0 status:(i64)4 transactionIndex:(i64)8 transactionSignHeight:(i64)304397
+    // document v0 : id:BdH274iP17nhquQVY4KMCAM6nwyPRc8AFJkUT91vxhbc owner_id:CH1EHBkN5FUuQ7z8ep1abroLPzzYjagvM5XV2NYR3DEh created_at:2024-11-21 12:31:03 updated_at:2024-11-21 12:31:03 amount:(i64)81005 coreFeePerByte:(i64)0 outputScript:bytes 2666e87b6cc7ddf2b63e7e52c348818c05e5562efa48f5 pooling:(i64)0 status:(i64)0
+    // document v0 : id:CCjaU67Pe79Vt51oXvQ5SkyNiypofNX9DS9PYydN9tpD owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:01 updated_at:2024-11-21 12:31:01 amount:(i64)455074 coreFeePerByte:(i64)0 outputScript:bytes acde2e1652771b50a2c68fd330ee1d4b8e115631ce72375432 pooling:(i64)0 status:(i64)3 transactionIndex:(i64)3 transactionSignHeight:(i64)261103
+    // document v0 : id:DxFzXvkb2mNQHmeVknsv3gWsc6rMtLk9AsS5zMpy6hou owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:05 updated_at:2024-11-21 12:31:05 amount:(i64)271303 coreFeePerByte:(i64)0 outputScript:bytes 0b845e8c3a4679f1913172f7fd939cc153f458519de8ed3d pooling:(i64)0 status:(i64)0
+    // document v0 : id:FDnvFN7e72LcZEojTWNmJTP7uzok3BtvbKnaa5gjqCpW owner_id:A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ created_at:2024-11-21 12:31:11 updated_at:2024-11-21 12:31:11 amount:(i64)123433 coreFeePerByte:(i64)0 outputScript:bytes 82712473b2d0fc5663afb1a08006913ccccbf38e091a8cc7 pooling:(i64)0 status:(i64)4 transactionIndex:(i64)6 transactionSignHeight:(i64)319518
+
+    let query_value = json!({
+        "where": [
+            ["$ownerId", "==", "A8GdKdMT7eDvtjnmMXe1Z3YaTtJzZdxNDRkeLb8goFrZ"]
+        ],
+        "startAfter":  "2kTB6gW4wCCnySj3UFUJQM3aUYBd6qDfLCY74BnWmFKu",
+        "limit": 3,
+        "orderBy": [
+            ["$updatedAt", "desc"]
+        ]
+    });
+
+    // This will use the identity recent index
+    // {
+    //     "name": "identityRecent",
+    //     "properties": [
+    //     {
+    //         "$ownerId": "asc"
+    //     },
+    //     {
+    //         "$updatedAt": "asc"
+    //     },
+    //     {
+    //         "status": "asc"
+    //     }
+    //     ],
+    //     "unique": false
+    // },
+
+    let where_cbor = cbor_serializer::serializable_value_to_cbor(&query_value, None)
+        .expect("expected to serialize to cbor");
+    let domain_document_type = contract
+        .document_type_for_name("withdrawal")
+        .expect("contract should have a domain document type");
+    let query = DriveDocumentQuery::from_cbor(
+        where_cbor.as_slice(),
+        &contract,
+        domain_document_type,
+        &drive.config,
+    )
+    .expect("query should be built");
+    let (results, _, _) = query
+        .execute_raw_results_no_proof(&drive, None, Some(&db_transaction), platform_version)
+        .expect("proof should be executed");
+    let names: Vec<String> = results
+        .iter()
+        .map(|result| {
+            let document =
+                Document::from_bytes(result.as_slice(), domain_document_type, platform_version)
+                    .expect("we should be able to deserialize the document");
+            document.id().to_string(Encoding::Base58)
+        })
+        .collect();
+
+    // We only get back 2 values, even though we put limit 3 because the time with status 0 is an
+    // empty tree and consumes a limit
+    let a_names = [
+        "DxFzXvkb2mNQHmeVknsv3gWsc6rMtLk9AsS5zMpy6hou".to_string(),
+        "CCjaU67Pe79Vt51oXvQ5SkyNiypofNX9DS9PYydN9tpD".to_string(),
+    ];
+
+    assert_eq!(names, a_names);
+
+    let (proof_root_hash, proof_results, _) = query
+        .execute_with_proof_only_get_elements(&drive, None, None, platform_version)
+        .expect("we should be able to a proof");
+    assert_eq!(root_hash, proof_root_hash);
+    assert_eq!(results, proof_results);
+}
+
 #[cfg(feature = "server")]
 #[test]
 fn test_query_a_b_c_d_e_contract() {

From 349c03f21f8a33ab29da1579aadd967de0a5b8f7 Mon Sep 17 00:00:00 2001
From: Quantum Explorer <quantum@dash.org>
Date: Mon, 25 Nov 2024 04:55:49 +0300
Subject: [PATCH 5/6] removed unneeded todo

---
 packages/rs-drive/src/query/mod.rs | 5 +----
 1 file changed, 1 insertion(+), 4 deletions(-)

diff --git a/packages/rs-drive/src/query/mod.rs b/packages/rs-drive/src/query/mod.rs
index fe35e85987..59fb671f7e 100644
--- a/packages/rs-drive/src/query/mod.rs
+++ b/packages/rs-drive/src/query/mod.rs
@@ -1503,9 +1503,6 @@ impl<'a> DriveDocumentQuery<'a> {
     /// here the * denotes the area needing a conditional
     /// We need a conditional subquery on Ford to say only things after Ford (with Ford included)
     /// We need a conditional subquery on Escape to say only things after Escape (with Escape included)
-    ///
-    /// The index property used to identify the document within its type.
-    /// This helps in determining the position of the document in query results.
     fn recursive_insert_on_query(
         query: &mut Query,
         left_over_index_properties: &[&IndexProperty],
@@ -1763,7 +1760,7 @@ impl<'a> DriveDocumentQuery<'a> {
 
                 DriveDocumentQuery::recursive_conditional_insert_on_query(
                     &mut non_conditional_query,
-                    lower_start_at_key, //todo
+                    lower_start_at_key,
                     left_over,
                     unique,
                     starts_at_document,

From f2ebf10786ea15a058dfcf979c69688c7273b26a Mon Sep 17 00:00:00 2001
From: Quantum Explorer <quantum@dash.org>
Date: Mon, 25 Nov 2024 04:57:35 +0300
Subject: [PATCH 6/6] small fix

---
 packages/rs-drive/src/query/mod.rs | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/packages/rs-drive/src/query/mod.rs b/packages/rs-drive/src/query/mod.rs
index 59fb671f7e..f6aa81deb2 100644
--- a/packages/rs-drive/src/query/mod.rs
+++ b/packages/rs-drive/src/query/mod.rs
@@ -1813,8 +1813,7 @@ impl<'a> DriveDocumentQuery<'a> {
                 !(self
                     .internal_clauses
                     .equal_clauses
-                    .get(field.name.as_str())
-                    .is_some()
+                    .contains_key(field.name.as_str())
                     || (last_clause.is_some() && last_clause.unwrap().field == field.name)
                     || (subquery_clause.is_some() && subquery_clause.unwrap().field == field.name))
             })