From 9d056e7649b6f1e59118b46889ee2f96bf6fe0de Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marko=20Budiseli=C4=87?= <marko.budiselic@memgraph.com>
Date: Thu, 29 Jun 2023 11:44:55 +0200
Subject: [PATCH] Add experimental/v1 of ON_DISK_TRANSACTIONAL storage (#850)

Co-authored-by: Andi Skrgat <andi8647@gmail.com>
Co-authored-by: Aidar Samerkhanov <aidar.samerkhanov@memgraph.io>
---
 include/mg_procedure.h                        |    2 +-
 src/http_handlers/metrics.hpp                 |    2 +-
 src/memgraph.cpp                              |   43 +-
 src/mg_import_csv.cpp                         |   35 +-
 src/query/db_accessor.cpp                     |    2 +-
 src/query/db_accessor.hpp                     |   14 +
 src/query/dump.cpp                            |    3 +-
 src/query/exceptions.hpp                      |   31 +
 src/query/frontend/ast/ast.hpp                |    2 +-
 .../frontend/ast/cypher_main_visitor.cpp      |    5 +-
 .../opencypher/grammar/MemgraphCypher.g4      |    2 +-
 .../opencypher/grammar/MemgraphCypherLexer.g4 |    1 +
 .../interpret/awesome_memgraph_functions.cpp  |    6 +-
 src/query/interpreter.cpp                     |  297 +-
 src/query/interpreter.hpp                     |   22 +-
 src/query/plan/operator.cpp                   |   38 +-
 src/query/plan/operator.hpp                   |    1 +
 src/query/plan/rewrite/index_lookup.hpp       |    1 -
 src/query/procedure/mg_procedure_impl.cpp     |   88 +-
 src/query/procedure/mg_procedure_impl.hpp     |   45 +-
 src/query/stream/streams.cpp                  |    4 +-
 src/query/trigger_context.cpp                 |    4 +-
 src/query/trigger_context.hpp                 |    7 +-
 src/query/typed_value.hpp                     |    2 +-
 src/storage/README.md                         |    3 +
 src/storage/v2/CMakeLists.txt                 |   15 +-
 src/storage/v2/all_vertices_iterable.cpp      |   44 +
 src/storage/v2/all_vertices_iterable.hpp      |   58 +
 src/storage/v2/config.hpp                     |   11 +
 src/storage/v2/constraints.hpp                |  199 -
 .../v2/constraints/constraint_violation.hpp   |   39 +
 src/storage/v2/constraints/constraints.hpp    |   48 +
 .../v2/constraints/existence_constraints.cpp  |   58 +
 .../v2/constraints/existence_constraints.hpp  |   62 +
 .../v2/constraints/unique_constraints.hpp     |   75 +
 src/storage/v2/delta.hpp                      |   35 +-
 src/storage/v2/disk/label_index.cpp           |  215 +
 src/storage/v2/disk/label_index.hpp           |   62 +
 src/storage/v2/disk/label_property_index.cpp  |  228 +
 src/storage/v2/disk/label_property_index.hpp  |   71 +
 src/storage/v2/disk/name_id_mapper.hpp        |  111 +
 src/storage/v2/disk/rocksdb_storage.cpp       |   83 +
 src/storage/v2/disk/rocksdb_storage.hpp       |   89 +
 src/storage/v2/disk/storage.cpp               | 1703 +++++
 src/storage/v2/disk/storage.hpp               |  303 +
 src/storage/v2/disk/unique_constraints.cpp    |  349 +
 src/storage/v2/disk/unique_constraints.hpp    |   78 +
 src/storage/v2/durability/durability.cpp      |   25 +-
 src/storage/v2/durability/durability.hpp      |    4 +-
 src/storage/v2/durability/snapshot.cpp        |    9 +-
 src/storage/v2/durability/snapshot.hpp        |    4 +-
 src/storage/v2/durability/wal.cpp             |    5 +-
 src/storage/v2/edge.hpp                       |    5 +-
 src/storage/v2/edge_accessor.cpp              |    5 +
 src/storage/v2/edge_accessor.hpp              |    1 -
 src/storage/v2/indices.cpp                    |  907 ---
 src/storage/v2/indices.hpp                    |  317 -
 src/storage/v2/indices/indices.cpp            |   38 +
 src/storage/v2/indices/indices.hpp            |   68 +
 src/storage/v2/indices/label_index.hpp        |   51 +
 .../v2/indices/label_property_index.hpp       |   59 +
 src/storage/v2/inmemory/indices_utils.hpp     |  357 +
 src/storage/v2/inmemory/label_index.cpp       |  192 +
 src/storage/v2/inmemory/label_index.hpp       |  117 +
 .../v2/inmemory/label_property_index.cpp      |  424 ++
 .../v2/inmemory/label_property_index.hpp      |  141 +
 src/storage/v2/inmemory/storage.cpp           | 2075 ++++++
 src/storage/v2/inmemory/storage.hpp           |  522 ++
 .../unique_constraints.cpp}                   |   83 +-
 .../v2/inmemory/unique_constraints.hpp        |  101 +
 src/storage/v2/mvcc.hpp                       |   20 +-
 src/storage/v2/name_id_mapper.hpp             |   41 +-
 src/storage/v2/property_store.cpp             |   73 +
 src/storage/v2/property_store.hpp             |   26 +
 src/storage/v2/property_value.hpp             |    6 +-
 .../v2/replication/replication_client.cpp     |   88 +-
 .../v2/replication/replication_client.hpp     |   12 +-
 .../v2/replication/replication_server.cpp     |   61 +-
 .../v2/replication/replication_server.hpp     |   13 +-
 src/storage/v2/storage.cpp                    | 2382 +------
 src/storage/v2/storage.hpp                    |  727 +--
 src/storage/v2/storage_error.hpp              |   26 +-
 src/storage/v2/storage_mode.cpp               |    2 +
 src/storage/v2/storage_mode.hpp               |    2 +-
 src/storage/v2/vertex.hpp                     |    5 +-
 src/storage/v2/vertex_accessor.cpp            |   32 +-
 src/storage/v2/vertex_accessor.hpp            |    3 +-
 src/storage/v2/vertices_iterable.cpp          |  250 +
 src/storage/v2/vertices_iterable.hpp          |   78 +
 src/utils/disk_utils.hpp                      |   28 +
 src/utils/logging.hpp                         |   13 +-
 src/utils/math.hpp                            |    8 +-
 src/utils/memory.hpp                          |    2 +-
 src/utils/rocksdb_serialization.hpp           |  291 +
 src/utils/skip_list.hpp                       |    9 +-
 tests/benchmark/expansion.cpp                 |   26 +-
 tests/benchmark/query/eval.cpp                |   13 +-
 tests/benchmark/query/execution.cpp           |  108 +-
 tests/benchmark/query/planner.cpp             |   28 +-
 tests/benchmark/storage_v2_gc.cpp             |   19 +-
 tests/concurrent/storage_indices.cpp          |   66 +-
 .../concurrent/storage_unique_constraints.cpp |   74 +-
 tests/e2e/CMakeLists.txt                      |    1 +
 tests/e2e/disk_storage/CMakeLists.txt         |   14 +
 tests/e2e/disk_storage/common.py              |   27 +
 .../disk_storage/create_edge_from_indices.py  |   34 +
 tests/e2e/disk_storage/data_import.py         |   45 +
 .../e2e/disk_storage/free_memory_disabled.py  |   29 +
 .../disk_storage/lock_data_dir_disabled.py    |   29 +
 .../e2e/disk_storage/replication_disabled.py  |   29 +
 tests/e2e/disk_storage/snapshot_disabled.py   |   29 +
 .../update_storage_mode_db_not_empty.py       |   31 +
 .../update_storage_mode_disk_to_memory.py     |   29 +
 .../update_storage_mode_memory_to_disk.py     |   24 +
 tests/e2e/disk_storage/workloads.yaml         |   53 +
 .../e2e/isolation_levels/isolation_levels.cpp |   60 +-
 tests/e2e/isolation_levels/workloads.yaml     |   12 +
 tests/e2e/magic_functions/workloads.yaml      |   13 +
 tests/e2e/memory/workloads.yaml               |    8 +
 tests/e2e/mock_api/workloads.yaml             |   83 +
 .../workloads.yaml                            |   13 +
 tests/e2e/run_e2e.sh                          |    4 +
 tests/e2e/runner.py                           |    7 +
 tests/e2e/temporal_types/workloads.yaml       |   12 +
 tests/e2e/transaction_queue/workloads.yaml    |   13 +
 tests/e2e/triggers/workloads.yaml             |   39 +
 tests/e2e/write_procedures/workloads.yaml     |   17 +
 tests/manual/interactive_planning.cpp         |    1 -
 tests/manual/query_planner.cpp                |   10 +-
 tests/manual/single_query.cpp                 |   11 +-
 tests/property_based/random_graph.cpp         |   25 +-
 tests/unit/CMakeLists.txt                     |   20 +-
 tests/unit/auth_checker.cpp                   |  167 +-
 tests/unit/bfs_common.hpp                     |  830 +--
 tests/unit/bfs_fine_grained.cpp               |   82 +-
 tests/unit/bfs_single_node.cpp                |   90 +-
 tests/unit/bolt_encoder.cpp                   |   58 +-
 tests/unit/clearing_old_disk_data.cpp         |  181 +
 tests/unit/cpp_api.cpp                        |   64 +-
 tests/unit/disk_test_utils.hpp                |   49 +
 tests/unit/interpreter.cpp                    |  648 +-
 tests/unit/interpreter_faker.hpp              |    2 +-
 tests/unit/plan_pretty_print.cpp              |  522 +-
 tests/unit/query_common.hpp                   |  157 +-
 tests/unit/query_cost_estimator.cpp           |   17 +-
 tests/unit/query_dump.cpp                     |  482 +-
 tests/unit/query_expression_evaluator.cpp     | 2294 ++++---
 tests/unit/query_plan.cpp                     |  688 +-
 .../unit/query_plan_accumulate_aggregate.cpp  |  413 +-
 tests/unit/query_plan_bag_semantics.cpp       |  103 +-
 tests/unit/query_plan_common.hpp              |   19 +-
 .../query_plan_create_set_remove_delete.cpp   | 1159 ++--
 tests/unit/query_plan_edge_cases.cpp          |   51 +-
 tests/unit/query_plan_match_filter_return.cpp | 1878 +++---
 .../unit/query_plan_read_write_typecheck.cpp  |  205 +-
 ...query_plan_v2_create_set_remove_delete.cpp |   71 +-
 tests/unit/query_pretty_print.cpp             |   69 +-
 tests/unit/query_procedure_mgp_type.cpp       |   67 +-
 tests/unit/query_procedure_py_module.cpp      |   87 +-
 tests/unit/query_procedures_mgp_graph.cpp     |  356 +-
 tests/unit/query_required_privileges.cpp      |    8 +-
 tests/unit/query_semantic.cpp                 |  348 +-
 tests/unit/query_streams.cpp                  |  165 +-
 tests/unit/query_trigger.cpp                  |  196 +-
 tests/unit/query_variable_start_planner.cpp   |  164 +-
 tests/unit/storage_rocks.cpp                  |  119 +
 tests/unit/storage_test_utils.cpp             |   11 +
 tests/unit/storage_v2.cpp                     | 1629 ++---
 tests/unit/storage_v2_constraints.cpp         |  985 +--
 tests/unit/storage_v2_disk.cpp                |   29 +
 ...cpp => storage_v2_durability_inmemory.cpp} |  989 +--
 ..._edge.cpp => storage_v2_edge_inmemory.cpp} | 1198 ++--
 tests/unit/storage_v2_edge_ondisk.cpp         | 5815 +++++++++++++++++
 tests/unit/storage_v2_gc.cpp                  |   76 +-
 tests/unit/storage_v2_indices.cpp             |  937 ++-
 tests/unit/storage_v2_isolation_level.cpp     |   79 +-
 tests/unit/storage_v2_property_store.cpp      |   38 +
 tests/unit/storage_v2_replication.cpp         |  709 +-
 tests/unit/storage_v2_storage_mode.cpp        |   45 +-
 tests/unit/transaction_queue.cpp              |   29 +-
 tests/unit/transaction_queue_multiple.cpp     |   35 +-
 tests/unit/typed_value.cpp                    |  101 +-
 182 files changed, 26406 insertions(+), 13468 deletions(-)
 create mode 100644 src/storage/README.md
 create mode 100644 src/storage/v2/all_vertices_iterable.cpp
 create mode 100644 src/storage/v2/all_vertices_iterable.hpp
 delete mode 100644 src/storage/v2/constraints.hpp
 create mode 100644 src/storage/v2/constraints/constraint_violation.hpp
 create mode 100644 src/storage/v2/constraints/constraints.hpp
 create mode 100644 src/storage/v2/constraints/existence_constraints.cpp
 create mode 100644 src/storage/v2/constraints/existence_constraints.hpp
 create mode 100644 src/storage/v2/constraints/unique_constraints.hpp
 create mode 100644 src/storage/v2/disk/label_index.cpp
 create mode 100644 src/storage/v2/disk/label_index.hpp
 create mode 100644 src/storage/v2/disk/label_property_index.cpp
 create mode 100644 src/storage/v2/disk/label_property_index.hpp
 create mode 100644 src/storage/v2/disk/name_id_mapper.hpp
 create mode 100644 src/storage/v2/disk/rocksdb_storage.cpp
 create mode 100644 src/storage/v2/disk/rocksdb_storage.hpp
 create mode 100644 src/storage/v2/disk/storage.cpp
 create mode 100644 src/storage/v2/disk/storage.hpp
 create mode 100644 src/storage/v2/disk/unique_constraints.cpp
 create mode 100644 src/storage/v2/disk/unique_constraints.hpp
 delete mode 100644 src/storage/v2/indices.cpp
 delete mode 100644 src/storage/v2/indices.hpp
 create mode 100644 src/storage/v2/indices/indices.cpp
 create mode 100644 src/storage/v2/indices/indices.hpp
 create mode 100644 src/storage/v2/indices/label_index.hpp
 create mode 100644 src/storage/v2/indices/label_property_index.hpp
 create mode 100644 src/storage/v2/inmemory/indices_utils.hpp
 create mode 100644 src/storage/v2/inmemory/label_index.cpp
 create mode 100644 src/storage/v2/inmemory/label_index.hpp
 create mode 100644 src/storage/v2/inmemory/label_property_index.cpp
 create mode 100644 src/storage/v2/inmemory/label_property_index.hpp
 create mode 100644 src/storage/v2/inmemory/storage.cpp
 create mode 100644 src/storage/v2/inmemory/storage.hpp
 rename src/storage/v2/{constraints.cpp => inmemory/unique_constraints.cpp} (80%)
 create mode 100644 src/storage/v2/inmemory/unique_constraints.hpp
 create mode 100644 src/storage/v2/vertices_iterable.cpp
 create mode 100644 src/storage/v2/vertices_iterable.hpp
 create mode 100644 src/utils/disk_utils.hpp
 create mode 100644 src/utils/rocksdb_serialization.hpp
 create mode 100644 tests/e2e/disk_storage/CMakeLists.txt
 create mode 100644 tests/e2e/disk_storage/common.py
 create mode 100644 tests/e2e/disk_storage/create_edge_from_indices.py
 create mode 100644 tests/e2e/disk_storage/data_import.py
 create mode 100644 tests/e2e/disk_storage/free_memory_disabled.py
 create mode 100644 tests/e2e/disk_storage/lock_data_dir_disabled.py
 create mode 100644 tests/e2e/disk_storage/replication_disabled.py
 create mode 100644 tests/e2e/disk_storage/snapshot_disabled.py
 create mode 100644 tests/e2e/disk_storage/update_storage_mode_db_not_empty.py
 create mode 100644 tests/e2e/disk_storage/update_storage_mode_disk_to_memory.py
 create mode 100644 tests/e2e/disk_storage/update_storage_mode_memory_to_disk.py
 create mode 100644 tests/e2e/disk_storage/workloads.yaml
 create mode 100644 tests/e2e/run_e2e.sh
 create mode 100644 tests/unit/clearing_old_disk_data.cpp
 create mode 100644 tests/unit/disk_test_utils.hpp
 create mode 100644 tests/unit/storage_rocks.cpp
 create mode 100644 tests/unit/storage_v2_disk.cpp
 rename tests/unit/{storage_v2_durability.cpp => storage_v2_durability_inmemory.cpp} (70%)
 rename tests/unit/{storage_v2_edge.cpp => storage_v2_edge_inmemory.cpp} (86%)
 create mode 100644 tests/unit/storage_v2_edge_ondisk.cpp

diff --git a/include/mg_procedure.h b/include/mg_procedure.h
index 8f4888d57..f92b0cf16 100644
--- a/include/mg_procedure.h
+++ b/include/mg_procedure.h
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
diff --git a/src/http_handlers/metrics.hpp b/src/http_handlers/metrics.hpp
index dc3315948..a83f6ee06 100644
--- a/src/http_handlers/metrics.hpp
+++ b/src/http_handlers/metrics.hpp
@@ -50,7 +50,7 @@ struct MetricsResponse {
 template <typename TSessionData>
 class MetricsService {
  public:
-  explicit MetricsService(TSessionData *data) : db_(data->db) {}
+  explicit MetricsService(TSessionData *data) : db_(data->interpreter_context->db.get()) {}
 
   nlohmann::json GetMetricsJSON() {
     auto response = GetMetrics();
diff --git a/src/memgraph.cpp b/src/memgraph.cpp
index 6d730a137..4d68afa61 100644
--- a/src/memgraph.cpp
+++ b/src/memgraph.cpp
@@ -56,6 +56,9 @@
 #include "query/procedure/module.hpp"
 #include "query/procedure/py_module.hpp"
 #include "requests/requests.hpp"
+#include "storage/v2/config.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/isolation_level.hpp"
 #include "storage/v2/storage.hpp"
 #include "storage/v2/view.hpp"
@@ -443,21 +446,19 @@ struct SessionData {
   // supplied.
 #if MG_ENTERPRISE
 
-  SessionData(memgraph::storage::Storage *db, memgraph::query::InterpreterContext *interpreter_context,
+  SessionData(memgraph::query::InterpreterContext *interpreter_context,
               memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth,
               memgraph::audit::Log *audit_log)
-      : db(db), interpreter_context(interpreter_context), auth(auth), audit_log(audit_log) {}
-  memgraph::storage::Storage *db;
+      : interpreter_context(interpreter_context), auth(auth), audit_log(audit_log) {}
   memgraph::query::InterpreterContext *interpreter_context;
   memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth;
   memgraph::audit::Log *audit_log;
 
 #else
 
-  SessionData(memgraph::storage::Storage *db, memgraph::query::InterpreterContext *interpreter_context,
+  SessionData(memgraph::query::InterpreterContext *interpreter_context,
               memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth)
-      : db(db), interpreter_context(interpreter_context), auth(auth) {}
-  memgraph::storage::Storage *db;
+      : interpreter_context(interpreter_context), auth(auth) {}
   memgraph::query::InterpreterContext *interpreter_context;
   memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth;
 
@@ -508,7 +509,6 @@ class BoltSession final : public memgraph::communication::bolt::Session<memgraph
               memgraph::communication::v2::OutputStream *output_stream)
       : memgraph::communication::bolt::Session<memgraph::communication::v2::InputStream,
                                                memgraph::communication::v2::OutputStream>(input_stream, output_stream),
-        db_(data->db),
         interpreter_context_(data->interpreter_context),
         interpreter_(data->interpreter_context),
         auth_(data->auth),
@@ -583,7 +583,7 @@ class BoltSession final : public memgraph::communication::bolt::Session<memgraph
 
   std::map<std::string, memgraph::communication::bolt::Value> Pull(TEncoder *encoder, std::optional<int> n,
                                                                    std::optional<int> qid) override {
-    TypedValueResultStream stream(encoder, db_);
+    TypedValueResultStream stream(encoder, interpreter_context_->db.get());
     return PullResults(stream, n, qid);
   }
 
@@ -617,7 +617,8 @@ class BoltSession final : public memgraph::communication::bolt::Session<memgraph
       const auto &summary = interpreter_.Pull(&stream, n, qid);
       std::map<std::string, memgraph::communication::bolt::Value> decoded_summary;
       for (const auto &kv : summary) {
-        auto maybe_value = memgraph::glue::ToBoltValue(kv.second, *db_, memgraph::storage::View::NEW);
+        auto maybe_value =
+            memgraph::glue::ToBoltValue(kv.second, *interpreter_context_->db, memgraph::storage::View::NEW);
         if (maybe_value.HasError()) {
           switch (maybe_value.GetError()) {
             case memgraph::storage::Error::DELETED_OBJECT:
@@ -681,7 +682,6 @@ class BoltSession final : public memgraph::communication::bolt::Session<memgraph
   };
 
   // NOTE: Needed only for ToBoltValue conversions
-  const memgraph::storage::Storage *db_;
   memgraph::query::InterpreterContext *interpreter_context_;
   memgraph::query::Interpreter interpreter_;
   memgraph::utils::Synchronized<memgraph::auth::Auth, memgraph::utils::WritePrioritizedRWLock> *auth_;
@@ -895,7 +895,15 @@ int main(int argc, char **argv) {
                      .items_per_batch = FLAGS_storage_items_per_batch,
                      .recovery_thread_count = FLAGS_storage_recovery_thread_count,
                      .allow_parallel_index_creation = FLAGS_storage_parallel_index_recovery},
-      .transaction = {.isolation_level = ParseIsolationLevel()}};
+      .transaction = {.isolation_level = ParseIsolationLevel()},
+      .disk = {.main_storage_directory = FLAGS_data_directory + "/rocksdb_main_storage",
+               .label_index_directory = FLAGS_data_directory + "/rocksdb_label_index",
+               .label_property_index_directory = FLAGS_data_directory + "/rocksdb_label_property_index",
+               .unique_constraints_directory = FLAGS_data_directory + "/rocksdb_unique_constraints",
+               .name_id_mapper_directory = FLAGS_data_directory + "/rocksdb_name_id_mapper",
+               .id_name_mapper_directory = FLAGS_data_directory + "/rocksdb_id_name_mapper",
+               .durability_directory = FLAGS_data_directory + "/rocksdb_durability",
+               .wal_directory = FLAGS_data_directory + "/rocksdb_wal"}};
   if (FLAGS_storage_snapshot_interval_sec == 0) {
     if (FLAGS_storage_wal_enabled) {
       LOG_FATAL(
@@ -914,10 +922,9 @@ int main(int argc, char **argv) {
     }
     db_config.durability.snapshot_interval = std::chrono::seconds(FLAGS_storage_snapshot_interval_sec);
   }
-  memgraph::storage::Storage db(db_config);
 
   memgraph::query::InterpreterContext interpreter_context{
-      &db,
+      db_config,
       {.query = {.allow_load_csv = FLAGS_allow_load_csv},
        .execution_timeout_sec = FLAGS_query_execution_timeout_sec,
        .replication_replica_check_frequency = std::chrono::seconds(FLAGS_replication_replica_check_frequency_sec),
@@ -927,9 +934,9 @@ int main(int argc, char **argv) {
        .stream_transaction_retry_interval = std::chrono::milliseconds(FLAGS_stream_transaction_retry_interval)},
       FLAGS_data_directory};
 #ifdef MG_ENTERPRISE
-  SessionData session_data{&db, &interpreter_context, &auth, &audit_log};
+  SessionData session_data{&interpreter_context, &auth, &audit_log};
 #else
-  SessionData session_data{&db, &interpreter_context, &auth};
+  SessionData session_data{&interpreter_context, &auth};
 #endif
 
   memgraph::query::procedure::gModuleRegistry.SetModulesDirectory(query_modules_directories, FLAGS_data_directory);
@@ -969,7 +976,7 @@ int main(int argc, char **argv) {
     // Triggers can execute query procedures, so we need to reload the modules first and then
     // the triggers
     auto storage_accessor = interpreter_context.db->Access();
-    auto dba = memgraph::query::DbAccessor{&storage_accessor};
+    auto dba = memgraph::query::DbAccessor{storage_accessor.get()};
     interpreter_context.trigger_store.RestoreTriggers(
         &interpreter_context.ast_cache, &dba, interpreter_context.config.query, interpreter_context.auth_checker);
   }
@@ -1002,8 +1009,8 @@ int main(int argc, char **argv) {
   std::optional<memgraph::telemetry::Telemetry> telemetry;
   if (FLAGS_telemetry_enabled) {
     telemetry.emplace(telemetry_server, data_directory / "telemetry", run_id, machine_id, std::chrono::minutes(10));
-    telemetry->AddCollector("storage", [&db]() -> nlohmann::json {
-      auto info = db.GetInfo();
+    telemetry->AddCollector("storage", [db_ = interpreter_context.db.get()]() -> nlohmann::json {
+      auto info = db_->GetInfo();
       return {{"vertices", info.vertex_count}, {"edges", info.edge_count}};
     });
     telemetry->AddCollector("event_counters", []() -> nlohmann::json {
diff --git a/src/mg_import_csv.cpp b/src/mg_import_csv.cpp
index 3b9717ae6..92ce0fa68 100644
--- a/src/mg_import_csv.cpp
+++ b/src/mg_import_csv.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -20,7 +20,8 @@
 #include <unordered_map>
 
 #include "helpers.hpp"
-#include "storage/v2/storage.hpp"
+#include "storage/v2/edge_accessor.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "utils/exceptions.hpp"
 #include "utils/logging.hpp"
 #include "utils/message.hpp"
@@ -421,7 +422,7 @@ void ProcessNodeRow(memgraph::storage::Storage *store, const std::vector<std::st
                     std::unordered_map<NodeId, memgraph::storage::Gid> *node_id_map) {
   std::optional<NodeId> id;
   auto acc = store->Access();
-  auto node = acc.CreateVertex();
+  auto node = acc->CreateVertex();
   for (size_t i = 0; i < row.size(); ++i) {
     const auto &field = fields[i];
     const auto &value = row[i];
@@ -450,29 +451,29 @@ void ProcessNodeRow(memgraph::storage::Storage *store, const std::vector<std::st
         } else {
           pv_id = memgraph::storage::PropertyValue(node_id.id);
         }
-        auto old_node_property = node.SetProperty(acc.NameToProperty(field.name), pv_id);
+        auto old_node_property = node.SetProperty(acc->NameToProperty(field.name), pv_id);
         if (!old_node_property.HasValue()) throw LoadException("Couldn't add property '{}' to the node", field.name);
         if (!old_node_property->IsNull()) throw LoadException("The property '{}' already exists", field.name);
       }
       id = node_id;
     } else if (field.type == "LABEL") {
       for (const auto &label : memgraph::utils::Split(value, FLAGS_array_delimiter)) {
-        auto node_label = node.AddLabel(acc.NameToLabel(label));
+        auto node_label = node.AddLabel(acc->NameToLabel(label));
         if (!node_label.HasValue()) throw LoadException("Couldn't add label '{}' to the node", label);
         if (!*node_label) throw LoadException("The label '{}' already exists", label);
       }
     } else if (field.type != "IGNORE") {
-      auto old_node_property = node.SetProperty(acc.NameToProperty(field.name), StringToValue(value, field.type));
+      auto old_node_property = node.SetProperty(acc->NameToProperty(field.name), StringToValue(value, field.type));
       if (!old_node_property.HasValue()) throw LoadException("Couldn't add property '{}' to the node", field.name);
       if (!old_node_property->IsNull()) throw LoadException("The property '{}' already exists", field.name);
     }
   }
   for (const auto &label : additional_labels) {
-    auto node_label = node.AddLabel(acc.NameToLabel(label));
+    auto node_label = node.AddLabel(acc->NameToLabel(label));
     if (!node_label.HasValue()) throw LoadException("Couldn't add label '{}' to the node", label);
     if (!*node_label) throw LoadException("The label '{}' already exists", label);
   }
-  if (acc.Commit().HasError()) throw LoadException("Couldn't store the node");
+  if (acc->Commit().HasError()) throw LoadException("Couldn't store the node");
 }
 
 void ProcessNodes(memgraph::storage::Storage *store, const std::string &nodes_path,
@@ -567,16 +568,16 @@ void ProcessRelationshipsRow(memgraph::storage::Storage *store, const std::vecto
   if (!relationship_type) throw LoadException("Relationship TYPE must be set");
 
   auto acc = store->Access();
-  auto from_node = acc.FindVertex(*start_id, memgraph::storage::View::NEW);
+  auto from_node = acc->FindVertex(*start_id, memgraph::storage::View::NEW);
   if (!from_node) throw LoadException("From node must be in the storage");
-  auto to_node = acc.FindVertex(*end_id, memgraph::storage::View::NEW);
+  auto to_node = acc->FindVertex(*end_id, memgraph::storage::View::NEW);
   if (!to_node) throw LoadException("To node must be in the storage");
 
-  auto relationship = acc.CreateEdge(&*from_node, &*to_node, acc.NameToEdgeType(*relationship_type));
+  auto relationship = acc->CreateEdge(&from_node.value(), &to_node.value(), acc->NameToEdgeType(*relationship_type));
   if (!relationship.HasValue()) throw LoadException("Couldn't create the relationship");
 
   for (const auto &property : properties) {
-    auto ret = relationship->SetProperty(acc.NameToProperty(property.first), property.second);
+    auto ret = relationship.GetValue().SetProperty(acc->NameToProperty(property.first), property.second);
     if (!ret.HasValue()) {
       if (ret.GetError() != memgraph::storage::Error::PROPERTIES_DISABLED) {
         throw LoadException("Couldn't add property '{}' to the relationship", property.first);
@@ -589,7 +590,7 @@ void ProcessRelationshipsRow(memgraph::storage::Storage *store, const std::vecto
     }
   }
 
-  if (acc.Commit().HasError()) throw LoadException("Couldn't store the relationship");
+  if (acc->Commit().HasError()) throw LoadException("Couldn't store the relationship");
 }
 
 void ProcessRelationships(memgraph::storage::Storage *store, const std::string &relationships_path,
@@ -699,13 +700,13 @@ int main(int argc, char *argv[]) {
   }
 
   std::unordered_map<NodeId, memgraph::storage::Gid> node_id_map;
-  memgraph::storage::Storage store{{
+  std::unique_ptr<memgraph::storage::Storage> store{new memgraph::storage::InMemoryStorage{{
       .items = {.properties_on_edges = FLAGS_storage_properties_on_edges},
       .durability = {.storage_directory = FLAGS_data_directory,
                      .recover_on_startup = false,
                      .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::DISABLED,
                      .snapshot_on_exit = true},
-  }};
+  }}};
 
   memgraph::utils::Timer load_timer;
 
@@ -715,7 +716,7 @@ int main(int argc, char *argv[]) {
     std::optional<std::vector<Field>> header;
     for (const auto &nodes_file : files) {
       spdlog::info("Loading {}", nodes_file);
-      ProcessNodes(&store, nodes_file, &header, &node_id_map, additional_labels);
+      ProcessNodes(store.get(), nodes_file, &header, &node_id_map, additional_labels);
     }
   }
 
@@ -725,7 +726,7 @@ int main(int argc, char *argv[]) {
     std::optional<std::vector<Field>> header;
     for (const auto &relationships_file : files) {
       spdlog::info("Loading {}", relationships_file);
-      ProcessRelationships(&store, relationships_file, type, &header, node_id_map);
+      ProcessRelationships(store.get(), relationships_file, type, &header, node_id_map);
     }
   }
 
diff --git a/src/query/db_accessor.cpp b/src/query/db_accessor.cpp
index 48034cdff..80f0b3e88 100644
--- a/src/query/db_accessor.cpp
+++ b/src/query/db_accessor.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
diff --git a/src/query/db_accessor.hpp b/src/query/db_accessor.hpp
index b80fb0fe3..b5414be73 100644
--- a/src/query/db_accessor.hpp
+++ b/src/query/db_accessor.hpp
@@ -17,9 +17,11 @@
 #include <cppitertools/imap.hpp>
 
 #include "query/exceptions.hpp"
+#include "storage/v2/edge_accessor.hpp"
 #include "storage/v2/id_types.hpp"
 #include "storage/v2/property_value.hpp"
 #include "storage/v2/result.hpp"
+#include "storage/v2/storage_mode.hpp"
 #include "utils/pmr/unordered_set.hpp"
 #include "utils/variant_helpers.hpp"
 
@@ -347,6 +349,10 @@ class DbAccessor final {
 
   VertexAccessor InsertVertex() { return VertexAccessor(accessor_->CreateVertex()); }
 
+  void PrefetchOutEdges(const VertexAccessor &vertex) const { accessor_->PrefetchOutEdges(vertex.impl_); }
+
+  void PrefetchInEdges(const VertexAccessor &vertex) const { accessor_->PrefetchInEdges(vertex.impl_); }
+
   storage::Result<EdgeAccessor> InsertEdge(VertexAccessor *from, VertexAccessor *to,
                                            const storage::EdgeTypeId &edge_type) {
     auto maybe_edge = accessor_->CreateEdge(&from->impl_, &to->impl_, edge_type);
@@ -372,6 +378,8 @@ class DbAccessor final {
       VertexAccessor *vertex_accessor) {
     using ReturnType = std::pair<VertexAccessor, std::vector<EdgeAccessor>>;
 
+    accessor_->PrefetchOutEdges(vertex_accessor->impl_);
+    accessor_->PrefetchInEdges(vertex_accessor->impl_);
     auto res = accessor_->DetachDeleteVertex(&vertex_accessor->impl_);
     if (res.HasError()) {
       return res.GetError();
@@ -424,6 +432,8 @@ class DbAccessor final {
 
   void Abort() { accessor_->Abort(); }
 
+  storage::StorageMode GetStorageMode() const { return accessor_->GetCreationStorageMode(); }
+
   bool LabelIndexExists(storage::LabelId label) const { return accessor_->LabelIndexExists(label); }
 
   bool LabelPropertyIndexExists(storage::LabelId label, storage::PropertyId prop) const {
@@ -508,6 +518,10 @@ class SubgraphDbAccessor final {
 
   const std::string &EdgeTypeToName(storage::EdgeTypeId type) const;
 
+  void PrefetchOutEdges(const SubgraphVertexAccessor &vertex) const { db_accessor_.PrefetchOutEdges(vertex.impl_); }
+
+  void PrefetchInEdges(const SubgraphVertexAccessor &vertex) const { db_accessor_.PrefetchInEdges(vertex.impl_); }
+
   storage::Result<std::optional<EdgeAccessor>> RemoveEdge(EdgeAccessor *edge);
 
   storage::Result<EdgeAccessor> InsertEdge(SubgraphVertexAccessor *from, SubgraphVertexAccessor *to,
diff --git a/src/query/dump.cpp b/src/query/dump.cpp
index 8421e4376..c668b460c 100644
--- a/src/query/dump.cpp
+++ b/src/query/dump.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -482,6 +482,7 @@ PullPlanDump::PullChunk PullPlanDump::CreateEdgePullChunk() {
       // If we have a saved iterable from a previous pull
       // we need to use the same iterable
       if (!maybe_edge_iterable) {
+        dba_->PrefetchOutEdges(vertex);
         maybe_edge_iterable = std::make_shared<EdgeAccessorIterable>(vertex.OutEdges(storage::View::OLD));
       }
       auto &maybe_edges = *maybe_edge_iterable;
diff --git a/src/query/exceptions.hpp b/src/query/exceptions.hpp
index 874a102f6..9b5c6651e 100644
--- a/src/query/exceptions.hpp
+++ b/src/query/exceptions.hpp
@@ -175,18 +175,34 @@ class ReplicationModificationInMulticommandTxException : public QueryException {
       : QueryException("Replication clause not allowed in multicommand transactions.") {}
 };
 
+class ReplicationDisabledOnDiskStorage : public QueryException {
+ public:
+  ReplicationDisabledOnDiskStorage() : QueryException("Replication not support with disk storage. ") {}
+};
+
 class LockPathModificationInMulticommandTxException : public QueryException {
  public:
   LockPathModificationInMulticommandTxException()
       : QueryException("Lock path query not allowed in multicommand transactions.") {}
 };
 
+class LockPathDisabledOnDiskStorage : public QueryException {
+ public:
+  LockPathDisabledOnDiskStorage()
+      : QueryException("Lock path disabled on disk storage since all data is already persisted. ") {}
+};
+
 class FreeMemoryModificationInMulticommandTxException : public QueryException {
  public:
   FreeMemoryModificationInMulticommandTxException()
       : QueryException("Free memory query not allowed in multicommand transactions.") {}
 };
 
+class FreeMemoryDisabledOnDiskStorage : public QueryException {
+ public:
+  FreeMemoryDisabledOnDiskStorage() : QueryException("Free memory does nothing when using disk storage. ") {}
+};
+
 class ShowConfigModificationInMulticommandTxException : public QueryException {
  public:
   ShowConfigModificationInMulticommandTxException()
@@ -232,6 +248,11 @@ class CreateSnapshotInMulticommandTxException final : public QueryException {
       : QueryException("Snapshot cannot be created in multicommand transactions.") {}
 };
 
+class CreateSnapshotDisabledOnDiskStorage final : public QueryException {
+ public:
+  CreateSnapshotDisabledOnDiskStorage() : QueryException("Data is already persisted when using disk storage. ") {}
+};
+
 class SettingConfigInMulticommandTxException final : public QueryException {
  public:
   SettingConfigInMulticommandTxException()
@@ -264,4 +285,14 @@ class TransactionQueueInMulticommandTxException : public QueryException {
       : QueryException("Transaction queue queries not allowed in multicommand transactions.") {}
 };
 
+class IndexPersistenceException : public QueryException {
+ public:
+  IndexPersistenceException() : QueryException("Persisting index on disk failed.") {}
+};
+
+class ConstraintsPersistenceException : public QueryException {
+ public:
+  ConstraintsPersistenceException() : QueryException("Persisting constraints on disk failed.") {}
+};
+
 }  // namespace memgraph::query
diff --git a/src/query/frontend/ast/ast.hpp b/src/query/frontend/ast/ast.hpp
index caee103a2..e30ffdb82 100644
--- a/src/query/frontend/ast/ast.hpp
+++ b/src/query/frontend/ast/ast.hpp
@@ -3143,7 +3143,7 @@ class StorageModeQuery : public memgraph::query::Query {
   static const utils::TypeInfo kType;
   const utils::TypeInfo &GetTypeInfo() const override { return kType; }
 
-  enum class StorageMode { IN_MEMORY_TRANSACTIONAL, IN_MEMORY_ANALYTICAL };
+  enum class StorageMode { IN_MEMORY_TRANSACTIONAL, IN_MEMORY_ANALYTICAL, ON_DISK_TRANSACTIONAL };
 
   StorageModeQuery() = default;
 
diff --git a/src/query/frontend/ast/cypher_main_visitor.cpp b/src/query/frontend/ast/cypher_main_visitor.cpp
index f2e037172..ef0a0aad9 100644
--- a/src/query/frontend/ast/cypher_main_visitor.cpp
+++ b/src/query/frontend/ast/cypher_main_visitor.cpp
@@ -504,7 +504,10 @@ antlrcpp::Any CypherMainVisitor::visitStorageModeQuery(MemgraphCypher::StorageMo
     if (mode->IN_MEMORY_ANALYTICAL()) {
       return StorageModeQuery::StorageMode::IN_MEMORY_ANALYTICAL;
     }
-    return StorageModeQuery::StorageMode::IN_MEMORY_TRANSACTIONAL;
+    if (mode->IN_MEMORY_TRANSACTIONAL()) {
+      return StorageModeQuery::StorageMode::IN_MEMORY_TRANSACTIONAL;
+    }
+    return StorageModeQuery::StorageMode::ON_DISK_TRANSACTIONAL;
   });
 
   query_ = storage_mode_query;
diff --git a/src/query/frontend/opencypher/grammar/MemgraphCypher.g4 b/src/query/frontend/opencypher/grammar/MemgraphCypher.g4
index f7ffe8803..a55d1281a 100644
--- a/src/query/frontend/opencypher/grammar/MemgraphCypher.g4
+++ b/src/query/frontend/opencypher/grammar/MemgraphCypher.g4
@@ -365,7 +365,7 @@ isolationLevelScope : GLOBAL | SESSION | NEXT ;
 
 isolationLevelQuery : SET isolationLevelScope TRANSACTION ISOLATION LEVEL isolationLevel ;
 
-storageMode : IN_MEMORY_ANALYTICAL | IN_MEMORY_TRANSACTIONAL ;
+storageMode : IN_MEMORY_ANALYTICAL | IN_MEMORY_TRANSACTIONAL | ON_DISK_TRANSACTIONAL ;
 
 storageModeQuery : STORAGE MODE storageMode ;
 
diff --git a/src/query/frontend/opencypher/grammar/MemgraphCypherLexer.g4 b/src/query/frontend/opencypher/grammar/MemgraphCypherLexer.g4
index 674a5f61d..149e637b1 100644
--- a/src/query/frontend/opencypher/grammar/MemgraphCypherLexer.g4
+++ b/src/query/frontend/opencypher/grammar/MemgraphCypherLexer.g4
@@ -85,6 +85,7 @@ MODULE_WRITE            : M O D U L E UNDERSCORE W R I T E ;
 NEXT                    : N E X T ;
 NO                      : N O ;
 NOTHING                 : N O T H I N G ;
+ON_DISK_TRANSACTIONAL   : O N UNDERSCORE D I S K UNDERSCORE T R A N S A C T I O N A L ;
 NULLIF                  : N U L L I F ;
 PASSWORD                : P A S S W O R D ;
 PORT                    : P O R T ;
diff --git a/src/query/interpret/awesome_memgraph_functions.cpp b/src/query/interpret/awesome_memgraph_functions.cpp
index 21d045601..a90aa70aa 100644
--- a/src/query/interpret/awesome_memgraph_functions.cpp
+++ b/src/query/interpret/awesome_memgraph_functions.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -467,6 +467,8 @@ TypedValue Degree(const TypedValue *args, int64_t nargs, const FunctionContext &
   FType<Or<Null, Vertex>>("degree", args, nargs);
   if (args[0].IsNull()) return TypedValue(ctx.memory);
   const auto &vertex = args[0].ValueVertex();
+  ctx.db_accessor->PrefetchInEdges(vertex);
+  ctx.db_accessor->PrefetchOutEdges(vertex);
   size_t out_degree = UnwrapDegreeResult(vertex.OutDegree(ctx.view));
   size_t in_degree = UnwrapDegreeResult(vertex.InDegree(ctx.view));
   return TypedValue(static_cast<int64_t>(out_degree + in_degree), ctx.memory);
@@ -476,6 +478,7 @@ TypedValue InDegree(const TypedValue *args, int64_t nargs, const FunctionContext
   FType<Or<Null, Vertex>>("inDegree", args, nargs);
   if (args[0].IsNull()) return TypedValue(ctx.memory);
   const auto &vertex = args[0].ValueVertex();
+  ctx.db_accessor->PrefetchInEdges(vertex);
   size_t in_degree = UnwrapDegreeResult(vertex.InDegree(ctx.view));
   return TypedValue(static_cast<int64_t>(in_degree), ctx.memory);
 }
@@ -484,6 +487,7 @@ TypedValue OutDegree(const TypedValue *args, int64_t nargs, const FunctionContex
   FType<Or<Null, Vertex>>("outDegree", args, nargs);
   if (args[0].IsNull()) return TypedValue(ctx.memory);
   const auto &vertex = args[0].ValueVertex();
+  ctx.db_accessor->PrefetchOutEdges(vertex);
   size_t out_degree = UnwrapDegreeResult(vertex.OutDegree(ctx.view));
   return TypedValue(static_cast<int64_t>(out_degree), ctx.memory);
 }
diff --git a/src/query/interpreter.cpp b/src/query/interpreter.cpp
index 087fdb09a..8b95b5ee2 100644
--- a/src/query/interpreter.cpp
+++ b/src/query/interpreter.cpp
@@ -58,21 +58,25 @@
 #include "query/trigger.hpp"
 #include "query/typed_value.hpp"
 #include "spdlog/spdlog.h"
+#include "storage/v2/disk/storage.hpp"
 #include "storage/v2/edge.hpp"
 #include "storage/v2/id_types.hpp"
-#include "storage/v2/isolation_level.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/property_value.hpp"
+#include "storage/v2/replication/config.hpp"
 #include "storage/v2/storage_mode.hpp"
 #include "utils/algorithm.hpp"
 #include "utils/build_info.hpp"
 #include "utils/event_counter.hpp"
 #include "utils/event_histogram.hpp"
 #include "utils/exceptions.hpp"
+#include "utils/file.hpp"
 #include "utils/flag_validation.hpp"
 #include "utils/likely.hpp"
 #include "utils/logging.hpp"
 #include "utils/memory.hpp"
 #include "utils/memory_tracker.hpp"
+#include "utils/message.hpp"
 #include "utils/on_scope_exit.hpp"
 #include "utils/readable_size.hpp"
 #include "utils/settings.hpp"
@@ -167,8 +171,9 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
 
   /// @throw QueryRuntimeException if an error ocurred.
   void SetReplicationRole(ReplicationQuery::ReplicationRole replication_role, std::optional<int64_t> port) override {
+    auto *mem_storage = static_cast<storage::InMemoryStorage *>(db_);
     if (replication_role == ReplicationQuery::ReplicationRole::MAIN) {
-      if (!db_->SetMainReplicationRole()) {
+      if (!mem_storage->SetMainReplicationRole()) {
         throw QueryRuntimeException("Couldn't set role to main!");
       }
     }
@@ -176,8 +181,9 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
       if (!port || *port < 0 || *port > std::numeric_limits<uint16_t>::max()) {
         throw QueryRuntimeException("Port number invalid!");
       }
-      if (!db_->SetReplicaRole(
-              io::network::Endpoint(storage::replication::kDefaultReplicationServerIp, static_cast<uint16_t>(*port)))) {
+      if (!mem_storage->SetReplicaRole(
+              io::network::Endpoint(storage::replication::kDefaultReplicationServerIp, static_cast<uint16_t>(*port)),
+              storage::replication::ReplicationServerConfig{})) {
         throw QueryRuntimeException("Couldn't set role to replica!");
       }
     }
@@ -185,7 +191,7 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
 
   /// @throw QueryRuntimeException if an error ocurred.
   ReplicationQuery::ReplicationRole ShowReplicationRole() const override {
-    switch (db_->GetReplicationRole()) {
+    switch (static_cast<storage::InMemoryStorage *>(db_)->GetReplicationRole()) {
       case storage::replication::ReplicationRole::MAIN:
         return ReplicationQuery::ReplicationRole::MAIN;
       case storage::replication::ReplicationRole::REPLICA:
@@ -198,7 +204,8 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
   void RegisterReplica(const std::string &name, const std::string &socket_address,
                        const ReplicationQuery::SyncMode sync_mode,
                        const std::chrono::seconds replica_check_frequency) override {
-    if (db_->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) {
+    auto *mem_storage = static_cast<storage::InMemoryStorage *>(db_);
+    if (mem_storage->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) {
       // replica can't register another replica
       throw QueryRuntimeException("Replica can't register another replica!");
     }
@@ -223,9 +230,9 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
         io::network::Endpoint::ParseSocketOrIpAddress(socket_address, storage::replication::kDefaultReplicationPort);
     if (maybe_ip_and_port) {
       auto [ip, port] = *maybe_ip_and_port;
-      auto ret = db_->RegisterReplica(name, {std::move(ip), port}, repl_mode,
-                                      storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
-                                      {.replica_check_frequency = replica_check_frequency, .ssl = std::nullopt});
+      auto ret = mem_storage->RegisterReplica(
+          name, {std::move(ip), port}, repl_mode, storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+          {.replica_check_frequency = replica_check_frequency, .ssl = std::nullopt});
       if (ret.HasError()) {
         throw QueryRuntimeException(fmt::format("Couldn't register replica '{}'!", name));
       }
@@ -236,23 +243,26 @@ class ReplQueryHandler final : public query::ReplicationQueryHandler {
 
   /// @throw QueryRuntimeException if an error ocurred.
   void DropReplica(const std::string &replica_name) override {
-    if (db_->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) {
+    auto *mem_storage = static_cast<storage::InMemoryStorage *>(db_);
+
+    if (mem_storage->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) {
       // replica can't unregister a replica
       throw QueryRuntimeException("Replica can't unregister a replica!");
     }
-    if (!db_->UnregisterReplica(replica_name)) {
+    if (!mem_storage->UnregisterReplica(replica_name)) {
       throw QueryRuntimeException(fmt::format("Couldn't unregister the replica '{}'", replica_name));
     }
   }
 
   using Replica = ReplicationQueryHandler::Replica;
   std::vector<Replica> ShowReplicas() const override {
-    if (db_->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) {
+    auto *mem_storage = static_cast<storage::InMemoryStorage *>(db_);
+    if (mem_storage->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) {
       // replica can't show registered replicas (it shouldn't have any)
       throw QueryRuntimeException("Replica can't show registered replicas (it shouldn't have any)!");
     }
 
-    auto repl_infos = db_->ReplicasInfo();
+    auto repl_infos = mem_storage->ReplicasInfo();
     std::vector<Replica> replicas;
     replicas.reserve(repl_infos.size());
 
@@ -534,7 +544,7 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
         notifications->emplace_back(SeverityLevel::WARNING, NotificationCode::REPLICA_PORT_WARNING,
                                     "Be careful the replication port must be different from the memgraph port!");
       }
-      callback.fn = [handler = ReplQueryHandler{interpreter_context->db}, role = repl_query->role_,
+      callback.fn = [handler = ReplQueryHandler{interpreter_context->db.get()}, role = repl_query->role_,
                      maybe_port]() mutable {
         handler.SetReplicationRole(role, maybe_port);
         return std::vector<std::vector<TypedValue>>();
@@ -547,7 +557,7 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
     }
     case ReplicationQuery::Action::SHOW_REPLICATION_ROLE: {
       callback.header = {"replication role"};
-      callback.fn = [handler = ReplQueryHandler{interpreter_context->db}] {
+      callback.fn = [handler = ReplQueryHandler{interpreter_context->db.get()}] {
         auto mode = handler.ShowReplicationRole();
         switch (mode) {
           case ReplicationQuery::ReplicationRole::MAIN: {
@@ -566,7 +576,7 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
       auto socket_address = repl_query->socket_address_->Accept(evaluator);
       const auto replica_check_frequency = interpreter_context->config.replication_replica_check_frequency;
 
-      callback.fn = [handler = ReplQueryHandler{interpreter_context->db}, name, socket_address, sync_mode,
+      callback.fn = [handler = ReplQueryHandler{interpreter_context->db.get()}, name, socket_address, sync_mode,
                      replica_check_frequency]() mutable {
         handler.RegisterReplica(name, std::string(socket_address.ValueString()), sync_mode, replica_check_frequency);
         return std::vector<std::vector<TypedValue>>();
@@ -578,7 +588,7 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
 
     case ReplicationQuery::Action::DROP_REPLICA: {
       const auto &name = repl_query->replica_name_;
-      callback.fn = [handler = ReplQueryHandler{interpreter_context->db}, name]() mutable {
+      callback.fn = [handler = ReplQueryHandler{interpreter_context->db.get()}, name]() mutable {
         handler.DropReplica(name);
         return std::vector<std::vector<TypedValue>>();
       };
@@ -591,7 +601,8 @@ Callback HandleReplicationQuery(ReplicationQuery *repl_query, const Parameters &
       callback.header = {
           "name", "socket_address", "sync_mode", "current_timestamp_of_replica", "number_of_timestamp_behind_master",
           "state"};
-      callback.fn = [handler = ReplQueryHandler{interpreter_context->db}, replica_nfields = callback.header.size()] {
+      callback.fn = [handler = ReplQueryHandler{interpreter_context->db.get()},
+                     replica_nfields = callback.header.size()] {
         const auto &replicas = handler.ShowReplicas();
         auto typed_replicas = std::vector<std::vector<TypedValue>>{};
         typed_replicas.reserve(replicas.size());
@@ -1188,11 +1199,38 @@ std::optional<plan::ProfilingStatsWithTotalTime> PullPlan::Pull(AnyStream *strea
 }
 
 using RWType = plan::ReadWriteTypeChecker::RWType;
+
+bool IsWriteQueryOnMainMemoryReplica(storage::Storage *storage,
+                                     const query::plan::ReadWriteTypeChecker::RWType query_type) {
+  if (auto storage_mode = storage->GetStorageMode(); storage_mode == storage::StorageMode::IN_MEMORY_ANALYTICAL ||
+                                                     storage_mode == storage::StorageMode::IN_MEMORY_TRANSACTIONAL) {
+    auto *mem_storage = static_cast<storage::InMemoryStorage *>(storage);
+    return (mem_storage->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA) &&
+           (query_type == RWType::W || query_type == RWType::RW);
+  }
+  return false;
+}
+
 }  // namespace
 
-InterpreterContext::InterpreterContext(storage::Storage *db, const InterpreterConfig config,
+InterpreterContext::InterpreterContext(const storage::Config storage_config, const InterpreterConfig interpreter_config,
                                        const std::filesystem::path &data_directory)
-    : db(db), trigger_store(data_directory / "triggers"), config(config), streams{this, data_directory / "streams"} {}
+    : trigger_store(data_directory / "triggers"),
+      config(interpreter_config),
+      streams{this, data_directory / "streams"} {
+  if (utils::DirExists(storage_config.disk.main_storage_directory)) {
+    db = std::make_unique<storage::DiskStorage>(storage_config);
+  } else {
+    db = std::make_unique<storage::InMemoryStorage>(storage_config);
+  }
+}
+
+InterpreterContext::InterpreterContext(std::unique_ptr<storage::Storage> db, InterpreterConfig interpreter_config,
+                                       const std::filesystem::path &data_directory)
+    : db(std::move(db)),
+      trigger_store(data_directory / "triggers"),
+      config(interpreter_config),
+      streams{this, data_directory / "streams"} {}
 
 Interpreter::Interpreter(InterpreterContext *interpreter_context) : interpreter_context_(interpreter_context) {
   MG_ASSERT(interpreter_context_, "Interpreter context must not be NULL");
@@ -1216,8 +1254,7 @@ PreparedQuery Interpreter::PrepareTransactionQuery(std::string_view query_upper,
       expect_rollback_ = false;
       metadata_ = GenOptional(metadata);
 
-      db_accessor_ =
-          std::make_unique<storage::Storage::Accessor>(interpreter_context_->db->Access(GetIsolationLevelOverride()));
+      db_accessor_ = interpreter_context_->db->Access(GetIsolationLevelOverride());
       execution_db_accessor_.emplace(db_accessor_.get());
       transaction_status_.store(TransactionStatus::ACTIVE, std::memory_order_release);
 
@@ -1845,6 +1882,8 @@ PreparedQuery PrepareIndexQuery(ParsedQuery parsed_query, bool in_explicit_trans
                   index_notification.code = NotificationCode::EXISTENT_INDEX;
                   index_notification.title = fmt::format("Index on label {} on properties {} already exists.",
                                                          label_name, properties_stringified);
+                } else if constexpr (std::is_same_v<ErrorType, storage::IndexPersistenceError>) {
+                  throw IndexPersistenceException();
                 } else {
                   static_assert(kAlwaysFalse<T>, "Missing type from variant visitor");
                 }
@@ -1880,6 +1919,8 @@ PreparedQuery PrepareIndexQuery(ParsedQuery parsed_query, bool in_explicit_trans
                   index_notification.code = NotificationCode::NONEXISTENT_INDEX;
                   index_notification.title = fmt::format("Index on label {} on properties {} doesn't exist.",
                                                          label_name, properties_stringified);
+                } else if constexpr (std::is_same_v<ErrorType, storage::IndexPersistenceError>) {
+                  throw IndexPersistenceException();
                 } else {
                   static_assert(kAlwaysFalse<T>, "Missing type from variant visitor");
                 }
@@ -1947,6 +1988,10 @@ PreparedQuery PrepareReplicationQuery(ParsedQuery parsed_query, bool in_explicit
     throw ReplicationModificationInMulticommandTxException();
   }
 
+  if (interpreter_context->db->GetStorageMode() == storage::StorageMode::ON_DISK_TRANSACTIONAL) {
+    throw ReplicationDisabledOnDiskStorage();
+  }
+
   auto *replication_query = utils::Downcast<ReplicationQuery>(parsed_query.query);
   auto callback =
       HandleReplicationQuery(replication_query, parsed_query.parameters, interpreter_context, dba, notifications);
@@ -1969,11 +2014,15 @@ PreparedQuery PrepareReplicationQuery(ParsedQuery parsed_query, bool in_explicit
 }
 
 PreparedQuery PrepareLockPathQuery(ParsedQuery parsed_query, bool in_explicit_transaction,
-                                   InterpreterContext *interpreter_context, DbAccessor *dba) {
+                                   InterpreterContext *interpreter_context) {
   if (in_explicit_transaction) {
     throw LockPathModificationInMulticommandTxException();
   }
 
+  if (interpreter_context->db->GetStorageMode() == storage::StorageMode::ON_DISK_TRANSACTIONAL) {
+    throw LockPathDisabledOnDiskStorage();
+  }
+
   auto *lock_path_query = utils::Downcast<LockPathQuery>(parsed_query.query);
 
   return PreparedQuery{
@@ -1981,12 +2030,13 @@ PreparedQuery PrepareLockPathQuery(ParsedQuery parsed_query, bool in_explicit_tr
       std::move(parsed_query.required_privileges),
       [interpreter_context, action = lock_path_query->action_](
           AnyStream *stream, std::optional<int> n) -> std::optional<QueryHandlerResult> {
+        auto *mem_storage = static_cast<storage::InMemoryStorage *>(interpreter_context->db.get());
         std::vector<std::vector<TypedValue>> status;
         std::string res;
 
         switch (action) {
           case LockPathQuery::Action::LOCK_PATH: {
-            const auto lock_success = interpreter_context->db->LockPath();
+            const auto lock_success = mem_storage->LockPath();
             if (lock_success.HasError()) [[unlikely]] {
               throw QueryRuntimeException("Failed to lock the data directory");
             }
@@ -1994,7 +2044,7 @@ PreparedQuery PrepareLockPathQuery(ParsedQuery parsed_query, bool in_explicit_tr
             break;
           }
           case LockPathQuery::Action::UNLOCK_PATH: {
-            const auto unlock_success = interpreter_context->db->UnlockPath();
+            const auto unlock_success = mem_storage->UnlockPath();
             if (unlock_success.HasError()) [[unlikely]] {
               throw QueryRuntimeException("Failed to unlock the data directory");
             }
@@ -2002,7 +2052,7 @@ PreparedQuery PrepareLockPathQuery(ParsedQuery parsed_query, bool in_explicit_tr
             break;
           }
           case LockPathQuery::Action::STATUS: {
-            const auto locked_status = interpreter_context->db->IsPathLocked();
+            const auto locked_status = mem_storage->IsPathLocked();
             if (locked_status.HasError()) [[unlikely]] {
               throw QueryRuntimeException("Failed to access the data directory");
             }
@@ -2027,6 +2077,10 @@ PreparedQuery PrepareFreeMemoryQuery(ParsedQuery parsed_query, bool in_explicit_
     throw FreeMemoryModificationInMulticommandTxException();
   }
 
+  if (interpreter_context->db->GetStorageMode() == storage::StorageMode::ON_DISK_TRANSACTIONAL) {
+    throw FreeMemoryDisabledOnDiskStorage();
+  }
+
   return PreparedQuery{
       {},
       std::move(parsed_query.required_privileges),
@@ -2240,9 +2294,23 @@ constexpr auto ToStorageMode(const StorageModeQuery::StorageMode storage_mode) n
       return storage::StorageMode::IN_MEMORY_TRANSACTIONAL;
     case StorageModeQuery::StorageMode::IN_MEMORY_ANALYTICAL:
       return storage::StorageMode::IN_MEMORY_ANALYTICAL;
+    case StorageModeQuery::StorageMode::ON_DISK_TRANSACTIONAL:
+      return storage::StorageMode::ON_DISK_TRANSACTIONAL;
   }
 }
 
+bool SwitchingFromInMemoryToDisk(storage::StorageMode current_mode, storage::StorageMode next_mode) {
+  return (current_mode == storage::StorageMode::IN_MEMORY_TRANSACTIONAL ||
+          current_mode == storage::StorageMode::IN_MEMORY_ANALYTICAL) &&
+         next_mode == storage::StorageMode::ON_DISK_TRANSACTIONAL;
+}
+
+bool SwitchingFromDiskToInMemory(storage::StorageMode current_mode, storage::StorageMode next_mode) {
+  return current_mode == storage::StorageMode::ON_DISK_TRANSACTIONAL &&
+         (next_mode == storage::StorageMode::IN_MEMORY_TRANSACTIONAL ||
+          next_mode == storage::StorageMode::IN_MEMORY_ANALYTICAL);
+}
+
 PreparedQuery PrepareIsolationLevelQuery(ParsedQuery parsed_query, const bool in_explicit_transaction,
                                          InterpreterContext *interpreter_context, Interpreter *interpreter) {
   if (in_explicit_transaction) {
@@ -2284,6 +2352,59 @@ PreparedQuery PrepareIsolationLevelQuery(ParsedQuery parsed_query, const bool in
       RWType::NONE};
 }
 
+Callback SwitchMemoryDevice(storage::StorageMode current_mode, storage::StorageMode requested_mode,
+                            InterpreterContext *interpreter_context) {
+  Callback callback;
+  callback.fn = [current_mode, requested_mode, interpreter_context]() mutable {
+    if (current_mode == requested_mode) {
+      return std::vector<std::vector<TypedValue>>();
+    }
+    if (SwitchingFromDiskToInMemory(current_mode, requested_mode)) {
+      throw utils::BasicException(
+          "You cannot switch from on-disk storage to in-memory storage while the database is running. "
+          "Please delete your data directory and restart the database. Once restarted, the Memgraph will automatically "
+          "start to use in-memory storage.");
+    }
+    if (SwitchingFromInMemoryToDisk(current_mode, requested_mode)) {
+      std::unique_lock main_guard{interpreter_context->db->main_lock_};
+
+      if (auto vertex_cnt_approx = interpreter_context->db->GetInfo().vertex_count; vertex_cnt_approx > 0) {
+        throw utils::BasicException(
+            "You cannot switch from in-memory storage to on-disk storage when the database "
+            "contains data. Please delete all entries from the database, run FREE MEMORY and then repeat this "
+            "query. ");
+      }
+
+      main_guard.unlock();
+      if (interpreter_context->interpreters->size() > 1) {
+        throw utils::BasicException(
+            "You cannot switch from in-memory storage to on-disk storage when there are "
+            "multiple sessions active. Please close all other sessions and try again. If you are using Memgraph Lab, "
+            "please start mgconsole "
+            "and run the STORAGE MODE ON_DISK_TRANSACTIONAL there first. Memgraph Lab is using multiple sessions to "
+            "run queries in parallel "
+            "so it is currently impossible to switch to on-disk storage while the Lab is running. After you switch "
+            "from the mgconsole, you can "
+            "continue to use Memgraph Lab as you wish.");
+      }
+
+      auto db_config = interpreter_context->db->config_;
+      interpreter_context->db = std::make_unique<memgraph::storage::DiskStorage>(db_config);
+    }
+    return std::vector<std::vector<TypedValue>>();
+  };
+  return callback;
+}
+
+bool ActiveTransactionsExist(InterpreterContext *interpreter_context) {
+  bool exists_active_transaction = interpreter_context->interpreters.WithLock([](const auto &interpreters_) {
+    return std::any_of(interpreters_.begin(), interpreters_.end(), [](const auto &interpreter) {
+      return interpreter->transaction_status_.load() != TransactionStatus::IDLE;
+    });
+  });
+  return exists_active_transaction;
+}
+
 PreparedQuery PrepareStorageModeQuery(ParsedQuery parsed_query, const bool in_explicit_transaction,
                                       InterpreterContext *interpreter_context) {
   if (in_explicit_transaction) {
@@ -2292,23 +2413,26 @@ PreparedQuery PrepareStorageModeQuery(ParsedQuery parsed_query, const bool in_ex
 
   auto *storage_mode_query = utils::Downcast<StorageModeQuery>(parsed_query.query);
   MG_ASSERT(storage_mode_query);
-  const auto storage_mode = ToStorageMode(storage_mode_query->storage_mode_);
+  const auto requested_mode = ToStorageMode(storage_mode_query->storage_mode_);
+  auto current_mode = interpreter_context->db->GetStorageMode();
 
-  auto exists_active_transaction = interpreter_context->interpreters.WithLock([](const auto &interpreters_) {
-    return std::any_of(interpreters_.begin(), interpreters_.end(), [](const auto &interpreter) {
-      return interpreter->transaction_status_.load() != TransactionStatus::IDLE;
-    });
-  });
-  if (exists_active_transaction) {
-    spdlog::info(
-        "Storage mode will be modified when there are no other active transactions. Check the status of the "
-        "transactions using 'SHOW TRANSACTIONS' query and ensure no other transactions are active.");
+  std::function<void()> callback;
+
+  if (current_mode == storage::StorageMode::ON_DISK_TRANSACTIONAL ||
+      requested_mode == storage::StorageMode::ON_DISK_TRANSACTIONAL) {
+    callback = SwitchMemoryDevice(current_mode, requested_mode, interpreter_context).fn;
+  } else {
+    if (ActiveTransactionsExist(interpreter_context)) {
+      spdlog::info(
+          "Storage mode will be modified when there are no other active transactions. Check the status of the "
+          "transactions using 'SHOW TRANSACTIONS' query and ensure no other transactions are active.");
+    }
+
+    callback = [requested_mode, interpreter_context]() -> std::function<void()> {
+      return [interpreter_context, requested_mode] { interpreter_context->db->SetStorageMode(requested_mode); };
+    }();
   }
 
-  auto callback = [storage_mode, interpreter_context]() -> std::function<void()> {
-    return [interpreter_context, storage_mode] { interpreter_context->db->SetStorageMode(storage_mode); };
-  }();
-
   return PreparedQuery{{},
                        std::move(parsed_query.required_privileges),
                        [callback = std::move(callback)](AnyStream * /*stream*/,
@@ -2325,20 +2449,25 @@ PreparedQuery PrepareCreateSnapshotQuery(ParsedQuery parsed_query, bool in_expli
     throw CreateSnapshotInMulticommandTxException();
   }
 
+  if (interpreter_context->db->GetStorageMode() == storage::StorageMode::ON_DISK_TRANSACTIONAL) {
+    throw CreateSnapshotDisabledOnDiskStorage();
+  }
+
   return PreparedQuery{
       {},
       std::move(parsed_query.required_privileges),
       [interpreter_context](AnyStream *stream, std::optional<int> n) -> std::optional<QueryHandlerResult> {
-        if (auto maybe_error = interpreter_context->db->CreateSnapshot({}); maybe_error.HasError()) {
+        auto *mem_storage = static_cast<storage::InMemoryStorage *>(interpreter_context->db.get());
+        if (auto maybe_error = mem_storage->CreateSnapshot({}); maybe_error.HasError()) {
           switch (maybe_error.GetError()) {
-            case storage::Storage::CreateSnapshotError::DisabledForReplica:
+            case storage::InMemoryStorage::CreateSnapshotError::DisabledForReplica:
               throw utils::BasicException(
                   "Failed to create a snapshot. Replica instances are not allowed to create them.");
-            case storage::Storage::CreateSnapshotError::DisabledForAnalyticsPeriodicCommit:
+            case storage::InMemoryStorage::CreateSnapshotError::DisabledForAnalyticsPeriodicCommit:
               spdlog::warn(utils::MessageWithLink("Periodic snapshots are disabled for analytical mode.",
                                                   "https://memgr.ph/replication"));
               break;
-            case storage::Storage::CreateSnapshotError::ReachedMaxNumTries:
+            case storage::InMemoryStorage::CreateSnapshotError::ReachedMaxNumTries:
               spdlog::warn("Failed to create snapshot. Reached max number of tries. Please contact support");
               break;
           }
@@ -2586,7 +2715,7 @@ PreparedQuery PrepareInfoQuery(ParsedQuery parsed_query, bool in_explicit_transa
     case InfoQuery::InfoType::INDEX:
       header = {"index type", "label", "property"};
       handler = [interpreter_context] {
-        auto *db = interpreter_context->db;
+        auto *db = interpreter_context->db.get();
         auto info = db->ListAllIndices();
         std::vector<std::vector<TypedValue>> results;
         results.reserve(info.label.size() + info.label_property.size());
@@ -2603,7 +2732,7 @@ PreparedQuery PrepareInfoQuery(ParsedQuery parsed_query, bool in_explicit_transa
     case InfoQuery::InfoType::CONSTRAINT:
       header = {"constraint type", "label", "properties"};
       handler = [interpreter_context] {
-        auto *db = interpreter_context->db;
+        auto *db = interpreter_context->db.get();
         auto info = db->ListAllConstraints();
         std::vector<std::vector<TypedValue>> results;
         results.reserve(info.existence.size() + info.unique.size());
@@ -2690,7 +2819,7 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
           handler = [interpreter_context, label, label_name = constraint_query->constraint_.label.name,
                      properties_stringified = std::move(properties_stringified),
                      properties = std::move(properties)](Notification &constraint_notification) {
-            auto maybe_constraint_error = interpreter_context->db->CreateExistenceConstraint(label, properties[0]);
+            auto maybe_constraint_error = interpreter_context->db->CreateExistenceConstraint(label, properties[0], {});
 
             if (maybe_constraint_error.HasError()) {
               const auto &error = maybe_constraint_error.GetError();
@@ -2713,9 +2842,12 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
                                       properties_stringified);
                     } else if constexpr (std::is_same_v<ErrorType, storage::ReplicationError>) {
                       throw ReplicationException(
-                          "At least one SYNC replica has not confirmed the creation of the EXISTS constraint on label "
+                          "At least one SYNC replica has not confirmed the creation of the EXISTS constraint on "
+                          "label "
                           "{} on properties {}.",
                           label_name, properties_stringified);
+                    } else if constexpr (std::is_same_v<ErrorType, storage::ConstraintsPersistenceError>) {
+                      throw ConstraintsPersistenceException();
                     } else {
                       static_assert(kAlwaysFalse<T>, "Missing type from variant visitor");
                     }
@@ -2738,11 +2870,12 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
           handler = [interpreter_context, label, label_name = constraint_query->constraint_.label.name,
                      properties_stringified = std::move(properties_stringified),
                      property_set = std::move(property_set)](Notification &constraint_notification) {
-            auto maybe_constraint_error = interpreter_context->db->CreateUniqueConstraint(label, property_set);
+            auto maybe_constraint_error = interpreter_context->db->CreateUniqueConstraint(label, property_set, {});
             if (maybe_constraint_error.HasError()) {
               const auto &error = maybe_constraint_error.GetError();
               std::visit(
-                  [&interpreter_context, &label_name, &properties_stringified]<typename T>(T &&arg) {
+                  [&interpreter_context, &label_name, &properties_stringified,
+                   &constraint_notification]<typename T>(T &&arg) {
                     using ErrorType = std::remove_cvref_t<T>;
                     if constexpr (std::is_same_v<ErrorType, storage::ConstraintViolation>) {
                       auto &violation = arg;
@@ -2756,10 +2889,18 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
                           "Unable to create unique constraint :{}({}), because an "
                           "existing node violates it.",
                           violation_label_name, property_names_stream.str());
+                    } else if constexpr (std::is_same_v<ErrorType, storage::ConstraintDefinitionError>) {
+                      constraint_notification.code = NotificationCode::EXISTENT_CONSTRAINT;
+                      constraint_notification.title =
+                          fmt::format("Constraint UNIQUE on label {} and properties {} couldn't be created.",
+                                      label_name, properties_stringified);
                     } else if constexpr (std::is_same_v<ErrorType, storage::ReplicationError>) {
-                      throw ReplicationException(fmt::format(
-                          "At least one SYNC replica has not confirmed the creation of the UNIQUE constraint: {}({}).",
-                          label_name, properties_stringified));
+                      throw ReplicationException(
+                          fmt::format("At least one SYNC replica has not confirmed the creation of the UNIQUE "
+                                      "constraint: {}({}).",
+                                      label_name, properties_stringified));
+                    } else if constexpr (std::is_same_v<ErrorType, storage::ConstraintsPersistenceError>) {
+                      throw ConstraintsPersistenceException();
                     } else {
                       static_assert(kAlwaysFalse<T>, "Missing type from variant visitor");
                     }
@@ -2805,7 +2946,7 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
           handler = [interpreter_context, label, label_name = constraint_query->constraint_.label.name,
                      properties_stringified = std::move(properties_stringified),
                      properties = std::move(properties)](Notification &constraint_notification) {
-            auto maybe_constraint_error = interpreter_context->db->DropExistenceConstraint(label, properties[0]);
+            auto maybe_constraint_error = interpreter_context->db->DropExistenceConstraint(label, properties[0], {});
             if (maybe_constraint_error.HasError()) {
               const auto &error = maybe_constraint_error.GetError();
               std::visit(
@@ -2821,6 +2962,8 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
                           fmt::format("At least one SYNC replica has not confirmed the dropping of the EXISTS "
                                       "constraint  on label {} on properties {}.",
                                       label_name, properties_stringified));
+                    } else if constexpr (std::is_same_v<ErrorType, storage::ConstraintsPersistenceError>) {
+                      throw ConstraintsPersistenceException();
                     } else {
                       static_assert(kAlwaysFalse<T>, "Missing type from variant visitor");
                     }
@@ -2844,7 +2987,7 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
           handler = [interpreter_context, label, label_name = constraint_query->constraint_.label.name,
                      properties_stringified = std::move(properties_stringified),
                      property_set = std::move(property_set)](Notification &constraint_notification) {
-            auto maybe_constraint_error = interpreter_context->db->DropUniqueConstraint(label, property_set);
+            auto maybe_constraint_error = interpreter_context->db->DropUniqueConstraint(label, property_set, {});
             if (maybe_constraint_error.HasError()) {
               const auto &error = maybe_constraint_error.GetError();
               std::visit(
@@ -2855,6 +2998,8 @@ PreparedQuery PrepareConstraintQuery(ParsedQuery parsed_query, bool in_explicit_
                           fmt::format("At least one SYNC replica has not confirmed the dropping of the UNIQUE "
                                       "constraint on label {} on properties {}.",
                                       label_name, properties_stringified));
+                    } else if constexpr (std::is_same_v<ErrorType, storage::ConstraintsPersistenceError>) {
+                      throw ConstraintsPersistenceException();
                     } else {
                       static_assert(kAlwaysFalse<T>, "Missing type from variant visitor");
                     }
@@ -3021,8 +3166,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
          utils::Downcast<TriggerQuery>(parsed_query.query) || utils::Downcast<AnalyzeGraphQuery>(parsed_query.query) ||
          utils::Downcast<TransactionQueueQuery>(parsed_query.query))) {
       memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveTransactions);
-      db_accessor_ =
-          std::make_unique<storage::Storage::Accessor>(interpreter_context_->db->Access(GetIsolationLevelOverride()));
+      db_accessor_ = interpreter_context_->db->Access(GetIsolationLevelOverride());
       execution_db_accessor_.emplace(db_accessor_.get());
       transaction_status_.store(TransactionStatus::ACTIVE, std::memory_order_release);
 
@@ -3066,7 +3210,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
           &*execution_db_accessor_, &query_execution->execution_memory_with_exception, username, &transaction_status_);
     } else if (utils::Downcast<InfoQuery>(parsed_query.query)) {
       prepared_query = PrepareInfoQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->summary,
-                                        interpreter_context_, interpreter_context_->db,
+                                        interpreter_context_, interpreter_context_->db.get(),
                                         &query_execution->execution_memory_with_exception, interpreter_isolation_level,
                                         next_transaction_isolation_level);
     } else if (utils::Downcast<ConstraintQuery>(parsed_query.query)) {
@@ -3077,8 +3221,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
           PrepareReplicationQuery(std::move(parsed_query), in_explicit_transaction_, &query_execution->notifications,
                                   interpreter_context_, &*execution_db_accessor_);
     } else if (utils::Downcast<LockPathQuery>(parsed_query.query)) {
-      prepared_query = PrepareLockPathQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_,
-                                            &*execution_db_accessor_);
+      prepared_query = PrepareLockPathQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_);
     } else if (utils::Downcast<FreeMemoryQuery>(parsed_query.query)) {
       prepared_query = PrepareFreeMemoryQuery(std::move(parsed_query), in_explicit_transaction_, interpreter_context_);
     } else if (utils::Downcast<ShowConfigQuery>(parsed_query.query)) {
@@ -3118,9 +3261,7 @@ Interpreter::PrepareResult Interpreter::Prepare(const std::string &query_string,
 
     UpdateTypeCount(rw_type);
 
-    if (const auto query_type = query_execution->prepared_query->rw_type;
-        interpreter_context_->db->GetReplicationRole() == storage::replication::ReplicationRole::REPLICA &&
-        (query_type == RWType::W || query_type == RWType::RW)) {
+    if (IsWriteQueryOnMainMemoryReplica(interpreter_context_->db.get(), rw_type)) {
       query_execution = nullptr;
       throw QueryException("Write query forbidden on the replica!");
     }
@@ -3165,6 +3306,9 @@ void Interpreter::Abort() {
   if (!db_accessor_) return;
 
   db_accessor_->Abort();
+  for (auto &qe : query_executions_) {
+    if (qe) qe->CleanRuntimeData();
+  }
   execution_db_accessor_.reset();
   db_accessor_.reset();
   trigger_context_collector_.reset();
@@ -3180,7 +3324,7 @@ void RunTriggersIndividually(const utils::SkipList<Trigger> &triggers, Interpret
 
     // create a new transaction for each trigger
     auto storage_acc = interpreter_context->db->Access();
-    DbAccessor db_accessor{&storage_acc};
+    DbAccessor db_accessor{storage_acc.get()};
 
     trigger_context.AdaptForAccessor(&db_accessor);
     try {
@@ -3223,6 +3367,8 @@ void RunTriggersIndividually(const utils::SkipList<Trigger> &triggers, Interpret
                                trigger.Name(), label_name, property_names_stream.str());
                 }
               }
+            } else if constexpr (std::is_same_v<ErrorType, storage::SerializationError>) {
+              throw QueryException("Unable to commit due to serialization error.");
             } else {
               static_assert(kAlwaysFalse<T>, "Missing type from variant visitor");
             }
@@ -3260,6 +3406,14 @@ void Interpreter::Commit() {
   utils::OnScopeExit clean_status(
       [this]() { transaction_status_.store(TransactionStatus::IDLE, std::memory_order_release); });
 
+  auto current_storage_mode = interpreter_context_->db->GetStorageMode();
+  auto creation_mode = db_accessor_->GetCreationStorageMode();
+  if (creation_mode != storage::StorageMode::ON_DISK_TRANSACTIONAL &&
+      current_storage_mode == storage::StorageMode::ON_DISK_TRANSACTIONAL) {
+    throw QueryException(
+        "Cannot commit transaction because the storage mode has changed from in-memory storage to on-disk storage.");
+  }
+
   utils::OnScopeExit update_metrics([]() {
     memgraph::metrics::IncrementCounter(memgraph::metrics::CommitedTransactions);
     memgraph::metrics::DecrementCounter(memgraph::metrics::ActiveTransactions);
@@ -3293,6 +3447,9 @@ void Interpreter::Commit() {
   }
 
   const auto reset_necessary_members = [this]() {
+    for (auto &qe : query_executions_) {
+      if (qe) qe->CleanRuntimeData();
+    }
     execution_db_accessor_.reset();
     db_accessor_.reset();
     trigger_context_collector_.reset();
@@ -3331,6 +3488,8 @@ void Interpreter::Commit() {
                                      property_names_stream.str());
               }
             }
+          } else if constexpr (std::is_same_v<ErrorType, storage::SerializationError>) {
+            throw QueryException("Unable to commit due to serialization error.");
           } else {
             static_assert(kAlwaysFalse<T>, "Missing type from variant visitor");
           }
@@ -3338,11 +3497,11 @@ void Interpreter::Commit() {
         error);
   }
 
-  // The ordered execution of after commit triggers is heavily depending on the exclusiveness of db_accessor_->Commit():
-  // only one of the transactions can be commiting at the same time, so when the commit is finished, that transaction
-  // probably will schedule its after commit triggers, because the other transactions that want to commit are still
-  // waiting for commiting or one of them just started commiting its changes.
-  // This means the ordered execution of after commit triggers are not guaranteed.
+  // The ordered execution of after commit triggers is heavily depending on the exclusiveness of
+  // db_accessor_->Commit(): only one of the transactions can be commiting at the same time, so when the commit is
+  // finished, that transaction probably will schedule its after commit triggers, because the other transactions that
+  // want to commit are still waiting for commiting or one of them just started commiting its changes. This means the
+  // ordered execution of after commit triggers are not guaranteed.
   if (trigger_context && interpreter_context_->trigger_store.AfterCommitTriggers().size() > 0) {
     interpreter_context_->after_commit_trigger_pool.AddTask(
         [this, trigger_context = std::move(*trigger_context),
diff --git a/src/query/interpreter.hpp b/src/query/interpreter.hpp
index a6b7052e2..f294fce3b 100644
--- a/src/query/interpreter.hpp
+++ b/src/query/interpreter.hpp
@@ -32,7 +32,10 @@
 #include "query/stream/streams.hpp"
 #include "query/trigger.hpp"
 #include "query/typed_value.hpp"
+#include "spdlog/spdlog.h"
+#include "storage/v2/disk/storage.hpp"
 #include "storage/v2/isolation_level.hpp"
+#include "storage/v2/storage.hpp"
 #include "utils/event_counter.hpp"
 #include "utils/logging.hpp"
 #include "utils/memory.hpp"
@@ -208,11 +211,15 @@ class Interpreter;
  * running concurrently).
  *
  */
+/// TODO: andi decouple in a separate file why here?
 struct InterpreterContext {
-  explicit InterpreterContext(storage::Storage *db, InterpreterConfig config,
+  explicit InterpreterContext(storage::Config storage_config, InterpreterConfig interpreter_config,
                               const std::filesystem::path &data_directory);
 
-  storage::Storage *db;
+  InterpreterContext(std::unique_ptr<storage::Storage> db, InterpreterConfig interpreter_config,
+                     const std::filesystem::path &data_directory);
+
+  std::unique_ptr<storage::Storage> db;
 
   // ANTLR has singleton instance that is shared between threads. It is
   // protected by locks inside of ANTLR. Unfortunately, they are not protected
@@ -319,9 +326,6 @@ class Interpreter final {
 
   void BeginTransaction(const std::map<std::string, storage::PropertyValue> &metadata = {});
 
-  /*
-  Returns transaction id or empty if the db_accessor is not initialized.
-  */
   std::optional<uint64_t> GetTransactionId() const;
 
   void CommitTransaction();
@@ -378,6 +382,13 @@ class Interpreter final {
       prepared_query.reset();
       std::visit([](auto &memory_resource) { memory_resource.Release(); }, execution_memory);
     }
+
+    void CleanRuntimeData() {
+      if (prepared_query.has_value()) {
+        prepared_query.reset();
+      }
+      notifications.clear();
+    }
   };
 
   // Interpreter supports multiple prepared queries at the same time.
@@ -533,4 +544,5 @@ std::map<std::string, TypedValue> Interpreter::Pull(TStream *result_stream, std:
   // don't return the execution summary as it's not finished
   return {{"has_more", TypedValue(true)}};
 }
+
 }  // namespace memgraph::query
diff --git a/src/query/plan/operator.cpp b/src/query/plan/operator.cpp
index 040f2da95..91ebad094 100644
--- a/src/query/plan/operator.cpp
+++ b/src/query/plan/operator.cpp
@@ -54,6 +54,7 @@
 #include "utils/likely.hpp"
 #include "utils/logging.hpp"
 #include "utils/memory.hpp"
+#include "utils/message.hpp"
 #include "utils/pmr/deque.hpp"
 #include "utils/pmr/list.hpp"
 #include "utils/pmr/unordered_map.hpp"
@@ -813,10 +814,12 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, ExecutionContext &context) {
         // old_node_value may be Null when using optional matching
         if (!existing_node.IsNull()) {
           ExpectType(self_.common_.node_symbol, existing_node, TypedValue::Type::Vertex);
+          context.db_accessor->PrefetchInEdges(vertex);
           in_edges_.emplace(
               UnwrapEdgesResult(vertex.InEdges(self_.view_, self_.common_.edge_types, existing_node.ValueVertex())));
         }
       } else {
+        context.db_accessor->PrefetchInEdges(vertex);
         in_edges_.emplace(UnwrapEdgesResult(vertex.InEdges(self_.view_, self_.common_.edge_types)));
       }
       if (in_edges_) {
@@ -830,10 +833,12 @@ bool Expand::ExpandCursor::InitEdges(Frame &frame, ExecutionContext &context) {
         // old_node_value may be Null when using optional matching
         if (!existing_node.IsNull()) {
           ExpectType(self_.common_.node_symbol, existing_node, TypedValue::Type::Vertex);
+          context.db_accessor->PrefetchOutEdges(vertex);
           out_edges_.emplace(
               UnwrapEdgesResult(vertex.OutEdges(self_.view_, self_.common_.edge_types, existing_node.ValueVertex())));
         }
       } else {
+        context.db_accessor->PrefetchOutEdges(vertex);
         out_edges_.emplace(UnwrapEdgesResult(vertex.OutEdges(self_.view_, self_.common_.edge_types)));
       }
       if (out_edges_) {
@@ -891,7 +896,8 @@ namespace {
  * @return See above.
  */
 auto ExpandFromVertex(const VertexAccessor &vertex, EdgeAtom::Direction direction,
-                      const std::vector<storage::EdgeTypeId> &edge_types, utils::MemoryResource *memory) {
+                      const std::vector<storage::EdgeTypeId> &edge_types, utils::MemoryResource *memory,
+                      DbAccessor *db_accessor) {
   // wraps an EdgeAccessor into a pair <accessor, direction>
   auto wrapper = [](EdgeAtom::Direction direction, auto &&edges) {
     return iter::imap([direction](const auto &edge) { return std::make_pair(edge, direction); },
@@ -902,6 +908,7 @@ auto ExpandFromVertex(const VertexAccessor &vertex, EdgeAtom::Direction directio
   utils::pmr::vector<decltype(wrapper(direction, *vertex.InEdges(view, edge_types)))> chain_elements(memory);
 
   if (direction != EdgeAtom::Direction::OUT) {
+    db_accessor->PrefetchInEdges(vertex);
     auto edges = UnwrapEdgesResult(vertex.InEdges(view, edge_types));
     if (edges.begin() != edges.end()) {
       chain_elements.emplace_back(wrapper(EdgeAtom::Direction::IN, std::move(edges)));
@@ -909,6 +916,7 @@ auto ExpandFromVertex(const VertexAccessor &vertex, EdgeAtom::Direction directio
   }
 
   if (direction != EdgeAtom::Direction::IN) {
+    db_accessor->PrefetchOutEdges(vertex);
     auto edges = UnwrapEdgesResult(vertex.OutEdges(view, edge_types));
     if (edges.begin() != edges.end()) {
       chain_elements.emplace_back(wrapper(EdgeAtom::Direction::OUT, std::move(edges)));
@@ -974,8 +982,9 @@ class ExpandVariableCursor : public Cursor {
 
   // a stack of edge iterables corresponding to the level/depth of
   // the expansion currently being Pulled
-  using ExpandEdges = decltype(ExpandFromVertex(std::declval<VertexAccessor>(), EdgeAtom::Direction::IN,
-                                                self_.common_.edge_types, utils::NewDeleteResource()));
+  using ExpandEdges =
+      decltype(ExpandFromVertex(std::declval<VertexAccessor>(), EdgeAtom::Direction::IN, self_.common_.edge_types,
+                                utils::NewDeleteResource(), std::declval<DbAccessor *>()));
 
   utils::pmr::vector<ExpandEdges> edges_;
   // an iterator indicating the position in the corresponding edges_ element
@@ -1016,7 +1025,8 @@ class ExpandVariableCursor : public Cursor {
 
       if (upper_bound_ > 0) {
         auto *memory = edges_.get_allocator().GetMemoryResource();
-        edges_.emplace_back(ExpandFromVertex(vertex, self_.common_.direction, self_.common_.edge_types, memory));
+        edges_.emplace_back(
+            ExpandFromVertex(vertex, self_.common_.direction, self_.common_.edge_types, memory, context.db_accessor));
         edges_it_.emplace_back(edges_.back().begin());
       }
 
@@ -1122,8 +1132,8 @@ class ExpandVariableCursor : public Cursor {
       // edge's expansions onto the stack, if we should continue to expand
       if (upper_bound_ > static_cast<int64_t>(edges_.size())) {
         auto *memory = edges_.get_allocator().GetMemoryResource();
-        edges_.emplace_back(
-            ExpandFromVertex(current_vertex, self_.common_.direction, self_.common_.edge_types, memory));
+        edges_.emplace_back(ExpandFromVertex(current_vertex, self_.common_.direction, self_.common_.edge_types, memory,
+                                             context.db_accessor));
         edges_it_.emplace_back(edges_.back().begin());
       }
 
@@ -1266,6 +1276,7 @@ class STShortestPathCursor : public query::plan::Cursor {
 
       for (const auto &vertex : source_frontier) {
         if (self_.common_.direction != EdgeAtom::Direction::IN) {
+          context.db_accessor->PrefetchOutEdges(vertex);
           auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
           for (const auto &edge : out_edges) {
 #ifdef MG_ENTERPRISE
@@ -1292,6 +1303,7 @@ class STShortestPathCursor : public query::plan::Cursor {
           }
         }
         if (self_.common_.direction != EdgeAtom::Direction::OUT) {
+          dba.PrefetchInEdges(vertex);
           auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
           for (const auto &edge : in_edges) {
 #ifdef MG_ENTERPRISE
@@ -1332,6 +1344,7 @@ class STShortestPathCursor : public query::plan::Cursor {
       // reversed.
       for (const auto &vertex : sink_frontier) {
         if (self_.common_.direction != EdgeAtom::Direction::OUT) {
+          context.db_accessor->PrefetchOutEdges(vertex);
           auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
           for (const auto &edge : out_edges) {
 #ifdef MG_ENTERPRISE
@@ -1357,6 +1370,7 @@ class STShortestPathCursor : public query::plan::Cursor {
           }
         }
         if (self_.common_.direction != EdgeAtom::Direction::IN) {
+          dba.PrefetchInEdges(vertex);
           auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
           for (const auto &edge : in_edges) {
 #ifdef MG_ENTERPRISE
@@ -1446,12 +1460,14 @@ class SingleSourceShortestPathCursor : public query::plan::Cursor {
     // populates the to_visit_next_ structure with expansions
     // from the given vertex. skips expansions that don't satisfy
     // the "where" condition.
-    auto expand_from_vertex = [this, &expand_pair](const auto &vertex) {
+    auto expand_from_vertex = [this, &expand_pair, &context](const auto &vertex) {
       if (self_.common_.direction != EdgeAtom::Direction::IN) {
+        context.db_accessor->PrefetchOutEdges(vertex);
         auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
         for (const auto &edge : out_edges) expand_pair(edge, edge.To());
       }
       if (self_.common_.direction != EdgeAtom::Direction::OUT) {
+        context.db_accessor->PrefetchInEdges(vertex);
         auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
         for (const auto &edge : in_edges) expand_pair(edge, edge.From());
       }
@@ -1646,15 +1662,17 @@ class ExpandWeightedShortestPathCursor : public query::plan::Cursor {
     // Populates the priority queue structure with expansions
     // from the given vertex. skips expansions that don't satisfy
     // the "where" condition.
-    auto expand_from_vertex = [this, &expand_pair](const VertexAccessor &vertex, const TypedValue &weight,
-                                                   int64_t depth) {
+    auto expand_from_vertex = [this, &expand_pair, &context](const VertexAccessor &vertex, const TypedValue &weight,
+                                                             int64_t depth) {
       if (self_.common_.direction != EdgeAtom::Direction::IN) {
+        context.db_accessor->PrefetchOutEdges(vertex);
         auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
         for (const auto &edge : out_edges) {
           expand_pair(edge, edge.To(), weight, depth);
         }
       }
       if (self_.common_.direction != EdgeAtom::Direction::OUT) {
+        context.db_accessor->PrefetchInEdges(vertex);
         auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
         for (const auto &edge : in_edges) {
           expand_pair(edge, edge.From(), weight, depth);
@@ -1913,6 +1931,7 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
     auto expand_from_vertex = [this, &expand_vertex, &context](const VertexAccessor &vertex, const TypedValue &weight,
                                                                int64_t depth) {
       if (self_.common_.direction != EdgeAtom::Direction::IN) {
+        context.db_accessor->PrefetchOutEdges(vertex);
         auto out_edges = UnwrapEdgesResult(vertex.OutEdges(storage::View::OLD, self_.common_.edge_types));
         for (const auto &edge : out_edges) {
 #ifdef MG_ENTERPRISE
@@ -1927,6 +1946,7 @@ class ExpandAllShortestPathsCursor : public query::plan::Cursor {
         }
       }
       if (self_.common_.direction != EdgeAtom::Direction::OUT) {
+        context.db_accessor->PrefetchInEdges(vertex);
         auto in_edges = UnwrapEdgesResult(vertex.InEdges(storage::View::OLD, self_.common_.edge_types));
         for (const auto &edge : in_edges) {
 #ifdef MG_ENTERPRISE
diff --git a/src/query/plan/operator.hpp b/src/query/plan/operator.hpp
index 3d95d1da0..79df74ffb 100644
--- a/src/query/plan/operator.hpp
+++ b/src/query/plan/operator.hpp
@@ -28,6 +28,7 @@
 #include "utils/fnv.hpp"
 #include "utils/logging.hpp"
 #include "utils/memory.hpp"
+#include "utils/synchronized.hpp"
 #include "utils/visitor.hpp"
 
 namespace memgraph {
diff --git a/src/query/plan/rewrite/index_lookup.hpp b/src/query/plan/rewrite/index_lookup.hpp
index 9e4fc7c9a..2ed261ccb 100644
--- a/src/query/plan/rewrite/index_lookup.hpp
+++ b/src/query/plan/rewrite/index_lookup.hpp
@@ -27,7 +27,6 @@
 
 #include "query/plan/operator.hpp"
 #include "query/plan/preprocess.hpp"
-#include "storage/v2/indices.hpp"
 
 DECLARE_int64(query_vertex_count_to_expand_existing);
 
diff --git a/src/query/procedure/mg_procedure_impl.cpp b/src/query/procedure/mg_procedure_impl.cpp
index bc4dbffcb..9309b2b4b 100644
--- a/src/query/procedure/mg_procedure_impl.cpp
+++ b/src/query/procedure/mg_procedure_impl.cpp
@@ -21,10 +21,12 @@
 #include <stdexcept>
 #include <type_traits>
 #include <utility>
+#include <variant>
 
 #include "license/license.hpp"
 #include "mg_procedure.h"
 #include "module.hpp"
+#include "query/db_accessor.hpp"
 #include "query/frontend/ast/ast.hpp"
 #include "query/procedure/cypher_types.hpp"
 #include "query/procedure/mg_procedure_helpers.hpp"
@@ -1950,9 +1952,9 @@ void NextPermittedEdge(mgp_edges_iterator &it, const bool for_in) {
     const auto *auth_checker = it.source_vertex.graph->ctx->auth_checker.get();
     const auto view = it.source_vertex.graph->view;
     while (*impl_it != end) {
-      if (auth_checker->Has(**impl_it, memgraph::query::AuthQuery::FineGrainedPrivilege::READ)) {
-        const auto &check_vertex =
-            it.source_vertex.getImpl() == (*impl_it)->From() ? (*impl_it)->To() : (*impl_it)->From();
+      auto edgeAcc = **impl_it;
+      if (auth_checker->Has(edgeAcc, memgraph::query::AuthQuery::FineGrainedPrivilege::READ)) {
+        const auto &check_vertex = it.source_vertex.getImpl() == edgeAcc.From() ? edgeAcc.To() : edgeAcc.From();
         if (auth_checker->Has(check_vertex, view, memgraph::query::AuthQuery::FineGrainedPrivilege::READ)) {
           break;
         }
@@ -1968,6 +1970,14 @@ void NextPermittedEdge(mgp_edges_iterator &it, const bool for_in) {
 mgp_error mgp_vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_iterator **result) {
   return WrapExceptions(
       [v, memory] {
+        auto dbAccessor = v->graph->impl;
+        if (std::holds_alternative<memgraph::query::DbAccessor *>(dbAccessor)) {
+          std::get<memgraph::query::DbAccessor *>(dbAccessor)
+              ->PrefetchInEdges(std::get<memgraph::query::VertexAccessor>(v->impl));
+        } else {
+          std::get<memgraph::query::SubgraphDbAccessor *>(dbAccessor)
+              ->PrefetchInEdges(std::get<memgraph::query::SubgraphVertexAccessor>(v->impl));
+        }
         auto it = NewMgpObject<mgp_edges_iterator>(memory, *v);
         MG_ASSERT(it != nullptr);
 
@@ -1995,19 +2005,20 @@ mgp_error mgp_vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_
 #endif
 
         if (*it->in_it != it->in->end()) {
-          std::visit(memgraph::utils::Overloaded{
-                         [&](memgraph::query::DbAccessor *) {
-                           it->current_e.emplace(**it->in_it, (**it->in_it).From(), (**it->in_it).To(), v->graph,
-                                                 it->GetMemoryResource());
-                         },
-                         [&](memgraph::query::SubgraphDbAccessor *impl) {
-                           it->current_e.emplace(
-                               **it->in_it,
-                               memgraph::query::SubgraphVertexAccessor((**it->in_it).From(), impl->getGraph()),
-                               memgraph::query::SubgraphVertexAccessor((**it->in_it).To(), impl->getGraph()), v->graph,
-                               it->GetMemoryResource());
-                         }},
-                     v->graph->impl);
+          std::visit(
+              memgraph::utils::Overloaded{
+                  [&](memgraph::query::DbAccessor *) {
+                    auto edgeAcc = **it->in_it;
+                    it->current_e.emplace(edgeAcc, edgeAcc.From(), edgeAcc.To(), v->graph, it->GetMemoryResource());
+                  },
+                  [&](memgraph::query::SubgraphDbAccessor *impl) {
+                    auto edgeAcc = **it->in_it;
+                    it->current_e.emplace(edgeAcc,
+                                          memgraph::query::SubgraphVertexAccessor(edgeAcc.From(), impl->getGraph()),
+                                          memgraph::query::SubgraphVertexAccessor(edgeAcc.To(), impl->getGraph()),
+                                          v->graph, it->GetMemoryResource());
+                  }},
+              v->graph->impl);
         }
 
         return it.release();
@@ -2018,6 +2029,14 @@ mgp_error mgp_vertex_iter_in_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_
 mgp_error mgp_vertex_iter_out_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges_iterator **result) {
   return WrapExceptions(
       [v, memory] {
+        auto dbAccessor = v->graph->impl;
+        if (std::holds_alternative<memgraph::query::DbAccessor *>(dbAccessor)) {
+          std::get<memgraph::query::DbAccessor *>(dbAccessor)
+              ->PrefetchOutEdges(std::get<memgraph::query::VertexAccessor>(v->impl));
+        } else {
+          std::get<memgraph::query::SubgraphDbAccessor *>(dbAccessor)
+              ->PrefetchOutEdges(std::get<memgraph::query::SubgraphVertexAccessor>(v->impl));
+        }
         auto it = NewMgpObject<mgp_edges_iterator>(memory, *v);
         MG_ASSERT(it != nullptr);
         auto maybe_edges = std::visit([v](auto &impl) { return impl.OutEdges(v->graph->view); }, v->impl);
@@ -2047,19 +2066,20 @@ mgp_error mgp_vertex_iter_out_edges(mgp_vertex *v, mgp_memory *memory, mgp_edges
 #endif
 
         if (*it->out_it != it->out->end()) {
-          std::visit(memgraph::utils::Overloaded{
-                         [&](memgraph::query::DbAccessor *) {
-                           it->current_e.emplace(**it->out_it, (**it->out_it).From(), (**it->out_it).To(), v->graph,
-                                                 it->GetMemoryResource());
-                         },
-                         [&](memgraph::query::SubgraphDbAccessor *impl) {
-                           it->current_e.emplace(
-                               **it->out_it,
-                               memgraph::query::SubgraphVertexAccessor((**it->out_it).From(), impl->getGraph()),
-                               memgraph::query::SubgraphVertexAccessor((**it->out_it).To(), impl->getGraph()), v->graph,
-                               it->GetMemoryResource());
-                         }},
-                     v->graph->impl);
+          std::visit(
+              memgraph::utils::Overloaded{
+                  [&](memgraph::query::DbAccessor *) {
+                    memgraph::query::EdgeAccessor edgeAcc = **it->out_it;
+                    it->current_e.emplace(edgeAcc, edgeAcc.From(), edgeAcc.To(), v->graph, it->GetMemoryResource());
+                  },
+                  [&](memgraph::query::SubgraphDbAccessor *impl) {
+                    auto edgeAcc = **it->out_it;
+                    it->current_e.emplace(edgeAcc,
+                                          memgraph::query::SubgraphVertexAccessor(edgeAcc.From(), impl->getGraph()),
+                                          memgraph::query::SubgraphVertexAccessor(edgeAcc.To(), impl->getGraph()),
+                                          v->graph, it->GetMemoryResource());
+                  }},
+              v->graph->impl);
         }
 
         return it.release();
@@ -2110,13 +2130,15 @@ mgp_error mgp_edges_iterator_next(mgp_edges_iterator *it, mgp_edge **result) {
           }
           std::visit(memgraph::utils::Overloaded{
                          [&](memgraph::query::DbAccessor *) {
-                           it->current_e.emplace(**impl_it, (**impl_it).From(), (**impl_it).To(),
-                                                 it->source_vertex.graph, it->GetMemoryResource());
+                           auto edgeAcc = **impl_it;
+                           it->current_e.emplace(edgeAcc, edgeAcc.From(), edgeAcc.To(), it->source_vertex.graph,
+                                                 it->GetMemoryResource());
                          },
                          [&](memgraph::query::SubgraphDbAccessor *impl) {
+                           auto edgeAcc = **impl_it;
                            it->current_e.emplace(
-                               **impl_it, memgraph::query::SubgraphVertexAccessor((**impl_it).From(), impl->getGraph()),
-                               memgraph::query::SubgraphVertexAccessor((**impl_it).To(), impl->getGraph()),
+                               edgeAcc, memgraph::query::SubgraphVertexAccessor(edgeAcc.From(), impl->getGraph()),
+                               memgraph::query::SubgraphVertexAccessor(edgeAcc.To(), impl->getGraph()),
                                it->source_vertex.graph, it->GetMemoryResource());
                          }},
                      it->source_vertex.graph->impl);
diff --git a/src/query/procedure/mg_procedure_impl.hpp b/src/query/procedure/mg_procedure_impl.hpp
index 209ea3aa9..a015103c7 100644
--- a/src/query/procedure/mg_procedure_impl.hpp
+++ b/src/query/procedure/mg_procedure_impl.hpp
@@ -435,28 +435,24 @@ struct mgp_vertex {
   /// the allocator which was used to allocate `this`.
   using allocator_type = memgraph::utils::Allocator<mgp_vertex>;
 
-  // Hopefully VertexAccessor copy constructor remains noexcept, so that we can
-  // have everything noexcept here.
-  static_assert(std::is_nothrow_copy_constructible_v<memgraph::query::VertexAccessor>);
-
-  mgp_vertex(memgraph::query::VertexAccessor v, mgp_graph *graph, memgraph::utils::MemoryResource *memory) noexcept
+  mgp_vertex(memgraph::query::VertexAccessor v, mgp_graph *graph, memgraph::utils::MemoryResource *memory)
       : memory(memory), impl(v), graph(graph) {}
 
-  mgp_vertex(memgraph::query::SubgraphVertexAccessor v, mgp_graph *graph,
-             memgraph::utils::MemoryResource *memory) noexcept
+  mgp_vertex(memgraph::query::SubgraphVertexAccessor v, mgp_graph *graph, memgraph::utils::MemoryResource *memory)
       : memory(memory), impl(v), graph(graph) {}
 
-  mgp_vertex(const mgp_vertex &other, memgraph::utils::MemoryResource *memory) noexcept
+  mgp_vertex(const mgp_vertex &other, memgraph::utils::MemoryResource *memory)
       : memory(memory), impl(other.impl), graph(other.graph) {}
 
-  mgp_vertex(mgp_vertex &&other, memgraph::utils::MemoryResource *memory) noexcept
+  mgp_vertex(mgp_vertex &&other, memgraph::utils::MemoryResource *memory)
       : memory(memory), impl(other.impl), graph(other.graph) {}
 
-  mgp_vertex(mgp_vertex &&other) noexcept : memory(other.memory), impl(other.impl), graph(other.graph) {}
+  // NOLINTNEXTLINE(hicpp-noexcept-move, performance-noexcept-move-constructor)
+  mgp_vertex(mgp_vertex &&other) : memory(other.memory), impl(other.impl), graph(other.graph) {}
 
   memgraph::query::VertexAccessor getImpl() const {
     return std::visit(
-        memgraph::utils::Overloaded{[](memgraph::query::VertexAccessor impl) { return impl; },
+        memgraph::utils::Overloaded{[](const memgraph::query::VertexAccessor &impl) { return impl; },
                                     [](memgraph::query::SubgraphVertexAccessor impl) { return impl.impl_; }},
         this->impl);
   }
@@ -486,33 +482,28 @@ struct mgp_edge {
   /// the allocator which was used to allocate `this`.
   using allocator_type = memgraph::utils::Allocator<mgp_edge>;
 
-  // Hopefully EdgeAccessor copy constructor remains noexcept, so that we can
-  // have everything noexcept here.
-  static_assert(std::is_nothrow_copy_constructible_v<memgraph::query::EdgeAccessor>);
-
   static mgp_edge *Copy(const mgp_edge &edge, mgp_memory &memory);
 
-  mgp_edge(const memgraph::query::EdgeAccessor &impl, mgp_graph *graph,
-           memgraph::utils::MemoryResource *memory) noexcept
+  mgp_edge(const memgraph::query::EdgeAccessor &impl, mgp_graph *graph, memgraph::utils::MemoryResource *memory)
       : memory(memory), impl(impl), from(impl.From(), graph, memory), to(impl.To(), graph, memory) {}
 
   mgp_edge(const memgraph::query::EdgeAccessor &impl, const memgraph::query::VertexAccessor &from_v,
-           const memgraph::query::VertexAccessor &to_v, mgp_graph *graph,
-           memgraph::utils::MemoryResource *memory) noexcept
+           const memgraph::query::VertexAccessor &to_v, mgp_graph *graph, memgraph::utils::MemoryResource *memory)
       : memory(memory), impl(impl), from(from_v, graph, memory), to(to_v, graph, memory) {}
 
   mgp_edge(const memgraph::query::EdgeAccessor &impl, const memgraph::query::SubgraphVertexAccessor &from_v,
            const memgraph::query::SubgraphVertexAccessor &to_v, mgp_graph *graph,
-           memgraph::utils::MemoryResource *memory) noexcept
+           memgraph::utils::MemoryResource *memory)
       : memory(memory), impl(impl), from(from_v, graph, memory), to(to_v, graph, memory) {}
 
-  mgp_edge(const mgp_edge &other, memgraph::utils::MemoryResource *memory) noexcept
+  mgp_edge(const mgp_edge &other, memgraph::utils::MemoryResource *memory)
       : memory(memory), impl(other.impl), from(other.from, memory), to(other.to, memory) {}
 
-  mgp_edge(mgp_edge &&other, memgraph::utils::MemoryResource *memory) noexcept
+  mgp_edge(mgp_edge &&other, memgraph::utils::MemoryResource *memory)
       : memory(other.memory), impl(other.impl), from(std::move(other.from), memory), to(std::move(other.to), memory) {}
 
-  mgp_edge(mgp_edge &&other) noexcept
+  // NOLINTNEXTLINE(hicpp-noexcept-move, performance-noexcept-move-constructor)
+  mgp_edge(mgp_edge &&other)
       : memory(other.memory), impl(other.impl), from(std::move(other.from)), to(std::move(other.to)) {}
 
   /// Copy construction without memgraph::utils::MemoryResource is not allowed.
@@ -671,14 +662,12 @@ struct mgp_properties_iterator {
 
 struct mgp_edges_iterator {
   using allocator_type = memgraph::utils::Allocator<mgp_edges_iterator>;
-  // Hopefully mgp_vertex copy constructor remains noexcept, so that we can
-  // have everything noexcept here.
-  static_assert(std::is_nothrow_constructible_v<mgp_vertex, const mgp_vertex &, memgraph::utils::MemoryResource *>);
 
-  mgp_edges_iterator(const mgp_vertex &v, memgraph::utils::MemoryResource *memory) noexcept
+  mgp_edges_iterator(const mgp_vertex &v, memgraph::utils::MemoryResource *memory)
       : memory(memory), source_vertex(v, memory) {}
 
-  mgp_edges_iterator(mgp_edges_iterator &&other) noexcept
+  // NOLINTNEXTLINE(hicpp-noexcept-move, performance-noexcept-move-constructor)
+  mgp_edges_iterator(mgp_edges_iterator &&other)
       : memory(other.memory),
         source_vertex(std::move(other.source_vertex)),
         in(std::move(other.in)),
diff --git a/src/query/stream/streams.cpp b/src/query/stream/streams.cpp
index e3666c510..8e4620d6a 100644
--- a/src/query/stream/streams.cpp
+++ b/src/query/stream/streams.cpp
@@ -496,7 +496,7 @@ Streams::StreamsMap::iterator Streams::CreateConsumer(StreamsMap &map, const std
         [interpreter_context, interpreter]() { interpreter_context->interpreters->erase(interpreter.get()); }};
 
     memgraph::metrics::IncrementCounter(memgraph::metrics::MessagesConsumed, messages.size());
-    CallCustomTransformation(transformation_name, messages, result, accessor, *memory_resource, stream_name);
+    CallCustomTransformation(transformation_name, messages, result, *accessor, *memory_resource, stream_name);
 
     DiscardValueResultStream stream;
 
@@ -743,7 +743,7 @@ TransformationResult Streams::Check(const std::string &stream_name, std::optiona
                                   &transformation_name = transformation_name, &result,
                                   &test_result]<typename T>(const std::vector<T> &messages) mutable {
           auto accessor = interpreter_context->db->Access();
-          CallCustomTransformation(transformation_name, messages, result, accessor, *memory_resource, stream_name);
+          CallCustomTransformation(transformation_name, messages, result, *accessor, *memory_resource, stream_name);
 
           auto result_row = std::vector<TypedValue>();
           result_row.reserve(kCheckStreamResultSize);
diff --git a/src/query/trigger_context.cpp b/src/query/trigger_context.cpp
index 2b1dc7079..2f30974e4 100644
--- a/src/query/trigger_context.cpp
+++ b/src/query/trigger_context.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -302,6 +302,7 @@ void TriggerContext::AdaptForAccessor(DbAccessor *accessor) {
       if (!maybe_from_vertex) {
         continue;
       }
+      accessor->PrefetchOutEdges(*maybe_from_vertex);
       auto maybe_out_edges = maybe_from_vertex->OutEdges(storage::View::OLD);
       MG_ASSERT(maybe_out_edges.HasValue());
       const auto edge_gid = created_edge.object.Gid();
@@ -323,6 +324,7 @@ void TriggerContext::AdaptForAccessor(DbAccessor *accessor) {
     auto it = values->begin();
     for (const auto &value : *values) {
       if (auto maybe_vertex = accessor->FindVertex(value.object.From().Gid(), storage::View::OLD); maybe_vertex) {
+        accessor->PrefetchOutEdges(*maybe_vertex);
         auto maybe_out_edges = maybe_vertex->OutEdges(storage::View::OLD);
         MG_ASSERT(maybe_out_edges.HasValue());
         for (const auto &edge : *maybe_out_edges) {
diff --git a/src/query/trigger_context.hpp b/src/query/trigger_context.hpp
index 39f4a89dd..abb1fd6b0 100644
--- a/src/query/trigger_context.hpp
+++ b/src/query/trigger_context.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -160,11 +160,6 @@ enum class TriggerEventType : uint8_t {
 
 const char *TriggerEventTypeToString(TriggerEventType event_type);
 
-static_assert(std::is_trivially_copy_constructible_v<VertexAccessor>,
-              "VertexAccessor is not trivially copy constructible, move it where possible and remove this assert");
-static_assert(std::is_trivially_copy_constructible_v<EdgeAccessor>,
-              "EdgeAccessor is not trivially copy constructible, move it where possible and remove this asssert");
-
 // Holds the information necessary for triggers
 class TriggerContext {
  public:
diff --git a/src/query/typed_value.hpp b/src/query/typed_value.hpp
index bebda5ce4..d5a38f9a1 100644
--- a/src/query/typed_value.hpp
+++ b/src/query/typed_value.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
diff --git a/src/storage/README.md b/src/storage/README.md
new file mode 100644
index 000000000..61d5512d8
--- /dev/null
+++ b/src/storage/README.md
@@ -0,0 +1,3 @@
+# Storage Modes
+
+* `ON_DISK_TRANSACTIONAL`
diff --git a/src/storage/v2/CMakeLists.txt b/src/storage/v2/CMakeLists.txt
index 984f63a65..099cf2ae1 100644
--- a/src/storage/v2/CMakeLists.txt
+++ b/src/storage/v2/CMakeLists.txt
@@ -1,16 +1,27 @@
 set(storage_v2_src_files
     commit_log.cpp
-    constraints.cpp
+    constraints/existence_constraints.cpp
     temporal.cpp
     durability/durability.cpp
     durability/serialization.cpp
     durability/snapshot.cpp
     durability/wal.cpp
     edge_accessor.cpp
-    indices.cpp
     property_store.cpp
     vertex_accessor.cpp
     storage.cpp
+    indices/indices.cpp
+    all_vertices_iterable.cpp
+    vertices_iterable.cpp
+    inmemory/storage.cpp
+    inmemory/label_index.cpp
+    inmemory/label_property_index.cpp
+    inmemory/unique_constraints.cpp
+    disk/storage.cpp
+    disk/rocksdb_storage.cpp
+    disk/label_index.cpp
+    disk/label_property_index.cpp
+    disk/unique_constraints.cpp
     storage_mode.cpp
     isolation_level.cpp)
 
diff --git a/src/storage/v2/all_vertices_iterable.cpp b/src/storage/v2/all_vertices_iterable.cpp
new file mode 100644
index 000000000..46fb8e521
--- /dev/null
+++ b/src/storage/v2/all_vertices_iterable.cpp
@@ -0,0 +1,44 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include "storage/v2/all_vertices_iterable.hpp"
+
+namespace memgraph::storage {
+
+auto AdvanceToVisibleVertex(utils::SkipList<Vertex>::Iterator it, utils::SkipList<Vertex>::Iterator end,
+                            std::optional<VertexAccessor> *vertex, Transaction *tx, View view, Indices *indices,
+                            Constraints *constraints, Config::Items config) {
+  while (it != end) {
+    *vertex = VertexAccessor::Create(&*it, tx, indices, constraints, config, view);
+    if (!*vertex) {
+      ++it;
+      continue;
+    }
+    break;
+  }
+  return it;
+}
+
+AllVerticesIterable::Iterator::Iterator(AllVerticesIterable *self, utils::SkipList<Vertex>::Iterator it)
+    : self_(self),
+      it_(AdvanceToVisibleVertex(it, self->vertices_accessor_.end(), &self->vertex_, self->transaction_, self->view_,
+                                 self->indices_, self_->constraints_, self->config_)) {}
+
+VertexAccessor AllVerticesIterable::Iterator::operator*() const { return *self_->vertex_; }
+
+AllVerticesIterable::Iterator &AllVerticesIterable::Iterator::operator++() {
+  ++it_;
+  it_ = AdvanceToVisibleVertex(it_, self_->vertices_accessor_.end(), &self_->vertex_, self_->transaction_, self_->view_,
+                               self_->indices_, self_->constraints_, self_->config_);
+  return *this;
+}
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/all_vertices_iterable.hpp b/src/storage/v2/all_vertices_iterable.hpp
new file mode 100644
index 000000000..19b12d50b
--- /dev/null
+++ b/src/storage/v2/all_vertices_iterable.hpp
@@ -0,0 +1,58 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include "storage/v2/vertex_accessor.hpp"
+#include "utils/skip_list.hpp"
+
+namespace memgraph::storage {
+
+class AllVerticesIterable final {
+  utils::SkipList<Vertex>::Accessor vertices_accessor_;
+  Transaction *transaction_;
+  View view_;
+  Indices *indices_;
+  Constraints *constraints_;
+  Config::Items config_;
+  std::optional<VertexAccessor> vertex_;
+
+ public:
+  class Iterator final {
+    AllVerticesIterable *self_;
+    utils::SkipList<Vertex>::Iterator it_;
+
+   public:
+    Iterator(AllVerticesIterable *self, utils::SkipList<Vertex>::Iterator it);
+
+    VertexAccessor operator*() const;
+
+    Iterator &operator++();
+
+    bool operator==(const Iterator &other) const { return self_ == other.self_ && it_ == other.it_; }
+
+    bool operator!=(const Iterator &other) const { return !(*this == other); }
+  };
+
+  AllVerticesIterable(utils::SkipList<Vertex>::Accessor vertices_accessor, Transaction *transaction, View view,
+                      Indices *indices, Constraints *constraints, Config::Items config)
+      : vertices_accessor_(std::move(vertices_accessor)),
+        transaction_(transaction),
+        view_(view),
+        indices_(indices),
+        constraints_(constraints),
+        config_(config) {}
+
+  Iterator begin() { return {this, vertices_accessor_.begin()}; }
+  Iterator end() { return {this, vertices_accessor_.end()}; }
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/config.hpp b/src/storage/v2/config.hpp
index 3b1beec92..04ce5882d 100644
--- a/src/storage/v2/config.hpp
+++ b/src/storage/v2/config.hpp
@@ -60,6 +60,17 @@ struct Config {
   struct Transaction {
     IsolationLevel isolation_level{IsolationLevel::SNAPSHOT_ISOLATION};
   } transaction;
+
+  struct DiskConfig {
+    std::filesystem::path main_storage_directory{"rocksdb_main_storage"};
+    std::filesystem::path label_index_directory{"rocksdb_label_index"};
+    std::filesystem::path label_property_index_directory{"rocksdb_label_property_index"};
+    std::filesystem::path unique_constraints_directory{"rocksdb_unique_constraints"};
+    std::filesystem::path name_id_mapper_directory{"rocksdb_name_id_mapper"};
+    std::filesystem::path id_name_mapper_directory{"rocksdb_id_name_mapper"};
+    std::filesystem::path durability_directory{"rocksdb_durability"};
+    std::filesystem::path wal_directory{"rocksdb_wal"};
+  } disk;
 };
 
 }  // namespace memgraph::storage
diff --git a/src/storage/v2/constraints.hpp b/src/storage/v2/constraints.hpp
deleted file mode 100644
index b209437f8..000000000
--- a/src/storage/v2/constraints.hpp
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2022 Memgraph Ltd.
-//
-// Use of this software is governed by the Business Source License
-// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
-// License, and you may not use this file except in compliance with the Business Source License.
-//
-// As of the Change Date specified in that file, in accordance with
-// the Business Source License, use of this software will be governed
-// by the Apache License, Version 2.0, included in the file
-// licenses/APL.txt.
-
-#pragma once
-
-#include <optional>
-#include <set>
-#include <vector>
-
-#include "storage/v2/id_types.hpp"
-#include "storage/v2/transaction.hpp"
-#include "storage/v2/vertex.hpp"
-#include "utils/logging.hpp"
-#include "utils/result.hpp"
-#include "utils/skip_list.hpp"
-
-namespace memgraph::storage {
-
-// NOLINTNEXTLINE(misc-definitions-in-headers)
-const size_t kUniqueConstraintsMaxProperties = 32;
-
-/// Utility class to store data in a fixed size array. The array is used
-/// instead of `std::vector` to avoid `std::bad_alloc` exception where not
-/// necessary.
-template <class T>
-struct FixedCapacityArray {
-  size_t size;
-  T values[kUniqueConstraintsMaxProperties];
-
-  explicit FixedCapacityArray(size_t array_size) : size(array_size) {
-    MG_ASSERT(size <= kUniqueConstraintsMaxProperties, "Invalid array size!");
-  }
-};
-
-using PropertyIdArray = FixedCapacityArray<PropertyId>;
-
-struct ConstraintViolation {
-  enum class Type {
-    EXISTENCE,
-    UNIQUE,
-  };
-
-  Type type;
-  LabelId label;
-
-  // While multiple properties are supported by unique constraints, the
-  // `properties` set will always have exactly one element in the case of
-  // existence constraint violation.
-  std::set<PropertyId> properties;
-};
-
-bool operator==(const ConstraintViolation &lhs, const ConstraintViolation &rhs);
-
-class UniqueConstraints {
- private:
-  struct Entry {
-    std::vector<PropertyValue> values;
-    const Vertex *vertex;
-    uint64_t timestamp;
-
-    bool operator<(const Entry &rhs);
-    bool operator==(const Entry &rhs);
-
-    bool operator<(const std::vector<PropertyValue> &rhs);
-    bool operator==(const std::vector<PropertyValue> &rhs);
-  };
-
- public:
-  /// Status for creation of unique constraints.
-  /// Note that this does not cover the case when the constraint is violated.
-  enum class CreationStatus {
-    SUCCESS,
-    ALREADY_EXISTS,
-    EMPTY_PROPERTIES,
-    PROPERTIES_SIZE_LIMIT_EXCEEDED,
-  };
-
-  /// Status for deletion of unique constraints.
-  enum class DeletionStatus {
-    SUCCESS,
-    NOT_FOUND,
-    EMPTY_PROPERTIES,
-    PROPERTIES_SIZE_LIMIT_EXCEEDED,
-  };
-
-  /// Indexes the given vertex for relevant labels and properties.
-  /// This method should be called before committing and validating vertices
-  /// against unique constraints.
-  /// @throw std::bad_alloc
-  void UpdateBeforeCommit(const Vertex *vertex, const Transaction &tx);
-
-  /// Creates unique constraint on the given `label` and a list of `properties`.
-  /// Returns constraint violation if there are multiple vertices with the same
-  /// label and property values. Returns `CreationStatus::ALREADY_EXISTS` if
-  /// constraint already existed, `CreationStatus::EMPTY_PROPERTIES` if the
-  /// given list of properties is empty,
-  /// `CreationStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED` if the list of properties
-  /// exceeds the maximum allowed number of properties, and
-  /// `CreationStatus::SUCCESS` on success.
-  /// @throw std::bad_alloc
-  utils::BasicResult<ConstraintViolation, CreationStatus> CreateConstraint(LabelId label,
-                                                                           const std::set<PropertyId> &properties,
-                                                                           utils::SkipList<Vertex>::Accessor vertices);
-
-  /// Deletes the specified constraint. Returns `DeletionStatus::NOT_FOUND` if
-  /// there is not such constraint in the storage,
-  /// `DeletionStatus::EMPTY_PROPERTIES` if the given set of `properties` is
-  /// empty, `DeletionStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED` if the given set
-  /// of `properties` exceeds the maximum allowed number of properties, and
-  /// `DeletionStatus::SUCCESS` on success.
-  DeletionStatus DropConstraint(LabelId label, const std::set<PropertyId> &properties);
-
-  bool ConstraintExists(LabelId label, const std::set<PropertyId> &properties) {
-    return constraints_.find({label, properties}) != constraints_.end();
-  }
-
-  /// Validates the given vertex against unique constraints before committing.
-  /// This method should be called while commit lock is active with
-  /// `commit_timestamp` being a potential commit timestamp of the transaction.
-  /// @throw std::bad_alloc
-  std::optional<ConstraintViolation> Validate(const Vertex &vertex, const Transaction &tx,
-                                              uint64_t commit_timestamp) const;
-
-  std::vector<std::pair<LabelId, std::set<PropertyId>>> ListConstraints() const;
-
-  /// GC method that removes outdated entries from constraints' storages.
-  void RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp);
-
-  void Clear() { constraints_.clear(); }
-
- private:
-  std::map<std::pair<LabelId, std::set<PropertyId>>, utils::SkipList<Entry>> constraints_;
-};
-
-struct Constraints {
-  std::vector<std::pair<LabelId, PropertyId>> existence_constraints;
-  UniqueConstraints unique_constraints;
-};
-
-/// Adds a unique constraint to `constraints`. Returns true if the constraint
-/// was successfully added, false if it already exists and a
-/// `ConstraintViolation` if there is an existing vertex violating the
-/// constraint.
-///
-/// @throw std::bad_alloc
-/// @throw std::length_error
-inline utils::BasicResult<ConstraintViolation, bool> CreateExistenceConstraint(
-    Constraints *constraints, LabelId label, PropertyId property, utils::SkipList<Vertex>::Accessor vertices) {
-  if (utils::Contains(constraints->existence_constraints, std::make_pair(label, property))) {
-    return false;
-  }
-  for (const auto &vertex : vertices) {
-    if (!vertex.deleted && utils::Contains(vertex.labels, label) && !vertex.properties.HasProperty(property)) {
-      return ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label, std::set<PropertyId>{property}};
-    }
-  }
-  constraints->existence_constraints.emplace_back(label, property);
-  return true;
-}
-
-/// Removes a unique constraint from `constraints`. Returns true if the
-/// constraint was removed, and false if it doesn't exist.
-inline bool DropExistenceConstraint(Constraints *constraints, LabelId label, PropertyId property) {
-  auto it = std::find(constraints->existence_constraints.begin(), constraints->existence_constraints.end(),
-                      std::make_pair(label, property));
-  if (it == constraints->existence_constraints.end()) {
-    return false;
-  }
-  constraints->existence_constraints.erase(it);
-  return true;
-}
-
-/// Verifies that the given vertex satisfies all existence constraints. Returns
-/// `std::nullopt` if all checks pass, and `ConstraintViolation` describing the
-/// violated constraint otherwise.
-[[nodiscard]] inline std::optional<ConstraintViolation> ValidateExistenceConstraints(const Vertex &vertex,
-                                                                                     const Constraints &constraints) {
-  for (const auto &[label, property] : constraints.existence_constraints) {
-    if (!vertex.deleted && utils::Contains(vertex.labels, label) && !vertex.properties.HasProperty(property)) {
-      return ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label, std::set<PropertyId>{property}};
-    }
-  }
-  return std::nullopt;
-}
-
-/// Returns a list of all created existence constraints.
-inline std::vector<std::pair<LabelId, PropertyId>> ListExistenceConstraints(const Constraints &constraints) {
-  return constraints.existence_constraints;
-}
-
-}  // namespace memgraph::storage
diff --git a/src/storage/v2/constraints/constraint_violation.hpp b/src/storage/v2/constraints/constraint_violation.hpp
new file mode 100644
index 000000000..40c8fd657
--- /dev/null
+++ b/src/storage/v2/constraints/constraint_violation.hpp
@@ -0,0 +1,39 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include <set>
+
+#include "storage/v2/id_types.hpp"
+
+namespace memgraph::storage {
+
+struct ConstraintViolation {
+  enum class Type {
+    EXISTENCE,
+    UNIQUE,
+  };
+
+  Type type;
+  LabelId label;
+
+  // While multiple properties are supported by unique constraints, the
+  // `properties` set will always have exactly one element in the case of
+  // existence constraint violation.
+  std::set<PropertyId> properties;
+};
+
+inline bool operator==(const ConstraintViolation &lhs, const ConstraintViolation &rhs) {
+  return lhs.type == rhs.type && lhs.label == rhs.label && lhs.properties == rhs.properties;
+}
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/constraints/constraints.hpp b/src/storage/v2/constraints/constraints.hpp
new file mode 100644
index 000000000..665c5518b
--- /dev/null
+++ b/src/storage/v2/constraints/constraints.hpp
@@ -0,0 +1,48 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include "storage/v2/config.hpp"
+#include "storage/v2/constraints/existence_constraints.hpp"
+#include "storage/v2/disk/unique_constraints.hpp"
+#include "storage/v2/inmemory/unique_constraints.hpp"
+#include "storage/v2/storage_mode.hpp"
+
+namespace memgraph::storage {
+
+struct Constraints {
+  Constraints(const Config &config, StorageMode storage_mode) {
+    std::invoke([this, config, storage_mode]() {
+      existence_constraints_ = std::make_unique<ExistenceConstraints>();
+      switch (storage_mode) {
+        case StorageMode::IN_MEMORY_TRANSACTIONAL:
+        case StorageMode::IN_MEMORY_ANALYTICAL:
+          unique_constraints_ = std::make_unique<InMemoryUniqueConstraints>();
+          break;
+        case StorageMode::ON_DISK_TRANSACTIONAL:
+          unique_constraints_ = std::make_unique<DiskUniqueConstraints>(config);
+          break;
+      };
+    });
+  }
+
+  Constraints(const Constraints &) = delete;
+  Constraints(Constraints &&) = delete;
+  Constraints &operator=(const Constraints &) = delete;
+  Constraints &operator=(Constraints &&) = delete;
+  ~Constraints() = default;
+
+  std::unique_ptr<ExistenceConstraints> existence_constraints_;
+  std::unique_ptr<UniqueConstraints> unique_constraints_;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/constraints/existence_constraints.cpp b/src/storage/v2/constraints/existence_constraints.cpp
new file mode 100644
index 000000000..692dddbb6
--- /dev/null
+++ b/src/storage/v2/constraints/existence_constraints.cpp
@@ -0,0 +1,58 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include "storage/v2/constraints/existence_constraints.hpp"
+#include "storage/v2/constraints/constraints.hpp"
+#include "storage/v2/id_types.hpp"
+#include "storage/v2/mvcc.hpp"
+#include "utils/logging.hpp"
+
+namespace memgraph::storage {
+
+bool ExistenceConstraints::ConstraintExists(LabelId label, PropertyId property) const {
+  return utils::Contains(constraints_, std::make_pair(label, property));
+}
+
+void ExistenceConstraints::InsertConstraint(LabelId label, PropertyId property) {
+  if (ConstraintExists(label, property)) {
+    return;
+  }
+  constraints_.emplace_back(label, property);
+}
+
+bool ExistenceConstraints::DropConstraint(LabelId label, PropertyId property) {
+  auto it = std::find(constraints_.begin(), constraints_.end(), std::make_pair(label, property));
+  if (it == constraints_.end()) {
+    return false;
+  }
+  constraints_.erase(it);
+  return true;
+}
+
+std::vector<std::pair<LabelId, PropertyId>> ExistenceConstraints::ListConstraints() const { return constraints_; }
+
+[[nodiscard]] std::optional<ConstraintViolation> ExistenceConstraints::Validate(const Vertex &vertex) {
+  for (const auto &[label, property] : constraints_) {
+    if (auto violation = ValidateVertexOnConstraint(vertex, label, property); violation.has_value()) {
+      return violation;
+    }
+  }
+  return std::nullopt;
+}
+
+void ExistenceConstraints::LoadExistenceConstraints(const std::vector<std::string> &keys) {
+  for (const auto &key : keys) {
+    const std::vector<std::string> parts = utils::Split(key, ",");
+    constraints_.emplace_back(LabelId::FromUint(std::stoull(parts[0])), PropertyId::FromUint(std::stoull(parts[1])));
+  }
+}
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/constraints/existence_constraints.hpp b/src/storage/v2/constraints/existence_constraints.hpp
new file mode 100644
index 000000000..77f7bc43a
--- /dev/null
+++ b/src/storage/v2/constraints/existence_constraints.hpp
@@ -0,0 +1,62 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include <optional>
+
+#include "storage/v2/constraints/constraint_violation.hpp"
+#include "storage/v2/vertex.hpp"
+#include "utils/skip_list.hpp"
+
+namespace memgraph::storage {
+
+class ExistenceConstraints {
+ public:
+  [[nodiscard]] static std::optional<ConstraintViolation> ValidateVertexOnConstraint(const Vertex &vertex,
+                                                                                     LabelId label,
+                                                                                     PropertyId property) {
+    if (!vertex.deleted && utils::Contains(vertex.labels, label) && !vertex.properties.HasProperty(property)) {
+      return ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label, std::set<PropertyId>{property}};
+    }
+    return std::nullopt;
+  }
+
+  [[nodiscard]] static std::optional<ConstraintViolation> ValidateVerticesOnConstraint(
+      utils::SkipList<Vertex>::Accessor vertices, LabelId label, PropertyId property) {
+    for (const auto &vertex : vertices) {
+      if (auto violation = ValidateVertexOnConstraint(vertex, label, property); violation.has_value()) {
+        return violation;
+      }
+    }
+    return std::nullopt;
+  }
+
+  bool ConstraintExists(LabelId label, PropertyId property) const;
+
+  void InsertConstraint(LabelId label, PropertyId property);
+
+  /// Returns true if the constraint was removed, and false if it doesn't exist.
+  bool DropConstraint(LabelId label, PropertyId property);
+
+  ///  Returns `std::nullopt` if all checks pass, and `ConstraintViolation` describing the violated constraint
+  ///  otherwise.
+  [[nodiscard]] std::optional<ConstraintViolation> Validate(const Vertex &vertex);
+
+  std::vector<std::pair<LabelId, PropertyId>> ListConstraints() const;
+
+  void LoadExistenceConstraints(const std::vector<std::string> &keys);
+
+ private:
+  std::vector<std::pair<LabelId, PropertyId>> constraints_;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/constraints/unique_constraints.hpp b/src/storage/v2/constraints/unique_constraints.hpp
new file mode 100644
index 000000000..b9ec04bfc
--- /dev/null
+++ b/src/storage/v2/constraints/unique_constraints.hpp
@@ -0,0 +1,75 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include <set>
+
+#include "storage/v2/constraints/constraint_violation.hpp"
+#include "storage/v2/transaction.hpp"
+#include "storage/v2/vertex.hpp"
+#include "utils/result.hpp"
+
+namespace memgraph::storage {
+
+// NOLINTNEXTLINE(misc-definitions-in-headers)
+const size_t kUniqueConstraintsMaxProperties = 32;
+
+class UniqueConstraints {
+ public:
+  UniqueConstraints() = default;
+  UniqueConstraints(const UniqueConstraints &) = delete;
+  UniqueConstraints(UniqueConstraints &&) = delete;
+  UniqueConstraints &operator=(const UniqueConstraints &) = delete;
+  UniqueConstraints &operator=(UniqueConstraints &&) = delete;
+  virtual ~UniqueConstraints() = default;
+
+  enum class CreationStatus {
+    SUCCESS,
+    ALREADY_EXISTS,
+    EMPTY_PROPERTIES,
+    PROPERTIES_SIZE_LIMIT_EXCEEDED,
+  };
+
+  enum class DeletionStatus {
+    SUCCESS,
+    NOT_FOUND,
+    EMPTY_PROPERTIES,
+    PROPERTIES_SIZE_LIMIT_EXCEEDED,
+  };
+
+  virtual DeletionStatus DropConstraint(LabelId label, const std::set<PropertyId> &properties) = 0;
+
+  virtual bool ConstraintExists(LabelId label, const std::set<PropertyId> &properties) const = 0;
+
+  virtual void UpdateOnRemoveLabel(LabelId removed_label, const Vertex &vertex_before_update,
+                                   uint64_t transaction_start_timestamp) = 0;
+
+  virtual void UpdateOnAddLabel(LabelId added_label, const Vertex &vertex_before_update,
+                                uint64_t transaction_start_timestamp) = 0;
+
+  virtual std::vector<std::pair<LabelId, std::set<PropertyId>>> ListConstraints() const = 0;
+
+  virtual void Clear() = 0;
+
+ protected:
+  static DeletionStatus CheckPropertiesBeforeDeletion(const std::set<PropertyId> &properties) {
+    if (properties.empty()) {
+      return UniqueConstraints::DeletionStatus::EMPTY_PROPERTIES;
+    }
+    if (properties.size() > kUniqueConstraintsMaxProperties) {
+      return UniqueConstraints::DeletionStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED;
+    }
+    return UniqueConstraints::DeletionStatus::SUCCESS;
+  }
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/delta.hpp b/src/storage/v2/delta.hpp
index 5aad1ab2f..c23f06298 100644
--- a/src/storage/v2/delta.hpp
+++ b/src/storage/v2/delta.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -12,6 +12,7 @@
 #pragma once
 
 #include <atomic>
+#include <cstdint>
 
 #include "storage/v2/edge_ref.hpp"
 #include "storage/v2/id_types.hpp"
@@ -122,7 +123,10 @@ inline bool operator!=(const PreviousPtr::Pointer &a, const PreviousPtr::Pointer
 
 struct Delta {
   enum class Action {
-    // Used for both Vertex and Edge
+    /// Use for Vertex and Edge
+    /// Used for disk storage for modifying MVCC logic and storing old key. Storing old key is necessary for
+    /// deleting old-data (compaction).
+    DELETE_DESERIALIZED_OBJECT,
     DELETE_OBJECT,
     RECREATE_OBJECT,
     SET_PROPERTY,
@@ -137,6 +141,7 @@ struct Delta {
   };
 
   // Used for both Vertex and Edge
+  struct DeleteDeserializedObjectTag {};
   struct DeleteObjectTag {};
   struct RecreateObjectTag {};
   struct SetPropertyTag {};
@@ -149,44 +154,48 @@ struct Delta {
   struct RemoveInEdgeTag {};
   struct RemoveOutEdgeTag {};
 
-  Delta(DeleteObjectTag, std::atomic<uint64_t> *timestamp, uint64_t command_id)
+  Delta(DeleteDeserializedObjectTag /*tag*/, std::atomic<uint64_t> *timestamp,
+        const std::optional<std::string> &old_disk_key)
+      : action(Action::DELETE_DESERIALIZED_OBJECT), timestamp(timestamp), command_id(0), old_disk_key(old_disk_key) {}
+
+  Delta(DeleteObjectTag /*tag*/, std::atomic<uint64_t> *timestamp, uint64_t command_id)
       : action(Action::DELETE_OBJECT), timestamp(timestamp), command_id(command_id) {}
 
-  Delta(RecreateObjectTag, std::atomic<uint64_t> *timestamp, uint64_t command_id)
+  Delta(RecreateObjectTag /*tag*/, std::atomic<uint64_t> *timestamp, uint64_t command_id)
       : action(Action::RECREATE_OBJECT), timestamp(timestamp), command_id(command_id) {}
 
-  Delta(AddLabelTag, LabelId label, std::atomic<uint64_t> *timestamp, uint64_t command_id)
+  Delta(AddLabelTag /*tag*/, LabelId label, std::atomic<uint64_t> *timestamp, uint64_t command_id)
       : action(Action::ADD_LABEL), timestamp(timestamp), command_id(command_id), label(label) {}
 
-  Delta(RemoveLabelTag, LabelId label, std::atomic<uint64_t> *timestamp, uint64_t command_id)
+  Delta(RemoveLabelTag /*tag*/, LabelId label, std::atomic<uint64_t> *timestamp, uint64_t command_id)
       : action(Action::REMOVE_LABEL), timestamp(timestamp), command_id(command_id), label(label) {}
 
-  Delta(SetPropertyTag, PropertyId key, const PropertyValue &value, std::atomic<uint64_t> *timestamp,
+  Delta(SetPropertyTag /*tag*/, PropertyId key, const PropertyValue &value, std::atomic<uint64_t> *timestamp,
         uint64_t command_id)
       : action(Action::SET_PROPERTY), timestamp(timestamp), command_id(command_id), property({key, value}) {}
 
-  Delta(AddInEdgeTag, EdgeTypeId edge_type, Vertex *vertex, EdgeRef edge, std::atomic<uint64_t> *timestamp,
+  Delta(AddInEdgeTag /*tag*/, EdgeTypeId edge_type, Vertex *vertex, EdgeRef edge, std::atomic<uint64_t> *timestamp,
         uint64_t command_id)
       : action(Action::ADD_IN_EDGE),
         timestamp(timestamp),
         command_id(command_id),
         vertex_edge({edge_type, vertex, edge}) {}
 
-  Delta(AddOutEdgeTag, EdgeTypeId edge_type, Vertex *vertex, EdgeRef edge, std::atomic<uint64_t> *timestamp,
+  Delta(AddOutEdgeTag /*tag*/, EdgeTypeId edge_type, Vertex *vertex, EdgeRef edge, std::atomic<uint64_t> *timestamp,
         uint64_t command_id)
       : action(Action::ADD_OUT_EDGE),
         timestamp(timestamp),
         command_id(command_id),
         vertex_edge({edge_type, vertex, edge}) {}
 
-  Delta(RemoveInEdgeTag, EdgeTypeId edge_type, Vertex *vertex, EdgeRef edge, std::atomic<uint64_t> *timestamp,
+  Delta(RemoveInEdgeTag /*tag*/, EdgeTypeId edge_type, Vertex *vertex, EdgeRef edge, std::atomic<uint64_t> *timestamp,
         uint64_t command_id)
       : action(Action::REMOVE_IN_EDGE),
         timestamp(timestamp),
         command_id(command_id),
         vertex_edge({edge_type, vertex, edge}) {}
 
-  Delta(RemoveOutEdgeTag, EdgeTypeId edge_type, Vertex *vertex, EdgeRef edge, std::atomic<uint64_t> *timestamp,
+  Delta(RemoveOutEdgeTag /*tag*/, EdgeTypeId edge_type, Vertex *vertex, EdgeRef edge, std::atomic<uint64_t> *timestamp,
         uint64_t command_id)
       : action(Action::REMOVE_OUT_EDGE),
         timestamp(timestamp),
@@ -209,6 +218,9 @@ struct Delta {
       case Action::REMOVE_IN_EDGE:
       case Action::REMOVE_OUT_EDGE:
         break;
+      case Action::DELETE_DESERIALIZED_OBJECT:
+        old_disk_key.reset();
+        break;
       case Action::SET_PROPERTY:
         property.value.~PropertyValue();
         break;
@@ -224,6 +236,7 @@ struct Delta {
   std::atomic<Delta *> next{nullptr};
 
   union {
+    std::optional<std::string> old_disk_key;
     LabelId label;
     struct {
       PropertyId key;
diff --git a/src/storage/v2/disk/label_index.cpp b/src/storage/v2/disk/label_index.cpp
new file mode 100644
index 000000000..1046405eb
--- /dev/null
+++ b/src/storage/v2/disk/label_index.cpp
@@ -0,0 +1,215 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include <rocksdb/options.h>
+#include <rocksdb/utilities/transaction.h>
+
+#include "storage/v2/disk/label_index.hpp"
+#include "utils/disk_utils.hpp"
+#include "utils/rocksdb_serialization.hpp"
+
+namespace memgraph::storage {
+
+namespace {
+
+[[nodiscard]] bool ClearTransactionEntriesWithRemovedIndexingLabel(
+    rocksdb::Transaction &disk_transaction, const std::map<Gid, std::vector<LabelId>> &transaction_entries) {
+  for (const auto &[vertex_gid, labels] : transaction_entries) {
+    for (const auto &indexing_label : labels) {
+      if (auto status = disk_transaction.Delete(utils::SerializeVertexAsKeyForLabelIndex(indexing_label, vertex_gid));
+          !status.ok()) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+/// TODO: duplication with label_property_index.cpp
+bool CommitWithTimestamp(rocksdb::Transaction *disk_transaction, uint64_t commit_ts) {
+  disk_transaction->SetCommitTimestamp(commit_ts);
+  const auto status = disk_transaction->Commit();
+  if (!status.ok()) {
+    spdlog::error("rocksdb: {}", status.getState());
+  }
+  return status.ok();
+}
+
+}  // namespace
+
+DiskLabelIndex::DiskLabelIndex(Indices *indices, Constraints *constraints, const Config &config)
+    : LabelIndex(indices, constraints, config) {
+  utils::EnsureDirOrDie(config.disk.label_index_directory);
+  kvstore_ = std::make_unique<RocksDBStorage>();
+  kvstore_->options_.create_if_missing = true;
+  kvstore_->options_.comparator = new ComparatorWithU64TsImpl();
+  logging::AssertRocksDBStatus(rocksdb::TransactionDB::Open(kvstore_->options_, rocksdb::TransactionDBOptions(),
+                                                            config.disk.label_index_directory, &kvstore_->db_));
+}
+
+bool DiskLabelIndex::CreateIndex(LabelId label, const std::vector<std::pair<std::string, std::string>> &vertices) {
+  if (!index_.emplace(label).second) {
+    return false;
+  }
+
+  auto disk_transaction = CreateRocksDBTransaction();
+  for (const auto &[key, value] : vertices) {
+    disk_transaction->Put(key, value);
+  }
+  return CommitWithTimestamp(disk_transaction.get(), 0);
+}
+
+std::unique_ptr<rocksdb::Transaction> DiskLabelIndex::CreateRocksDBTransaction() const {
+  return std::unique_ptr<rocksdb::Transaction>(
+      kvstore_->db_->BeginTransaction(rocksdb::WriteOptions(), rocksdb::TransactionOptions()));
+}
+
+std::unique_ptr<rocksdb::Transaction> DiskLabelIndex::CreateAllReadingRocksDBTransaction() const {
+  auto tx = CreateRocksDBTransaction();
+  tx->SetReadTimestampForValidation(std::numeric_limits<uint64_t>::max());
+  return tx;
+}
+
+bool DiskLabelIndex::SyncVertexToLabelIndexStorage(const Vertex &vertex, uint64_t commit_timestamp) const {
+  auto disk_transaction = CreateRocksDBTransaction();
+
+  if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(vertex.delta); maybe_old_disk_key.has_value()) {
+    if (!disk_transaction->Delete(maybe_old_disk_key.value()).ok()) {
+      return false;
+    }
+  }
+
+  for (const LabelId index_label : index_) {
+    if (!utils::Contains(vertex.labels, index_label)) {
+      continue;
+    }
+    if (!disk_transaction
+             ->Put(utils::SerializeVertexAsKeyForLabelIndex(index_label, vertex.gid),
+                   utils::SerializeVertexAsValueForLabelIndex(index_label, vertex.labels, vertex.properties))
+             .ok()) {
+      return false;
+    }
+  }
+
+  return CommitWithTimestamp(disk_transaction.get(), commit_timestamp);
+}
+
+/// TODO: this can probably be optimized
+bool DiskLabelIndex::ClearDeletedVertex(std::string_view gid, uint64_t transaction_commit_timestamp) const {
+  auto disk_transaction = CreateAllReadingRocksDBTransaction();
+
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto it = std::unique_ptr<rocksdb::Iterator>(disk_transaction->GetIterator(ro));
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    if (std::string key = it->key().ToString(); gid == utils::ExtractGidFromLabelIndexStorage(key)) {
+      if (!disk_transaction->Delete(key).ok()) {
+        return false;
+      }
+    }
+  }
+
+  return CommitWithTimestamp(disk_transaction.get(), transaction_commit_timestamp);
+}
+
+bool DiskLabelIndex::DeleteVerticesWithRemovedIndexingLabel(uint64_t transaction_start_timestamp,
+                                                            uint64_t transaction_commit_timestamp) {
+  auto disk_transaction = CreateAllReadingRocksDBTransaction();
+
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  bool deletion_success = entries_for_deletion.WithLock(
+      [transaction_start_timestamp, disk_transaction_ptr = disk_transaction.get()](auto &tx_to_entries_for_deletion) {
+        if (auto tx_it = tx_to_entries_for_deletion.find(transaction_start_timestamp);
+            tx_it != tx_to_entries_for_deletion.end()) {
+          bool res = ClearTransactionEntriesWithRemovedIndexingLabel(*disk_transaction_ptr, tx_it->second);
+          tx_to_entries_for_deletion.erase(tx_it);
+          return res;
+        }
+        return true;
+      });
+  if (deletion_success) {
+    return CommitWithTimestamp(disk_transaction.get(), transaction_commit_timestamp);
+  }
+  return false;
+}
+
+void DiskLabelIndex::UpdateOnAddLabel(LabelId added_label, Vertex *vertex_before_update, const Transaction &tx) {
+  entries_for_deletion.WithLock([added_label, vertex_before_update, &tx](auto &tx_to_entries_for_deletion) {
+    auto tx_it = tx_to_entries_for_deletion.find(tx.start_timestamp);
+    if (tx_it == tx_to_entries_for_deletion.end()) {
+      return;
+    }
+    auto vertex_label_index_it = tx_it->second.find(vertex_before_update->gid);
+    if (vertex_label_index_it == tx_it->second.end()) {
+      return;
+    }
+    std::erase_if(vertex_label_index_it->second,
+                  [added_label](const LabelId &indexed_label) { return indexed_label == added_label; });
+  });
+}
+
+void DiskLabelIndex::UpdateOnRemoveLabel(LabelId removed_label, Vertex *vertex_before_update, const Transaction &tx) {
+  if (!IndexExists(removed_label)) {
+    return;
+  }
+  entries_for_deletion.WithLock([&removed_label, &tx, vertex_before_update](auto &tx_to_entries_for_deletion) {
+    auto [it, _] = tx_to_entries_for_deletion.emplace(
+        std::piecewise_construct, std::forward_as_tuple(tx.start_timestamp), std::forward_as_tuple());
+    auto &vertex_map_store = it->second;
+    auto [it_vertex_map_store, emplaced] = vertex_map_store.emplace(
+        std::piecewise_construct, std::forward_as_tuple(vertex_before_update->gid), std::forward_as_tuple());
+    it_vertex_map_store->second.emplace_back(removed_label);
+  });
+}
+
+/// TODO: andi Here will come Bloom filter deletion
+bool DiskLabelIndex::DropIndex(LabelId label) {
+  if (!(index_.erase(label) > 0)) {
+    return false;
+  }
+  auto disk_transaction = CreateAllReadingRocksDBTransaction();
+
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto it = std::unique_ptr<rocksdb::Iterator>(disk_transaction->GetIterator(ro));
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    std::string key = it->key().ToString();
+    if (key.starts_with(utils::SerializeIdType(label))) {
+      disk_transaction->Delete(it->key().ToString());
+    }
+  }
+
+  return CommitWithTimestamp(disk_transaction.get(), 0);
+}
+
+bool DiskLabelIndex::IndexExists(LabelId label) const { return index_.find(label) != index_.end(); }
+
+std::vector<LabelId> DiskLabelIndex::ListIndices() const { return {index_.begin(), index_.end()}; }
+
+uint64_t DiskLabelIndex::ApproximateVertexCount(LabelId /*label*/) const { return 10; }
+
+void DiskLabelIndex::LoadIndexInfo(const std::vector<std::string> &labels) {
+  for (const std::string &label : labels) {
+    LabelId label_id = LabelId::FromUint(std::stoull(label));
+    index_.insert(label_id);
+  }
+}
+
+RocksDBStorage *DiskLabelIndex::GetRocksDBStorage() const { return kvstore_.get(); }
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/disk/label_index.hpp b/src/storage/v2/disk/label_index.hpp
new file mode 100644
index 000000000..76151ef22
--- /dev/null
+++ b/src/storage/v2/disk/label_index.hpp
@@ -0,0 +1,62 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include <rocksdb/iterator.h>
+#include <rocksdb/utilities/transaction.h>
+
+#include "storage/v2/disk/rocksdb_storage.hpp"
+#include "storage/v2/id_types.hpp"
+#include "storage/v2/indices/label_index.hpp"
+#include "storage/v2/vertex.hpp"
+#include "utils/rocksdb_serialization.hpp"
+
+namespace memgraph::storage {
+class DiskLabelIndex : public storage::LabelIndex {
+ public:
+  DiskLabelIndex(Indices *indices, Constraints *constraints, const Config &config);
+
+  [[nodiscard]] bool CreateIndex(LabelId label, const std::vector<std::pair<std::string, std::string>> &vertices);
+
+  std::unique_ptr<rocksdb::Transaction> CreateRocksDBTransaction() const;
+
+  std::unique_ptr<rocksdb::Transaction> CreateAllReadingRocksDBTransaction() const;
+
+  [[nodiscard]] bool SyncVertexToLabelIndexStorage(const Vertex &vertex, uint64_t commit_timestamp) const;
+
+  [[nodiscard]] bool ClearDeletedVertex(std::string_view gid, uint64_t transaction_commit_timestamp) const;
+
+  [[nodiscard]] bool DeleteVerticesWithRemovedIndexingLabel(uint64_t transaction_start_timestamp,
+                                                            uint64_t transaction_commit_timestamp);
+  /// @throw std::bad_alloc
+  void UpdateOnAddLabel(LabelId added_label, Vertex *vertex_before_update, const Transaction &tx) override;
+
+  void UpdateOnRemoveLabel(LabelId removed_label, Vertex *vertex_before_update, const Transaction &tx) override;
+
+  /// Returns false if there was no index to drop
+  bool DropIndex(LabelId label) override;
+
+  bool IndexExists(LabelId label) const override;
+
+  std::vector<LabelId> ListIndices() const override;
+
+  uint64_t ApproximateVertexCount(LabelId label) const override;
+
+  RocksDBStorage *GetRocksDBStorage() const;
+
+  void LoadIndexInfo(const std::vector<std::string> &labels);
+
+ private:
+  utils::Synchronized<std::map<uint64_t, std::map<Gid, std::vector<LabelId>>>> entries_for_deletion;
+  std::unordered_set<LabelId> index_;
+  std::unique_ptr<RocksDBStorage> kvstore_;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/disk/label_property_index.cpp b/src/storage/v2/disk/label_property_index.cpp
new file mode 100644
index 000000000..ad2f8f59f
--- /dev/null
+++ b/src/storage/v2/disk/label_property_index.cpp
@@ -0,0 +1,228 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+/// TODO: clear dependencies
+
+#include "storage/v2/disk/label_property_index.hpp"
+#include "storage/v2/id_types.hpp"
+#include "storage/v2/inmemory/indices_utils.hpp"
+#include "storage/v2/property_value.hpp"
+#include "utils/disk_utils.hpp"
+#include "utils/exceptions.hpp"
+#include "utils/file.hpp"
+#include "utils/skip_list.hpp"
+
+namespace memgraph::storage {
+
+namespace {
+
+bool IsVertexIndexedByLabelProperty(const Vertex &vertex, LabelId label, PropertyId property) {
+  return utils::Contains(vertex.labels, label) && vertex.properties.HasProperty(property);
+}
+
+[[nodiscard]] bool ClearTransactionEntriesWithRemovedIndexingLabel(
+    rocksdb::Transaction &disk_transaction,
+    const std::map<Gid, std::vector<std::pair<LabelId, PropertyId>>> &transaction_entries) {
+  for (const auto &[vertex_gid, index] : transaction_entries) {
+    for (const auto &[indexing_label, indexing_property] : index) {
+      if (auto status = disk_transaction.Delete(
+              utils::SerializeVertexAsKeyForLabelPropertyIndex(indexing_label, indexing_property, vertex_gid));
+          !status.ok()) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool CommitWithTimestamp(rocksdb::Transaction *disk_transaction, uint64_t commit_ts) {
+  disk_transaction->SetCommitTimestamp(commit_ts);
+  const auto status = disk_transaction->Commit();
+  if (!status.ok()) {
+    spdlog::error("rocksdb: {}", status.getState());
+  }
+  return status.ok();
+}
+
+}  // namespace
+
+DiskLabelPropertyIndex::DiskLabelPropertyIndex(Indices *indices, Constraints *constraints, const Config &config)
+    : LabelPropertyIndex(indices, constraints, config) {
+  utils::EnsureDirOrDie(config.disk.label_property_index_directory);
+  kvstore_ = std::make_unique<RocksDBStorage>();
+  kvstore_->options_.create_if_missing = true;
+  kvstore_->options_.comparator = new ComparatorWithU64TsImpl();
+  logging::AssertRocksDBStatus(rocksdb::TransactionDB::Open(
+      kvstore_->options_, rocksdb::TransactionDBOptions(), config.disk.label_property_index_directory, &kvstore_->db_));
+}
+
+bool DiskLabelPropertyIndex::CreateIndex(LabelId label, PropertyId property,
+                                         const std::vector<std::pair<std::string, std::string>> &vertices) {
+  if (!index_.emplace(label, property).second) {
+    return false;
+  }
+
+  auto disk_transaction = CreateRocksDBTransaction();
+  for (const auto &[key, value] : vertices) {
+    disk_transaction->Put(key, value);
+  }
+
+  return CommitWithTimestamp(disk_transaction.get(), 0);
+}
+
+std::unique_ptr<rocksdb::Transaction> DiskLabelPropertyIndex::CreateRocksDBTransaction() const {
+  return std::unique_ptr<rocksdb::Transaction>(
+      kvstore_->db_->BeginTransaction(rocksdb::WriteOptions(), rocksdb::TransactionOptions()));
+}
+
+std::unique_ptr<rocksdb::Transaction> DiskLabelPropertyIndex::CreateAllReadingRocksDBTransaction() const {
+  auto tx = CreateRocksDBTransaction();
+  tx->SetReadTimestampForValidation(std::numeric_limits<uint64_t>::max());
+  return tx;
+}
+
+bool DiskLabelPropertyIndex::SyncVertexToLabelPropertyIndexStorage(const Vertex &vertex,
+                                                                   uint64_t commit_timestamp) const {
+  auto disk_transaction = CreateRocksDBTransaction();
+
+  if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(vertex.delta); maybe_old_disk_key.has_value()) {
+    if (!disk_transaction->Delete(maybe_old_disk_key.value()).ok()) {
+      return false;
+    }
+  }
+  for (const auto &[index_label, index_property] : index_) {
+    if (IsVertexIndexedByLabelProperty(vertex, index_label, index_property)) {
+      if (!disk_transaction
+               ->Put(utils::SerializeVertexAsKeyForLabelPropertyIndex(index_label, index_property, vertex.gid),
+                     utils::SerializeVertexAsValueForLabelPropertyIndex(index_label, vertex.labels, vertex.properties))
+               .ok()) {
+        return false;
+      }
+    }
+  }
+  return CommitWithTimestamp(disk_transaction.get(), commit_timestamp);
+}
+
+bool DiskLabelPropertyIndex::ClearDeletedVertex(std::string_view gid, uint64_t transaction_commit_timestamp) const {
+  auto disk_transaction = CreateAllReadingRocksDBTransaction();
+
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto it = std::unique_ptr<rocksdb::Iterator>(disk_transaction->GetIterator(ro));
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    if (std::string key = it->key().ToString(); gid == utils::ExtractGidFromLabelPropertyIndexStorage(key)) {
+      if (!disk_transaction->Delete(key).ok()) {
+        return false;
+      }
+    }
+  }
+  return CommitWithTimestamp(disk_transaction.get(), transaction_commit_timestamp);
+}
+
+bool DiskLabelPropertyIndex::DeleteVerticesWithRemovedIndexingLabel(uint64_t transaction_start_timestamp,
+                                                                    uint64_t transaction_commit_timestamp) {
+  auto disk_transaction = CreateAllReadingRocksDBTransaction();
+
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  bool deletion_success = entries_for_deletion.WithLock(
+      [transaction_start_timestamp, disk_transaction_ptr = disk_transaction.get()](auto &tx_to_entries_for_deletion) {
+        if (auto tx_it = tx_to_entries_for_deletion.find(transaction_start_timestamp);
+            tx_it != tx_to_entries_for_deletion.end()) {
+          bool res = ClearTransactionEntriesWithRemovedIndexingLabel(*disk_transaction_ptr, tx_it->second);
+          tx_to_entries_for_deletion.erase(tx_it);
+          return res;
+        }
+        return true;
+      });
+  if (deletion_success) {
+    return CommitWithTimestamp(disk_transaction.get(), transaction_commit_timestamp);
+  }
+  return false;
+}
+
+void DiskLabelPropertyIndex::UpdateOnAddLabel(LabelId added_label, Vertex *vertex_after_update, const Transaction &tx) {
+  entries_for_deletion.WithLock([added_label, vertex_after_update, &tx](auto &tx_to_entries_for_deletion) {
+    auto tx_it = tx_to_entries_for_deletion.find(tx.start_timestamp);
+    if (tx_it == tx_to_entries_for_deletion.end()) {
+      return;
+    }
+    auto vertex_label_index_it = tx_it->second.find(vertex_after_update->gid);
+    if (vertex_label_index_it == tx_it->second.end()) {
+      return;
+    }
+    std::erase_if(vertex_label_index_it->second,
+                  [added_label](const std::pair<LabelId, PropertyId> &index) { return index.first == added_label; });
+  });
+}
+
+void DiskLabelPropertyIndex::UpdateOnRemoveLabel(LabelId removed_label, Vertex *vertex_after_update,
+                                                 const Transaction &tx) {
+  for (const auto &index_entry : index_) {
+    if (index_entry.first != removed_label) {
+      continue;
+    }
+    entries_for_deletion.WithLock([&index_entry, &tx, vertex_after_update](auto &tx_to_entries_for_deletion) {
+      const auto &[indexing_label, indexing_property] = index_entry;
+      auto [it, _] = tx_to_entries_for_deletion.emplace(
+          std::piecewise_construct, std::forward_as_tuple(tx.start_timestamp), std::forward_as_tuple());
+      auto &vertex_map_store = it->second;
+      auto [it_vertex_map_store, emplaced] = vertex_map_store.emplace(
+          std::piecewise_construct, std::forward_as_tuple(vertex_after_update->gid), std::forward_as_tuple());
+      it_vertex_map_store->second.emplace_back(indexing_label, indexing_property);
+    });
+  }
+}
+
+/// TODO: andi If stays the same, move it to the hpp
+void DiskLabelPropertyIndex::UpdateOnSetProperty(PropertyId property, const PropertyValue &value, Vertex *vertex,
+                                                 const Transaction &tx) {}
+
+bool DiskLabelPropertyIndex::DropIndex(LabelId label, PropertyId property) {
+  return index_.erase({label, property}) > 0;
+}
+
+bool DiskLabelPropertyIndex::IndexExists(LabelId label, PropertyId property) const {
+  return utils::Contains(index_, std::make_pair(label, property));
+}
+
+std::vector<std::pair<LabelId, PropertyId>> DiskLabelPropertyIndex::ListIndices() const {
+  return {index_.begin(), index_.end()};
+}
+
+uint64_t DiskLabelPropertyIndex::ApproximateVertexCount(LabelId /*label*/, PropertyId /*property*/) const { return 10; }
+
+uint64_t DiskLabelPropertyIndex::ApproximateVertexCount(LabelId /*label*/, PropertyId /*property*/,
+                                                        const PropertyValue & /*value*/) const {
+  return 10;
+}
+
+uint64_t DiskLabelPropertyIndex::ApproximateVertexCount(
+    LabelId /*label*/, PropertyId /*property*/, const std::optional<utils::Bound<PropertyValue>> & /*lower*/,
+    const std::optional<utils::Bound<PropertyValue>> & /*upper*/) const {
+  return 10;
+}
+
+void DiskLabelPropertyIndex::LoadIndexInfo(const std::vector<std::string> &keys) {
+  for (const auto &label_property : keys) {
+    std::vector<std::string> label_property_split = utils::Split(label_property, ",");
+    index_.emplace(std::make_pair(LabelId::FromUint(std::stoull(label_property_split[0])),
+                                  PropertyId::FromUint(std::stoull(label_property_split[1]))));
+  }
+}
+
+RocksDBStorage *DiskLabelPropertyIndex::GetRocksDBStorage() const { return kvstore_.get(); }
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/disk/label_property_index.hpp b/src/storage/v2/disk/label_property_index.hpp
new file mode 100644
index 000000000..9be8287f7
--- /dev/null
+++ b/src/storage/v2/disk/label_property_index.hpp
@@ -0,0 +1,71 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include "storage/v2/disk/rocksdb_storage.hpp"
+#include "storage/v2/indices/label_property_index.hpp"
+
+namespace memgraph::storage {
+
+/// TODO: andi. Too many copies, extract at one place
+using ParalellizedIndexCreationInfo =
+    std::pair<std::vector<std::pair<Gid, uint64_t>> /*vertex_recovery_info*/, uint64_t /*thread_count*/>;
+
+class DiskLabelPropertyIndex : public storage::LabelPropertyIndex {
+ public:
+  DiskLabelPropertyIndex(Indices *indices, Constraints *constraints, const Config &config);
+
+  bool CreateIndex(LabelId label, PropertyId property,
+                   const std::vector<std::pair<std::string, std::string>> &vertices);
+
+  std::unique_ptr<rocksdb::Transaction> CreateRocksDBTransaction() const;
+
+  std::unique_ptr<rocksdb::Transaction> CreateAllReadingRocksDBTransaction() const;
+
+  [[nodiscard]] bool SyncVertexToLabelPropertyIndexStorage(const Vertex &vertex, uint64_t commit_timestamp) const;
+
+  [[nodiscard]] bool ClearDeletedVertex(std::string_view gid, uint64_t transaction_commit_timestamp) const;
+
+  [[nodiscard]] bool DeleteVerticesWithRemovedIndexingLabel(uint64_t transaction_start_timestamp,
+                                                            uint64_t transaction_commit_timestamp);
+
+  void UpdateOnAddLabel(LabelId added_label, Vertex *vertex_after_update, const Transaction &tx) override;
+
+  void UpdateOnRemoveLabel(LabelId removed_label, Vertex *vertex_after_update, const Transaction &tx) override;
+
+  void UpdateOnSetProperty(PropertyId property, const PropertyValue &value, Vertex *vertex,
+                           const Transaction &tx) override;
+
+  bool DropIndex(LabelId label, PropertyId property) override;
+
+  bool IndexExists(LabelId label, PropertyId property) const override;
+
+  std::vector<std::pair<LabelId, PropertyId>> ListIndices() const override;
+
+  uint64_t ApproximateVertexCount(LabelId label, PropertyId property) const override;
+
+  uint64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const override;
+
+  uint64_t ApproximateVertexCount(LabelId label, PropertyId property,
+                                  const std::optional<utils::Bound<PropertyValue>> &lower,
+                                  const std::optional<utils::Bound<PropertyValue>> &upper) const override;
+
+  RocksDBStorage *GetRocksDBStorage() const;
+
+  void LoadIndexInfo(const std::vector<std::string> &keys);
+
+ private:
+  utils::Synchronized<std::map<uint64_t, std::map<Gid, std::vector<std::pair<LabelId, PropertyId>>>>>
+      entries_for_deletion;
+  std::set<std::pair<LabelId, PropertyId>> index_;
+  std::unique_ptr<RocksDBStorage> kvstore_;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/disk/name_id_mapper.hpp b/src/storage/v2/disk/name_id_mapper.hpp
new file mode 100644
index 000000000..89d23993c
--- /dev/null
+++ b/src/storage/v2/disk/name_id_mapper.hpp
@@ -0,0 +1,111 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include <atomic>
+#include <cstddef>
+#include <filesystem>
+#include <memory>
+#include <string>
+#include <string_view>
+
+#include "kvstore/kvstore.hpp"
+#include "spdlog/spdlog.h"
+#include "storage/v2/name_id_mapper.hpp"
+#include "utils/logging.hpp"
+#include "utils/string.hpp"
+
+namespace memgraph::storage {
+
+/// Implements class adapter. Object adapters are usually better but here we need access to protected members
+/// of base class and we don't want to make them public. Also, from the performance perspective, it doesn't matter
+/// since we either have dynamic virtual dispatch here or on storage level.
+class DiskNameIdMapper final : public NameIdMapper {
+ public:
+  explicit DiskNameIdMapper(std::filesystem::path name_to_id_path, std::filesystem::path id_to_name_path)
+      : name_to_id_storage_(std::make_unique<kvstore::KVStore>(name_to_id_path)),
+        id_to_name_storage_(std::make_unique<kvstore::KVStore>(id_to_name_path)) {
+    InitializeFromDisk();
+  }
+
+  uint64_t NameToId(const std::string_view name) override {
+    if (auto maybe_id = MaybeNameToId(name); maybe_id.has_value()) {
+      return maybe_id.value();
+    }
+    uint64_t res_id = 0;
+    if (auto maybe_id_from_disk = name_to_id_storage_->Get(std::string(name)); maybe_id_from_disk.has_value()) {
+      res_id = std::stoull(maybe_id_from_disk.value());
+      InsertNameIdEntryToCache(std::string(name), res_id);
+      InsertIdNameEntryToCache(res_id, std::string(name));
+    } else {
+      res_id = NameIdMapper::NameToId(name);
+      MG_ASSERT(id_to_name_storage_->Put(std::to_string(res_id), std::string(name)),
+                "Failed to store id to name to disk!");
+      MG_ASSERT(name_to_id_storage_->Put(std::string(name), std::to_string(res_id)),
+                "Failed to store id to name to disk!");
+    }
+
+    return res_id;
+  }
+
+  const std::string &IdToName(uint64_t id) override {
+    auto maybe_name = NameIdMapper::MaybeIdToName(id);
+    if (maybe_name.has_value()) {
+      return maybe_name.value();
+    }
+
+    auto maybe_name_from_disk = id_to_name_storage_->Get(std::to_string(id));
+    MG_ASSERT(maybe_name_from_disk.has_value(), "Trying to get a name from disk for an invalid ID!");
+
+    InsertIdNameEntryToCache(id, maybe_name_from_disk.value());
+    return InsertNameIdEntryToCache(maybe_name_from_disk.value(), id);
+  }
+
+ private:
+  std::optional<std::reference_wrapper<const uint64_t>> MaybeNameToId(const std::string_view name) const {
+    auto name_to_id_acc = name_to_id_.access();
+    auto result = name_to_id_acc.find(name);
+    if (result == name_to_id_acc.end()) {
+      return std::nullopt;
+    }
+    return result->id;
+  }
+
+  const std::string &InsertNameIdEntryToCache(const std::string &name, uint64_t id) {
+    auto name_to_id_acc = name_to_id_.access();
+    return name_to_id_acc.insert({std::string(name), id}).first->name;
+  }
+
+  const std::string &InsertIdNameEntryToCache(uint64_t id, const std::string &name) {
+    auto id_to_name_acc = id_to_name_.access();
+    return id_to_name_acc.insert({id, std::string(name)}).first->name;
+  }
+
+  void InitializeFromDisk() {
+    for (auto itr = name_to_id_storage_->begin(); itr != name_to_id_storage_->end(); ++itr) {
+      auto name = itr->first;
+      auto id = std::stoull(itr->second);
+      InsertNameIdEntryToCache(name, id);
+      counter_.fetch_add(1, std::memory_order_release);
+    }
+    for (auto itr = id_to_name_storage_->begin(); itr != id_to_name_storage_->end(); ++itr) {
+      auto id = std::stoull(itr->first);
+      auto name = itr->second;
+      InsertIdNameEntryToCache(id, name);
+    }
+  }
+
+  std::unique_ptr<kvstore::KVStore> name_to_id_storage_;
+  std::unique_ptr<kvstore::KVStore> id_to_name_storage_;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/disk/rocksdb_storage.cpp b/src/storage/v2/disk/rocksdb_storage.cpp
new file mode 100644
index 000000000..61c5bd97d
--- /dev/null
+++ b/src/storage/v2/disk/rocksdb_storage.cpp
@@ -0,0 +1,83 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include "rocksdb_storage.hpp"
+#include <string_view>
+#include "utils/rocksdb_serialization.hpp"
+
+namespace memgraph::storage {
+
+namespace {
+
+inline rocksdb::Slice StripTimestampFromUserKey(const rocksdb::Slice &user_key, size_t ts_sz) {
+  rocksdb::Slice ret = user_key;
+  ret.remove_suffix(ts_sz);
+  return ret;
+}
+
+/// NOTE: Timestamp is encoded as last 8B in user key.
+inline rocksdb::Slice ExtractTimestampFromUserKey(const rocksdb::Slice &user_key) {
+  assert(user_key.size() >= sizeof(uint64_t));
+  return {user_key.data() + user_key.size() - sizeof(uint64_t), sizeof(uint64_t)};
+}
+
+// Extracts global id from user key. User key must be without timestamp.
+std::string_view ExtractGidFromUserKey(const rocksdb::Slice &key) {
+  assert(key.size() >= 2);
+  auto keyStrView = key.ToStringView();
+  return keyStrView.substr(keyStrView.find_last_of('|') + 1);
+}
+
+}  // namespace
+
+ComparatorWithU64TsImpl::ComparatorWithU64TsImpl()
+    : Comparator(/*ts_sz=*/sizeof(uint64_t)), cmp_without_ts_(rocksdb::BytewiseComparator()) {
+  assert(cmp_without_ts_->timestamp_size() == 0);
+}
+
+int ComparatorWithU64TsImpl::Compare(const rocksdb::Slice &a, const rocksdb::Slice &b) const {
+  int ret = CompareWithoutTimestamp(a, b);
+  if (ret != 0) {
+    return ret;
+  }
+  // Compare timestamp.
+  // For the same user key with different timestamps, larger (newer) timestamp
+  // comes first.
+  return CompareTimestamp(ExtractTimestampFromUserKey(b), ExtractTimestampFromUserKey(a));
+}
+
+int ComparatorWithU64TsImpl::CompareWithoutTimestamp(const rocksdb::Slice &a, bool a_has_ts, const rocksdb::Slice &b,
+                                                     bool b_has_ts) const {
+  const size_t ts_sz = timestamp_size();
+  assert(!a_has_ts || a.size() >= ts_sz);
+  assert(!b_has_ts || b.size() >= ts_sz);
+  rocksdb::Slice lhsUserKey = a_has_ts ? StripTimestampFromUserKey(a, ts_sz) : a;
+  rocksdb::Slice rhsUserKey = b_has_ts ? StripTimestampFromUserKey(b, ts_sz) : b;
+  rocksdb::Slice lhsGid = ExtractGidFromUserKey(lhsUserKey);
+  rocksdb::Slice rhsGid = ExtractGidFromUserKey(rhsUserKey);
+  return cmp_without_ts_->Compare(lhsGid, rhsGid);
+}
+
+int ComparatorWithU64TsImpl::CompareTimestamp(const rocksdb::Slice &ts1, const rocksdb::Slice &ts2) const {
+  assert(ts1.size() == sizeof(uint64_t));
+  assert(ts2.size() == sizeof(uint64_t));
+  uint64_t lhs = utils::DecodeFixed64(ts1.data());
+  uint64_t rhs = utils::DecodeFixed64(ts2.data());
+  if (lhs < rhs) {
+    return -1;
+  }
+  if (lhs > rhs) {
+    return 1;
+  }
+  return 0;
+}
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/disk/rocksdb_storage.hpp b/src/storage/v2/disk/rocksdb_storage.hpp
new file mode 100644
index 000000000..b55e139ca
--- /dev/null
+++ b/src/storage/v2/disk/rocksdb_storage.hpp
@@ -0,0 +1,89 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include <rocksdb/comparator.h>
+#include <rocksdb/db.h>
+#include <rocksdb/iterator.h>
+#include <rocksdb/options.h>
+#include <rocksdb/status.h>
+#include <rocksdb/utilities/transaction_db.h>
+
+#include "storage/v2/id_types.hpp"
+#include "storage/v2/property_store.hpp"
+#include "utils/logging.hpp"
+
+namespace memgraph::storage {
+
+/// TODO: this should be somehow more wrapped inside the storage class so from the software engineering perspective
+/// it isn't great to have this here. But for now it is ok.
+/// Wraps RocksDB objects inside a struct. Vertex_chandle and edge_chandle are column family handles that may be
+/// nullptr. In that case client should take care about them.
+struct RocksDBStorage {
+  explicit RocksDBStorage() {}
+
+  RocksDBStorage(const RocksDBStorage &) = delete;
+  RocksDBStorage &operator=(const RocksDBStorage &) = delete;
+  RocksDBStorage(RocksDBStorage &&) = delete;
+  RocksDBStorage &operator=(RocksDBStorage &&) = delete;
+
+  ~RocksDBStorage() {
+    delete db_;
+    db_ = nullptr;
+    delete options_.comparator;
+    options_.comparator = nullptr;
+  }
+
+  rocksdb::Options options_;
+  rocksdb::TransactionDB *db_;
+  rocksdb::ColumnFamilyHandle *vertex_chandle = nullptr;
+  rocksdb::ColumnFamilyHandle *edge_chandle = nullptr;
+  rocksdb::ColumnFamilyHandle *default_chandle = nullptr;
+
+  uint64_t ApproximateVertexCount() const {
+    uint64_t estimate_num_keys = 0;
+    db_->GetIntProperty(vertex_chandle, "rocksdb.estimate-num-keys", &estimate_num_keys);
+    return estimate_num_keys;
+  }
+
+  uint64_t ApproximateEdgeCount() const {
+    uint64_t estimate_num_keys = 0;
+    db_->GetIntProperty(edge_chandle, "rocksdb.estimate-num-keys", &estimate_num_keys);
+    return estimate_num_keys;
+  }
+};
+
+/// RocksDB comparator that compares keys with timestamps.
+class ComparatorWithU64TsImpl : public rocksdb::Comparator {
+ public:
+  explicit ComparatorWithU64TsImpl();
+
+  static const char *kClassName() { return "be"; }
+
+  const char *Name() const override { return kClassName(); }
+
+  void FindShortSuccessor(std::string * /*key*/) const override {}
+  void FindShortestSeparator(std::string * /*start*/, const rocksdb::Slice & /*limit*/) const override {}
+
+  int Compare(const rocksdb::Slice &a, const rocksdb::Slice &b) const override;
+
+  using Comparator::CompareWithoutTimestamp;
+  int CompareWithoutTimestamp(const rocksdb::Slice &a, bool a_has_ts, const rocksdb::Slice &b,
+                              bool b_has_ts) const override;
+
+  int CompareTimestamp(const rocksdb::Slice &ts1, const rocksdb::Slice &ts2) const override;
+
+ private:
+  const Comparator *cmp_without_ts_{nullptr};
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/disk/storage.cpp b/src/storage/v2/disk/storage.cpp
new file mode 100644
index 000000000..63a5ad23c
--- /dev/null
+++ b/src/storage/v2/disk/storage.cpp
@@ -0,0 +1,1703 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include <limits>
+#include <optional>
+#include <stdexcept>
+#include <vector>
+
+#include <rocksdb/comparator.h>
+#include <rocksdb/db.h>
+#include <rocksdb/slice.h>
+
+#include <rocksdb/options.h>
+#include <rocksdb/utilities/transaction.h>
+#include <rocksdb/utilities/transaction_db.h>
+
+#include "kvstore/kvstore.hpp"
+#include "spdlog/spdlog.h"
+#include "storage/v2/constraints/unique_constraints.hpp"
+#include "storage/v2/disk/rocksdb_storage.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/disk/unique_constraints.hpp"
+#include "storage/v2/id_types.hpp"
+#include "storage/v2/mvcc.hpp"
+#include "storage/v2/property_store.hpp"
+#include "storage/v2/property_value.hpp"
+#include "storage/v2/result.hpp"
+#include "storage/v2/storage.hpp"
+#include "storage/v2/storage_error.hpp"
+#include "storage/v2/transaction.hpp"
+#include "storage/v2/vertex_accessor.hpp"
+#include "storage/v2/view.hpp"
+#include "utils/disk_utils.hpp"
+#include "utils/exceptions.hpp"
+#include "utils/file.hpp"
+#include "utils/memory_tracker.hpp"
+#include "utils/message.hpp"
+#include "utils/on_scope_exit.hpp"
+#include "utils/readable_size.hpp"
+#include "utils/result.hpp"
+#include "utils/rocksdb_serialization.hpp"
+#include "utils/skip_list.hpp"
+#include "utils/stat.hpp"
+#include "utils/string.hpp"
+
+namespace memgraph::storage {
+
+using OOMExceptionEnabler = utils::MemoryTracker::OutOfMemoryExceptionEnabler;
+
+namespace {
+
+constexpr const char *vertexHandle = "vertex";
+constexpr const char *edgeHandle = "edge";
+constexpr const char *defaultHandle = "default";
+constexpr const char *lastTransactionStartTimeStamp = "last_transaction_start_timestamp";
+constexpr const char *label_index_str = "label_index";
+constexpr const char *label_property_index_str = "label_property_index";
+constexpr const char *existence_constraints_str = "existence_constraints";
+constexpr const char *unique_constraints_str = "unique_constraints";
+
+bool VertexExistsInCache(const utils::SkipList<Vertex>::Accessor &accessor, Gid gid) {
+  return accessor.find(gid) != accessor.end();
+}
+
+bool VertexHasLabel(const Vertex &vertex, LabelId label, Transaction *transaction, View view) {
+  bool deleted = false;
+  bool has_label = false;
+  Delta *delta = nullptr;
+  {
+    std::lock_guard<utils::SpinLock> guard(vertex.lock);
+    deleted = vertex.deleted;
+    has_label = std::find(vertex.labels.begin(), vertex.labels.end(), label) != vertex.labels.end();
+    delta = vertex.delta;
+  }
+  ApplyDeltasForRead(transaction, delta, view, [&deleted, &has_label, label](const Delta &delta) {
+    switch (delta.action) {
+      case Delta::Action::REMOVE_LABEL: {
+        if (delta.label == label) {
+          MG_ASSERT(has_label, "Invalid database state!");
+          has_label = false;
+        }
+        break;
+      }
+      case Delta::Action::ADD_LABEL: {
+        if (delta.label == label) {
+          MG_ASSERT(!has_label, "Invalid database state!");
+          has_label = true;
+        }
+        break;
+      }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+      case Delta::Action::DELETE_OBJECT:
+      case Delta::Action::RECREATE_OBJECT: {
+        deleted = false;
+        break;
+      }
+      case Delta::Action::SET_PROPERTY:
+      case Delta::Action::ADD_IN_EDGE:
+      case Delta::Action::ADD_OUT_EDGE:
+      case Delta::Action::REMOVE_IN_EDGE:
+      case Delta::Action::REMOVE_OUT_EDGE:
+        break;
+    }
+  });
+  return has_label && !deleted;
+}
+
+PropertyValue GetVertexProperty(const Vertex &vertex, PropertyId property, Transaction *transaction, View view) {
+  bool deleted = false;
+  PropertyValue value;
+  Delta *delta = nullptr;
+  {
+    std::lock_guard<utils::SpinLock> guard(vertex.lock);
+    deleted = vertex.deleted;
+    value = vertex.properties.GetProperty(property);
+    delta = vertex.delta;
+  }
+  ApplyDeltasForRead(transaction, delta, view, [&deleted, &value, property](const Delta &delta) {
+    switch (delta.action) {
+      case Delta::Action::SET_PROPERTY: {
+        if (delta.property.key == property) {
+          value = delta.property.value;
+        }
+        break;
+      }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+      case Delta::Action::DELETE_OBJECT:
+      case Delta::Action::RECREATE_OBJECT: {
+        deleted = false;
+        break;
+      }
+      case Delta::Action::ADD_LABEL:
+      case Delta::Action::REMOVE_LABEL:
+      case Delta::Action::ADD_IN_EDGE:
+      case Delta::Action::ADD_OUT_EDGE:
+      case Delta::Action::REMOVE_IN_EDGE:
+      case Delta::Action::REMOVE_OUT_EDGE:
+        break;
+    }
+  });
+  if (deleted) {
+    return {};
+  }
+  return value;
+}
+
+bool HasVertexProperty(const Vertex &vertex, PropertyId property, Transaction *transaction, View view) {
+  return !GetVertexProperty(vertex, property, transaction, view).IsNull();
+}
+
+bool HasVertexEqualPropertyValue(const Vertex &vertex, PropertyId property_id, PropertyValue property_value,
+                                 Transaction *transaction, View view) {
+  return GetVertexProperty(vertex, property_id, transaction, view) == property_value;
+}
+
+bool IsPropertyValueWithinInterval(const PropertyValue &value,
+                                   const std::optional<utils::Bound<PropertyValue>> &lower_bound,
+                                   const std::optional<utils::Bound<PropertyValue>> &upper_bound) {
+  if (lower_bound && (!PropertyValue::AreComparableTypes(value.type(), lower_bound->value().type()) ||
+                      value < lower_bound->value() || (lower_bound->IsExclusive() && value == lower_bound->value()))) {
+    return false;
+  }
+  if (upper_bound && (!PropertyValue::AreComparableTypes(value.type(), upper_bound->value().type()) ||
+                      value > upper_bound->value() || (upper_bound->IsExclusive() && value == upper_bound->value()))) {
+    return false;
+  }
+  return true;
+}
+
+}  // namespace
+
+void DiskStorage::LoadTimestampIfExists() {
+  if (!utils::DirExists(config_.disk.durability_directory)) {
+    return;
+  }
+  if (auto last_timestamp_ = durability_kvstore_->Get(lastTransactionStartTimeStamp); last_timestamp_.has_value()) {
+    timestamp_ = std::stoull(last_timestamp_.value());
+  }
+}
+
+void DiskStorage::LoadIndexInfoIfExists() const {
+  if (utils::DirExists(config_.disk.durability_directory)) {
+    LoadLabelIndexInfoIfExists();
+    LoadLabelPropertyIndexInfoIfExists();
+  }
+}
+
+void DiskStorage::LoadLabelIndexInfoIfExists() const {
+  if (auto label_index = durability_kvstore_->Get(label_index_str); label_index.has_value()) {
+    auto *disk_label_index = static_cast<DiskLabelIndex *>(indices_.label_index_.get());
+    const std::vector<std::string> labels{utils::Split(label_index.value(), "|")};
+    disk_label_index->LoadIndexInfo(labels);
+  }
+}
+
+void DiskStorage::LoadLabelPropertyIndexInfoIfExists() const {
+  if (auto label_property_index = durability_kvstore_->Get(label_property_index_str);
+      label_property_index.has_value()) {
+    auto *disk_label_property_index = static_cast<DiskLabelPropertyIndex *>(indices_.label_property_index_.get());
+    const std::vector<std::string> keys{utils::Split(label_property_index.value(), "|")};
+    disk_label_property_index->LoadIndexInfo(keys);
+  }
+}
+
+void DiskStorage::LoadConstraintsInfoIfExists() const {
+  if (utils::DirExists(config_.disk.durability_directory)) {
+    LoadExistenceConstraintInfoIfExists();
+    LoadUniqueConstraintInfoIfExists();
+  }
+}
+
+void DiskStorage::LoadExistenceConstraintInfoIfExists() const {
+  if (auto existence_constraints = durability_kvstore_->Get(existence_constraints_str);
+      existence_constraints.has_value()) {
+    std::vector<std::string> keys = utils::Split(existence_constraints.value(), "|");
+    constraints_.existence_constraints_->LoadExistenceConstraints(keys);
+  }
+}
+
+void DiskStorage::LoadUniqueConstraintInfoIfExists() const {
+  if (auto unique_constraints = durability_kvstore_->Get(unique_constraints_str); unique_constraints.has_value()) {
+    std::vector<std::string> keys = utils::Split(unique_constraints.value(), "|");
+    auto *disk_unique_constraints = static_cast<DiskUniqueConstraints *>(constraints_.unique_constraints_.get());
+    disk_unique_constraints->LoadUniqueConstraints(keys);
+  }
+}
+
+DiskStorage::DiskStorage(Config config)
+    : Storage(config, StorageMode::ON_DISK_TRANSACTIONAL),
+      kvstore_(std::make_unique<RocksDBStorage>()),
+      durability_kvstore_(std::make_unique<kvstore::KVStore>(config.disk.durability_directory)) {
+  LoadTimestampIfExists();
+  LoadIndexInfoIfExists();
+  LoadConstraintsInfoIfExists();
+  kvstore_->options_.create_if_missing = true;
+  kvstore_->options_.comparator = new ComparatorWithU64TsImpl();
+  kvstore_->options_.compression = rocksdb::kNoCompression;
+  kvstore_->options_.wal_recovery_mode = rocksdb::WALRecoveryMode::kPointInTimeRecovery;
+  kvstore_->options_.wal_dir = config_.disk.wal_directory;
+  kvstore_->options_.wal_compression = rocksdb::kNoCompression;
+  std::vector<rocksdb::ColumnFamilyHandle *> column_handles;
+  std::vector<rocksdb::ColumnFamilyDescriptor> column_families;
+  if (utils::DirExists(config.disk.main_storage_directory)) {
+    column_families.emplace_back(vertexHandle, kvstore_->options_);
+    column_families.emplace_back(edgeHandle, kvstore_->options_);
+    column_families.emplace_back(defaultHandle, kvstore_->options_);
+
+    logging::AssertRocksDBStatus(rocksdb::TransactionDB::Open(kvstore_->options_, rocksdb::TransactionDBOptions(),
+                                                              config.disk.main_storage_directory, column_families,
+                                                              &column_handles, &kvstore_->db_));
+    kvstore_->vertex_chandle = column_handles[0];
+    kvstore_->edge_chandle = column_handles[1];
+    kvstore_->default_chandle = column_handles[2];
+  } else {
+    logging::AssertRocksDBStatus(rocksdb::TransactionDB::Open(kvstore_->options_, rocksdb::TransactionDBOptions(),
+                                                              config.disk.main_storage_directory, &kvstore_->db_));
+    logging::AssertRocksDBStatus(
+        kvstore_->db_->CreateColumnFamily(kvstore_->options_, vertexHandle, &kvstore_->vertex_chandle));
+    logging::AssertRocksDBStatus(
+        kvstore_->db_->CreateColumnFamily(kvstore_->options_, edgeHandle, &kvstore_->edge_chandle));
+  }
+}
+
+DiskStorage::~DiskStorage() {
+  durability_kvstore_->Put(lastTransactionStartTimeStamp, std::to_string(timestamp_));
+  logging::AssertRocksDBStatus(kvstore_->db_->DestroyColumnFamilyHandle(kvstore_->vertex_chandle));
+  logging::AssertRocksDBStatus(kvstore_->db_->DestroyColumnFamilyHandle(kvstore_->edge_chandle));
+  if (kvstore_->default_chandle) {
+    // We must destroy default column family handle only if it was read from existing database.
+    // https://github.com/facebook/rocksdb/issues/5006#issuecomment-1003154821
+    logging::AssertRocksDBStatus(kvstore_->db_->DestroyColumnFamilyHandle(kvstore_->default_chandle));
+  }
+  delete kvstore_->options_.comparator;
+  kvstore_->options_.comparator = nullptr;
+}
+
+DiskStorage::DiskAccessor::DiskAccessor(DiskStorage *storage, IsolationLevel isolation_level, StorageMode storage_mode)
+    : Accessor(storage, isolation_level, storage_mode), config_(storage->config_.items) {
+  rocksdb::WriteOptions write_options;
+  auto txOptions = rocksdb::TransactionOptions{.set_snapshot = true};
+  disk_transaction_ = storage->kvstore_->db_->BeginTransaction(write_options, txOptions);
+  disk_transaction_->SetReadTimestampForValidation(transaction_.start_timestamp);
+}
+
+DiskStorage::DiskAccessor::DiskAccessor(DiskAccessor &&other) noexcept
+    : Accessor(std::move(other)), config_(other.config_) {
+  other.is_transaction_active_ = false;
+  other.commit_timestamp_.reset();
+}
+
+DiskStorage::DiskAccessor::~DiskAccessor() {
+  if (is_transaction_active_) {
+    Abort();
+  }
+
+  FinalizeTransaction();
+}
+
+/// NOTE: This will create Delta object which will cause deletion of old key entry on the disk
+std::optional<storage::VertexAccessor> DiskStorage::DiskAccessor::LoadVertexToMainMemoryCache(
+    const rocksdb::Slice &key, const rocksdb::Slice &value) {
+  auto main_storage_accessor = vertices_.access();
+
+  const std::string key_str = key.ToString();
+  storage::Gid gid = Gid::FromUint(std::stoull(utils::ExtractGidFromKey(key_str)));
+  if (VertexExistsInCache(main_storage_accessor, gid)) {
+    return std::nullopt;
+  }
+  std::vector<LabelId> labels_id = utils::DeserializeLabelsFromMainDiskStorage(key_str);
+  return CreateVertex(main_storage_accessor, gid, labels_id,
+                      utils::DeserializePropertiesFromMainDiskStorage(value.ToStringView()),
+                      CreateDeleteDeserializedObjectDelta(&transaction_, key.ToString()));
+}
+
+std::optional<storage::VertexAccessor> DiskStorage::DiskAccessor::LoadVertexToLabelIndexCache(
+    const rocksdb::Slice &key, const rocksdb::Slice &value, Delta *index_delta,
+    utils::SkipList<storage::Vertex>::Accessor index_accessor) {
+  storage::Gid gid = Gid::FromUint(std::stoull(utils::ExtractGidFromLabelIndexStorage(key.ToString())));
+  if (VertexExistsInCache(index_accessor, gid)) {
+    return std::nullopt;
+  }
+
+  const std::string value_str{value.ToString()};
+  const auto labels{utils::DeserializeLabelsFromLabelIndexStorage(value_str)};
+  return CreateVertex(index_accessor, gid, labels, utils::DeserializePropertiesFromLabelIndexStorage(value_str),
+                      index_delta);
+}
+
+std::optional<storage::VertexAccessor> DiskStorage::DiskAccessor::LoadVertexToLabelPropertyIndexCache(
+    const rocksdb::Slice &key, const rocksdb::Slice &value, Delta *index_delta,
+    utils::SkipList<storage::Vertex>::Accessor index_accessor) {
+  storage::Gid gid = Gid::FromUint(std::stoull(utils::ExtractGidFromLabelPropertyIndexStorage(key.ToString())));
+  if (VertexExistsInCache(index_accessor, gid)) {
+    return std::nullopt;
+  }
+
+  const std::string value_str{value.ToString()};
+  const auto labels{utils::DeserializeLabelsFromLabelPropertyIndexStorage(value_str)};
+  return CreateVertex(index_accessor, gid, labels,
+                      utils::DeserializePropertiesFromLabelPropertyIndexStorage(value.ToString()), index_delta);
+}
+
+std::optional<EdgeAccessor> DiskStorage::DiskAccessor::DeserializeEdge(const rocksdb::Slice &key,
+                                                                       const rocksdb::Slice &value) {
+  const auto edge_parts = utils::Split(key.ToStringView(), "|");
+  const Gid edge_gid = Gid::FromUint(std::stoull(edge_parts[4]));
+
+  auto edge_acc = edges_.access();
+  auto res = edge_acc.find(edge_gid);
+  if (res != edge_acc.end()) {
+    return std::nullopt;
+  }
+
+  const auto [from_gid, to_gid] = std::invoke(
+      [](const auto &edge_parts) {
+        if (edge_parts[2] == "0") {  // out edge
+          return std::make_pair(edge_parts[0], edge_parts[1]);
+        }
+        // in edge
+        return std::make_pair(edge_parts[1], edge_parts[0]);
+      },
+      edge_parts);
+
+  const auto from_acc = FindVertex(Gid::FromUint(std::stoull(from_gid)), View::OLD);
+  const auto to_acc = FindVertex(Gid::FromUint(std::stoull(to_gid)), View::OLD);
+  if (!from_acc || !to_acc) {
+    throw utils::BasicException("Non-existing vertices found during edge deserialization");
+  }
+  const auto edge_type_id = storage::EdgeTypeId::FromUint(std::stoull(edge_parts[3]));
+  auto maybe_edge = CreateEdge(&*from_acc, &*to_acc, edge_type_id, edge_gid, value.ToStringView(), key.ToString());
+  MG_ASSERT(maybe_edge.HasValue());
+
+  return *maybe_edge;
+}
+
+VerticesIterable DiskStorage::DiskAccessor::Vertices(View view) {
+  auto *disk_storage = static_cast<DiskStorage *>(storage_);
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(transaction_.start_timestamp);
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto it =
+      std::unique_ptr<rocksdb::Iterator>(disk_transaction_->GetIterator(ro, disk_storage->kvstore_->vertex_chandle));
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    LoadVertexToMainMemoryCache(it->key(), it->value());
+  }
+  return VerticesIterable(AllVerticesIterable(vertices_.access(), &transaction_, view, &storage_->indices_,
+                                              &storage_->constraints_, storage_->config_.items));
+}
+
+VerticesIterable DiskStorage::DiskAccessor::Vertices(LabelId label, View view) {
+  index_storage_.emplace_back(std::make_unique<utils::SkipList<storage::Vertex>>());
+  auto &indexed_vertices = index_storage_.back();
+  index_deltas_storage_.emplace_back(std::list<Delta>());
+  auto &index_deltas = index_deltas_storage_.back();
+
+  auto *disk_label_index = static_cast<DiskLabelIndex *>(storage_->indices_.label_index_.get());
+  auto disk_index_transaction = disk_label_index->CreateRocksDBTransaction();
+  disk_index_transaction->SetReadTimestampForValidation(transaction_.start_timestamp);
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(transaction_.start_timestamp);
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto index_it = std::unique_ptr<rocksdb::Iterator>(disk_index_transaction->GetIterator(ro));
+
+  auto main_cache_acc = vertices_.access();
+  std::unordered_set<storage::Gid> gids(main_cache_acc.size());
+  for (const auto &vertex : main_cache_acc) {
+    gids.insert(vertex.gid);
+    if (VertexHasLabel(vertex, label, &transaction_, view)) {
+      spdlog::debug("Loaded vertex with gid: {} from main index storage to label index",
+                    utils::SerializeIdType(vertex.gid));
+      LoadVertexToLabelIndexCache(utils::SerializeVertexAsKeyForLabelIndex(label, vertex.gid),
+                                  utils::SerializeVertexAsValueForLabelIndex(label, vertex.labels, vertex.properties),
+                                  CreateDeleteDeserializedIndexObjectDelta(&transaction_, index_deltas, std::nullopt),
+                                  indexed_vertices->access());
+    }
+  }
+
+  for (index_it->SeekToFirst(); index_it->Valid(); index_it->Next()) {
+    std::string key = index_it->key().ToString();
+    Gid curr_gid = Gid::FromUint(std::stoull(utils::ExtractGidFromLabelIndexStorage(key)));
+    spdlog::debug("Loaded vertex with key: {} from label index storage", key);
+    /// TODO: optimize
+    if (key.starts_with(utils::SerializeIdType(label)) && !utils::Contains(gids, curr_gid)) {
+      LoadVertexToLabelIndexCache(index_it->key(), index_it->value(),
+                                  CreateDeleteDeserializedIndexObjectDelta(&transaction_, index_deltas, key),
+                                  indexed_vertices->access());
+    }
+  }
+
+  return VerticesIterable(AllVerticesIterable(indexed_vertices->access(), &transaction_, view, &storage_->indices_,
+                                              &storage_->constraints_, storage_->config_.items));
+}
+
+VerticesIterable DiskStorage::DiskAccessor::Vertices(LabelId label, PropertyId property, View view) {
+  index_storage_.emplace_back(std::make_unique<utils::SkipList<storage::Vertex>>());
+  auto &indexed_vertices = index_storage_.back();
+  index_deltas_storage_.emplace_back(std::list<Delta>());
+  auto &index_deltas = index_deltas_storage_.back();
+
+  auto *disk_label_property_index =
+      static_cast<DiskLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
+
+  auto disk_index_transaction = disk_label_property_index->CreateRocksDBTransaction();
+  disk_index_transaction->SetReadTimestampForValidation(transaction_.start_timestamp);
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(transaction_.start_timestamp);
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto index_it = std::unique_ptr<rocksdb::Iterator>(disk_index_transaction->GetIterator(ro));
+
+  auto main_cache_acc = vertices_.access();
+  std::unordered_set<storage::Gid> gids(main_cache_acc.size());
+  for (const auto &vertex : main_cache_acc) {
+    gids.insert(vertex.gid);
+    /// TODO: delta support for clearing old disk keys
+    if (VertexHasLabel(vertex, label, &transaction_, view) &&
+        HasVertexProperty(vertex, property, &transaction_, view)) {
+      LoadVertexToLabelPropertyIndexCache(
+          utils::SerializeVertexAsKeyForLabelPropertyIndex(label, property, vertex.gid),
+          utils::SerializeVertexAsValueForLabelPropertyIndex(label, vertex.labels, vertex.properties),
+          CreateDeleteDeserializedIndexObjectDelta(&transaction_, index_deltas, std::nullopt),
+          indexed_vertices->access());
+    }
+  }
+
+  for (index_it->SeekToFirst(); index_it->Valid(); index_it->Next()) {
+    std::string key = index_it->key().ToString();
+    Gid curr_gid = Gid::FromUint(std::stoull(utils::ExtractGidFromLabelPropertyIndexStorage(key)));
+    /// TODO: optimize
+    if (key.starts_with(utils::SerializeIdType(label) + "|" + utils::SerializeIdType(property)) &&
+        !utils::Contains(gids, curr_gid)) {
+      LoadVertexToLabelPropertyIndexCache(index_it->key(), index_it->value(),
+                                          CreateDeleteDeserializedIndexObjectDelta(&transaction_, index_deltas, key),
+                                          indexed_vertices->access());
+    }
+  }
+
+  return VerticesIterable(AllVerticesIterable(indexed_vertices->access(), &transaction_, view, &storage_->indices_,
+                                              &storage_->constraints_, storage_->config_.items));
+}
+
+VerticesIterable DiskStorage::DiskAccessor::Vertices(LabelId label, PropertyId property, const PropertyValue &value,
+                                                     View view) {
+  index_storage_.emplace_back(std::make_unique<utils::SkipList<storage::Vertex>>());
+  auto &indexed_vertices = index_storage_.back();
+  index_deltas_storage_.emplace_back(std::list<Delta>());
+  auto &index_deltas = index_deltas_storage_.back();
+
+  auto *disk_label_property_index =
+      static_cast<DiskLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
+
+  auto disk_index_transaction = disk_label_property_index->CreateRocksDBTransaction();
+  disk_index_transaction->SetReadTimestampForValidation(transaction_.start_timestamp);
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(transaction_.start_timestamp);
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto index_it = std::unique_ptr<rocksdb::Iterator>(disk_index_transaction->GetIterator(ro));
+
+  auto main_cache_acc = vertices_.access();
+  std::unordered_set<storage::Gid> gids(main_cache_acc.size());
+  for (const auto &vertex : main_cache_acc) {
+    gids.insert(vertex.gid);
+    if (VertexHasLabel(vertex, label, &transaction_, view) &&
+        HasVertexEqualPropertyValue(vertex, property, value, &transaction_, view)) {
+      LoadVertexToLabelPropertyIndexCache(
+          utils::SerializeVertexAsKeyForLabelPropertyIndex(label, property, vertex.gid),
+          utils::SerializeVertexAsValueForLabelPropertyIndex(label, vertex.labels, vertex.properties),
+          CreateDeleteDeserializedIndexObjectDelta(&transaction_, index_deltas, std::nullopt),
+          indexed_vertices->access());
+    }
+  }
+
+  for (index_it->SeekToFirst(); index_it->Valid(); index_it->Next()) {
+    std::string key_str = index_it->key().ToString();
+    std::string it_value_str = index_it->value().ToString();
+    Gid curr_gid = Gid::FromUint(std::stoull(utils::ExtractGidFromLabelPropertyIndexStorage(key_str)));
+    /// TODO: optimize
+    /// TODO: couple this condition
+    PropertyStore properties = utils::DeserializePropertiesFromLabelPropertyIndexStorage(it_value_str);
+    if (key_str.starts_with(utils::SerializeIdType(label) + "|" + utils::SerializeIdType(property)) &&
+        !utils::Contains(gids, curr_gid) && properties.IsPropertyEqual(property, value)) {
+      LoadVertexToLabelPropertyIndexCache(
+          index_it->key(), index_it->value(),
+          CreateDeleteDeserializedIndexObjectDelta(&transaction_, index_deltas, key_str), indexed_vertices->access());
+    }
+  }
+
+  return VerticesIterable(AllVerticesIterable(indexed_vertices->access(), &transaction_, view, &storage_->indices_,
+                                              &storage_->constraints_, storage_->config_.items));
+}
+
+VerticesIterable DiskStorage::DiskAccessor::Vertices(LabelId label, PropertyId property,
+                                                     const std::optional<utils::Bound<PropertyValue>> &lower_bound,
+                                                     const std::optional<utils::Bound<PropertyValue>> &upper_bound,
+                                                     View view) {
+  index_storage_.emplace_back(std::make_unique<utils::SkipList<storage::Vertex>>());
+  auto &indexed_vertices = index_storage_.back();
+  index_deltas_storage_.emplace_back(std::list<Delta>());
+  auto &index_deltas = index_deltas_storage_.back();
+
+  auto *disk_label_property_index =
+      static_cast<DiskLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
+
+  auto disk_index_transaction = disk_label_property_index->CreateRocksDBTransaction();
+  disk_index_transaction->SetReadTimestampForValidation(transaction_.start_timestamp);
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(transaction_.start_timestamp);
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto index_it = std::unique_ptr<rocksdb::Iterator>(disk_index_transaction->GetIterator(ro));
+
+  auto main_cache_acc = vertices_.access();
+  std::unordered_set<storage::Gid> gids(main_cache_acc.size());
+  for (const auto &vertex : main_cache_acc) {
+    gids.insert(vertex.gid);
+    /// TODO: refactor in one method
+    auto prop_value = GetVertexProperty(vertex, property, &transaction_, view);
+    if (VertexHasLabel(vertex, label, &transaction_, view) &&
+        IsPropertyValueWithinInterval(prop_value, lower_bound, upper_bound)) {
+      LoadVertexToLabelPropertyIndexCache(
+          utils::SerializeVertexAsKeyForLabelPropertyIndex(label, property, vertex.gid),
+          utils::SerializeVertexAsValueForLabelPropertyIndex(label, vertex.labels, vertex.properties),
+          CreateDeleteDeserializedIndexObjectDelta(&transaction_, index_deltas, std::nullopt),
+          indexed_vertices->access());
+    }
+  }
+
+  for (index_it->SeekToFirst(); index_it->Valid(); index_it->Next()) {
+    std::string key_str = index_it->key().ToString();
+    std::string it_value_str = index_it->value().ToString();
+    Gid curr_gid = Gid::FromUint(std::stoull(utils::ExtractGidFromLabelPropertyIndexStorage(key_str)));
+    // TODO: andi this will be optimized bla bla
+    /// TODO: couple this condition
+    PropertyStore properties = utils::DeserializePropertiesFromLabelPropertyIndexStorage(it_value_str);
+    auto prop_value = properties.GetProperty(property);
+    if (key_str.starts_with(utils::SerializeIdType(label) + "|" + utils::SerializeIdType(property)) &&
+        !utils::Contains(gids, curr_gid) && IsPropertyValueWithinInterval(prop_value, lower_bound, upper_bound)) {
+      LoadVertexToLabelPropertyIndexCache(
+          index_it->key(), index_it->value(),
+          CreateDeleteDeserializedIndexObjectDelta(&transaction_, index_deltas, key_str), indexed_vertices->access());
+    }
+  }
+
+  return VerticesIterable(AllVerticesIterable(indexed_vertices->access(), &transaction_, view, &storage_->indices_,
+                                              &storage_->constraints_, storage_->config_.items));
+}
+
+uint64_t DiskStorage::DiskAccessor::ApproximateVertexCount() const {
+  auto *disk_storage = static_cast<DiskStorage *>(storage_);
+  return disk_storage->kvstore_->ApproximateVertexCount();
+}
+
+bool DiskStorage::PersistLabelIndexCreation(LabelId label) const {
+  if (auto label_index_store = durability_kvstore_->Get(label_index_str); label_index_store.has_value()) {
+    std::string &value = label_index_store.value();
+    value += "|" + utils::SerializeIdType(label);
+    return durability_kvstore_->Put(label_index_str, value);
+  }
+  return durability_kvstore_->Put(label_index_str, utils::SerializeIdType(label));
+}
+
+bool DiskStorage::PersistLabelIndexDeletion(LabelId label) const {
+  if (auto label_index_store = durability_kvstore_->Get(label_index_str); label_index_store.has_value()) {
+    const std::string &value = label_index_store.value();
+    std::vector<std::string> labels = utils::Split(value, "|");
+    std::erase(labels, utils::SerializeIdType(label));
+    if (labels.empty()) {
+      return durability_kvstore_->Delete(label_index_str);
+    }
+    return durability_kvstore_->Put(label_index_str, utils::Join(labels, "|"));
+  }
+  return true;
+}
+
+bool DiskStorage::PersistLabelPropertyIndexAndExistenceConstraintCreation(LabelId label, PropertyId property,
+                                                                          const char *key) const {
+  if (auto label_property_index_store = durability_kvstore_->Get(key); label_property_index_store.has_value()) {
+    std::string &value = label_property_index_store.value();
+    value += "|" + utils::SerializeIdType(label) + "," + utils::SerializeIdType(property);
+    return durability_kvstore_->Put(key, value);
+  }
+  return durability_kvstore_->Put(key, utils::SerializeIdType(label) + "," + utils::SerializeIdType(property));
+}
+
+bool DiskStorage::PersistLabelPropertyIndexAndExistenceConstraintDeletion(LabelId label, PropertyId property,
+                                                                          const char *key) const {
+  if (auto label_property_index_store = durability_kvstore_->Get(key); label_property_index_store.has_value()) {
+    const std::string &value = label_property_index_store.value();
+    std::vector<std::string> label_properties = utils::Split(value, "|");
+    std::erase(label_properties, utils::SerializeIdType(label) + "," + utils::SerializeIdType(property));
+    if (label_properties.empty()) {
+      return durability_kvstore_->Delete(key);
+    }
+    return durability_kvstore_->Put(key, utils::Join(label_properties, "|"));
+  }
+  return true;
+}
+
+bool DiskStorage::PersistUniqueConstraintCreation(LabelId label, const std::set<PropertyId> &properties) const {
+  std::string entry = utils::SerializeIdType(label);
+  for (auto property : properties) {
+    entry += "," + utils::SerializeIdType(property);
+  }
+
+  if (auto unique_store = durability_kvstore_->Get(unique_constraints_str); unique_store.has_value()) {
+    std::string &value = unique_store.value();
+    value += "|" + entry;
+    return durability_kvstore_->Put(unique_constraints_str, value);
+  }
+  return durability_kvstore_->Put(unique_constraints_str, entry);
+}
+
+bool DiskStorage::PersistUniqueConstraintDeletion(LabelId label, const std::set<PropertyId> &properties) const {
+  std::string entry = utils::SerializeIdType(label);
+  for (auto property : properties) {
+    entry += "," + utils::SerializeIdType(property);
+  }
+
+  if (auto unique_store = durability_kvstore_->Get(unique_constraints_str); unique_store.has_value()) {
+    const std::string &value = unique_store.value();
+    std::vector<std::string> unique_constraints = utils::Split(value, "|");
+    std::erase(unique_constraints, entry);
+    if (unique_constraints.empty()) {
+      return durability_kvstore_->Delete(unique_constraints_str);
+    }
+    return durability_kvstore_->Put(unique_constraints_str, utils::Join(unique_constraints, "|"));
+  }
+  return true;
+}
+
+uint64_t DiskStorage::GetDiskSpaceUsage() const {
+  uint64_t main_disk_storage_size = utils::GetDirDiskUsage(config_.disk.main_storage_directory);
+  uint64_t index_disk_storage_size = utils::GetDirDiskUsage(config_.disk.label_index_directory) +
+                                     utils::GetDirDiskUsage(config_.disk.label_property_index_directory);
+  uint64_t constraints_disk_storage_size = utils::GetDirDiskUsage(config_.disk.unique_constraints_directory);
+  uint64_t metadata_disk_storage_size = utils::GetDirDiskUsage(config_.disk.id_name_mapper_directory) +
+                                        utils::GetDirDiskUsage(config_.disk.name_id_mapper_directory);
+  uint64_t durability_disk_storage_size =
+      utils::GetDirDiskUsage(config_.disk.durability_directory) + utils::GetDirDiskUsage(config_.disk.wal_directory);
+  return main_disk_storage_size + index_disk_storage_size + constraints_disk_storage_size + metadata_disk_storage_size +
+         durability_disk_storage_size;
+}
+
+StorageInfo DiskStorage::GetInfo() const {
+  auto vertex_count = kvstore_->ApproximateVertexCount();
+  auto edge_count = kvstore_->ApproximateEdgeCount();
+  double average_degree = 0.0;
+  if (vertex_count) {
+    // NOLINTNEXTLINE(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions)
+    average_degree = 2.0 * static_cast<double>(edge_count) / vertex_count;
+  }
+
+  return {vertex_count, edge_count, average_degree, utils::GetMemoryUsage(), GetDiskSpaceUsage()};
+}
+
+VertexAccessor DiskStorage::DiskAccessor::CreateVertex() {
+  auto gid = storage_->vertex_id_.fetch_add(1, std::memory_order_acq_rel);
+  auto acc = vertices_.access();
+
+  auto *delta = CreateDeleteObjectDelta(&transaction_);
+  auto [it, inserted] = acc.insert(Vertex{storage::Gid::FromUint(gid), delta});
+  MG_ASSERT(inserted, "The vertex must be inserted here!");
+  MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
+
+  if (delta) {
+    delta->prev.Set(&*it);
+  }
+
+  return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
+}
+
+VertexAccessor DiskStorage::DiskAccessor::CreateVertex(utils::SkipList<Vertex>::Accessor &accessor, storage::Gid gid,
+                                                       const std::vector<LabelId> &label_ids,
+                                                       PropertyStore &&properties, Delta *delta) {
+  OOMExceptionEnabler oom_exception;
+  auto *disk_storage = static_cast<DiskStorage *>(storage_);
+  disk_storage->vertex_id_.store(std::max(disk_storage->vertex_id_.load(std::memory_order_acquire), gid.AsUint() + 1),
+                                 std::memory_order_release);
+  auto [it, inserted] = accessor.insert(Vertex{gid, delta});
+  MG_ASSERT(inserted, "The vertex must be inserted here!");
+  MG_ASSERT(it != accessor.end(), "Invalid Vertex accessor!");
+  /// TODO: move
+  for (auto label_id : label_ids) {
+    it->labels.push_back(label_id);
+  }
+  it->properties = std::move(properties);
+  if (delta) {
+    delta->prev.Set(&*it);
+  }
+  return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
+}
+
+std::optional<VertexAccessor> DiskStorage::DiskAccessor::FindVertex(storage::Gid gid, View view) {
+  auto acc = vertices_.access();
+  auto vertex_it = acc.find(gid);
+  if (vertex_it != acc.end()) {
+    return VertexAccessor::Create(&*vertex_it, &transaction_, &storage_->indices_, &storage_->constraints_, config_,
+                                  view);
+  }
+  for (const auto &vec : index_storage_) {
+    acc = vec->access();
+    auto index_it = acc.find(gid);
+    if (index_it != acc.end()) {
+      return VertexAccessor::Create(&*index_it, &transaction_, &storage_->indices_, &storage_->constraints_, config_,
+                                    view);
+    }
+  }
+
+  rocksdb::ReadOptions read_opts;
+  auto strTs = utils::StringTimestamp(transaction_.start_timestamp);
+  rocksdb::Slice ts(strTs);
+  read_opts.timestamp = &ts;
+  auto *disk_storage = static_cast<DiskStorage *>(storage_);
+  auto it = std::unique_ptr<rocksdb::Iterator>(
+      disk_transaction_->GetIterator(read_opts, disk_storage->kvstore_->vertex_chandle));
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    const auto &key = it->key();
+    if (Gid::FromUint(std::stoull(utils::ExtractGidFromKey(key.ToString()))) == gid) {
+      return LoadVertexToMainMemoryCache(key, it->value());
+    }
+  }
+  return std::nullopt;
+}
+
+Result<std::optional<VertexAccessor>> DiskStorage::DiskAccessor::DeleteVertex(VertexAccessor *vertex) {
+  MG_ASSERT(vertex->transaction_ == &transaction_,
+            "VertexAccessor must be from the same transaction as the storage "
+            "accessor when deleting a vertex!");
+  auto *vertex_ptr = vertex->vertex_;
+
+  std::lock_guard<utils::SpinLock> guard(vertex_ptr->lock);
+
+  if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
+
+  if (vertex_ptr->deleted) {
+    return std::optional<VertexAccessor>{};
+  }
+
+  if (!vertex_ptr->in_edges.empty() || !vertex_ptr->out_edges.empty()) return Error::VERTEX_HAS_EDGES;
+
+  CreateAndLinkDelta(&transaction_, vertex_ptr, Delta::RecreateObjectTag());
+  vertex_ptr->deleted = true;
+  vertices_to_delete_.emplace_back(utils::SerializeIdType(vertex_ptr->gid), utils::SerializeVertex(*vertex_ptr));
+
+  return std::make_optional<VertexAccessor>(vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_,
+                                            config_, true);
+}
+
+Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>>
+DiskStorage::DiskAccessor::DetachDeleteVertex(VertexAccessor *vertex) {
+  using ReturnType = std::pair<VertexAccessor, std::vector<EdgeAccessor>>;
+  MG_ASSERT(vertex->transaction_ == &transaction_,
+            "VertexAccessor must be from the same transaction as the storage "
+            "accessor when deleting a vertex!");
+  auto *vertex_ptr = vertex->vertex_;
+
+  std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> in_edges;
+  std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges;
+
+  {
+    std::lock_guard<utils::SpinLock> guard(vertex_ptr->lock);
+
+    if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
+
+    if (vertex_ptr->deleted) return std::optional<ReturnType>{};
+
+    in_edges = vertex_ptr->in_edges;
+    out_edges = vertex_ptr->out_edges;
+  }
+
+  std::vector<EdgeAccessor> deleted_edges;
+  for (const auto &item : in_edges) {
+    auto [edge_type, from_vertex, edge] = item;
+    EdgeAccessor e(edge, edge_type, from_vertex, vertex_ptr, &transaction_, &storage_->indices_,
+                   &storage_->constraints_, config_);
+    auto ret = DeleteEdge(&e);
+    if (ret.HasError()) {
+      MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
+      return ret.GetError();
+    }
+
+    if (ret.GetValue()) {
+      deleted_edges.push_back(*ret.GetValue());
+    }
+  }
+  for (const auto &item : out_edges) {
+    auto [edge_type, to_vertex, edge] = item;
+    EdgeAccessor e(edge, edge_type, vertex_ptr, to_vertex, &transaction_, &storage_->indices_, &storage_->constraints_,
+                   config_);
+    auto ret = DeleteEdge(&e);
+    if (ret.HasError()) {
+      MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
+      return ret.GetError();
+    }
+
+    if (ret.GetValue()) {
+      deleted_edges.push_back(*ret.GetValue());
+    }
+  }
+
+  std::lock_guard<utils::SpinLock> guard(vertex_ptr->lock);
+
+  // We need to check again for serialization errors because we unlocked the
+  // vertex. Some other transaction could have modified the vertex in the
+  // meantime if we didn't have any edges to delete.
+
+  if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
+
+  MG_ASSERT(!vertex_ptr->deleted, "Invalid database state!");
+
+  CreateAndLinkDelta(&transaction_, vertex_ptr, Delta::RecreateObjectTag());
+  vertex_ptr->deleted = true;
+  vertices_to_delete_.emplace_back(utils::SerializeIdType(vertex_ptr->gid), utils::SerializeVertex(*vertex_ptr));
+
+  return std::make_optional<ReturnType>(
+      VertexAccessor{vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_, config_, true},
+      std::move(deleted_edges));
+}
+
+void DiskStorage::DiskAccessor::PrefetchEdges(const auto &prefetch_edge_filter) {
+  rocksdb::ReadOptions read_opts;
+  auto strTs = utils::StringTimestamp(transaction_.start_timestamp);
+  rocksdb::Slice ts(strTs);
+  read_opts.timestamp = &ts;
+  auto *disk_storage = static_cast<DiskStorage *>(storage_);
+  auto it = std::unique_ptr<rocksdb::Iterator>(
+      disk_transaction_->GetIterator(read_opts, disk_storage->kvstore_->edge_chandle));
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    const rocksdb::Slice &key = it->key();
+    const auto edge_parts = utils::Split(key.ToStringView(), "|");
+    if (prefetch_edge_filter(edge_parts)) {
+      DeserializeEdge(key, it->value());
+    }
+  }
+}
+
+void DiskStorage::DiskAccessor::PrefetchInEdges(const VertexAccessor &vertex_acc) {
+  PrefetchEdges([&vertex_acc](const std::vector<std::string> &disk_edge_parts) -> bool {
+    auto disk_vertex_in_edge_gid = disk_edge_parts[1];
+    auto edge_gid = disk_edge_parts[4];
+    auto in_edges_res = vertex_acc.InEdges(storage::View::NEW);
+    if (in_edges_res.HasValue()) {
+      for (const auto &edge_acc : in_edges_res.GetValue()) {
+        if (utils::SerializeIdType(edge_acc.Gid()) == edge_gid) {
+          // We already inserted this edge into the vertex's in_edges list.
+          return false;
+        }
+      }
+    }
+    return disk_vertex_in_edge_gid == utils::SerializeIdType(vertex_acc.Gid());
+  });
+}
+
+void DiskStorage::DiskAccessor::PrefetchOutEdges(const VertexAccessor &vertex_acc) {
+  PrefetchEdges([&vertex_acc](const std::vector<std::string> &disk_edge_parts) -> bool {
+    auto disk_vertex_out_edge_gid = disk_edge_parts[0];
+    auto edge_gid = disk_edge_parts[4];
+    auto out_edges_res = vertex_acc.OutEdges(storage::View::NEW);
+    if (out_edges_res.HasValue()) {
+      for (const auto &edge_acc : out_edges_res.GetValue()) {
+        if (utils::SerializeIdType(edge_acc.Gid()) == edge_gid) {
+          // We already inserted this edge into the vertex's out_edges list.
+          return false;
+        }
+      }
+    }
+    return disk_vertex_out_edge_gid == utils::SerializeIdType(vertex_acc.Gid());
+  });
+}
+
+Result<EdgeAccessor> DiskStorage::DiskAccessor::CreateEdge(const VertexAccessor *from, const VertexAccessor *to,
+                                                           EdgeTypeId edge_type, storage::Gid gid,
+                                                           const std::string_view properties,
+                                                           const std::string &old_disk_key) {
+  OOMExceptionEnabler oom_exception;
+  MG_ASSERT(from->transaction_ == to->transaction_,
+            "VertexAccessors must be from the same transaction when creating "
+            "an edge!");
+  MG_ASSERT(from->transaction_ == &transaction_,
+            "VertexAccessors must be from the same transaction in when "
+            "creating an edge!");
+
+  auto *from_vertex = from->vertex_;
+  auto *to_vertex = to->vertex_;
+
+  // Obtain the locks by `gid` order to avoid lock cycles.
+  std::unique_lock<utils::SpinLock> guard_from(from_vertex->lock, std::defer_lock);
+  std::unique_lock<utils::SpinLock> guard_to(to_vertex->lock, std::defer_lock);
+  if (from_vertex->gid < to_vertex->gid) {
+    guard_from.lock();
+    guard_to.lock();
+  } else if (from_vertex->gid > to_vertex->gid) {
+    guard_to.lock();
+    guard_from.lock();
+  } else {
+    // The vertices are the same vertex, only lock one.
+    guard_from.lock();
+  }
+
+  if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
+  if (from_vertex->deleted) return Error::DELETED_OBJECT;
+
+  if (to_vertex != from_vertex) {
+    if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
+    if (to_vertex->deleted) return Error::DELETED_OBJECT;
+  }
+
+  auto *disk_storage = static_cast<DiskStorage *>(storage_);
+  disk_storage->edge_id_.store(std::max(disk_storage->edge_id_.load(std::memory_order_acquire), gid.AsUint() + 1),
+                               std::memory_order_release);
+
+  EdgeRef edge(gid);
+  if (config_.properties_on_edges) {
+    auto acc = edges_.access();
+    auto *delta = CreateDeleteDeserializedObjectDelta(&transaction_, old_disk_key);
+    auto [it, inserted] = acc.insert(Edge(gid, delta));
+    MG_ASSERT(inserted, "The edge must be inserted here!");
+    MG_ASSERT(it != acc.end(), "Invalid Edge accessor!");
+    edge = EdgeRef(&*it);
+    if (delta) {
+      delta->prev.Set(&*it);
+    }
+    edge.ptr->properties.SetBuffer(properties);
+  }
+
+  from_vertex->out_edges.emplace_back(edge_type, to_vertex, edge);
+  to_vertex->in_edges.emplace_back(edge_type, from_vertex, edge);
+
+  storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
+
+  return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
+                      &storage_->constraints_, config_);
+}
+
+Result<EdgeAccessor> DiskStorage::DiskAccessor::CreateEdge(VertexAccessor *from, VertexAccessor *to,
+                                                           EdgeTypeId edge_type) {
+  MG_ASSERT(from->transaction_ == &transaction_,
+            "VertexAccessor must be from the same transaction as the storage "
+            "accessor when deleting a vertex!");
+  MG_ASSERT(to->transaction_ == &transaction_,
+            "VertexAccessor must be from the same transaction as the storage "
+            "accessor when deleting a vertex!");
+
+  auto *from_vertex = from->vertex_;
+  auto *to_vertex = to->vertex_;
+
+  // Obtain the locks by `gid` order to avoid lock cycles.
+  std::unique_lock<utils::SpinLock> guard_from(from_vertex->lock, std::defer_lock);
+  std::unique_lock<utils::SpinLock> guard_to(to_vertex->lock, std::defer_lock);
+  if (from_vertex->gid < to_vertex->gid) {
+    guard_from.lock();
+    guard_to.lock();
+  } else if (from_vertex->gid > to_vertex->gid) {
+    guard_to.lock();
+    guard_from.lock();
+  } else {
+    // The vertices are the same vertex, only lock one.
+    guard_from.lock();
+  }
+
+  if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
+  if (from_vertex->deleted) return Error::DELETED_OBJECT;
+
+  if (to_vertex != from_vertex) {
+    if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
+    if (to_vertex->deleted) return Error::DELETED_OBJECT;
+  }
+
+  auto *disk_storage = static_cast<DiskStorage *>(storage_);
+  auto gid = storage::Gid::FromUint(disk_storage->edge_id_.fetch_add(1, std::memory_order_acq_rel));
+  EdgeRef edge(gid);
+  if (config_.properties_on_edges) {
+    auto acc = edges_.access();
+    auto *delta = CreateDeleteObjectDelta(&transaction_);
+    auto [it, inserted] = acc.insert(Edge(gid, delta));
+    MG_ASSERT(inserted, "The edge must be inserted here!");
+    MG_ASSERT(it != acc.end(), "Invalid Edge accessor!");
+    edge = EdgeRef(&*it);
+    delta->prev.Set(&*it);
+  }
+
+  CreateAndLinkDelta(&transaction_, from_vertex, Delta::RemoveOutEdgeTag(), edge_type, to_vertex, edge);
+  from_vertex->out_edges.emplace_back(edge_type, to_vertex, edge);
+
+  CreateAndLinkDelta(&transaction_, to_vertex, Delta::RemoveInEdgeTag(), edge_type, from_vertex, edge);
+  to_vertex->in_edges.emplace_back(edge_type, from_vertex, edge);
+
+  // Increment edge count.
+  storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
+
+  return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
+                      &storage_->constraints_, config_);
+}
+
+Result<std::optional<EdgeAccessor>> DiskStorage::DiskAccessor::DeleteEdge(EdgeAccessor *edge) {
+  MG_ASSERT(edge->transaction_ == &transaction_,
+            "EdgeAccessor must be from the same transaction as the storage "
+            "accessor when deleting an edge!");
+  const auto edge_ref = edge->edge_;
+  const auto edge_type = edge->edge_type_;
+
+  std::unique_lock<utils::SpinLock> guard;
+  if (config_.properties_on_edges) {
+    const auto *edge_ptr = edge_ref.ptr;
+    guard = std::unique_lock<utils::SpinLock>(edge_ptr->lock);
+
+    if (!PrepareForWrite(&transaction_, edge_ptr)) return Error::SERIALIZATION_ERROR;
+
+    if (edge_ptr->deleted) return std::optional<EdgeAccessor>{};
+  }
+
+  auto *from_vertex = edge->from_vertex_;
+  auto *to_vertex = edge->to_vertex_;
+
+  // Obtain the locks by `gid` order to avoid lock cycles.
+  std::unique_lock<utils::SpinLock> guard_from(from_vertex->lock, std::defer_lock);
+  std::unique_lock<utils::SpinLock> guard_to(to_vertex->lock, std::defer_lock);
+  if (from_vertex->gid < to_vertex->gid) {
+    guard_from.lock();
+    guard_to.lock();
+  } else if (from_vertex->gid > to_vertex->gid) {
+    guard_to.lock();
+    guard_from.lock();
+  } else {
+    // The vertices are the same vertex, only lock one.
+    guard_from.lock();
+  }
+
+  if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
+  MG_ASSERT(!from_vertex->deleted, "Invalid database state!");
+
+  if (to_vertex != from_vertex) {
+    if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
+    MG_ASSERT(!to_vertex->deleted, "Invalid database state!");
+  }
+
+  auto delete_edge_from_storage = [&edge_type, &edge_ref, this](auto *vertex, auto *edges) {
+    const std::tuple<EdgeTypeId, Vertex *, EdgeRef> link(edge_type, vertex, edge_ref);
+    auto it = std::find(edges->begin(), edges->end(), link);
+    if (config_.properties_on_edges) {
+      MG_ASSERT(it != edges->end(), "Invalid database state!");
+    } else if (it == edges->end()) {
+      return false;
+    }
+    std::swap(*it, *edges->rbegin());
+    edges->pop_back();
+    return true;
+  };
+
+  const auto op1 = delete_edge_from_storage(to_vertex, &from_vertex->out_edges);
+  const auto op2 = delete_edge_from_storage(from_vertex, &to_vertex->in_edges);
+
+  const std::string src_dest_del_key{
+      utils::SerializeEdge(from_vertex->gid, to_vertex->gid, edge_type, edge_ref, config_.properties_on_edges)};
+  edges_to_delete_.emplace_back(src_dest_del_key);
+
+  if (config_.properties_on_edges) {
+    MG_ASSERT((op1 && op2), "Invalid database state!");
+  } else {
+    MG_ASSERT((op1 && op2) || (!op1 && !op2), "Invalid database state!");
+    if (!op1 && !op2) {
+      // The edge is already deleted.
+      return std::optional<EdgeAccessor>{};
+    }
+  }
+
+  if (config_.properties_on_edges) {
+    auto *edge_ptr = edge_ref.ptr;
+    CreateAndLinkDelta(&transaction_, edge_ptr, Delta::RecreateObjectTag());
+    edge_ptr->deleted = true;
+  }
+
+  CreateAndLinkDelta(&transaction_, from_vertex, Delta::AddOutEdgeTag(), edge_type, to_vertex, edge_ref);
+  CreateAndLinkDelta(&transaction_, to_vertex, Delta::AddInEdgeTag(), edge_type, from_vertex, edge_ref);
+
+  // Decrement edge count.
+  storage_->edge_count_.fetch_add(-1, std::memory_order_acq_rel);
+
+  return std::make_optional<EdgeAccessor>(edge_ref, edge_type, from_vertex, to_vertex, &transaction_,
+                                          &storage_->indices_, &storage_->constraints_, config_, true);
+}
+
+/// TODO: at which storage naming
+/// TODO: this method should also delete the old key
+bool DiskStorage::DiskAccessor::WriteVertexToDisk(const Vertex &vertex) {
+  auto *disk_storage = static_cast<DiskStorage *>(storage_);
+  auto status = disk_transaction_->Put(disk_storage->kvstore_->vertex_chandle, utils::SerializeVertex(vertex),
+                                       utils::SerializeProperties(vertex.properties));
+  if (status.ok()) {
+    spdlog::debug("rocksdb: Saved vertex with key {} and ts {}", utils::SerializeVertex(vertex), *commit_timestamp_);
+  } else if (status.IsBusy()) {
+    spdlog::error("rocksdb: Vertex with key {} and ts {} was changed and committed in another transaction",
+                  utils::SerializeVertex(vertex), *commit_timestamp_);
+    return false;
+  } else {
+    spdlog::error("rocksdb: Failed to save vertex with key {} and ts {}", utils::SerializeVertex(vertex),
+                  *commit_timestamp_);
+    return false;
+  }
+  return true;
+}
+
+/// TODO: at which storage naming
+bool DiskStorage::DiskAccessor::WriteEdgeToDisk(const EdgeRef edge, const std::string &serializedEdgeKey) {
+  auto *disk_storage = static_cast<DiskStorage *>(storage_);
+  rocksdb::Status status;
+  if (config_.properties_on_edges) {
+    status = disk_transaction_->Put(disk_storage->kvstore_->edge_chandle, serializedEdgeKey,
+                                    utils::SerializeProperties(edge.ptr->properties));
+  } else {
+    status = disk_transaction_->Put(disk_storage->kvstore_->edge_chandle, serializedEdgeKey, "");
+  }
+  if (status.ok()) {
+    spdlog::debug("rocksdb: Saved edge with key {} and ts {}", serializedEdgeKey, *commit_timestamp_);
+  } else if (status.IsBusy()) {
+    spdlog::error("rocksdb: Edge with key {} and ts {} was changed and committed in another transaction",
+                  serializedEdgeKey, *commit_timestamp_);
+    return false;
+  } else {
+    spdlog::error("rocksdb: Failed to save edge with key {} and ts {}", serializedEdgeKey, *commit_timestamp_);
+    return false;
+  }
+  return true;
+}
+
+bool DiskStorage::DiskAccessor::DeleteVertexFromDisk(const std::string &vertex) {
+  auto *disk_storage = static_cast<DiskStorage *>(storage_);
+  auto status = disk_transaction_->Delete(disk_storage->kvstore_->vertex_chandle, vertex);
+  if (status.ok()) {
+    spdlog::debug("rocksdb: Deleted vertex with key {}", vertex);
+  } else if (status.IsBusy()) {
+    spdlog::error("rocksdb: Vertex with key {} was changed and committed in another transaction", vertex);
+    return false;
+  } else {
+    spdlog::error("rocksdb: Failed to delete vertex with key {}", vertex);
+    return false;
+  }
+  return true;
+}
+
+bool DiskStorage::DiskAccessor::DeleteEdgeFromDisk(const std::string &edge) {
+  auto *disk_storage = static_cast<DiskStorage *>(storage_);
+  auto status = disk_transaction_->Delete(disk_storage->kvstore_->edge_chandle, edge);
+  if (status.ok()) {
+    spdlog::debug("rocksdb: Deleted edge with key {}", edge);
+  } else if (status.IsBusy()) {
+    spdlog::error("rocksdb: Edge with key {} was changed and committed in another transaction", edge);
+    return false;
+  } else {
+    spdlog::error("rocksdb: Failed to delete edge with key {}", edge);
+    return false;
+  }
+  return true;
+}
+
+[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void>
+DiskStorage::DiskAccessor::CheckVertexConstraintsBeforeCommit(
+    const Vertex &vertex, std::vector<std::vector<PropertyValue>> &unique_storage) const {
+  if (auto existence_constraint_validation_result = storage_->constraints_.existence_constraints_->Validate(vertex);
+      existence_constraint_validation_result.has_value()) {
+    return StorageDataManipulationError{existence_constraint_validation_result.value()};
+  }
+
+  auto *disk_unique_constraints =
+      static_cast<DiskUniqueConstraints *>(storage_->constraints_.unique_constraints_.get());
+  if (auto unique_constraint_validation_result = disk_unique_constraints->Validate(vertex, unique_storage);
+      unique_constraint_validation_result.has_value()) {
+    return StorageDataManipulationError{unique_constraint_validation_result.value()};
+  }
+  return {};
+}
+
+[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> DiskStorage::DiskAccessor::FlushMainMemoryCache() {
+  auto vertex_acc = vertices_.access();
+
+  std::vector<std::vector<PropertyValue>> unique_storage;
+  auto *disk_unique_constraints =
+      static_cast<DiskUniqueConstraints *>(storage_->constraints_.unique_constraints_.get());
+  auto *disk_label_index = static_cast<DiskLabelIndex *>(storage_->indices_.label_index_.get());
+  auto *disk_label_property_index =
+      static_cast<DiskLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
+
+  /// TODO: andi I don't like that std::optional is used for checking errors but that's how it was before, refactor!
+  for (Vertex &vertex : vertex_acc) {
+    if (auto check_result = CheckVertexConstraintsBeforeCommit(vertex, unique_storage); check_result.HasError()) {
+      return check_result.GetError();
+    }
+
+    /// TODO: what if something is changed and then deleted
+    if (vertex.deleted) {
+      continue;
+    }
+
+    /// TODO: expose temporal coupling
+    /// NOTE: this deletion has to come before writing, otherwise RocksDB thinks that all entries are deleted
+    /// TODO: This has to deal with index storage if read from index cache
+    if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(vertex.delta); maybe_old_disk_key.has_value()) {
+      if (!DeleteVertexFromDisk(maybe_old_disk_key.value())) {
+        return StorageDataManipulationError{SerializationError{}};
+      }
+    }
+
+    if (!WriteVertexToDisk(vertex)) {
+      return StorageDataManipulationError{SerializationError{}};
+    }
+
+    /// TODO: andi don't ignore the return value
+    if (!disk_unique_constraints->SyncVertexToUniqueConstraintsStorage(vertex, *commit_timestamp_) ||
+        !disk_label_index->SyncVertexToLabelIndexStorage(vertex, *commit_timestamp_) ||
+        !disk_label_property_index->SyncVertexToLabelPropertyIndexStorage(vertex, *commit_timestamp_)) {
+      return StorageDataManipulationError{SerializationError{}};
+    }
+
+    for (auto &edge_entry : vertex.out_edges) {
+      EdgeRef edge = std::get<2>(edge_entry);
+      auto src_dest_key = utils::SerializeEdge(vertex.gid, std::get<1>(edge_entry)->gid, std::get<0>(edge_entry), edge,
+                                               config_.properties_on_edges);
+
+      /// TODO: expose temporal coupling
+      /// NOTE: this deletion has to come before writing, otherwise RocksDB thinks that all entries are deleted
+      if (config_.properties_on_edges) {
+        if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(edge.ptr->delta); maybe_old_disk_key.has_value()) {
+          if (!DeleteEdgeFromDisk(maybe_old_disk_key.value())) {
+            return StorageDataManipulationError{SerializationError{}};
+          }
+        }
+      }
+
+      if (!WriteEdgeToDisk(edge, src_dest_key)) {
+        return StorageDataManipulationError{SerializationError{}};
+      }
+
+      /// TODO: what if edge has already been deleted
+    }
+  }
+
+  for (const auto &[vertex_gid, serialized_vertex_to_delete] : vertices_to_delete_) {
+    if (!DeleteVertexFromDisk(serialized_vertex_to_delete) ||
+        !disk_unique_constraints->ClearDeletedVertex(vertex_gid, *commit_timestamp_) ||
+        !disk_label_index->ClearDeletedVertex(vertex_gid, *commit_timestamp_) ||
+        !disk_label_property_index->ClearDeletedVertex(vertex_gid, *commit_timestamp_)) {
+      return StorageDataManipulationError{SerializationError{}};
+    }
+  }
+
+  for (const auto &edge_to_delete : edges_to_delete_) {
+    if (!DeleteEdgeFromDisk(edge_to_delete)) {
+      return StorageDataManipulationError{SerializationError{}};
+    }
+  }
+
+  if (!disk_unique_constraints->DeleteVerticesWithRemovedConstraintLabel(transaction_.start_timestamp,
+                                                                         *commit_timestamp_) ||
+      !disk_label_index->DeleteVerticesWithRemovedIndexingLabel(transaction_.start_timestamp, *commit_timestamp_) ||
+      !disk_label_property_index->DeleteVerticesWithRemovedIndexingLabel(transaction_.start_timestamp,
+                                                                         *commit_timestamp_)) {
+    return StorageDataManipulationError{SerializationError{}};
+  }
+
+  return {};
+}
+
+/// TODO: I think unique_storage is not needed here
+[[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> DiskStorage::DiskAccessor::FlushIndexCache() {
+  std::vector<std::vector<PropertyValue>> unique_storage;
+  auto *disk_unique_constraints =
+      static_cast<DiskUniqueConstraints *>(storage_->constraints_.unique_constraints_.get());
+  auto *disk_label_index = static_cast<DiskLabelIndex *>(storage_->indices_.label_index_.get());
+  auto *disk_label_property_index =
+      static_cast<DiskLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
+
+  for (const auto &vec : index_storage_) {
+    auto vertex_acc = vec->access();
+    for (Vertex &vertex : vertex_acc) {
+      if (auto check_result = CheckVertexConstraintsBeforeCommit(vertex, unique_storage); check_result.HasError()) {
+        return check_result.GetError();
+      }
+
+      /// TODO: what if something is changed and then deleted
+      if (vertex.deleted) {
+        continue;
+      }
+
+      if (!WriteVertexToDisk(vertex)) {
+        return StorageDataManipulationError{SerializationError{}};
+      }
+
+      /// TODO: andi don't ignore the return value
+      if (!disk_unique_constraints->SyncVertexToUniqueConstraintsStorage(vertex, *commit_timestamp_) ||
+          !disk_label_index->SyncVertexToLabelIndexStorage(vertex, *commit_timestamp_) ||
+          !disk_label_property_index->SyncVertexToLabelPropertyIndexStorage(vertex, *commit_timestamp_)) {
+        return StorageDataManipulationError{SerializationError{}};
+      }
+
+      for (auto &edge_entry : vertex.out_edges) {
+        EdgeRef edge = std::get<2>(edge_entry);
+        auto src_dest_key = utils::SerializeEdge(vertex.gid, std::get<1>(edge_entry)->gid, std::get<0>(edge_entry),
+                                                 edge, config_.properties_on_edges);
+
+        if (!WriteEdgeToDisk(edge, src_dest_key)) {
+          return StorageDataManipulationError{SerializationError{}};
+        }
+
+        /// TODO: what if edge has already been deleted
+      }
+    }
+  }
+
+  return {};
+}
+
+[[nodiscard]] std::optional<ConstraintViolation> DiskStorage::CheckExistingVerticesBeforeCreatingExistenceConstraint(
+    LabelId label, PropertyId property) const {
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto it = std::unique_ptr<rocksdb::Iterator>(kvstore_->db_->NewIterator(ro, kvstore_->vertex_chandle));
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    std::vector<LabelId> labels = utils::DeserializeLabelsFromMainDiskStorage(it->key().ToString());
+    PropertyStore properties = utils::DeserializePropertiesFromMainDiskStorage(it->value().ToStringView());
+    if (utils::Contains(labels, label) && !properties.HasProperty(property)) {
+      return ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label, std::set<PropertyId>{property}};
+    }
+  }
+  return std::nullopt;
+}
+
+[[nodiscard]] utils::BasicResult<ConstraintViolation, std::vector<std::pair<std::string, std::string>>>
+DiskStorage::CheckExistingVerticesBeforeCreatingUniqueConstraint(LabelId label,
+                                                                 const std::set<PropertyId> &properties) const {
+  std::set<std::vector<PropertyValue>> unique_storage;
+  std::vector<std::pair<std::string, std::string>> vertices_for_constraints;
+
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto it = std::unique_ptr<rocksdb::Iterator>(kvstore_->db_->NewIterator(ro, kvstore_->vertex_chandle));
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    const std::string key_str = it->key().ToString();
+    std::vector<LabelId> labels = utils::DeserializeLabelsFromMainDiskStorage(key_str);
+    PropertyStore property_store = utils::DeserializePropertiesFromMainDiskStorage(it->value().ToStringView());
+    if (utils::Contains(labels, label) && property_store.HasAllProperties(properties)) {
+      if (auto target_property_values = property_store.ExtractPropertyValues(properties);
+          target_property_values.has_value() && !utils::Contains(unique_storage, *target_property_values)) {
+        unique_storage.insert(*target_property_values);
+        vertices_for_constraints.emplace_back(
+            utils::SerializeVertexAsKeyForUniqueConstraint(label, properties, utils::ExtractGidFromKey(key_str)),
+            utils::SerializeVertexAsValueForUniqueConstraint(label, labels, property_store));
+      } else {
+        return ConstraintViolation{ConstraintViolation::Type::UNIQUE, label, properties};
+      }
+    }
+  }
+  return vertices_for_constraints;
+}
+
+// NOLINTNEXTLINE(google-default-arguments)
+utils::BasicResult<StorageDataManipulationError, void> DiskStorage::DiskAccessor::Commit(
+    const std::optional<uint64_t> desired_commit_timestamp) {
+  MG_ASSERT(is_transaction_active_, "The transaction is already terminated!");
+  MG_ASSERT(!transaction_.must_abort, "The transaction can't be committed!");
+
+  auto *disk_storage = static_cast<DiskStorage *>(storage_);
+
+  if (transaction_.deltas.empty() ||
+      std::all_of(transaction_.deltas.begin(), transaction_.deltas.end(),
+                  [](const Delta &delta) { return delta.action == Delta::Action::DELETE_DESERIALIZED_OBJECT; })) {
+  } else {
+    std::unique_lock<utils::SpinLock> engine_guard(storage_->engine_lock_);
+    commit_timestamp_.emplace(disk_storage->CommitTimestamp(desired_commit_timestamp));
+
+    if (auto res = FlushMainMemoryCache(); res.HasError()) {
+      Abort();
+      return res;
+    }
+
+    if (auto res = FlushIndexCache(); res.HasError()) {
+      Abort();
+      return res;
+    }
+  }
+
+  if (commit_timestamp_) {
+    // commit_timestamp_ is set only if the transaction has writes.
+    logging::AssertRocksDBStatus(disk_transaction_->SetCommitTimestamp(*commit_timestamp_));
+  }
+  auto commitStatus = disk_transaction_->Commit();
+  delete disk_transaction_;
+  disk_transaction_ = nullptr;
+  if (!commitStatus.ok()) {
+    spdlog::error("rocksdb: Commit failed with status {}", commitStatus.ToString());
+    return StorageDataManipulationError{SerializationError{}};
+  }
+  spdlog::debug("rocksdb: Commit successful");
+
+  is_transaction_active_ = false;
+
+  return {};
+}
+
+std::vector<std::pair<std::string, std::string>> DiskStorage::SerializeVerticesForLabelIndex(LabelId label) {
+  std::vector<std::pair<std::string, std::string>> vertices_to_be_indexed;
+
+  rocksdb::ReadOptions ro;
+  auto strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto it = std::unique_ptr<rocksdb::Iterator>(kvstore_->db_->NewIterator(ro, kvstore_->vertex_chandle));
+
+  const std::string serialized_label = utils::SerializeIdType(label);
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    const std::string key_str = it->key().ToString();
+    if (const std::vector<std::string> labels_str = utils::ExtractLabelsFromMainDiskStorage(key_str);
+        utils::Contains(labels_str, serialized_label)) {
+      std::vector<LabelId> labels = utils::DeserializeLabelsFromMainDiskStorage(key_str);
+      PropertyStore property_store = utils::DeserializePropertiesFromMainDiskStorage(it->value().ToStringView());
+      vertices_to_be_indexed.emplace_back(
+          utils::SerializeVertexAsKeyForLabelIndex(utils::SerializeIdType(label),
+                                                   utils::ExtractGidFromMainDiskStorage(key_str)),
+          utils::SerializeVertexAsValueForLabelIndex(label, labels, property_store));
+    }
+  }
+  return vertices_to_be_indexed;
+}
+
+std::vector<std::pair<std::string, std::string>> DiskStorage::SerializeVerticesForLabelPropertyIndex(
+    LabelId label, PropertyId property) {
+  std::vector<std::pair<std::string, std::string>> vertices_to_be_indexed;
+
+  rocksdb::ReadOptions ro;
+  auto strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto it = std::unique_ptr<rocksdb::Iterator>(kvstore_->db_->NewIterator(ro, kvstore_->vertex_chandle));
+
+  const std::string serialized_label = utils::SerializeIdType(label);
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    const std::string key_str = it->key().ToString();
+    PropertyStore property_store = utils::DeserializePropertiesFromMainDiskStorage(it->value().ToString());
+    if (const std::vector<std::string> labels_str = utils::ExtractLabelsFromMainDiskStorage(key_str);
+        utils::Contains(labels_str, serialized_label) && property_store.HasProperty(property)) {
+      std::vector<LabelId> labels = utils::DeserializeLabelsFromMainDiskStorage(key_str);
+      vertices_to_be_indexed.emplace_back(
+          utils::SerializeVertexAsKeyForLabelPropertyIndex(utils::SerializeIdType(label),
+                                                           utils::SerializeIdType(property),
+                                                           utils::ExtractGidFromMainDiskStorage(key_str)),
+          utils::SerializeVertexAsValueForLabelPropertyIndex(label, labels, property_store));
+    }
+  }
+  return vertices_to_be_indexed;
+}
+
+/// TODO: what to do with all that?
+void DiskStorage::DiskAccessor::Abort() {
+  MG_ASSERT(is_transaction_active_, "The transaction is already terminated!");
+  // NOTE: On abort we need to delete disk transaction because after storage remove we couldn't remove
+  // disk_transaction correctly in destructor.
+  // This happens in tests when we create and remove storage in one test. For example, in
+  // query_plan_accumulate_aggregate.cpp
+  disk_transaction_->Rollback();
+  disk_transaction_->ClearSnapshot();
+  delete disk_transaction_;
+  disk_transaction_ = nullptr;
+
+  is_transaction_active_ = false;
+}
+
+void DiskStorage::DiskAccessor::FinalizeTransaction() {
+  if (commit_timestamp_) {
+    commit_timestamp_.reset();
+  }
+}
+
+utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::CreateIndex(
+    LabelId label, const std::optional<uint64_t> /*desired_commit_timestamp*/) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+
+  auto *disk_label_index = static_cast<DiskLabelIndex *>(indices_.label_index_.get());
+  if (!disk_label_index->CreateIndex(label, SerializeVerticesForLabelIndex(label))) {
+    return StorageIndexDefinitionError{IndexDefinitionError{}};
+  }
+
+  if (!PersistLabelIndexCreation(label)) {
+    return StorageIndexDefinitionError{IndexPersistenceError{}};
+  }
+
+  // We don't care if there is a replication error because on main node the change will go through
+  memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveLabelIndices);
+
+  return {};
+}
+
+utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::CreateIndex(
+    LabelId label, PropertyId property, const std::optional<uint64_t> /*desired_commit_timestamp*/) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+
+  auto *disk_label_property_index = static_cast<DiskLabelPropertyIndex *>(indices_.label_property_index_.get());
+  if (!disk_label_property_index->CreateIndex(label, property,
+                                              SerializeVerticesForLabelPropertyIndex(label, property))) {
+    return StorageIndexDefinitionError{IndexDefinitionError{}};
+  }
+
+  if (!PersistLabelPropertyIndexAndExistenceConstraintCreation(label, property, label_property_index_str)) {
+    return StorageIndexDefinitionError{IndexPersistenceError{}};
+  }
+
+  // We don't care if there is a replication error because on main node the change will go through
+  memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveLabelPropertyIndices);
+
+  return {};
+}
+
+utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::DropIndex(
+    LabelId label, const std::optional<uint64_t> /*desired_commit_timestamp*/) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+
+  if (!indices_.label_index_->DropIndex(label)) {
+    return StorageIndexDefinitionError{IndexDefinitionError{}};
+  }
+
+  if (!PersistLabelIndexDeletion(label)) {
+    return StorageIndexDefinitionError{IndexPersistenceError{}};
+  }
+
+  // We don't care if there is a replication error because on main node the change will go through
+  memgraph::metrics::DecrementCounter(memgraph::metrics::ActiveLabelIndices);
+
+  return {};
+}
+
+utils::BasicResult<StorageIndexDefinitionError, void> DiskStorage::DropIndex(
+    LabelId label, PropertyId property, const std::optional<uint64_t> /*desired_commit_timestamp*/) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+
+  if (!indices_.label_property_index_->DropIndex(label, property)) {
+    return StorageIndexDefinitionError{IndexDefinitionError{}};
+  }
+
+  if (!PersistLabelPropertyIndexAndExistenceConstraintDeletion(label, property, label_property_index_str)) {
+    return StorageIndexDefinitionError{IndexPersistenceError{}};
+  }
+
+  // We don't care if there is a replication error because on main node the change will go through
+  memgraph::metrics::DecrementCounter(memgraph::metrics::ActiveLabelPropertyIndices);
+
+  return {};
+}
+
+utils::BasicResult<StorageExistenceConstraintDefinitionError, void> DiskStorage::CreateExistenceConstraint(
+    LabelId label, PropertyId property, const std::optional<uint64_t> /*desired_commit_timestamp*/) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+
+  if (constraints_.existence_constraints_->ConstraintExists(label, property)) {
+    return StorageExistenceConstraintDefinitionError{ConstraintDefinitionError{}};
+  }
+
+  if (auto check = CheckExistingVerticesBeforeCreatingExistenceConstraint(label, property); check.has_value()) {
+    return StorageExistenceConstraintDefinitionError{check.value()};
+  }
+
+  constraints_.existence_constraints_->InsertConstraint(label, property);
+
+  if (!PersistLabelPropertyIndexAndExistenceConstraintCreation(label, property, existence_constraints_str)) {
+    return StorageExistenceConstraintDefinitionError{ConstraintsPersistenceError{}};
+  }
+
+  return {};
+}
+
+utils::BasicResult<StorageExistenceConstraintDroppingError, void> DiskStorage::DropExistenceConstraint(
+    LabelId label, PropertyId property, const std::optional<uint64_t> /*desired_commit_timestamp*/) {
+  if (!constraints_.existence_constraints_->DropConstraint(label, property)) {
+    return StorageExistenceConstraintDroppingError{ConstraintDefinitionError{}};
+  }
+
+  if (!PersistLabelPropertyIndexAndExistenceConstraintDeletion(label, property, existence_constraints_str)) {
+    return StorageExistenceConstraintDroppingError{ConstraintsPersistenceError{}};
+  }
+
+  return {};
+}
+
+utils::BasicResult<StorageUniqueConstraintDefinitionError, UniqueConstraints::CreationStatus>
+DiskStorage::CreateUniqueConstraint(LabelId label, const std::set<PropertyId> &properties,
+                                    const std::optional<uint64_t> /*desired_commit_timestamp*/) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+
+  auto *disk_unique_constraints = static_cast<DiskUniqueConstraints *>(constraints_.unique_constraints_.get());
+
+  if (auto constraint_check = disk_unique_constraints->CheckIfConstraintCanBeCreated(label, properties);
+      constraint_check != UniqueConstraints::CreationStatus::SUCCESS) {
+    return constraint_check;
+  }
+
+  auto check = CheckExistingVerticesBeforeCreatingUniqueConstraint(label, properties);
+  if (check.HasError()) {
+    return StorageUniqueConstraintDefinitionError{check.GetError()};
+  }
+
+  if (!disk_unique_constraints->InsertConstraint(label, properties, check.GetValue())) {
+    return StorageUniqueConstraintDefinitionError{ConstraintDefinitionError{}};
+  }
+
+  if (!PersistUniqueConstraintCreation(label, properties)) {
+    return StorageUniqueConstraintDefinitionError{ConstraintsPersistenceError{}};
+  }
+
+  return UniqueConstraints::CreationStatus::SUCCESS;
+}
+
+utils::BasicResult<StorageUniqueConstraintDroppingError, UniqueConstraints::DeletionStatus>
+DiskStorage::DropUniqueConstraint(LabelId label, const std::set<PropertyId> &properties,
+                                  const std::optional<uint64_t> /*desired_commit_timestamp*/) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+  auto ret = constraints_.unique_constraints_->DropConstraint(label, properties);
+  if (ret != UniqueConstraints::DeletionStatus::SUCCESS) {
+    return ret;
+  }
+
+  if (!PersistUniqueConstraintDeletion(label, properties)) {
+    return StorageUniqueConstraintDroppingError{ConstraintsPersistenceError{}};
+  }
+
+  return UniqueConstraints::DeletionStatus::SUCCESS;
+}
+
+Transaction DiskStorage::CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) {
+  /// We acquire the transaction engine lock here because we access (and
+  /// modify) the transaction engine variables (`transaction_id` and
+  /// `timestamp`) below.
+  uint64_t transaction_id = 0;
+  uint64_t start_timestamp = 0;
+  {
+    std::lock_guard<utils::SpinLock> guard(engine_lock_);
+    transaction_id = transaction_id_++;
+    /// TODO: when we introduce replication to the disk storage, take care of start_timestamp
+    start_timestamp = timestamp_++;
+  }
+  return {transaction_id, start_timestamp, isolation_level, storage_mode};
+}
+
+uint64_t DiskStorage::CommitTimestamp(const std::optional<uint64_t> desired_commit_timestamp) {
+  if (!desired_commit_timestamp) {
+    return timestamp_++;
+  }
+  timestamp_ = std::max(timestamp_, *desired_commit_timestamp + 1);
+  return *desired_commit_timestamp;
+}
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/disk/storage.hpp b/src/storage/v2/disk/storage.hpp
new file mode 100644
index 000000000..f05dc00a7
--- /dev/null
+++ b/src/storage/v2/disk/storage.hpp
@@ -0,0 +1,303 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include "kvstore/kvstore.hpp"
+#include "storage/v2/constraints/constraint_violation.hpp"
+#include "storage/v2/disk/rocksdb_storage.hpp"
+#include "storage/v2/id_types.hpp"
+#include "storage/v2/isolation_level.hpp"
+#include "storage/v2/property_store.hpp"
+#include "storage/v2/storage.hpp"
+#include "utils/rw_lock.hpp"
+
+#include <rocksdb/db.h>
+
+namespace memgraph::storage {
+
+class DiskStorage final : public Storage {
+ public:
+  explicit DiskStorage(Config config = Config());
+
+  DiskStorage(const DiskStorage &) = delete;
+  DiskStorage(DiskStorage &&) = delete;
+  DiskStorage &operator=(const DiskStorage &) = delete;
+  DiskStorage &operator=(DiskStorage &&) = delete;
+
+  ~DiskStorage() override;
+
+  class DiskAccessor final : public Storage::Accessor {
+   private:
+    friend class DiskStorage;
+
+    explicit DiskAccessor(DiskStorage *storage, IsolationLevel isolation_level, StorageMode storage_mode);
+
+   public:
+    DiskAccessor(const DiskAccessor &) = delete;
+    DiskAccessor &operator=(const DiskAccessor &) = delete;
+    DiskAccessor &operator=(DiskAccessor &&other) = delete;
+
+    DiskAccessor(DiskAccessor &&other) noexcept;
+
+    ~DiskAccessor() override;
+
+    VertexAccessor CreateVertex() override;
+
+    std::optional<VertexAccessor> FindVertex(Gid gid, View view) override;
+
+    VerticesIterable Vertices(View view) override;
+
+    VerticesIterable Vertices(LabelId label, View view) override;
+
+    VerticesIterable Vertices(LabelId label, PropertyId property, View view) override;
+
+    VerticesIterable Vertices(LabelId label, PropertyId property, const PropertyValue &value, View view) override;
+
+    VerticesIterable Vertices(LabelId label, PropertyId property,
+                              const std::optional<utils::Bound<PropertyValue>> &lower_bound,
+                              const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) override;
+
+    uint64_t ApproximateVertexCount() const override;
+
+    uint64_t ApproximateVertexCount(LabelId /*label*/) const override { return 10; }
+
+    uint64_t ApproximateVertexCount(LabelId /*label*/, PropertyId /*property*/) const override { return 10; }
+
+    uint64_t ApproximateVertexCount(LabelId /*label*/, PropertyId /*property*/,
+                                    const PropertyValue & /*value*/) const override {
+      return 10;
+    }
+
+    uint64_t ApproximateVertexCount(LabelId /*label*/, PropertyId /*property*/,
+                                    const std::optional<utils::Bound<PropertyValue>> & /*lower*/,
+                                    const std::optional<utils::Bound<PropertyValue>> & /*upper*/) const override {
+      return 10;
+    }
+
+    std::optional<storage::LabelIndexStats> GetIndexStats(const storage::LabelId & /*label*/) const override {
+      return {};
+    }
+
+    std::optional<storage::LabelPropertyIndexStats> GetIndexStats(
+        const storage::LabelId & /*label*/, const storage::PropertyId & /*property*/) const override {
+      return {};
+    }
+
+    std::vector<LabelId> ClearLabelIndexStats() override {
+      throw utils::NotYetImplemented("ClearIndexStats() is not implemented for DiskStorage.");
+    }
+
+    std::vector<std::pair<LabelId, PropertyId>> ClearLabelPropertyIndexStats() override {
+      throw utils::NotYetImplemented("ClearIndexStats() is not implemented for DiskStorage.");
+    }
+
+    std::vector<LabelId> DeleteLabelIndexStats(std::span<std::string> /*labels*/) override {
+      throw utils::NotYetImplemented("DeleteIndexStatsForLabels(labels) is not implemented for DiskStorage.");
+    }
+
+    std::vector<std::pair<LabelId, PropertyId>> DeleteLabelPropertyIndexStats(
+        const std::span<std::string> /*labels*/) override {
+      throw utils::NotYetImplemented("DeleteIndexStatsForLabels(labels) is not implemented for DiskStorage.");
+    }
+
+    void SetIndexStats(const storage::LabelId & /*label*/, const LabelIndexStats & /*stats*/) override {
+      throw utils::NotYetImplemented("SetIndexStats(stats) is not implemented for DiskStorage.");
+    }
+
+    void SetIndexStats(const storage::LabelId & /*label*/, const storage::PropertyId & /*property*/,
+                       const LabelPropertyIndexStats & /*stats*/) override {
+      throw utils::NotYetImplemented("SetIndexStats(stats) is not implemented for DiskStorage.");
+    }
+
+    /// TODO: It is just marked as deleted but the memory isn't reclaimed because of the in-memory storage
+    Result<std::optional<VertexAccessor>> DeleteVertex(VertexAccessor *vertex) override;
+
+    Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachDeleteVertex(
+        VertexAccessor *vertex) override;
+
+    void PrefetchInEdges(const VertexAccessor &vertex_acc) override;
+
+    void PrefetchOutEdges(const VertexAccessor &vertex_acc) override;
+
+    Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type) override;
+
+    Result<std::optional<EdgeAccessor>> DeleteEdge(EdgeAccessor *edge) override;
+
+    bool LabelIndexExists(LabelId label) const override {
+      auto *disk_storage = static_cast<DiskStorage *>(storage_);
+      return disk_storage->indices_.label_index_->IndexExists(label);
+    }
+
+    bool LabelPropertyIndexExists(LabelId label, PropertyId property) const override {
+      auto *disk_storage = static_cast<DiskStorage *>(storage_);
+      return disk_storage->indices_.label_property_index_->IndexExists(label, property);
+    }
+
+    IndicesInfo ListAllIndices() const override {
+      auto *disk_storage = static_cast<DiskStorage *>(storage_);
+      return disk_storage->ListAllIndices();
+    }
+
+    ConstraintsInfo ListAllConstraints() const override {
+      auto *disk_storage = static_cast<DiskStorage *>(storage_);
+      return disk_storage->ListAllConstraints();
+    }
+
+    // NOLINTNEXTLINE(google-default-arguments)
+    utils::BasicResult<StorageDataManipulationError, void> Commit(
+        std::optional<uint64_t> desired_commit_timestamp = {}) override;
+
+    void Abort() override;
+
+    void FinalizeTransaction() override;
+
+    std::optional<storage::VertexAccessor> LoadVertexToLabelIndexCache(
+        const rocksdb::Slice &key, const rocksdb::Slice &value, Delta *index_delta,
+        utils::SkipList<storage::Vertex>::Accessor index_accessor);
+
+    std::optional<storage::VertexAccessor> LoadVertexToMainMemoryCache(const rocksdb::Slice &key,
+                                                                       const rocksdb::Slice &value);
+
+    std::optional<storage::VertexAccessor> LoadVertexToLabelPropertyIndexCache(
+        const rocksdb::Slice &key, const rocksdb::Slice &value, Delta *index_delta,
+        utils::SkipList<storage::Vertex>::Accessor index_accessor);
+
+    std::optional<storage::EdgeAccessor> DeserializeEdge(const rocksdb::Slice &key, const rocksdb::Slice &value);
+
+   private:
+    VertexAccessor CreateVertex(utils::SkipList<Vertex>::Accessor &accessor, storage::Gid gid,
+                                const std::vector<LabelId> &label_ids, PropertyStore &&properties, Delta *delta);
+
+    void PrefetchEdges(const auto &prefetch_edge_filter);
+
+    Result<EdgeAccessor> CreateEdge(const VertexAccessor *from, const VertexAccessor *to, EdgeTypeId edge_type,
+                                    storage::Gid gid, std::string_view properties, const std::string &old_disk_key);
+
+    /// Flushes vertices and edges to the disk with the commit timestamp.
+    /// At the time of calling, the commit_timestamp_ must already exist.
+    /// After this method, the vertex and edge caches are cleared.
+    [[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> FlushMainMemoryCache();
+
+    [[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> FlushIndexCache();
+
+    [[nodiscard]] utils::BasicResult<StorageDataManipulationError, void> CheckVertexConstraintsBeforeCommit(
+        const Vertex &vertex, std::vector<std::vector<PropertyValue>> &unique_storage) const;
+
+    bool WriteVertexToDisk(const Vertex &vertex);
+    bool WriteEdgeToDisk(EdgeRef edge, const std::string &serializedEdgeKey);
+    bool DeleteVertexFromDisk(const std::string &vertex);
+    bool DeleteEdgeFromDisk(const std::string &edge);
+
+    /// Main storage
+    utils::SkipList<storage::Vertex> vertices_;
+    std::vector<std::unique_ptr<utils::SkipList<storage::Vertex>>> index_storage_;
+
+    /// We need them because query context for indexed reading is cleared after the query is done not after the
+    /// transaction is done
+    std::vector<std::list<Delta>> index_deltas_storage_;
+    utils::SkipList<storage::Edge> edges_;
+    Config::Items config_;
+    std::vector<std::string> edges_to_delete_;
+    std::vector<std::pair<std::string, std::string>> vertices_to_delete_;
+    rocksdb::Transaction *disk_transaction_;
+  };
+
+  std::unique_ptr<Storage::Accessor> Access(std::optional<IsolationLevel> override_isolation_level) override {
+    auto isolation_level = override_isolation_level.value_or(isolation_level_);
+    if (isolation_level != IsolationLevel::SNAPSHOT_ISOLATION) {
+      throw utils::NotYetImplemented("Disk storage supports only SNAPSHOT isolation level.");
+    }
+    return std::unique_ptr<DiskAccessor>(new DiskAccessor{this, isolation_level, storage_mode_});
+  }
+
+  RocksDBStorage *GetRocksDBStorage() const { return kvstore_.get(); }
+
+  utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(
+      LabelId label, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(
+      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(
+      LabelId label, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(
+      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  utils::BasicResult<StorageExistenceConstraintDefinitionError, void> CreateExistenceConstraint(
+      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  utils::BasicResult<StorageExistenceConstraintDroppingError, void> DropExistenceConstraint(
+      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  utils::BasicResult<StorageUniqueConstraintDefinitionError, UniqueConstraints::CreationStatus> CreateUniqueConstraint(
+      LabelId label, const std::set<PropertyId> &properties, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  utils::BasicResult<StorageUniqueConstraintDroppingError, UniqueConstraints::DeletionStatus> DropUniqueConstraint(
+      LabelId label, const std::set<PropertyId> &properties, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  Transaction CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) override;
+
+ private:
+  void LoadIndexInfoIfExists() const;
+
+  /// TODO (andi): Maybe good to separate these methods and durability kvstore into a separate class
+  bool PersistLabelIndexCreation(LabelId label) const;
+
+  bool PersistLabelIndexDeletion(LabelId label) const;
+
+  void LoadLabelIndexInfoIfExists() const;
+
+  bool PersistLabelPropertyIndexAndExistenceConstraintCreation(LabelId label, PropertyId property,
+                                                               const char *key) const;
+
+  bool PersistLabelPropertyIndexAndExistenceConstraintDeletion(LabelId label, PropertyId property,
+                                                               const char *key) const;
+
+  void LoadLabelPropertyIndexInfoIfExists() const;
+
+  void LoadConstraintsInfoIfExists() const;
+
+  void LoadExistenceConstraintInfoIfExists() const;
+
+  bool PersistUniqueConstraintCreation(LabelId label, const std::set<PropertyId> &properties) const;
+
+  bool PersistUniqueConstraintDeletion(LabelId label, const std::set<PropertyId> &properties) const;
+
+  void LoadUniqueConstraintInfoIfExists() const;
+
+  uint64_t GetDiskSpaceUsage() const;
+
+  void LoadTimestampIfExists();
+
+  [[nodiscard]] std::optional<ConstraintViolation> CheckExistingVerticesBeforeCreatingExistenceConstraint(
+      LabelId label, PropertyId property) const;
+
+  [[nodiscard]] utils::BasicResult<ConstraintViolation, std::vector<std::pair<std::string, std::string>>>
+  CheckExistingVerticesBeforeCreatingUniqueConstraint(LabelId label, const std::set<PropertyId> &properties) const;
+
+  std::vector<std::pair<std::string, std::string>> SerializeVerticesForLabelIndex(LabelId label);
+
+  std::vector<std::pair<std::string, std::string>> SerializeVerticesForLabelPropertyIndex(LabelId label,
+                                                                                          PropertyId property);
+
+  StorageInfo GetInfo() const override;
+
+  void FreeMemory(std::unique_lock<utils::RWLock> /*lock*/) override {}
+
+  uint64_t CommitTimestamp(std::optional<uint64_t> desired_commit_timestamp = {});
+
+  std::unique_ptr<RocksDBStorage> kvstore_;
+  std::unique_ptr<kvstore::KVStore> durability_kvstore_;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/disk/unique_constraints.cpp b/src/storage/v2/disk/unique_constraints.cpp
new file mode 100644
index 000000000..86e41541f
--- /dev/null
+++ b/src/storage/v2/disk/unique_constraints.cpp
@@ -0,0 +1,349 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include "storage/v2/disk/unique_constraints.hpp"
+#include <rocksdb/utilities/transaction.h>
+#include <limits>
+#include <optional>
+#include <tuple>
+#include "spdlog/spdlog.h"
+#include "storage/v2/constraints/unique_constraints.hpp"
+#include "storage/v2/id_types.hpp"
+#include "storage/v2/property_value.hpp"
+#include "storage/v2/vertex.hpp"
+#include "utils/algorithm.hpp"
+#include "utils/disk_utils.hpp"
+#include "utils/file.hpp"
+namespace memgraph::storage {
+
+namespace {
+
+bool IsVertexUnderConstraint(const Vertex &vertex, const LabelId &constraint_label,
+                             const std::set<PropertyId> &constraint_properties) {
+  return utils::Contains(vertex.labels, constraint_label) && vertex.properties.HasAllProperties(constraint_properties);
+}
+
+bool IsDifferentVertexWithSameConstraintLabel(const std::string &key, const Gid gid, const LabelId constraint_label) {
+  const std::vector<std::string> vertex_parts = utils::Split(key, "|");
+  if (std::string local_gid = vertex_parts[1]; local_gid == utils::SerializeIdType(gid)) {
+    return false;
+  }
+  return utils::DeserializeConstraintLabelFromUniqueConstraintStorage(key) == constraint_label;
+}
+
+[[nodiscard]] bool ClearTransactionEntriesWithRemovedConstraintLabel(
+    rocksdb::Transaction &disk_transaction,
+    const std::map<Gid, std::set<std::pair<LabelId, std::set<PropertyId>>>> &transaction_entries) {
+  for (const auto &[vertex_gid, constraints] : transaction_entries) {
+    for (const auto &[constraint_label, constraint_properties] : constraints) {
+      auto key_to_delete = utils::SerializeVertexAsKeyForUniqueConstraint(constraint_label, constraint_properties,
+                                                                          utils::SerializeIdType(vertex_gid));
+      if (auto status = disk_transaction.Delete(key_to_delete); !status.ok()) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+}  // namespace
+
+DiskUniqueConstraints::DiskUniqueConstraints(const Config &config) {
+  kvstore_ = std::make_unique<RocksDBStorage>();
+  utils::EnsureDirOrDie(config.disk.unique_constraints_directory);
+  kvstore_->options_.create_if_missing = true;
+  kvstore_->options_.comparator = new ComparatorWithU64TsImpl();
+  logging::AssertRocksDBStatus(rocksdb::TransactionDB::Open(kvstore_->options_, rocksdb::TransactionDBOptions(),
+                                                            config.disk.unique_constraints_directory, &kvstore_->db_));
+}
+
+bool DiskUniqueConstraints::InsertConstraint(
+    LabelId label, const std::set<PropertyId> &properties,
+    const std::vector<std::pair<std::string, std::string>> &vertices_under_constraint) {
+  if (!constraints_.insert(std::make_pair(label, properties)).second) {
+    return false;
+  }
+
+  auto disk_transaction = std::unique_ptr<rocksdb::Transaction>(
+      kvstore_->db_->BeginTransaction(rocksdb::WriteOptions(), rocksdb::TransactionOptions()));
+  for (const auto &[key, value] : vertices_under_constraint) {
+    disk_transaction->Put(key, value);
+  }
+
+  /// TODO: figure out a better way to handle this
+  disk_transaction->SetCommitTimestamp(0);
+  /// TODO: how about extracting to commit
+  auto status = disk_transaction->Commit();
+  if (!status.ok()) {
+    spdlog::error("rocksdb: {}", status.getState());
+  }
+  return status.ok();
+}
+
+std::optional<ConstraintViolation> DiskUniqueConstraints::Validate(
+    const Vertex &vertex, std::vector<std::vector<PropertyValue>> &unique_storage) const {
+  for (const auto &[constraint_label, constraint_properties] : constraints_) {
+    if (IsVertexUnderConstraint(vertex, constraint_label, constraint_properties)) {
+      if (auto vertex_check_result =
+              TestIfVertexSatisifiesUniqueConstraint(vertex, unique_storage, constraint_label, constraint_properties);
+          vertex_check_result.has_value()) {
+        return vertex_check_result.value();
+      }
+    }
+  }
+  return std::nullopt;
+}
+
+std::optional<ConstraintViolation> DiskUniqueConstraints::TestIfVertexSatisifiesUniqueConstraint(
+    const Vertex &vertex, std::vector<std::vector<PropertyValue>> &unique_storage, const LabelId &constraint_label,
+    const std::set<PropertyId> &constraint_properties) const {
+  auto property_values = vertex.properties.ExtractPropertyValues(constraint_properties);
+
+  /// TODO: better naming. Is vertex unique
+  if (property_values.has_value() &&
+      VertexIsUnique(property_values.value(), unique_storage, constraint_label, constraint_properties, vertex.gid)) {
+    unique_storage.emplace_back(std::move(property_values.value()));
+    return std::nullopt;
+  }
+
+  return ConstraintViolation{ConstraintViolation::Type::UNIQUE, constraint_label, constraint_properties};
+}
+
+bool DiskUniqueConstraints::VertexIsUnique(const std::vector<PropertyValue> &property_values,
+                                           const std::vector<std::vector<PropertyValue>> &unique_storage,
+                                           const LabelId &constraint_label,
+                                           const std::set<PropertyId> &constraint_properties, const Gid gid) const {
+  if (utils::Contains(unique_storage, property_values)) {
+    return false;
+  }
+
+  auto disk_transaction = std::unique_ptr<rocksdb::Transaction>(
+      kvstore_->db_->BeginTransaction(rocksdb::WriteOptions(), rocksdb::TransactionOptions()));
+  disk_transaction->SetReadTimestampForValidation(std::numeric_limits<uint64_t>::max());
+
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto it = std::unique_ptr<rocksdb::Iterator>(disk_transaction->GetIterator(ro));
+
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    if (IsDifferentVertexWithSameConstraintLabel(it->key().ToString(), gid, constraint_label)) {
+      if (utils::DeserializePropertiesFromUniqueConstraintStorage(it->value().ToString())
+              .ExtractPropertyValues(constraint_properties) == property_values) {
+        return false;
+      }
+    }
+  }
+  return true;
+}
+
+bool DiskUniqueConstraints::ClearDeletedVertex(const std::string_view gid,
+                                               uint64_t transaction_commit_timestamp) const {
+  auto disk_transaction = std::unique_ptr<rocksdb::Transaction>(
+      kvstore_->db_->BeginTransaction(rocksdb::WriteOptions(), rocksdb::TransactionOptions()));
+  disk_transaction->SetReadTimestampForValidation(std::numeric_limits<uint64_t>::max());
+
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto it = std::unique_ptr<rocksdb::Iterator>(disk_transaction->GetIterator(ro));
+
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    if (std::string key = it->key().ToString(); gid == utils::ExtractGidFromUniqueConstraintStorage(key)) {
+      if (!disk_transaction->Delete(key).ok()) {
+        return false;
+      }
+    }
+  }
+  disk_transaction->SetCommitTimestamp(transaction_commit_timestamp);
+  auto status = disk_transaction->Commit();
+  if (!status.ok()) {
+    spdlog::error("rocksdb: {}", status.getState());
+  }
+  return status.ok();
+}
+
+bool DiskUniqueConstraints::DeleteVerticesWithRemovedConstraintLabel(uint64_t transaction_start_timestamp,
+                                                                     uint64_t transaction_commit_timestamp) {
+  auto disk_transaction = std::unique_ptr<rocksdb::Transaction>(
+      kvstore_->db_->BeginTransaction(rocksdb::WriteOptions(), rocksdb::TransactionOptions()));
+  disk_transaction->SetReadTimestampForValidation(std::numeric_limits<uint64_t>::max());
+
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+
+  bool deletion_success = true;
+  entries_for_deletion.WithLock([&deletion_success, transaction_start_timestamp,
+                                 disk_transaction_ptr = disk_transaction.get()](auto &tx_to_entries_for_deletion) {
+    if (auto tx_it = tx_to_entries_for_deletion.find(transaction_start_timestamp);
+        tx_it != tx_to_entries_for_deletion.end()) {
+      deletion_success = ClearTransactionEntriesWithRemovedConstraintLabel(*disk_transaction_ptr, tx_it->second);
+      tx_to_entries_for_deletion.erase(tx_it);
+    }
+  });
+  if (deletion_success) {
+    /// TODO: Extract to some useful method
+    disk_transaction->SetCommitTimestamp(transaction_commit_timestamp);
+    auto status = disk_transaction->Commit();
+    if (!status.ok()) {
+      /// TODO: better naming
+      spdlog::error("rocksdb: {}", status.getState());
+    }
+    return status.ok();
+  }
+  spdlog::error("Deletetion of vertices with removed constraint label failed.");
+  return false;
+}
+
+bool DiskUniqueConstraints::SyncVertexToUniqueConstraintsStorage(const Vertex &vertex,
+                                                                 uint64_t commit_timestamp) const {
+  /// TODO: create method for writing transaction
+  auto disk_transaction = std::unique_ptr<rocksdb::Transaction>(
+      kvstore_->db_->BeginTransaction(rocksdb::WriteOptions(), rocksdb::TransactionOptions()));
+
+  if (auto maybe_old_disk_key = utils::GetOldDiskKeyOrNull(vertex.delta); maybe_old_disk_key.has_value()) {
+    spdlog::debug("Found old disk key {} for vertex {}", maybe_old_disk_key.value(),
+                  utils::SerializeIdType(vertex.gid));
+    if (auto status = disk_transaction->Delete(maybe_old_disk_key.value()); !status.ok()) {
+      return false;
+    }
+  }
+
+  for (const auto &[constraint_label, constraint_properties] : constraints_) {
+    if (IsVertexUnderConstraint(vertex, constraint_label, constraint_properties)) {
+      auto key = utils::SerializeVertexAsKeyForUniqueConstraint(constraint_label, constraint_properties,
+                                                                utils::SerializeIdType(vertex.gid));
+      auto value = utils::SerializeVertexAsValueForUniqueConstraint(constraint_label, vertex.labels, vertex.properties);
+      if (!disk_transaction->Put(key, value).ok()) {
+        return false;
+      }
+    }
+  }
+  /// TODO: extract and better message
+  disk_transaction->SetCommitTimestamp(commit_timestamp);
+  auto status = disk_transaction->Commit();
+  if (!status.ok()) {
+    spdlog::error("rocksdb: {}", status.getState());
+  }
+  return status.ok();
+}
+
+DiskUniqueConstraints::CreationStatus DiskUniqueConstraints::CheckIfConstraintCanBeCreated(
+    LabelId label, const std::set<PropertyId> &properties) const {
+  if (properties.empty()) {
+    return CreationStatus::EMPTY_PROPERTIES;
+  }
+  if (properties.size() > kUniqueConstraintsMaxProperties) {
+    return CreationStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED;
+  }
+  if (utils::Contains(constraints_, std::make_pair(label, properties))) {
+    return CreationStatus::ALREADY_EXISTS;
+  }
+  return CreationStatus::SUCCESS;
+};
+
+DiskUniqueConstraints::DeletionStatus DiskUniqueConstraints::DropConstraint(LabelId label,
+                                                                            const std::set<PropertyId> &properties) {
+  if (auto drop_properties_check_result = UniqueConstraints::CheckPropertiesBeforeDeletion(properties);
+      drop_properties_check_result != UniqueConstraints::DeletionStatus::SUCCESS) {
+    return drop_properties_check_result;
+  }
+  if (constraints_.erase({label, properties}) > 0) {
+    return UniqueConstraints::DeletionStatus::SUCCESS;
+  }
+  return UniqueConstraints::DeletionStatus::NOT_FOUND;
+}
+
+bool DiskUniqueConstraints::ConstraintExists(LabelId label, const std::set<PropertyId> &properties) const {
+  return utils::Contains(constraints_, std::make_pair(label, properties));
+}
+
+void DiskUniqueConstraints::UpdateOnRemoveLabel(LabelId removed_label, const Vertex &vertex_before_update,
+                                                uint64_t transaction_start_timestamp) {
+  for (const auto &constraint : constraints_) {
+    if (constraint.first == removed_label) {
+      entries_for_deletion.WithLock(
+          [&constraint, transaction_start_timestamp, &vertex_before_update](auto &tx_to_entries_for_deletion) {
+            const auto &[constraint_label, constraint_properties] = constraint;
+            auto [it, _] = tx_to_entries_for_deletion.emplace(
+                std::piecewise_construct, std::forward_as_tuple(transaction_start_timestamp), std::forward_as_tuple());
+            auto &vertex_map_store = it->second;
+            auto [it_vertex_map_store, emplaced] = vertex_map_store.emplace(
+                std::piecewise_construct, std::forward_as_tuple(vertex_before_update.gid), std::forward_as_tuple());
+            it_vertex_map_store->second.emplace(constraint_label, constraint_properties);
+          });
+    }
+  }
+}
+
+void DiskUniqueConstraints::UpdateOnAddLabel(LabelId added_label, const Vertex &vertex_before_update,
+                                             uint64_t transaction_start_timestamp) {
+  entries_for_deletion.WithLock(
+      [transaction_start_timestamp, &vertex_before_update, added_label](auto &tx_to_entries_for_deletion) {
+        /// TODO: change to only one if condition and maybe optimize erase if
+        if (auto tx_it = tx_to_entries_for_deletion.find(transaction_start_timestamp);
+            tx_it != tx_to_entries_for_deletion.end()) {
+          if (auto vertex_constraints_it = tx_it->second.find(vertex_before_update.gid);
+              vertex_constraints_it != tx_it->second.end()) {
+            std::erase_if(vertex_constraints_it->second,
+                          [added_label](const auto &constraint) { return constraint.first == added_label; });
+          }
+        }
+      });
+}
+
+std::vector<std::pair<LabelId, std::set<PropertyId>>> DiskUniqueConstraints::ListConstraints() const {
+  return {constraints_.begin(), constraints_.end()};
+}
+
+void DiskUniqueConstraints::Clear() {
+  constraints_.clear();
+
+  auto disk_transaction = std::unique_ptr<rocksdb::Transaction>(
+      kvstore_->db_->BeginTransaction(rocksdb::WriteOptions(), rocksdb::TransactionOptions()));
+  disk_transaction->SetReadTimestampForValidation(std::numeric_limits<uint64_t>::max());
+
+  rocksdb::ReadOptions ro;
+  std::string strTs = utils::StringTimestamp(std::numeric_limits<uint64_t>::max());
+  rocksdb::Slice ts(strTs);
+  ro.timestamp = &ts;
+  auto it = std::unique_ptr<rocksdb::Iterator>(disk_transaction->GetIterator(ro));
+
+  for (it->SeekToFirst(); it->Valid(); it->Next()) {
+    disk_transaction->Delete(it->key().ToString());
+  }
+
+  disk_transaction->SetCommitTimestamp(0);
+  auto status = disk_transaction->Commit();
+  if (!status.ok()) {
+    spdlog::error("rocksdb: {}", status.getState());
+  }
+}
+
+RocksDBStorage *DiskUniqueConstraints::GetRocksDBStorage() const { return kvstore_.get(); }
+
+void DiskUniqueConstraints::LoadUniqueConstraints(const std::vector<std::string> &keys) {
+  for (const auto &key : keys) {
+    std::vector<std::string> key_parts = utils::Split(key, ",");
+    LabelId label = LabelId::FromUint(std::stoull(key_parts[0]));
+    std::set<PropertyId> properties;
+    for (int i = 1; i < key_parts.size(); i++) {
+      properties.insert(PropertyId::FromUint(std::stoull(key_parts[i])));
+    }
+    constraints_.emplace(std::make_pair(label, properties));
+  }
+}
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/disk/unique_constraints.hpp b/src/storage/v2/disk/unique_constraints.hpp
new file mode 100644
index 000000000..0cc5a9586
--- /dev/null
+++ b/src/storage/v2/disk/unique_constraints.hpp
@@ -0,0 +1,78 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include "storage/v2/config.hpp"
+#include "storage/v2/constraints/unique_constraints.hpp"
+#include "storage/v2/disk/rocksdb_storage.hpp"
+#include "storage/v2/id_types.hpp"
+#include "storage/v2/property_value.hpp"
+#include "storage/v2/transaction.hpp"
+#include "utils/rocksdb_serialization.hpp"
+#include "utils/synchronized.hpp"
+
+namespace memgraph::storage {
+
+class DiskUniqueConstraints : public UniqueConstraints {
+ public:
+  explicit DiskUniqueConstraints(const Config &config);
+
+  CreationStatus CheckIfConstraintCanBeCreated(LabelId label, const std::set<PropertyId> &properties) const;
+
+  [[nodiscard]] bool InsertConstraint(
+      LabelId label, const std::set<PropertyId> &properties,
+      const std::vector<std::pair<std::string, std::string>> &vertices_under_constraint);
+
+  std::optional<ConstraintViolation> Validate(const Vertex &vertex,
+                                              std::vector<std::vector<PropertyValue>> &unique_storage) const;
+
+  [[nodiscard]] bool ClearDeletedVertex(std::string_view gid, uint64_t transaction_commit_timestamp) const;
+
+  [[nodiscard]] bool DeleteVerticesWithRemovedConstraintLabel(uint64_t transaction_start_timestamp,
+                                                              uint64_t transaction_commit_timestamp);
+
+  [[nodiscard]] bool SyncVertexToUniqueConstraintsStorage(const Vertex &vertex, uint64_t commit_timestamp) const;
+
+  DeletionStatus DropConstraint(LabelId label, const std::set<PropertyId> &properties) override;
+
+  [[nodiscard]] bool ConstraintExists(LabelId label, const std::set<PropertyId> &properties) const override;
+
+  void UpdateOnRemoveLabel(LabelId removed_label, const Vertex &vertex_before_update,
+                           uint64_t transaction_start_timestamp) override;
+
+  void UpdateOnAddLabel(LabelId added_label, const Vertex &vertex_before_update,
+                        uint64_t transaction_start_timestamp) override;
+
+  std::vector<std::pair<LabelId, std::set<PropertyId>>> ListConstraints() const override;
+
+  void Clear() override;
+
+  RocksDBStorage *GetRocksDBStorage() const;
+
+  void LoadUniqueConstraints(const std::vector<std::string> &keys);
+
+ private:
+  utils::Synchronized<std::map<uint64_t, std::map<Gid, std::set<std::pair<LabelId, std::set<PropertyId>>>>>>
+      entries_for_deletion;
+  std::set<std::pair<LabelId, std::set<PropertyId>>> constraints_;
+  std::unique_ptr<RocksDBStorage> kvstore_;
+
+  [[nodiscard]] std::optional<ConstraintViolation> TestIfVertexSatisifiesUniqueConstraint(
+      const Vertex &vertex, std::vector<std::vector<PropertyValue>> &unique_storage, const LabelId &constraint_label,
+      const std::set<PropertyId> &constraint_properties) const;
+
+  bool VertexIsUnique(const std::vector<PropertyValue> &property_values,
+                      const std::vector<std::vector<PropertyValue>> &unique_storage, const LabelId &constraint_label,
+                      const std::set<PropertyId> &constraint_properties, Gid gid) const;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/durability/durability.cpp b/src/storage/v2/durability/durability.cpp
index 1f1052528..99b286332 100644
--- a/src/storage/v2/durability/durability.cpp
+++ b/src/storage/v2/durability/durability.cpp
@@ -27,6 +27,7 @@
 #include "storage/v2/durability/paths.hpp"
 #include "storage/v2/durability/snapshot.hpp"
 #include "storage/v2/durability/wal.hpp"
+#include "storage/v2/indices/label_property_index.hpp"
 #include "utils/event_histogram.hpp"
 #include "utils/logging.hpp"
 #include "utils/memory_tracker.hpp"
@@ -131,7 +132,8 @@ void RecoverIndicesAndConstraints(const RecoveredIndicesAndConstraints &indices_
   // Recover label indices.
   spdlog::info("Recreating {} label indices from metadata.", indices_constraints.indices.label.size());
   for (const auto &item : indices_constraints.indices.label) {
-    if (!indices->label_index.CreateIndex(item, vertices->access(), paralell_exec_info))
+    auto *mem_label_index = static_cast<InMemoryLabelIndex *>(indices->label_index_.get());
+    if (!mem_label_index->CreateIndex(item, vertices->access(), paralell_exec_info))
       throw RecoveryFailure("The label index must be created here!");
 
     spdlog::info("A label index is recreated from metadata.");
@@ -141,8 +143,9 @@ void RecoverIndicesAndConstraints(const RecoveredIndicesAndConstraints &indices_
   // Recover label+property indices.
   spdlog::info("Recreating {} label+property indices from metadata.",
                indices_constraints.indices.label_property.size());
+  auto *mem_label_property_index = static_cast<InMemoryLabelPropertyIndex *>(indices->label_property_index_.get());
   for (const auto &item : indices_constraints.indices.label_property) {
-    if (!indices->label_property_index.CreateIndex(item.first, item.second, vertices->access()))
+    if (!mem_label_property_index->CreateIndex(item.first, item.second, vertices->access(), std::nullopt))
       throw RecoveryFailure("The label+property index must be created here!");
     spdlog::info("A label+property index is recreated from metadata.");
   }
@@ -152,9 +155,18 @@ void RecoverIndicesAndConstraints(const RecoveredIndicesAndConstraints &indices_
   spdlog::info("Recreating constraints from metadata.");
   // Recover existence constraints.
   spdlog::info("Recreating {} existence constraints from metadata.", indices_constraints.constraints.existence.size());
-  for (const auto &item : indices_constraints.constraints.existence) {
-    auto ret = CreateExistenceConstraint(constraints, item.first, item.second, vertices->access());
-    if (ret.HasError() || !ret.GetValue()) throw RecoveryFailure("The existence constraint must be created here!");
+  for (const auto &[label, property] : indices_constraints.constraints.existence) {
+    if (constraints->existence_constraints_->ConstraintExists(label, property)) {
+      throw RecoveryFailure("The existence constraint already exists!");
+    }
+
+    if (auto violation = ExistenceConstraints::ValidateVerticesOnConstraint(vertices->access(), label, property);
+        violation.has_value()) {
+      throw RecoveryFailure("The existence constraint failed because it couldn't be validated!");
+    }
+
+    constraints->existence_constraints_->InsertConstraint(label, property);
+
     spdlog::info("A existence constraint is recreated from metadata.");
   }
   spdlog::info("Existence constraints are recreated from metadata.");
@@ -162,7 +174,8 @@ void RecoverIndicesAndConstraints(const RecoveredIndicesAndConstraints &indices_
   // Recover unique constraints.
   spdlog::info("Recreating {} unique constraints from metadata.", indices_constraints.constraints.unique.size());
   for (const auto &item : indices_constraints.constraints.unique) {
-    auto ret = constraints->unique_constraints.CreateConstraint(item.first, item.second, vertices->access());
+    auto *mem_unique_constraints = static_cast<InMemoryUniqueConstraints *>(constraints->unique_constraints_.get());
+    auto ret = mem_unique_constraints->CreateConstraint(item.first, item.second, vertices->access());
     if (ret.HasError() || ret.GetValue() != UniqueConstraints::CreationStatus::SUCCESS)
       throw RecoveryFailure("The unique constraint must be created here!");
     spdlog::info("A unique constraint is recreated from metadata.");
diff --git a/src/storage/v2/durability/durability.hpp b/src/storage/v2/durability/durability.hpp
index 1af07136f..2e13a776a 100644
--- a/src/storage/v2/durability/durability.hpp
+++ b/src/storage/v2/durability/durability.hpp
@@ -19,11 +19,11 @@
 #include <variant>
 
 #include "storage/v2/config.hpp"
-#include "storage/v2/constraints.hpp"
+#include "storage/v2/constraints/constraints.hpp"
 #include "storage/v2/durability/metadata.hpp"
 #include "storage/v2/durability/wal.hpp"
 #include "storage/v2/edge.hpp"
-#include "storage/v2/indices.hpp"
+#include "storage/v2/indices/indices.hpp"
 #include "storage/v2/name_id_mapper.hpp"
 #include "storage/v2/vertex.hpp"
 #include "utils/skip_list.hpp"
diff --git a/src/storage/v2/durability/snapshot.cpp b/src/storage/v2/durability/snapshot.cpp
index 61c159c99..7e9ff0289 100644
--- a/src/storage/v2/durability/snapshot.cpp
+++ b/src/storage/v2/durability/snapshot.cpp
@@ -1395,6 +1395,7 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
             is_visible = true;
             break;
           }
+          case Delta::Action::DELETE_DESERIALIZED_OBJECT:
           case Delta::Action::DELETE_OBJECT: {
             is_visible = false;
             break;
@@ -1517,7 +1518,7 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
 
     // Write label indices.
     {
-      auto label = indices->label_index.ListIndices();
+      auto label = indices->label_index_->ListIndices();
       snapshot.WriteUint(label.size());
       for (const auto &item : label) {
         write_mapping(item);
@@ -1526,7 +1527,7 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
 
     // Write label+property indices.
     {
-      auto label_property = indices->label_property_index.ListIndices();
+      auto label_property = indices->label_property_index_->ListIndices();
       snapshot.WriteUint(label_property.size());
       for (const auto &item : label_property) {
         write_mapping(item.first);
@@ -1542,7 +1543,7 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
 
     // Write existence constraints.
     {
-      auto existence = ListExistenceConstraints(*constraints);
+      auto existence = constraints->existence_constraints_->ListConstraints();
       snapshot.WriteUint(existence.size());
       for (const auto &item : existence) {
         write_mapping(item.first);
@@ -1552,7 +1553,7 @@ void CreateSnapshot(Transaction *transaction, const std::filesystem::path &snaps
 
     // Write unique constraints.
     {
-      auto unique = constraints->unique_constraints.ListConstraints();
+      auto unique = constraints->unique_constraints_->ListConstraints();
       snapshot.WriteUint(unique.size());
       for (const auto &item : unique) {
         write_mapping(item.first);
diff --git a/src/storage/v2/durability/snapshot.hpp b/src/storage/v2/durability/snapshot.hpp
index 41b91751b..2f16088a0 100644
--- a/src/storage/v2/durability/snapshot.hpp
+++ b/src/storage/v2/durability/snapshot.hpp
@@ -16,10 +16,10 @@
 #include <string>
 
 #include "storage/v2/config.hpp"
-#include "storage/v2/constraints.hpp"
+#include "storage/v2/constraints/constraints.hpp"
 #include "storage/v2/durability/metadata.hpp"
 #include "storage/v2/edge.hpp"
-#include "storage/v2/indices.hpp"
+#include "storage/v2/indices/indices.hpp"
 #include "storage/v2/name_id_mapper.hpp"
 #include "storage/v2/transaction.hpp"
 #include "storage/v2/vertex.hpp"
diff --git a/src/storage/v2/durability/wal.cpp b/src/storage/v2/durability/wal.cpp
index a8faf064a..570520bbd 100644
--- a/src/storage/v2/durability/wal.cpp
+++ b/src/storage/v2/durability/wal.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -101,6 +101,7 @@ Marker VertexActionToMarker(Delta::Action action) {
   // because the Delta's represent undo actions and we want to store redo
   // actions.
   switch (action) {
+    case Delta::Action::DELETE_DESERIALIZED_OBJECT:
     case Delta::Action::DELETE_OBJECT:
       return Marker::DELTA_VERTEX_CREATE;
     case Delta::Action::RECREATE_OBJECT:
@@ -491,6 +492,7 @@ void EncodeDelta(BaseEncoder *encoder, NameIdMapper *name_id_mapper, Config::Ite
   encoder->WriteUint(timestamp);
   std::lock_guard<utils::SpinLock> guard(vertex.lock);
   switch (delta.action) {
+    case Delta::Action::DELETE_DESERIALIZED_OBJECT:
     case Delta::Action::DELETE_OBJECT:
     case Delta::Action::RECREATE_OBJECT: {
       encoder->WriteMarker(VertexActionToMarker(delta.action));
@@ -558,6 +560,7 @@ void EncodeDelta(BaseEncoder *encoder, NameIdMapper *name_id_mapper, const Delta
       encoder->WritePropertyValue(edge.properties.GetProperty(delta.property.key));
       break;
     }
+    case Delta::Action::DELETE_DESERIALIZED_OBJECT:
     case Delta::Action::DELETE_OBJECT:
     case Delta::Action::RECREATE_OBJECT:
       // These actions are already encoded in vertex *_OUT_EDGE actions. Also,
diff --git a/src/storage/v2/edge.hpp b/src/storage/v2/edge.hpp
index fcf8ff8e7..89bbc83e5 100644
--- a/src/storage/v2/edge.hpp
+++ b/src/storage/v2/edge.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -25,7 +25,8 @@ struct Vertex;
 
 struct Edge {
   Edge(Gid gid, Delta *delta) : gid(gid), deleted(false), delta(delta) {
-    MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT,
+    MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT ||
+                  delta->action == Delta::Action::DELETE_DESERIALIZED_OBJECT,
               "Edge must be created with an initial DELETE_OBJECT delta!");
   }
 
diff --git a/src/storage/v2/edge_accessor.cpp b/src/storage/v2/edge_accessor.cpp
index d14c8d56d..1c47e7102 100644
--- a/src/storage/v2/edge_accessor.cpp
+++ b/src/storage/v2/edge_accessor.cpp
@@ -14,6 +14,7 @@
 #include <memory>
 #include <tuple>
 
+#include "storage/v2/delta.hpp"
 #include "storage/v2/mvcc.hpp"
 #include "storage/v2/property_value.hpp"
 #include "storage/v2/vertex_accessor.hpp"
@@ -44,6 +45,7 @@ bool EdgeAccessor::IsVisible(const View view) const {
         case Delta::Action::REMOVE_IN_EDGE:
         case Delta::Action::ADD_IN_EDGE:
         case Delta::Action::RECREATE_OBJECT:
+        case Delta::Action::DELETE_DESERIALIZED_OBJECT:
         case Delta::Action::DELETE_OBJECT:
           break;
         case Delta::Action::ADD_OUT_EDGE: {  // relevant for the from_vertex_ -> we just deleted the edge
@@ -83,6 +85,7 @@ bool EdgeAccessor::IsVisible(const View view) const {
         deleted = false;
         break;
       }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
       case Delta::Action::DELETE_OBJECT: {
         exists = false;
         break;
@@ -181,6 +184,7 @@ Result<PropertyValue> EdgeAccessor::GetProperty(PropertyId property, View view)
         }
         break;
       }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
       case Delta::Action::DELETE_OBJECT: {
         exists = false;
         break;
@@ -232,6 +236,7 @@ Result<std::map<PropertyId, PropertyValue>> EdgeAccessor::Properties(View view)
         }
         break;
       }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
       case Delta::Action::DELETE_OBJECT: {
         exists = false;
         break;
diff --git a/src/storage/v2/edge_accessor.hpp b/src/storage/v2/edge_accessor.hpp
index ee8176365..93e995a05 100644
--- a/src/storage/v2/edge_accessor.hpp
+++ b/src/storage/v2/edge_accessor.hpp
@@ -88,7 +88,6 @@ class EdgeAccessor final {
   }
   bool operator!=(const EdgeAccessor &other) const noexcept { return !(*this == other); }
 
- private:
   EdgeRef edge_;
   EdgeTypeId edge_type_;
   Vertex *from_vertex_;
diff --git a/src/storage/v2/indices.cpp b/src/storage/v2/indices.cpp
deleted file mode 100644
index 596631899..000000000
--- a/src/storage/v2/indices.cpp
+++ /dev/null
@@ -1,907 +0,0 @@
-// Copyright 2023 Memgraph Ltd.
-//
-// Use of this software is governed by the Business Source License
-// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
-// License, and you may not use this file except in compliance with the Business Source License.
-//
-// As of the Change Date specified in that file, in accordance with
-// the Business Source License, use of this software will be governed
-// by the Apache License, Version 2.0, included in the file
-// licenses/APL.txt.
-
-#include "indices.hpp"
-#include <algorithm>
-#include <iterator>
-#include <limits>
-#include <thread>
-
-#include "storage/v2/mvcc.hpp"
-#include "storage/v2/property_value.hpp"
-#include "utils/bound.hpp"
-#include "utils/logging.hpp"
-#include "utils/memory_tracker.hpp"
-#include "utils/synchronized.hpp"
-
-namespace memgraph::storage {
-
-namespace {
-
-/// Traverses deltas visible from transaction with start timestamp greater than
-/// the provided timestamp, and calls the provided callback function for each
-/// delta. If the callback ever returns true, traversal is stopped and the
-/// function returns true. Otherwise, the function returns false.
-template <typename TCallback>
-bool AnyVersionSatisfiesPredicate(uint64_t timestamp, const Delta *delta, const TCallback &predicate) {
-  while (delta != nullptr) {
-    auto ts = delta->timestamp->load(std::memory_order_acquire);
-    // This is a committed change that we see so we shouldn't undo it.
-    if (ts < timestamp) {
-      break;
-    }
-    if (predicate(*delta)) {
-      return true;
-    }
-    // Move to the next delta.
-    delta = delta->next.load(std::memory_order_acquire);
-  }
-  return false;
-}
-
-/// Helper function for label index garbage collection. Returns true if there's
-/// a reachable version of the vertex that has the given label.
-bool AnyVersionHasLabel(const Vertex &vertex, LabelId label, uint64_t timestamp) {
-  bool has_label;
-  bool deleted;
-  const Delta *delta;
-  {
-    std::lock_guard<utils::SpinLock> guard(vertex.lock);
-    has_label = utils::Contains(vertex.labels, label);
-    deleted = vertex.deleted;
-    delta = vertex.delta;
-  }
-  if (!deleted && has_label) {
-    return true;
-  }
-  return AnyVersionSatisfiesPredicate(timestamp, delta, [&has_label, &deleted, label](const Delta &delta) {
-    switch (delta.action) {
-      case Delta::Action::ADD_LABEL:
-        if (delta.label == label) {
-          MG_ASSERT(!has_label, "Invalid database state!");
-          has_label = true;
-        }
-        break;
-      case Delta::Action::REMOVE_LABEL:
-        if (delta.label == label) {
-          MG_ASSERT(has_label, "Invalid database state!");
-          has_label = false;
-        }
-        break;
-      case Delta::Action::RECREATE_OBJECT: {
-        MG_ASSERT(deleted, "Invalid database state!");
-        deleted = false;
-        break;
-      }
-      case Delta::Action::DELETE_OBJECT: {
-        MG_ASSERT(!deleted, "Invalid database state!");
-        deleted = true;
-        break;
-      }
-      case Delta::Action::SET_PROPERTY:
-      case Delta::Action::ADD_IN_EDGE:
-      case Delta::Action::ADD_OUT_EDGE:
-      case Delta::Action::REMOVE_IN_EDGE:
-      case Delta::Action::REMOVE_OUT_EDGE:
-        break;
-    }
-    return !deleted && has_label;
-  });
-}
-
-/// Helper function for label-property index garbage collection. Returns true if
-/// there's a reachable version of the vertex that has the given label and
-/// property value.
-bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, PropertyId key, const PropertyValue &value,
-                                uint64_t timestamp) {
-  bool has_label;
-  bool current_value_equal_to_value = value.IsNull();
-  bool deleted;
-  const Delta *delta;
-  {
-    std::lock_guard<utils::SpinLock> guard(vertex.lock);
-    has_label = utils::Contains(vertex.labels, label);
-    current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value);
-    deleted = vertex.deleted;
-    delta = vertex.delta;
-  }
-
-  if (!deleted && has_label && current_value_equal_to_value) {
-    return true;
-  }
-
-  return AnyVersionSatisfiesPredicate(
-      timestamp, delta, [&has_label, &current_value_equal_to_value, &deleted, label, key, &value](const Delta &delta) {
-        switch (delta.action) {
-          case Delta::Action::ADD_LABEL:
-            if (delta.label == label) {
-              MG_ASSERT(!has_label, "Invalid database state!");
-              has_label = true;
-            }
-            break;
-          case Delta::Action::REMOVE_LABEL:
-            if (delta.label == label) {
-              MG_ASSERT(has_label, "Invalid database state!");
-              has_label = false;
-            }
-            break;
-          case Delta::Action::SET_PROPERTY:
-            if (delta.property.key == key) {
-              current_value_equal_to_value = delta.property.value == value;
-            }
-            break;
-          case Delta::Action::RECREATE_OBJECT: {
-            MG_ASSERT(deleted, "Invalid database state!");
-            deleted = false;
-            break;
-          }
-          case Delta::Action::DELETE_OBJECT: {
-            MG_ASSERT(!deleted, "Invalid database state!");
-            deleted = true;
-            break;
-          }
-          case Delta::Action::ADD_IN_EDGE:
-          case Delta::Action::ADD_OUT_EDGE:
-          case Delta::Action::REMOVE_IN_EDGE:
-          case Delta::Action::REMOVE_OUT_EDGE:
-            break;
-        }
-        return !deleted && has_label && current_value_equal_to_value;
-      });
-}
-
-// Helper function for iterating through label index. Returns true if this
-// transaction can see the given vertex, and the visible version has the given
-// label.
-bool CurrentVersionHasLabel(const Vertex &vertex, LabelId label, Transaction *transaction, View view) {
-  bool deleted;
-  bool has_label;
-  const Delta *delta;
-  {
-    std::lock_guard<utils::SpinLock> guard(vertex.lock);
-    deleted = vertex.deleted;
-    has_label = utils::Contains(vertex.labels, label);
-    delta = vertex.delta;
-  }
-  ApplyDeltasForRead(transaction, delta, view, [&deleted, &has_label, label](const Delta &delta) {
-    switch (delta.action) {
-      case Delta::Action::REMOVE_LABEL: {
-        if (delta.label == label) {
-          MG_ASSERT(has_label, "Invalid database state!");
-          has_label = false;
-        }
-        break;
-      }
-      case Delta::Action::ADD_LABEL: {
-        if (delta.label == label) {
-          MG_ASSERT(!has_label, "Invalid database state!");
-          has_label = true;
-        }
-        break;
-      }
-      case Delta::Action::DELETE_OBJECT: {
-        MG_ASSERT(!deleted, "Invalid database state!");
-        deleted = true;
-        break;
-      }
-      case Delta::Action::RECREATE_OBJECT: {
-        MG_ASSERT(deleted, "Invalid database state!");
-        deleted = false;
-        break;
-      }
-      case Delta::Action::SET_PROPERTY:
-      case Delta::Action::ADD_IN_EDGE:
-      case Delta::Action::ADD_OUT_EDGE:
-      case Delta::Action::REMOVE_IN_EDGE:
-      case Delta::Action::REMOVE_OUT_EDGE:
-        break;
-    }
-  });
-  return !deleted && has_label;
-}
-
-// Helper function for iterating through label-property index. Returns true if
-// this transaction can see the given vertex, and the visible version has the
-// given label and property.
-bool CurrentVersionHasLabelProperty(const Vertex &vertex, LabelId label, PropertyId key, const PropertyValue &value,
-                                    Transaction *transaction, View view) {
-  bool deleted;
-  bool has_label;
-  bool current_value_equal_to_value = value.IsNull();
-  const Delta *delta;
-  {
-    std::lock_guard<utils::SpinLock> guard(vertex.lock);
-    deleted = vertex.deleted;
-    has_label = utils::Contains(vertex.labels, label);
-    current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value);
-    delta = vertex.delta;
-  }
-  ApplyDeltasForRead(transaction, delta, view,
-                     [&deleted, &has_label, &current_value_equal_to_value, key, label, &value](const Delta &delta) {
-                       switch (delta.action) {
-                         case Delta::Action::SET_PROPERTY: {
-                           if (delta.property.key == key) {
-                             current_value_equal_to_value = delta.property.value == value;
-                           }
-                           break;
-                         }
-                         case Delta::Action::DELETE_OBJECT: {
-                           MG_ASSERT(!deleted, "Invalid database state!");
-                           deleted = true;
-                           break;
-                         }
-                         case Delta::Action::RECREATE_OBJECT: {
-                           MG_ASSERT(deleted, "Invalid database state!");
-                           deleted = false;
-                           break;
-                         }
-                         case Delta::Action::ADD_LABEL:
-                           if (delta.label == label) {
-                             MG_ASSERT(!has_label, "Invalid database state!");
-                             has_label = true;
-                           }
-                           break;
-                         case Delta::Action::REMOVE_LABEL:
-                           if (delta.label == label) {
-                             MG_ASSERT(has_label, "Invalid database state!");
-                             has_label = false;
-                           }
-                           break;
-                         case Delta::Action::ADD_IN_EDGE:
-                         case Delta::Action::ADD_OUT_EDGE:
-                         case Delta::Action::REMOVE_IN_EDGE:
-                         case Delta::Action::REMOVE_OUT_EDGE:
-                           break;
-                       }
-                     });
-  return !deleted && has_label && current_value_equal_to_value;
-}
-
-template <typename TIndexAccessor>
-void TryInsertLabelIndex(Vertex &vertex, LabelId label, TIndexAccessor &index_accessor) {
-  if (vertex.deleted || !utils::Contains(vertex.labels, label)) {
-    return;
-  }
-
-  index_accessor.insert({&vertex, 0});
-}
-
-template <typename TIndexAccessor>
-void TryInsertLabelPropertyIndex(Vertex &vertex, std::pair<LabelId, PropertyId> label_property_pair,
-                                 TIndexAccessor &index_accessor) {
-  if (vertex.deleted || !utils::Contains(vertex.labels, label_property_pair.first)) {
-    return;
-  }
-  auto value = vertex.properties.GetProperty(label_property_pair.second);
-  if (value.IsNull()) {
-    return;
-  }
-  index_accessor.insert({std::move(value), &vertex, 0});
-}
-
-template <typename TSkiplistIter, typename TIndex, typename TIndexKey, typename TFunc>
-void CreateIndexOnSingleThread(utils::SkipList<Vertex>::Accessor &vertices, TSkiplistIter it, TIndex &index,
-                               TIndexKey key, const TFunc &func) {
-  utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
-  try {
-    auto acc = it->second.access();
-    for (Vertex &vertex : vertices) {
-      func(vertex, key, acc);
-    }
-  } catch (const utils::OutOfMemoryException &) {
-    utils::MemoryTracker::OutOfMemoryExceptionBlocker oom_exception_blocker;
-    index.erase(it);
-    throw;
-  }
-}
-
-template <typename TIndex, typename TIndexKey, typename TSKiplistIter, typename TFunc>
-void CreateIndexOnMultipleThreads(utils::SkipList<Vertex>::Accessor &vertices, TSKiplistIter skiplist_iter,
-                                  TIndex &index, TIndexKey key, const ParalellizedIndexCreationInfo &paralell_exec_info,
-                                  const TFunc &func) {
-  utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
-
-  const auto &vertex_batches = paralell_exec_info.first;
-  const auto thread_count = std::min(paralell_exec_info.second, vertex_batches.size());
-
-  MG_ASSERT(!vertex_batches.empty(),
-            "The size of batches should always be greater than zero if you want to use the parallel version of index "
-            "creation!");
-
-  std::atomic<uint64_t> batch_counter = 0;
-
-  utils::Synchronized<std::optional<utils::OutOfMemoryException>, utils::SpinLock> maybe_error{};
-  {
-    std::vector<std::jthread> threads;
-    threads.reserve(thread_count);
-
-    for (auto i{0U}; i < thread_count; ++i) {
-      threads.emplace_back(
-          [&skiplist_iter, &func, &index, &vertex_batches, &maybe_error, &batch_counter, &key, &vertices]() {
-            while (!maybe_error.Lock()->has_value()) {
-              const auto batch_index = batch_counter++;
-              if (batch_index >= vertex_batches.size()) {
-                return;
-              }
-              const auto &batch = vertex_batches[batch_index];
-              auto index_accessor = index.at(key).access();
-              auto it = vertices.find(batch.first);
-
-              try {
-                for (auto i{0U}; i < batch.second; ++i, ++it) {
-                  func(*it, key, index_accessor);
-                }
-
-              } catch (utils::OutOfMemoryException &failure) {
-                utils::MemoryTracker::OutOfMemoryExceptionBlocker oom_exception_blocker;
-                index.erase(skiplist_iter);
-                *maybe_error.Lock() = std::move(failure);
-              }
-            }
-          });
-    }
-  }
-  if (maybe_error.Lock()->has_value()) {
-    throw utils::OutOfMemoryException((*maybe_error.Lock())->what());
-  }
-}
-
-}  // namespace
-
-void LabelIndex::UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx) {
-  auto it = index_.find(label);
-  if (it == index_.end()) return;
-  auto acc = it->second.access();
-  acc.insert(Entry{vertex, tx.start_timestamp});
-}
-
-bool LabelIndex::CreateIndex(LabelId label, utils::SkipList<Vertex>::Accessor vertices,
-                             const std::optional<ParalellizedIndexCreationInfo> &paralell_exec_info) {
-  auto create_index_seq = [this](LabelId label, utils::SkipList<Vertex>::Accessor &vertices,
-                                 std::map<LabelId, utils::SkipList<Entry>>::iterator it) {
-    using IndexAccessor = decltype(it->second.access());
-
-    CreateIndexOnSingleThread(vertices, it, index_, label,
-                              [](Vertex &vertex, LabelId label, IndexAccessor &index_accessor) {
-                                TryInsertLabelIndex(vertex, label, index_accessor);
-                              });
-
-    return true;
-  };
-
-  auto create_index_par = [this](LabelId label, utils::SkipList<Vertex>::Accessor &vertices,
-                                 std::map<LabelId, utils::SkipList<Entry>>::iterator label_it,
-                                 const ParalellizedIndexCreationInfo &paralell_exec_info) {
-    using IndexAccessor = decltype(label_it->second.access());
-
-    CreateIndexOnMultipleThreads(vertices, label_it, index_, label, paralell_exec_info,
-                                 [](Vertex &vertex, LabelId label, IndexAccessor &index_accessor) {
-                                   TryInsertLabelIndex(vertex, label, index_accessor);
-                                 });
-
-    return true;
-  };
-
-  auto [it, emplaced] = index_.emplace(std::piecewise_construct, std::forward_as_tuple(label), std::forward_as_tuple());
-  if (!emplaced) {
-    // Index already exists.
-    return false;
-  }
-
-  if (paralell_exec_info) {
-    return create_index_par(label, vertices, it, *paralell_exec_info);
-  }
-  return create_index_seq(label, vertices, it);
-}
-
-std::vector<LabelId> LabelIndex::ListIndices() const {
-  std::vector<LabelId> ret;
-  ret.reserve(index_.size());
-  for (const auto &item : index_) {
-    ret.push_back(item.first);
-  }
-  return ret;
-}
-
-void LabelIndex::RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp) {
-  for (auto &label_storage : index_) {
-    auto vertices_acc = label_storage.second.access();
-    for (auto it = vertices_acc.begin(); it != vertices_acc.end();) {
-      auto next_it = it;
-      ++next_it;
-
-      if (it->timestamp >= oldest_active_start_timestamp) {
-        it = next_it;
-        continue;
-      }
-
-      if ((next_it != vertices_acc.end() && it->vertex == next_it->vertex) ||
-          !AnyVersionHasLabel(*it->vertex, label_storage.first, oldest_active_start_timestamp)) {
-        vertices_acc.remove(*it);
-      }
-
-      it = next_it;
-    }
-  }
-}
-
-LabelIndex::Iterable::Iterator::Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator)
-    : self_(self),
-      index_iterator_(index_iterator),
-      current_vertex_accessor_(nullptr, nullptr, nullptr, nullptr, self_->config_),
-      current_vertex_(nullptr) {
-  AdvanceUntilValid();
-}
-
-LabelIndex::Iterable::Iterator &LabelIndex::Iterable::Iterator::operator++() {
-  ++index_iterator_;
-  AdvanceUntilValid();
-  return *this;
-}
-
-void LabelIndex::Iterable::Iterator::AdvanceUntilValid() {
-  for (; index_iterator_ != self_->index_accessor_.end(); ++index_iterator_) {
-    if (index_iterator_->vertex == current_vertex_) {
-      continue;
-    }
-    if (CurrentVersionHasLabel(*index_iterator_->vertex, self_->label_, self_->transaction_, self_->view_)) {
-      current_vertex_ = index_iterator_->vertex;
-      current_vertex_accessor_ =
-          VertexAccessor{current_vertex_, self_->transaction_, self_->indices_, self_->constraints_, self_->config_};
-      break;
-    }
-  }
-}
-
-LabelIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, View view,
-                               Transaction *transaction, Indices *indices, Constraints *constraints,
-                               Config::Items config)
-    : index_accessor_(std::move(index_accessor)),
-      label_(label),
-      view_(view),
-      transaction_(transaction),
-      indices_(indices),
-      constraints_(constraints),
-      config_(config) {}
-
-void LabelIndex::RunGC() {
-  for (auto &index_entry : index_) {
-    index_entry.second.run_gc();
-  }
-}
-
-void LabelIndex::SetIndexStats(const storage::LabelId &label, const storage::LabelIndexStats &stats) {
-  stats_[label] = stats;
-}
-
-std::optional<LabelIndexStats> LabelIndex::GetIndexStats(const storage::LabelId &label) const {
-  if (auto it = stats_.find(label); it != stats_.end()) {
-    return it->second;
-  }
-  return {};
-}
-
-std::vector<LabelId> LabelIndex::ClearIndexStats() {
-  std::vector<LabelId> deleted_indexes;
-  deleted_indexes.reserve(stats_.size());
-  std::transform(stats_.begin(), stats_.end(), std::back_inserter(deleted_indexes),
-                 [](const auto &elem) { return elem.first; });
-  stats_.clear();
-  return deleted_indexes;
-}
-
-std::vector<LabelId> LabelIndex::DeleteIndexStats(const storage::LabelId &label) {
-  std::vector<LabelId> deleted_indexes;
-  for (auto it = stats_.cbegin(); it != stats_.cend();) {
-    if (it->first == label) {
-      deleted_indexes.push_back(it->first);
-      it = stats_.erase(it);
-    } else {
-      ++it;
-    }
-  }
-
-  return deleted_indexes;
-}
-
-bool LabelPropertyIndex::Entry::operator<(const Entry &rhs) {
-  if (value < rhs.value) {
-    return true;
-  }
-  if (rhs.value < value) {
-    return false;
-  }
-  return std::make_tuple(vertex, timestamp) < std::make_tuple(rhs.vertex, rhs.timestamp);
-}
-
-bool LabelPropertyIndex::Entry::operator==(const Entry &rhs) {
-  return value == rhs.value && vertex == rhs.vertex && timestamp == rhs.timestamp;
-}
-
-bool LabelPropertyIndex::Entry::operator<(const PropertyValue &rhs) { return value < rhs; }
-
-bool LabelPropertyIndex::Entry::operator==(const PropertyValue &rhs) { return value == rhs; }
-
-void LabelPropertyIndex::UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx) {
-  for (auto &[label_prop, storage] : index_) {
-    if (label_prop.first != label) {
-      continue;
-    }
-    auto prop_value = vertex->properties.GetProperty(label_prop.second);
-    if (!prop_value.IsNull()) {
-      auto acc = storage.access();
-      acc.insert(Entry{std::move(prop_value), vertex, tx.start_timestamp});
-    }
-  }
-}
-
-void LabelPropertyIndex::UpdateOnSetProperty(PropertyId property, const PropertyValue &value, Vertex *vertex,
-                                             const Transaction &tx) {
-  if (value.IsNull()) {
-    return;
-  }
-  for (auto &[label_prop, storage] : index_) {
-    if (label_prop.second != property) {
-      continue;
-    }
-    if (utils::Contains(vertex->labels, label_prop.first)) {
-      auto acc = storage.access();
-      acc.insert(Entry{value, vertex, tx.start_timestamp});
-    }
-  }
-}
-
-bool LabelPropertyIndex::CreateIndex(LabelId label, PropertyId property, utils::SkipList<Vertex>::Accessor vertices,
-                                     const std::optional<ParalellizedIndexCreationInfo> &paralell_exec_info) {
-  auto create_index_seq = [this](LabelId label, PropertyId property, utils::SkipList<Vertex>::Accessor &vertices,
-                                 std::map<std::pair<LabelId, PropertyId>, utils::SkipList<Entry>>::iterator it) {
-    using IndexAccessor = decltype(it->second.access());
-
-    CreateIndexOnSingleThread(vertices, it, index_, std::make_pair(label, property),
-                              [](Vertex &vertex, std::pair<LabelId, PropertyId> key, IndexAccessor &index_accessor) {
-                                TryInsertLabelPropertyIndex(vertex, key, index_accessor);
-                              });
-
-    return true;
-  };
-
-  auto create_index_par =
-      [this](LabelId label, PropertyId property, utils::SkipList<Vertex>::Accessor &vertices,
-             std::map<std::pair<LabelId, PropertyId>, utils::SkipList<Entry>>::iterator label_property_it,
-             const ParalellizedIndexCreationInfo &paralell_exec_info) {
-        using IndexAccessor = decltype(label_property_it->second.access());
-
-        CreateIndexOnMultipleThreads(
-            vertices, label_property_it, index_, std::make_pair(label, property), paralell_exec_info,
-            [](Vertex &vertex, std::pair<LabelId, PropertyId> key, IndexAccessor &index_accessor) {
-              TryInsertLabelPropertyIndex(vertex, key, index_accessor);
-            });
-
-        return true;
-      };
-
-  auto [it, emplaced] =
-      index_.emplace(std::piecewise_construct, std::forward_as_tuple(label, property), std::forward_as_tuple());
-  if (!emplaced) {
-    // Index already exists.
-    return false;
-  }
-
-  if (paralell_exec_info) {
-    return create_index_par(label, property, vertices, it, *paralell_exec_info);
-  }
-  return create_index_seq(label, property, vertices, it);
-}
-
-std::vector<std::pair<LabelId, PropertyId>> LabelPropertyIndex::ListIndices() const {
-  std::vector<std::pair<LabelId, PropertyId>> ret;
-  ret.reserve(index_.size());
-  for (const auto &item : index_) {
-    ret.push_back(item.first);
-  }
-  return ret;
-}
-
-void LabelPropertyIndex::RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp) {
-  for (auto &[label_property, index] : index_) {
-    auto index_acc = index.access();
-    for (auto it = index_acc.begin(); it != index_acc.end();) {
-      auto next_it = it;
-      ++next_it;
-
-      if (it->timestamp >= oldest_active_start_timestamp) {
-        it = next_it;
-        continue;
-      }
-
-      if ((next_it != index_acc.end() && it->vertex == next_it->vertex && it->value == next_it->value) ||
-          !AnyVersionHasLabelProperty(*it->vertex, label_property.first, label_property.second, it->value,
-                                      oldest_active_start_timestamp)) {
-        index_acc.remove(*it);
-      }
-      it = next_it;
-    }
-  }
-}
-
-LabelPropertyIndex::Iterable::Iterator::Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator)
-    : self_(self),
-      index_iterator_(index_iterator),
-      current_vertex_accessor_(nullptr, nullptr, nullptr, nullptr, self_->config_),
-      current_vertex_(nullptr) {
-  AdvanceUntilValid();
-}
-
-LabelPropertyIndex::Iterable::Iterator &LabelPropertyIndex::Iterable::Iterator::operator++() {
-  ++index_iterator_;
-  AdvanceUntilValid();
-  return *this;
-}
-
-void LabelPropertyIndex::Iterable::Iterator::AdvanceUntilValid() {
-  for (; index_iterator_ != self_->index_accessor_.end(); ++index_iterator_) {
-    if (index_iterator_->vertex == current_vertex_) {
-      continue;
-    }
-
-    if (self_->lower_bound_) {
-      if (index_iterator_->value < self_->lower_bound_->value()) {
-        continue;
-      }
-      if (!self_->lower_bound_->IsInclusive() && index_iterator_->value == self_->lower_bound_->value()) {
-        continue;
-      }
-    }
-    if (self_->upper_bound_) {
-      if (self_->upper_bound_->value() < index_iterator_->value) {
-        index_iterator_ = self_->index_accessor_.end();
-        break;
-      }
-      if (!self_->upper_bound_->IsInclusive() && index_iterator_->value == self_->upper_bound_->value()) {
-        index_iterator_ = self_->index_accessor_.end();
-        break;
-      }
-    }
-
-    if (CurrentVersionHasLabelProperty(*index_iterator_->vertex, self_->label_, self_->property_,
-                                       index_iterator_->value, self_->transaction_, self_->view_)) {
-      current_vertex_ = index_iterator_->vertex;
-      current_vertex_accessor_ =
-          VertexAccessor(current_vertex_, self_->transaction_, self_->indices_, self_->constraints_, self_->config_);
-      break;
-    }
-  }
-}
-
-// These constants represent the smallest possible value of each type that is
-// contained in a `PropertyValue`. Note that numbers (integers and doubles) are
-// treated as the same "type" in `PropertyValue`.
-const PropertyValue kSmallestBool = PropertyValue(false);
-static_assert(-std::numeric_limits<double>::infinity() < std::numeric_limits<int64_t>::min());
-const PropertyValue kSmallestNumber = PropertyValue(-std::numeric_limits<double>::infinity());
-const PropertyValue kSmallestString = PropertyValue("");
-const PropertyValue kSmallestList = PropertyValue(std::vector<PropertyValue>());
-const PropertyValue kSmallestMap = PropertyValue(std::map<std::string, PropertyValue>());
-const PropertyValue kSmallestTemporalData =
-    PropertyValue(TemporalData{static_cast<TemporalType>(0), std::numeric_limits<int64_t>::min()});
-
-LabelPropertyIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label,
-                                       PropertyId property,
-                                       const std::optional<utils::Bound<PropertyValue>> &lower_bound,
-                                       const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view,
-                                       Transaction *transaction, Indices *indices, Constraints *constraints,
-                                       Config::Items config)
-    : index_accessor_(std::move(index_accessor)),
-      label_(label),
-      property_(property),
-      lower_bound_(lower_bound),
-      upper_bound_(upper_bound),
-      view_(view),
-      transaction_(transaction),
-      indices_(indices),
-      constraints_(constraints),
-      config_(config) {
-  // We have to fix the bounds that the user provided to us. If the user
-  // provided only one bound we should make sure that only values of that type
-  // are returned by the iterator. We ensure this by supplying either an
-  // inclusive lower bound of the same type, or an exclusive upper bound of the
-  // following type. If neither bound is set we yield all items in the index.
-
-  // First we statically verify that our assumptions about the `PropertyValue`
-  // type ordering holds.
-  static_assert(PropertyValue::Type::Bool < PropertyValue::Type::Int);
-  static_assert(PropertyValue::Type::Int < PropertyValue::Type::Double);
-  static_assert(PropertyValue::Type::Double < PropertyValue::Type::String);
-  static_assert(PropertyValue::Type::String < PropertyValue::Type::List);
-  static_assert(PropertyValue::Type::List < PropertyValue::Type::Map);
-
-  // Remove any bounds that are set to `Null` because that isn't a valid value.
-  if (lower_bound_ && lower_bound_->value().IsNull()) {
-    lower_bound_ = std::nullopt;
-  }
-  if (upper_bound_ && upper_bound_->value().IsNull()) {
-    upper_bound_ = std::nullopt;
-  }
-
-  // Check whether the bounds are of comparable types if both are supplied.
-  if (lower_bound_ && upper_bound_ &&
-      !PropertyValue::AreComparableTypes(lower_bound_->value().type(), upper_bound_->value().type())) {
-    bounds_valid_ = false;
-    return;
-  }
-
-  // Set missing bounds.
-  if (lower_bound_ && !upper_bound_) {
-    // Here we need to supply an upper bound. The upper bound is set to an
-    // exclusive lower bound of the following type.
-    switch (lower_bound_->value().type()) {
-      case PropertyValue::Type::Null:
-        // This shouldn't happen because of the nullopt-ing above.
-        LOG_FATAL("Invalid database state!");
-        break;
-      case PropertyValue::Type::Bool:
-        upper_bound_ = utils::MakeBoundExclusive(kSmallestNumber);
-        break;
-      case PropertyValue::Type::Int:
-      case PropertyValue::Type::Double:
-        // Both integers and doubles are treated as the same type in
-        // `PropertyValue` and they are interleaved when sorted.
-        upper_bound_ = utils::MakeBoundExclusive(kSmallestString);
-        break;
-      case PropertyValue::Type::String:
-        upper_bound_ = utils::MakeBoundExclusive(kSmallestList);
-        break;
-      case PropertyValue::Type::List:
-        upper_bound_ = utils::MakeBoundExclusive(kSmallestMap);
-        break;
-      case PropertyValue::Type::Map:
-        upper_bound_ = utils::MakeBoundExclusive(kSmallestTemporalData);
-        break;
-      case PropertyValue::Type::TemporalData:
-        // This is the last type in the order so we leave the upper bound empty.
-        break;
-    }
-  }
-  if (upper_bound_ && !lower_bound_) {
-    // Here we need to supply a lower bound. The lower bound is set to an
-    // inclusive lower bound of the current type.
-    switch (upper_bound_->value().type()) {
-      case PropertyValue::Type::Null:
-        // This shouldn't happen because of the nullopt-ing above.
-        LOG_FATAL("Invalid database state!");
-        break;
-      case PropertyValue::Type::Bool:
-        lower_bound_ = utils::MakeBoundInclusive(kSmallestBool);
-        break;
-      case PropertyValue::Type::Int:
-      case PropertyValue::Type::Double:
-        // Both integers and doubles are treated as the same type in
-        // `PropertyValue` and they are interleaved when sorted.
-        lower_bound_ = utils::MakeBoundInclusive(kSmallestNumber);
-        break;
-      case PropertyValue::Type::String:
-        lower_bound_ = utils::MakeBoundInclusive(kSmallestString);
-        break;
-      case PropertyValue::Type::List:
-        lower_bound_ = utils::MakeBoundInclusive(kSmallestList);
-        break;
-      case PropertyValue::Type::Map:
-        lower_bound_ = utils::MakeBoundInclusive(kSmallestMap);
-        break;
-      case PropertyValue::Type::TemporalData:
-        lower_bound_ = utils::MakeBoundInclusive(kSmallestTemporalData);
-        break;
-    }
-  }
-}
-
-LabelPropertyIndex::Iterable::Iterator LabelPropertyIndex::Iterable::begin() {
-  // If the bounds are set and don't have comparable types we don't yield any
-  // items from the index.
-  if (!bounds_valid_) return Iterator(this, index_accessor_.end());
-  auto index_iterator = index_accessor_.begin();
-  if (lower_bound_) {
-    index_iterator = index_accessor_.find_equal_or_greater(lower_bound_->value());
-  }
-  return Iterator(this, index_iterator);
-}
-
-LabelPropertyIndex::Iterable::Iterator LabelPropertyIndex::Iterable::end() {
-  return Iterator(this, index_accessor_.end());
-}
-
-int64_t LabelPropertyIndex::ApproximateVertexCount(LabelId label, PropertyId property,
-                                                   const PropertyValue &value) const {
-  auto it = index_.find({label, property});
-  MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(), property.AsUint());
-  auto acc = it->second.access();
-  if (!value.IsNull()) {
-    return acc.estimate_count(value, utils::SkipListLayerForCountEstimation(acc.size()));
-  } else {
-    // The value `Null` won't ever appear in the index because it indicates that
-    // the property shouldn't exist. Instead, this value is used as an indicator
-    // to estimate the average number of equal elements in the list (for any
-    // given value).
-    return acc.estimate_average_number_of_equals(
-        [](const auto &first, const auto &second) { return first.value == second.value; },
-        utils::SkipListLayerForAverageEqualsEstimation(acc.size()));
-  }
-}
-
-int64_t LabelPropertyIndex::ApproximateVertexCount(LabelId label, PropertyId property,
-                                                   const std::optional<utils::Bound<PropertyValue>> &lower,
-                                                   const std::optional<utils::Bound<PropertyValue>> &upper) const {
-  auto it = index_.find({label, property});
-  MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(), property.AsUint());
-  auto acc = it->second.access();
-  return acc.estimate_range_count(lower, upper, utils::SkipListLayerForCountEstimation(acc.size()));
-}
-
-/*
-Iterate over all property-label pairs and deletes if label from the index is equal to label parameter.
-*/
-std::vector<std::pair<LabelId, PropertyId>> LabelPropertyIndex::DeleteIndexStats(const storage::LabelId &label) {
-  std::vector<std::pair<LabelId, PropertyId>> deleted_indexes;
-  for (auto it = stats_.cbegin(); it != stats_.cend();) {
-    if (it->first.first == label) {
-      deleted_indexes.push_back(it->first);
-      it = stats_.erase(it);
-    } else {
-      ++it;
-    }
-  }
-  return deleted_indexes;
-}
-
-std::vector<std::pair<LabelId, PropertyId>> LabelPropertyIndex::ClearIndexStats() {
-  std::vector<std::pair<LabelId, PropertyId>> deleted_indexes;
-  deleted_indexes.reserve(stats_.size());
-  std::transform(stats_.begin(), stats_.end(), std::back_inserter(deleted_indexes),
-                 [](const auto &elem) { return elem.first; });
-  stats_.clear();
-  return deleted_indexes;
-}
-
-void LabelPropertyIndex::SetIndexStats(const std::pair<storage::LabelId, storage::PropertyId> &key,
-                                       const storage::LabelPropertyIndexStats &stats) {
-  stats_[key] = stats;
-}
-
-std::optional<storage::LabelPropertyIndexStats> LabelPropertyIndex::GetIndexStats(
-    const std::pair<storage::LabelId, storage::PropertyId> &key) const {
-  if (auto it = stats_.find(key); it != stats_.end()) {
-    return it->second;
-  }
-  return {};
-}
-
-void LabelPropertyIndex::RunGC() {
-  for (auto &index_entry : index_) {
-    index_entry.second.run_gc();
-  }
-}
-
-void RemoveObsoleteEntries(Indices *indices, uint64_t oldest_active_start_timestamp) {
-  indices->label_index.RemoveObsoleteEntries(oldest_active_start_timestamp);
-  indices->label_property_index.RemoveObsoleteEntries(oldest_active_start_timestamp);
-}
-
-void UpdateOnAddLabel(Indices *indices, LabelId label, Vertex *vertex, const Transaction &tx) {
-  indices->label_index.UpdateOnAddLabel(label, vertex, tx);
-  indices->label_property_index.UpdateOnAddLabel(label, vertex, tx);
-}
-
-void UpdateOnSetProperty(Indices *indices, PropertyId property, const PropertyValue &value, Vertex *vertex,
-                         const Transaction &tx) {
-  indices->label_property_index.UpdateOnSetProperty(property, value, vertex, tx);
-}
-
-}  // namespace memgraph::storage
diff --git a/src/storage/v2/indices.hpp b/src/storage/v2/indices.hpp
deleted file mode 100644
index b5dc28114..000000000
--- a/src/storage/v2/indices.hpp
+++ /dev/null
@@ -1,317 +0,0 @@
-// Copyright 2023 Memgraph Ltd.
-//
-// Use of this software is governed by the Business Source License
-// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
-// License, and you may not use this file except in compliance with the Business Source License.
-//
-// As of the Change Date specified in that file, in accordance with
-// the Business Source License, use of this software will be governed
-// by the Apache License, Version 2.0, included in the file
-// licenses/APL.txt.
-
-#pragma once
-
-#include <optional>
-#include <tuple>
-#include <utility>
-
-#include "storage/v2/config.hpp"
-#include "storage/v2/property_value.hpp"
-#include "storage/v2/transaction.hpp"
-#include "storage/v2/vertex_accessor.hpp"
-#include "utils/bound.hpp"
-#include "utils/logging.hpp"
-#include "utils/skip_list.hpp"
-
-namespace memgraph::storage {
-
-struct Indices;
-struct Constraints;
-
-using ParalellizedIndexCreationInfo =
-    std::pair<std::vector<std::pair<Gid, uint64_t>> /*vertex_recovery_info*/, uint64_t /*thread_count*/>;
-
-struct LabelIndexStats {
-  uint64_t count;
-  double avg_degree;
-};
-
-class LabelIndex {
- private:
-  struct Entry {
-    Vertex *vertex;
-    uint64_t timestamp;
-
-    bool operator<(const Entry &rhs) {
-      return std::make_tuple(vertex, timestamp) < std::make_tuple(rhs.vertex, rhs.timestamp);
-    }
-    bool operator==(const Entry &rhs) { return vertex == rhs.vertex && timestamp == rhs.timestamp; }
-  };
-
-  struct LabelStorage {
-    LabelId label;
-    utils::SkipList<Entry> vertices;
-
-    bool operator<(const LabelStorage &rhs) { return label < rhs.label; }
-    bool operator<(LabelId rhs) { return label < rhs; }
-    bool operator==(const LabelStorage &rhs) { return label == rhs.label; }
-    bool operator==(LabelId rhs) { return label == rhs; }
-  };
-
- public:
-  LabelIndex(Indices *indices, Constraints *constraints, Config::Items config)
-      : indices_(indices), constraints_(constraints), config_(config) {}
-
-  /// @throw std::bad_alloc
-  void UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx);
-
-  /// @throw std::bad_alloc
-  bool CreateIndex(LabelId label, utils::SkipList<Vertex>::Accessor vertices,
-                   const std::optional<ParalellizedIndexCreationInfo> &paralell_exec_info = std::nullopt);
-
-  /// Returns false if there was no index to drop
-  bool DropIndex(LabelId label) { return index_.erase(label) > 0; }
-
-  bool IndexExists(LabelId label) const { return index_.find(label) != index_.end(); }
-
-  std::vector<LabelId> ListIndices() const;
-
-  void RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp);
-
-  class Iterable {
-   public:
-    Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, View view, Transaction *transaction,
-             Indices *indices, Constraints *constraints, Config::Items config);
-
-    class Iterator {
-     public:
-      Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator);
-
-      VertexAccessor operator*() const { return current_vertex_accessor_; }
-
-      bool operator==(const Iterator &other) const { return index_iterator_ == other.index_iterator_; }
-      bool operator!=(const Iterator &other) const { return index_iterator_ != other.index_iterator_; }
-
-      Iterator &operator++();
-
-     private:
-      void AdvanceUntilValid();
-
-      Iterable *self_;
-      utils::SkipList<Entry>::Iterator index_iterator_;
-      VertexAccessor current_vertex_accessor_;
-      Vertex *current_vertex_;
-    };
-
-    Iterator begin() { return Iterator(this, index_accessor_.begin()); }
-    Iterator end() { return Iterator(this, index_accessor_.end()); }
-
-   private:
-    utils::SkipList<Entry>::Accessor index_accessor_;
-    LabelId label_;
-    View view_;
-    Transaction *transaction_;
-    Indices *indices_;
-    Constraints *constraints_;
-    Config::Items config_;
-  };
-
-  /// Returns an self with vertices visible from the given transaction.
-  Iterable Vertices(LabelId label, View view, Transaction *transaction) {
-    auto it = index_.find(label);
-    MG_ASSERT(it != index_.end(), "Index for label {} doesn't exist", label.AsUint());
-    return Iterable(it->second.access(), label, view, transaction, indices_, constraints_, config_);
-  }
-
-  int64_t ApproximateVertexCount(LabelId label) {
-    auto it = index_.find(label);
-    MG_ASSERT(it != index_.end(), "Index for label {} doesn't exist", label.AsUint());
-    return it->second.size();
-  }
-
-  void SetIndexStats(const storage::LabelId &label, const storage::LabelIndexStats &stats);
-
-  std::optional<storage::LabelIndexStats> GetIndexStats(const storage::LabelId &label) const;
-
-  std::vector<LabelId> ClearIndexStats();
-
-  std::vector<LabelId> DeleteIndexStats(const storage::LabelId &label);
-
-  void Clear() { index_.clear(); }
-
-  void RunGC();
-
- private:
-  std::map<LabelId, utils::SkipList<Entry>> index_;
-  std::map<LabelId, storage::LabelIndexStats> stats_;
-  Indices *indices_;
-  Constraints *constraints_;
-  Config::Items config_;
-};
-
-struct LabelPropertyIndexStats {
-  uint64_t count, distinct_values_count;
-  double statistic, avg_group_size, avg_degree;
-};
-
-class LabelPropertyIndex {
- private:
-  struct Entry {
-    PropertyValue value;
-    Vertex *vertex;
-    uint64_t timestamp;
-
-    bool operator<(const Entry &rhs);
-    bool operator==(const Entry &rhs);
-
-    bool operator<(const PropertyValue &rhs);
-    bool operator==(const PropertyValue &rhs);
-  };
-
- public:
-  LabelPropertyIndex(Indices *indices, Constraints *constraints, Config::Items config)
-      : indices_(indices), constraints_(constraints), config_(config) {}
-
-  /// @throw std::bad_alloc
-  void UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx);
-
-  /// @throw std::bad_alloc
-  void UpdateOnSetProperty(PropertyId property, const PropertyValue &value, Vertex *vertex, const Transaction &tx);
-
-  /// @throw std::bad_alloc
-  bool CreateIndex(LabelId label, PropertyId property, utils::SkipList<Vertex>::Accessor vertices,
-                   const std::optional<ParalellizedIndexCreationInfo> &paralell_exec_info = std::nullopt);
-
-  bool DropIndex(LabelId label, PropertyId property) { return index_.erase({label, property}) > 0; }
-
-  bool IndexExists(LabelId label, PropertyId property) const { return index_.find({label, property}) != index_.end(); }
-
-  std::vector<std::pair<LabelId, PropertyId>> ListIndices() const;
-
-  void RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp);
-
-  class Iterable {
-   public:
-    Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, PropertyId property,
-             const std::optional<utils::Bound<PropertyValue>> &lower_bound,
-             const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction,
-             Indices *indices, Constraints *constraints, Config::Items config);
-
-    class Iterator {
-     public:
-      Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator);
-
-      VertexAccessor operator*() const { return current_vertex_accessor_; }
-
-      bool operator==(const Iterator &other) const { return index_iterator_ == other.index_iterator_; }
-      bool operator!=(const Iterator &other) const { return index_iterator_ != other.index_iterator_; }
-
-      Iterator &operator++();
-
-     private:
-      void AdvanceUntilValid();
-
-      Iterable *self_;
-      utils::SkipList<Entry>::Iterator index_iterator_;
-      VertexAccessor current_vertex_accessor_;
-      Vertex *current_vertex_;
-    };
-
-    Iterator begin();
-    Iterator end();
-
-   private:
-    utils::SkipList<Entry>::Accessor index_accessor_;
-    LabelId label_;
-    PropertyId property_;
-    std::optional<utils::Bound<PropertyValue>> lower_bound_;
-    std::optional<utils::Bound<PropertyValue>> upper_bound_;
-    bool bounds_valid_{true};
-    View view_;
-    Transaction *transaction_;
-    Indices *indices_;
-    Constraints *constraints_;
-    Config::Items config_;
-  };
-
-  Iterable Vertices(LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
-                    const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view,
-                    Transaction *transaction) {
-    auto it = index_.find({label, property});
-    MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(),
-              property.AsUint());
-    return Iterable(it->second.access(), label, property, lower_bound, upper_bound, view, transaction, indices_,
-                    constraints_, config_);
-  }
-
-  int64_t ApproximateVertexCount(LabelId label, PropertyId property) const {
-    auto it = index_.find({label, property});
-    MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(),
-              property.AsUint());
-    return it->second.size();
-  }
-
-  /// Supplying a specific value into the count estimation function will return
-  /// an estimated count of nodes which have their property's value set to
-  /// `value`. If the `value` specified is `Null`, then an average number of
-  /// equal elements is returned.
-  int64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const;
-
-  int64_t ApproximateVertexCount(LabelId label, PropertyId property,
-                                 const std::optional<utils::Bound<PropertyValue>> &lower,
-                                 const std::optional<utils::Bound<PropertyValue>> &upper) const;
-
-  std::vector<std::pair<LabelId, PropertyId>> ClearIndexStats();
-
-  std::vector<std::pair<LabelId, PropertyId>> DeleteIndexStats(const storage::LabelId &label);
-
-  void SetIndexStats(const std::pair<storage::LabelId, storage::PropertyId> &key,
-                     const storage::LabelPropertyIndexStats &stats);
-
-  std::optional<storage::LabelPropertyIndexStats> GetIndexStats(
-      const std::pair<storage::LabelId, storage::PropertyId> &key) const;
-
-  void Clear() { index_.clear(); }
-
-  void RunGC();
-
- private:
-  std::map<std::pair<LabelId, PropertyId>, utils::SkipList<Entry>> index_;
-  std::map<std::pair<LabelId, PropertyId>, storage::LabelPropertyIndexStats> stats_;
-  Indices *indices_;
-  Constraints *constraints_;
-  Config::Items config_;
-};
-
-struct Indices {
-  Indices(Constraints *constraints, Config::Items config)
-      : label_index(this, constraints, config), label_property_index(this, constraints, config) {}
-
-  // Disable copy and move because members hold pointer to `this`.
-  Indices(const Indices &) = delete;
-  Indices(Indices &&) = delete;
-  Indices &operator=(const Indices &) = delete;
-  Indices &operator=(Indices &&) = delete;
-  ~Indices() = default;
-
-  LabelIndex label_index;
-  LabelPropertyIndex label_property_index;
-};
-
-/// This function should be called from garbage collection to clean-up the
-/// index.
-void RemoveObsoleteEntries(Indices *indices, uint64_t oldest_active_start_timestamp);
-
-// Indices are updated whenever an update occurs, instead of only on commit or
-// advance command. This is necessary because we want indices to support `NEW`
-// view for use in Merge.
-
-/// This function should be called whenever a label is added to a vertex.
-/// @throw std::bad_alloc
-void UpdateOnAddLabel(Indices *indices, LabelId label, Vertex *vertex, const Transaction &tx);
-
-/// This function should be called whenever a property is modified on a vertex.
-/// @throw std::bad_alloc
-void UpdateOnSetProperty(Indices *indices, PropertyId property, const PropertyValue &value, Vertex *vertex,
-                         const Transaction &tx);
-}  // namespace memgraph::storage
diff --git a/src/storage/v2/indices/indices.cpp b/src/storage/v2/indices/indices.cpp
new file mode 100644
index 000000000..59915ab18
--- /dev/null
+++ b/src/storage/v2/indices/indices.cpp
@@ -0,0 +1,38 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include "storage/v2/indices/indices.hpp"
+#include "storage/v2/inmemory/label_index.hpp"
+
+namespace memgraph::storage {
+
+void Indices::RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp) const {
+  static_cast<InMemoryLabelIndex *>(label_index_.get())->RemoveObsoleteEntries(oldest_active_start_timestamp);
+  static_cast<InMemoryLabelPropertyIndex *>(label_property_index_.get())
+      ->RemoveObsoleteEntries(oldest_active_start_timestamp);
+}
+
+void Indices::UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx) const {
+  label_index_->UpdateOnAddLabel(label, vertex, tx);
+  label_property_index_->UpdateOnAddLabel(label, vertex, tx);
+}
+
+void Indices::UpdateOnRemoveLabel(LabelId label, Vertex *vertex, const Transaction &tx) const {
+  label_index_->UpdateOnRemoveLabel(label, vertex, tx);
+  label_property_index_->UpdateOnRemoveLabel(label, vertex, tx);
+}
+
+void Indices::UpdateOnSetProperty(PropertyId property, const PropertyValue &value, Vertex *vertex,
+                                  const Transaction &tx) const {
+  label_property_index_->UpdateOnSetProperty(property, value, vertex, tx);
+}
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/indices/indices.hpp b/src/storage/v2/indices/indices.hpp
new file mode 100644
index 000000000..06d0837c4
--- /dev/null
+++ b/src/storage/v2/indices/indices.hpp
@@ -0,0 +1,68 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include <memory>
+#include "storage/v2/disk/label_index.hpp"
+#include "storage/v2/disk/label_property_index.hpp"
+#include "storage/v2/indices/label_index.hpp"
+#include "storage/v2/indices/label_property_index.hpp"
+#include "storage/v2/inmemory/label_index.hpp"
+#include "storage/v2/inmemory/label_property_index.hpp"
+#include "storage/v2/storage_mode.hpp"
+
+namespace memgraph::storage {
+
+struct Indices {
+  Indices(Constraints *constraints, const Config &config, StorageMode storage_mode) {
+    std::invoke([this, constraints, config, storage_mode]() {
+      if (storage_mode == StorageMode::IN_MEMORY_TRANSACTIONAL || storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
+        label_index_ = std::make_unique<InMemoryLabelIndex>(this, constraints, config);
+        label_property_index_ = std::make_unique<InMemoryLabelPropertyIndex>(this, constraints, config);
+      } else {
+        label_index_ = std::make_unique<DiskLabelIndex>(this, constraints, config);
+        label_property_index_ = std::make_unique<DiskLabelPropertyIndex>(this, constraints, config);
+      }
+    });
+  }
+
+  Indices(const Indices &) = delete;
+  Indices(Indices &&) = delete;
+  Indices &operator=(const Indices &) = delete;
+  Indices &operator=(Indices &&) = delete;
+  ~Indices() = default;
+
+  /// This function should be called from garbage collection to clean-up the
+  /// index.
+  /// TODO: unused in disk indices
+  void RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp) const;
+
+  // Indices are updated whenever an update occurs, instead of only on commit or
+  // advance command. This is necessary because we want indices to support `NEW`
+  // view for use in Merge.
+
+  /// This function should be called whenever a label is added to a vertex.
+  /// @throw std::bad_alloc
+  void UpdateOnAddLabel(LabelId label, Vertex *vertex, const Transaction &tx) const;
+
+  void UpdateOnRemoveLabel(LabelId label, Vertex *vertex, const Transaction &tx) const;
+
+  /// This function should be called whenever a property is modified on a vertex.
+  /// @throw std::bad_alloc
+  void UpdateOnSetProperty(PropertyId property, const PropertyValue &value, Vertex *vertex,
+                           const Transaction &tx) const;
+
+  std::unique_ptr<LabelIndex> label_index_;
+  std::unique_ptr<LabelPropertyIndex> label_property_index_;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/indices/label_index.hpp b/src/storage/v2/indices/label_index.hpp
new file mode 100644
index 000000000..977e4e7a7
--- /dev/null
+++ b/src/storage/v2/indices/label_index.hpp
@@ -0,0 +1,51 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include "storage/v2/constraints/constraints.hpp"
+#include "storage/v2/vertex.hpp"
+#include "storage/v2/vertex_accessor.hpp"
+
+namespace memgraph::storage {
+
+class LabelIndex {
+ public:
+  LabelIndex(Indices *indices, Constraints *constraints, const Config &config)
+      : indices_(indices), constraints_(constraints), config_(config) {}
+
+  LabelIndex(const LabelIndex &) = delete;
+  LabelIndex(LabelIndex &&) = delete;
+  LabelIndex &operator=(const LabelIndex &) = delete;
+  LabelIndex &operator=(LabelIndex &&) = delete;
+
+  virtual ~LabelIndex() = default;
+
+  virtual void UpdateOnAddLabel(LabelId added_label, Vertex *vertex_after_update, const Transaction &tx) = 0;
+
+  virtual void UpdateOnRemoveLabel(LabelId removed_label, Vertex *vertex_after_update, const Transaction &tx) = 0;
+
+  virtual bool DropIndex(LabelId label) = 0;
+
+  virtual bool IndexExists(LabelId label) const = 0;
+
+  virtual std::vector<LabelId> ListIndices() const = 0;
+
+  virtual uint64_t ApproximateVertexCount(LabelId label) const = 0;
+
+ protected:
+  /// TODO: andi maybe no need for have those in abstract class if disk storage isn't using it
+  Indices *indices_;
+  Constraints *constraints_;
+  Config config_;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/indices/label_property_index.hpp b/src/storage/v2/indices/label_property_index.hpp
new file mode 100644
index 000000000..5d9ed90e3
--- /dev/null
+++ b/src/storage/v2/indices/label_property_index.hpp
@@ -0,0 +1,59 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include "storage/v2/constraints/constraints.hpp"
+#include "storage/v2/vertex.hpp"
+#include "storage/v2/vertex_accessor.hpp"
+
+namespace memgraph::storage {
+
+class LabelPropertyIndex {
+ public:
+  LabelPropertyIndex(Indices *indices, Constraints *constraints, const Config &config)
+      : indices_(indices), constraints_(constraints), config_(config) {}
+
+  LabelPropertyIndex(const LabelPropertyIndex &) = delete;
+  LabelPropertyIndex(LabelPropertyIndex &&) = delete;
+  LabelPropertyIndex &operator=(const LabelPropertyIndex &) = delete;
+  LabelPropertyIndex &operator=(LabelPropertyIndex &&) = delete;
+
+  virtual ~LabelPropertyIndex() = default;
+
+  virtual void UpdateOnAddLabel(LabelId added_label, Vertex *vertex_after_update, const Transaction &tx) = 0;
+
+  virtual void UpdateOnRemoveLabel(LabelId removed_label, Vertex *vertex_after_update, const Transaction &tx) = 0;
+
+  virtual void UpdateOnSetProperty(PropertyId property, const PropertyValue &value, Vertex *vertex,
+                                   const Transaction &tx) = 0;
+
+  virtual bool DropIndex(LabelId label, PropertyId property) = 0;
+
+  virtual bool IndexExists(LabelId label, PropertyId property) const = 0;
+
+  virtual std::vector<std::pair<LabelId, PropertyId>> ListIndices() const = 0;
+
+  virtual uint64_t ApproximateVertexCount(LabelId label, PropertyId property) const = 0;
+
+  virtual uint64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const = 0;
+
+  virtual uint64_t ApproximateVertexCount(LabelId label, PropertyId property,
+                                          const std::optional<utils::Bound<PropertyValue>> &lower,
+                                          const std::optional<utils::Bound<PropertyValue>> &upper) const = 0;
+
+ protected:
+  Indices *indices_;
+  Constraints *constraints_;
+  Config config_;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/inmemory/indices_utils.hpp b/src/storage/v2/inmemory/indices_utils.hpp
new file mode 100644
index 000000000..dbfc8e80c
--- /dev/null
+++ b/src/storage/v2/inmemory/indices_utils.hpp
@@ -0,0 +1,357 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include <thread>
+#include "storage/v2/delta.hpp"
+#include "storage/v2/mvcc.hpp"
+#include "storage/v2/transaction.hpp"
+#include "storage/v2/vertex.hpp"
+#include "utils/spin_lock.hpp"
+#include "utils/synchronized.hpp"
+
+namespace memgraph::storage {
+
+using ParalellizedIndexCreationInfo =
+    std::pair<std::vector<std::pair<Gid, uint64_t>> /*vertex_recovery_info*/, uint64_t /*thread_count*/>;
+
+/// Traverses deltas visible from transaction with start timestamp greater than
+/// the provided timestamp, and calls the provided callback function for each
+/// delta. If the callback ever returns true, traversal is stopped and the
+/// function returns true. Otherwise, the function returns false.
+template <typename TCallback>
+inline bool AnyVersionSatisfiesPredicate(uint64_t timestamp, const Delta *delta, const TCallback &predicate) {
+  while (delta != nullptr) {
+    const auto ts = delta->timestamp->load(std::memory_order_acquire);
+    // This is a committed change that we see so we shouldn't undo it.
+    if (ts < timestamp) {
+      break;
+    }
+    if (predicate(*delta)) {
+      return true;
+    }
+    // Move to the next delta.
+    delta = delta->next.load(std::memory_order_acquire);
+  }
+  return false;
+}
+
+/// Helper function for label index garbage collection. Returns true if there's
+/// a reachable version of the vertex that has the given label.
+inline bool AnyVersionHasLabel(const Vertex &vertex, LabelId label, uint64_t timestamp) {
+  bool has_label{false};
+  bool deleted{false};
+  const Delta *delta = nullptr;
+  {
+    std::lock_guard<utils::SpinLock> guard(vertex.lock);
+    has_label = utils::Contains(vertex.labels, label);
+    deleted = vertex.deleted;
+    delta = vertex.delta;
+  }
+  if (!deleted && has_label) {
+    return true;
+  }
+  return AnyVersionSatisfiesPredicate(timestamp, delta, [&has_label, &deleted, label](const Delta &delta) {
+    switch (delta.action) {
+      case Delta::Action::ADD_LABEL:
+        if (delta.label == label) {
+          MG_ASSERT(!has_label, "Invalid database state!");
+          has_label = true;
+        }
+        break;
+      case Delta::Action::REMOVE_LABEL:
+        if (delta.label == label) {
+          MG_ASSERT(has_label, "Invalid database state!");
+          has_label = false;
+        }
+        break;
+      case Delta::Action::RECREATE_OBJECT: {
+        MG_ASSERT(deleted, "Invalid database state!");
+        deleted = false;
+        break;
+      }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+      case Delta::Action::DELETE_OBJECT: {
+        MG_ASSERT(!deleted, "Invalid database state!");
+        deleted = true;
+        break;
+      }
+      case Delta::Action::SET_PROPERTY:
+      case Delta::Action::ADD_IN_EDGE:
+      case Delta::Action::ADD_OUT_EDGE:
+      case Delta::Action::REMOVE_IN_EDGE:
+      case Delta::Action::REMOVE_OUT_EDGE:
+        break;
+    }
+    return !deleted && has_label;
+  });
+}
+
+/// Helper function for label-property index garbage collection. Returns true if
+/// there's a reachable version of the vertex that has the given label and
+/// property value.
+inline bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, PropertyId key, const PropertyValue &value,
+                                       uint64_t timestamp) {
+  bool has_label{false};
+  bool current_value_equal_to_value{value.IsNull()};
+  bool deleted{false};
+  const Delta *delta = nullptr;
+  {
+    std::lock_guard<utils::SpinLock> guard(vertex.lock);
+    has_label = utils::Contains(vertex.labels, label);
+    current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value);
+    deleted = vertex.deleted;
+    delta = vertex.delta;
+  }
+
+  if (!deleted && has_label && current_value_equal_to_value) {
+    return true;
+  }
+
+  return AnyVersionSatisfiesPredicate(
+      timestamp, delta, [&has_label, &current_value_equal_to_value, &deleted, label, key, &value](const Delta &delta) {
+        switch (delta.action) {
+          case Delta::Action::ADD_LABEL:
+            if (delta.label == label) {
+              MG_ASSERT(!has_label, "Invalid database state!");
+              has_label = true;
+            }
+            break;
+          case Delta::Action::REMOVE_LABEL:
+            if (delta.label == label) {
+              MG_ASSERT(has_label, "Invalid database state!");
+              has_label = false;
+            }
+            break;
+          case Delta::Action::SET_PROPERTY:
+            if (delta.property.key == key) {
+              current_value_equal_to_value = delta.property.value == value;
+            }
+            break;
+          case Delta::Action::RECREATE_OBJECT: {
+            MG_ASSERT(deleted, "Invalid database state!");
+            deleted = false;
+            break;
+          }
+          case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+          case Delta::Action::DELETE_OBJECT: {
+            MG_ASSERT(!deleted, "Invalid database state!");
+            deleted = true;
+            break;
+          }
+          case Delta::Action::ADD_IN_EDGE:
+          case Delta::Action::ADD_OUT_EDGE:
+          case Delta::Action::REMOVE_IN_EDGE:
+          case Delta::Action::REMOVE_OUT_EDGE:
+            break;
+        }
+        return !deleted && has_label && current_value_equal_to_value;
+      });
+}
+
+// Helper function for iterating through label index. Returns true if this
+// transaction can see the given vertex, and the visible version has the given
+// label.
+inline bool CurrentVersionHasLabel(const Vertex &vertex, LabelId label, Transaction *transaction, View view) {
+  bool deleted = false;
+  bool has_label = false;
+  const Delta *delta = nullptr;
+  {
+    std::lock_guard<utils::SpinLock> guard(vertex.lock);
+    deleted = vertex.deleted;
+    has_label = utils::Contains(vertex.labels, label);
+    delta = vertex.delta;
+  }
+  ApplyDeltasForRead(transaction, delta, view, [&deleted, &has_label, label](const Delta &delta) {
+    switch (delta.action) {
+      case Delta::Action::REMOVE_LABEL: {
+        if (delta.label == label) {
+          MG_ASSERT(has_label, "Invalid database state!");
+          has_label = false;
+        }
+        break;
+      }
+      case Delta::Action::ADD_LABEL: {
+        if (delta.label == label) {
+          MG_ASSERT(!has_label, "Invalid database state!");
+          has_label = true;
+        }
+        break;
+      }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+      case Delta::Action::DELETE_OBJECT: {
+        MG_ASSERT(!deleted, "Invalid database state!");
+        deleted = true;
+        break;
+      }
+      case Delta::Action::RECREATE_OBJECT: {
+        MG_ASSERT(deleted, "Invalid database state!");
+        deleted = false;
+        break;
+      }
+      case Delta::Action::SET_PROPERTY:
+      case Delta::Action::ADD_IN_EDGE:
+      case Delta::Action::ADD_OUT_EDGE:
+      case Delta::Action::REMOVE_IN_EDGE:
+      case Delta::Action::REMOVE_OUT_EDGE:
+        break;
+    }
+  });
+  return !deleted && has_label;
+}
+
+// Helper function for iterating through label-property index. Returns true if
+// this transaction can see the given vertex, and the visible version has the
+// given label and property.
+inline bool CurrentVersionHasLabelProperty(const Vertex &vertex, LabelId label, PropertyId key,
+                                           const PropertyValue &value, Transaction *transaction, View view) {
+  bool deleted = false;
+  bool has_label = false;
+  bool current_value_equal_to_value = value.IsNull();
+  const Delta *delta = nullptr;
+  {
+    std::lock_guard<utils::SpinLock> guard(vertex.lock);
+    deleted = vertex.deleted;
+    has_label = utils::Contains(vertex.labels, label);
+    current_value_equal_to_value = vertex.properties.IsPropertyEqual(key, value);
+    delta = vertex.delta;
+  }
+  ApplyDeltasForRead(transaction, delta, view,
+                     [&deleted, &has_label, &current_value_equal_to_value, key, label, &value](const Delta &delta) {
+                       switch (delta.action) {
+                         case Delta::Action::SET_PROPERTY: {
+                           if (delta.property.key == key) {
+                             current_value_equal_to_value = delta.property.value == value;
+                           }
+                           break;
+                         }
+                         case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+                         case Delta::Action::DELETE_OBJECT: {
+                           MG_ASSERT(!deleted, "Invalid database state!");
+                           deleted = true;
+                           break;
+                         }
+                         case Delta::Action::RECREATE_OBJECT: {
+                           MG_ASSERT(deleted, "Invalid database state!");
+                           deleted = false;
+                           break;
+                         }
+                         case Delta::Action::ADD_LABEL:
+                           if (delta.label == label) {
+                             MG_ASSERT(!has_label, "Invalid database state!");
+                             has_label = true;
+                           }
+                           break;
+                         case Delta::Action::REMOVE_LABEL:
+                           if (delta.label == label) {
+                             MG_ASSERT(has_label, "Invalid database state!");
+                             has_label = false;
+                           }
+                           break;
+                         case Delta::Action::ADD_IN_EDGE:
+                         case Delta::Action::ADD_OUT_EDGE:
+                         case Delta::Action::REMOVE_IN_EDGE:
+                         case Delta::Action::REMOVE_OUT_EDGE:
+                           break;
+                       }
+                     });
+  return !deleted && has_label && current_value_equal_to_value;
+}
+
+template <typename TIndexAccessor>
+inline void TryInsertLabelIndex(Vertex &vertex, LabelId label, TIndexAccessor &index_accessor) {
+  if (vertex.deleted || !utils::Contains(vertex.labels, label)) {
+    return;
+  }
+
+  index_accessor.insert({&vertex, 0});
+}
+
+template <typename TIndexAccessor>
+inline void TryInsertLabelPropertyIndex(Vertex &vertex, std::pair<LabelId, PropertyId> label_property_pair,
+                                        TIndexAccessor &index_accessor) {
+  if (vertex.deleted || !utils::Contains(vertex.labels, label_property_pair.first)) {
+    return;
+  }
+  auto value = vertex.properties.GetProperty(label_property_pair.second);
+  if (value.IsNull()) {
+    return;
+  }
+  index_accessor.insert({std::move(value), &vertex, 0});
+}
+
+template <typename TSkiplistIter, typename TIndex, typename TIndexKey, typename TFunc>
+inline void CreateIndexOnSingleThread(utils::SkipList<Vertex>::Accessor &vertices, TSkiplistIter it, TIndex &index,
+                                      TIndexKey key, const TFunc &func) {
+  utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
+  try {
+    auto acc = it->second.access();
+    for (Vertex &vertex : vertices) {
+      func(vertex, key, acc);
+    }
+  } catch (const utils::OutOfMemoryException &) {
+    utils::MemoryTracker::OutOfMemoryExceptionBlocker oom_exception_blocker;
+    index.erase(it);
+    throw;
+  }
+}
+
+template <typename TIndex, typename TIndexKey, typename TSKiplistIter, typename TFunc>
+inline void CreateIndexOnMultipleThreads(utils::SkipList<Vertex>::Accessor &vertices, TSKiplistIter skiplist_iter,
+                                         TIndex &index, TIndexKey key,
+                                         const ParalellizedIndexCreationInfo &paralell_exec_info, const TFunc &func) {
+  utils::MemoryTracker::OutOfMemoryExceptionEnabler oom_exception;
+
+  const auto &vertex_batches = paralell_exec_info.first;
+  const auto thread_count = std::min(paralell_exec_info.second, vertex_batches.size());
+
+  MG_ASSERT(!vertex_batches.empty(),
+            "The size of batches should always be greater than zero if you want to use the parallel version of index "
+            "creation!");
+
+  std::atomic<uint64_t> batch_counter = 0;
+
+  utils::Synchronized<std::optional<utils::OutOfMemoryException>, utils::SpinLock> maybe_error{};
+  {
+    std::vector<std::jthread> threads;
+    threads.reserve(thread_count);
+
+    for (auto i{0U}; i < thread_count; ++i) {
+      threads.emplace_back(
+          [&skiplist_iter, &func, &index, &vertex_batches, &maybe_error, &batch_counter, &key, &vertices]() {
+            while (!maybe_error.Lock()->has_value()) {
+              const auto batch_index = batch_counter++;
+              if (batch_index >= vertex_batches.size()) {
+                return;
+              }
+              const auto &batch = vertex_batches[batch_index];
+              auto index_accessor = index.at(key).access();
+              auto it = vertices.find(batch.first);
+
+              try {
+                for (auto i{0U}; i < batch.second; ++i, ++it) {
+                  func(*it, key, index_accessor);
+                }
+
+              } catch (utils::OutOfMemoryException &failure) {
+                utils::MemoryTracker::OutOfMemoryExceptionBlocker oom_exception_blocker;
+                index.erase(skiplist_iter);
+                *maybe_error.Lock() = std::move(failure);
+              }
+            }
+          });
+    }
+  }
+  if (maybe_error.Lock()->has_value()) {
+    throw utils::OutOfMemoryException((*maybe_error.Lock())->what());
+  }
+}
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/inmemory/label_index.cpp b/src/storage/v2/inmemory/label_index.cpp
new file mode 100644
index 000000000..625590f5e
--- /dev/null
+++ b/src/storage/v2/inmemory/label_index.cpp
@@ -0,0 +1,192 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include "storage/v2/inmemory/label_index.hpp"
+#include "storage/v2/inmemory/indices_utils.hpp"
+
+namespace memgraph::storage {
+
+InMemoryLabelIndex::InMemoryLabelIndex(Indices *indices, Constraints *constraints, Config config)
+    : LabelIndex(indices, constraints, config) {}
+
+void InMemoryLabelIndex::UpdateOnAddLabel(LabelId added_label, Vertex *vertex_after_update, const Transaction &tx) {
+  auto it = index_.find(added_label);
+  if (it == index_.end()) return;
+  auto acc = it->second.access();
+  acc.insert(Entry{vertex_after_update, tx.start_timestamp});
+}
+
+bool InMemoryLabelIndex::CreateIndex(LabelId label, utils::SkipList<Vertex>::Accessor vertices,
+                                     const std::optional<ParalellizedIndexCreationInfo> &paralell_exec_info) {
+  const auto create_index_seq = [this](LabelId label, utils::SkipList<Vertex>::Accessor &vertices,
+                                       std::map<LabelId, utils::SkipList<Entry>>::iterator it) {
+    using IndexAccessor = decltype(it->second.access());
+
+    CreateIndexOnSingleThread(vertices, it, index_, label,
+                              [](Vertex &vertex, LabelId label, IndexAccessor &index_accessor) {
+                                TryInsertLabelIndex(vertex, label, index_accessor);
+                              });
+
+    return true;
+  };
+
+  const auto create_index_par = [this](LabelId label, utils::SkipList<Vertex>::Accessor &vertices,
+                                       std::map<LabelId, utils::SkipList<Entry>>::iterator label_it,
+                                       const ParalellizedIndexCreationInfo &paralell_exec_info) {
+    using IndexAccessor = decltype(label_it->second.access());
+
+    CreateIndexOnMultipleThreads(vertices, label_it, index_, label, paralell_exec_info,
+                                 [](Vertex &vertex, LabelId label, IndexAccessor &index_accessor) {
+                                   TryInsertLabelIndex(vertex, label, index_accessor);
+                                 });
+
+    return true;
+  };
+
+  auto [it, emplaced] = index_.emplace(std::piecewise_construct, std::forward_as_tuple(label), std::forward_as_tuple());
+  if (!emplaced) {
+    // Index already exists.
+    return false;
+  }
+
+  if (paralell_exec_info) {
+    return create_index_par(label, vertices, it, *paralell_exec_info);
+  }
+  return create_index_seq(label, vertices, it);
+}
+
+bool InMemoryLabelIndex::DropIndex(LabelId label) { return index_.erase(label) > 0; }
+
+bool InMemoryLabelIndex::IndexExists(LabelId label) const { return index_.find(label) != index_.end(); }
+
+std::vector<LabelId> InMemoryLabelIndex::ListIndices() const {
+  std::vector<LabelId> ret;
+  ret.reserve(index_.size());
+  for (const auto &item : index_) {
+    ret.push_back(item.first);
+  }
+  return ret;
+}
+
+void InMemoryLabelIndex::RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp) {
+  for (auto &label_storage : index_) {
+    auto vertices_acc = label_storage.second.access();
+    for (auto it = vertices_acc.begin(); it != vertices_acc.end();) {
+      auto next_it = it;
+      ++next_it;
+
+      if (it->timestamp >= oldest_active_start_timestamp) {
+        it = next_it;
+        continue;
+      }
+
+      if ((next_it != vertices_acc.end() && it->vertex == next_it->vertex) ||
+          !AnyVersionHasLabel(*it->vertex, label_storage.first, oldest_active_start_timestamp)) {
+        vertices_acc.remove(*it);
+      }
+
+      it = next_it;
+    }
+  }
+}
+
+InMemoryLabelIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, View view,
+                                       Transaction *transaction, Indices *indices, Constraints *constraints,
+                                       const Config &config)
+    : index_accessor_(std::move(index_accessor)),
+      label_(label),
+      view_(view),
+      transaction_(transaction),
+      indices_(indices),
+      constraints_(constraints),
+      config_(config) {}
+
+InMemoryLabelIndex::Iterable::Iterator::Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator)
+    : self_(self),
+      index_iterator_(index_iterator),
+      current_vertex_accessor_(nullptr, nullptr, nullptr, nullptr, self_->config_.items),
+      current_vertex_(nullptr) {
+  AdvanceUntilValid();
+}
+
+InMemoryLabelIndex::Iterable::Iterator &InMemoryLabelIndex::Iterable::Iterator::operator++() {
+  ++index_iterator_;
+  AdvanceUntilValid();
+  return *this;
+}
+
+void InMemoryLabelIndex::Iterable::Iterator::AdvanceUntilValid() {
+  for (; index_iterator_ != self_->index_accessor_.end(); ++index_iterator_) {
+    if (index_iterator_->vertex == current_vertex_) {
+      continue;
+    }
+    if (CurrentVersionHasLabel(*index_iterator_->vertex, self_->label_, self_->transaction_, self_->view_)) {
+      current_vertex_ = index_iterator_->vertex;
+      current_vertex_accessor_ = VertexAccessor{current_vertex_, self_->transaction_, self_->indices_,
+                                                self_->constraints_, self_->config_.items};
+      break;
+    }
+  }
+}
+
+uint64_t InMemoryLabelIndex::ApproximateVertexCount(LabelId label) const {
+  auto it = index_.find(label);
+  MG_ASSERT(it != index_.end(), "Index for label {} doesn't exist", label.AsUint());
+  return it->second.size();
+}
+
+void InMemoryLabelIndex::RunGC() {
+  for (auto &index_entry : index_) {
+    index_entry.second.run_gc();
+  }
+}
+
+InMemoryLabelIndex::Iterable InMemoryLabelIndex::Vertices(LabelId label, View view, Transaction *transaction) {
+  const auto it = index_.find(label);
+  MG_ASSERT(it != index_.end(), "Index for label {} doesn't exist", label.AsUint());
+  return {it->second.access(), label, view, transaction, indices_, constraints_, config_};
+}
+
+void InMemoryLabelIndex::SetIndexStats(const storage::LabelId &label, const storage::LabelIndexStats &stats) {
+  stats_[label] = stats;
+}
+
+std::optional<LabelIndexStats> InMemoryLabelIndex::GetIndexStats(const storage::LabelId &label) const {
+  if (auto it = stats_.find(label); it != stats_.end()) {
+    return it->second;
+  }
+  return {};
+}
+
+std::vector<LabelId> InMemoryLabelIndex::ClearIndexStats() {
+  std::vector<LabelId> deleted_indexes;
+  deleted_indexes.reserve(stats_.size());
+  std::transform(stats_.begin(), stats_.end(), std::back_inserter(deleted_indexes),
+                 [](const auto &elem) { return elem.first; });
+  stats_.clear();
+  return deleted_indexes;
+}
+
+std::vector<LabelId> InMemoryLabelIndex::DeleteIndexStats(const storage::LabelId &label) {
+  std::vector<LabelId> deleted_indexes;
+  for (auto it = stats_.cbegin(); it != stats_.cend();) {
+    if (it->first == label) {
+      deleted_indexes.push_back(it->first);
+      it = stats_.erase(it);
+    } else {
+      ++it;
+    }
+  }
+
+  return deleted_indexes;
+}
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/inmemory/label_index.hpp b/src/storage/v2/inmemory/label_index.hpp
new file mode 100644
index 000000000..4a32640f6
--- /dev/null
+++ b/src/storage/v2/inmemory/label_index.hpp
@@ -0,0 +1,117 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include "storage/v2/indices/label_index.hpp"
+#include "storage/v2/vertex.hpp"
+
+namespace memgraph::storage {
+
+struct LabelIndexStats {
+  uint64_t count;
+  double avg_degree;
+};
+
+using ParalellizedIndexCreationInfo =
+    std::pair<std::vector<std::pair<Gid, uint64_t>> /*vertex_recovery_info*/, uint64_t /*thread_count*/>;
+
+class InMemoryLabelIndex : public storage::LabelIndex {
+ private:
+  struct Entry {
+    Vertex *vertex;
+    uint64_t timestamp;
+
+    bool operator<(const Entry &rhs) {
+      return std::make_tuple(vertex, timestamp) < std::make_tuple(rhs.vertex, rhs.timestamp);
+    }
+    bool operator==(const Entry &rhs) const { return vertex == rhs.vertex && timestamp == rhs.timestamp; }
+  };
+
+ public:
+  InMemoryLabelIndex(Indices *indices, Constraints *constraints, Config config);
+
+  /// @throw std::bad_alloc
+  void UpdateOnAddLabel(LabelId added_label, Vertex *vertex_after_update, const Transaction &tx) override;
+
+  void UpdateOnRemoveLabel(LabelId removed_label, Vertex *vertex_before_update, const Transaction &tx) override {}
+
+  /// @throw std::bad_alloc
+  bool CreateIndex(LabelId label, utils::SkipList<Vertex>::Accessor vertices,
+                   const std::optional<ParalellizedIndexCreationInfo> &paralell_exec_info);
+
+  /// Returns false if there was no index to drop
+  bool DropIndex(LabelId label) override;
+
+  bool IndexExists(LabelId label) const override;
+
+  std::vector<LabelId> ListIndices() const override;
+
+  void RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp);
+
+  class Iterable {
+   public:
+    Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, View view, Transaction *transaction,
+             Indices *indices, Constraints *constraints, const Config &config);
+
+    class Iterator {
+     public:
+      Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator);
+
+      VertexAccessor operator*() const { return current_vertex_accessor_; }
+
+      bool operator==(const Iterator &other) const { return index_iterator_ == other.index_iterator_; }
+      bool operator!=(const Iterator &other) const { return index_iterator_ != other.index_iterator_; }
+
+      Iterator &operator++();
+
+     private:
+      void AdvanceUntilValid();
+
+      Iterable *self_;
+      utils::SkipList<Entry>::Iterator index_iterator_;
+      VertexAccessor current_vertex_accessor_;
+      Vertex *current_vertex_;
+    };
+
+    Iterator begin() { return {this, index_accessor_.begin()}; }
+    Iterator end() { return {this, index_accessor_.end()}; }
+
+   private:
+    utils::SkipList<Entry>::Accessor index_accessor_;
+    LabelId label_;
+    View view_;
+    Transaction *transaction_;
+    Indices *indices_;
+    Constraints *constraints_;
+    Config config_;
+  };
+
+  uint64_t ApproximateVertexCount(LabelId label) const override;
+
+  void RunGC();
+
+  Iterable Vertices(LabelId label, View view, Transaction *transaction);
+
+  void SetIndexStats(const storage::LabelId &label, const storage::LabelIndexStats &stats);
+
+  std::optional<storage::LabelIndexStats> GetIndexStats(const storage::LabelId &label) const;
+
+  std::vector<LabelId> ClearIndexStats();
+
+  std::vector<LabelId> DeleteIndexStats(const storage::LabelId &label);
+
+ private:
+  std::map<LabelId, utils::SkipList<Entry>> index_;
+  std::map<LabelId, storage::LabelIndexStats> stats_;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/inmemory/label_property_index.cpp b/src/storage/v2/inmemory/label_property_index.cpp
new file mode 100644
index 000000000..b19e316f0
--- /dev/null
+++ b/src/storage/v2/inmemory/label_property_index.cpp
@@ -0,0 +1,424 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include "storage/v2/inmemory/label_property_index.hpp"
+#include "storage/v2/inmemory/indices_utils.hpp"
+
+namespace memgraph::storage {
+
+bool InMemoryLabelPropertyIndex::Entry::operator<(const Entry &rhs) const {
+  if (value < rhs.value) {
+    return true;
+  }
+  if (rhs.value < value) {
+    return false;
+  }
+  return std::make_tuple(vertex, timestamp) < std::make_tuple(rhs.vertex, rhs.timestamp);
+}
+
+bool InMemoryLabelPropertyIndex::Entry::operator==(const Entry &rhs) const {
+  return value == rhs.value && vertex == rhs.vertex && timestamp == rhs.timestamp;
+}
+
+bool InMemoryLabelPropertyIndex::Entry::operator<(const PropertyValue &rhs) const { return value < rhs; }
+
+bool InMemoryLabelPropertyIndex::Entry::operator==(const PropertyValue &rhs) const { return value == rhs; }
+
+InMemoryLabelPropertyIndex::InMemoryLabelPropertyIndex(Indices *indices, Constraints *constraints, const Config &config)
+    : LabelPropertyIndex(indices, constraints, config) {}
+
+bool InMemoryLabelPropertyIndex::CreateIndex(LabelId label, PropertyId property,
+                                             utils::SkipList<Vertex>::Accessor vertices,
+                                             const std::optional<ParalellizedIndexCreationInfo> &paralell_exec_info) {
+  auto create_index_seq = [this](LabelId label, PropertyId property, utils::SkipList<Vertex>::Accessor &vertices,
+                                 std::map<std::pair<LabelId, PropertyId>, utils::SkipList<Entry>>::iterator it) {
+    using IndexAccessor = decltype(it->second.access());
+
+    CreateIndexOnSingleThread(vertices, it, index_, std::make_pair(label, property),
+                              [](Vertex &vertex, std::pair<LabelId, PropertyId> key, IndexAccessor &index_accessor) {
+                                TryInsertLabelPropertyIndex(vertex, key, index_accessor);
+                              });
+
+    return true;
+  };
+
+  auto create_index_par =
+      [this](LabelId label, PropertyId property, utils::SkipList<Vertex>::Accessor &vertices,
+             std::map<std::pair<LabelId, PropertyId>, utils::SkipList<Entry>>::iterator label_property_it,
+             const ParalellizedIndexCreationInfo &paralell_exec_info) {
+        using IndexAccessor = decltype(label_property_it->second.access());
+
+        CreateIndexOnMultipleThreads(
+            vertices, label_property_it, index_, std::make_pair(label, property), paralell_exec_info,
+            [](Vertex &vertex, std::pair<LabelId, PropertyId> key, IndexAccessor &index_accessor) {
+              TryInsertLabelPropertyIndex(vertex, key, index_accessor);
+            });
+
+        return true;
+      };
+
+  auto [it, emplaced] =
+      index_.emplace(std::piecewise_construct, std::forward_as_tuple(label, property), std::forward_as_tuple());
+  if (!emplaced) {
+    // Index already exists.
+    return false;
+  }
+
+  if (paralell_exec_info) {
+    return create_index_par(label, property, vertices, it, *paralell_exec_info);
+  }
+  return create_index_seq(label, property, vertices, it);
+}
+
+void InMemoryLabelPropertyIndex::UpdateOnAddLabel(LabelId added_label, Vertex *vertex_after_update,
+                                                  const Transaction &tx) {
+  for (auto &[label_prop, storage] : index_) {
+    if (label_prop.first != added_label) {
+      continue;
+    }
+    auto prop_value = vertex_after_update->properties.GetProperty(label_prop.second);
+    if (!prop_value.IsNull()) {
+      auto acc = storage.access();
+      acc.insert(Entry{std::move(prop_value), vertex_after_update, tx.start_timestamp});
+    }
+  }
+}
+
+void InMemoryLabelPropertyIndex::UpdateOnSetProperty(PropertyId property, const PropertyValue &value, Vertex *vertex,
+                                                     const Transaction &tx) {
+  if (value.IsNull()) {
+    return;
+  }
+  for (auto &[label_prop, storage] : index_) {
+    if (label_prop.second != property) {
+      continue;
+    }
+    if (utils::Contains(vertex->labels, label_prop.first)) {
+      auto acc = storage.access();
+      acc.insert(Entry{value, vertex, tx.start_timestamp});
+    }
+  }
+}
+
+bool InMemoryLabelPropertyIndex::DropIndex(LabelId label, PropertyId property) {
+  return index_.erase({label, property}) > 0;
+}
+
+bool InMemoryLabelPropertyIndex::IndexExists(LabelId label, PropertyId property) const {
+  return index_.find({label, property}) != index_.end();
+}
+
+std::vector<std::pair<LabelId, PropertyId>> InMemoryLabelPropertyIndex::ListIndices() const {
+  std::vector<std::pair<LabelId, PropertyId>> ret;
+  ret.reserve(index_.size());
+  for (const auto &item : index_) {
+    ret.push_back(item.first);
+  }
+  return ret;
+}
+
+void InMemoryLabelPropertyIndex::RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp) {
+  for (auto &[label_property, index] : index_) {
+    auto index_acc = index.access();
+    for (auto it = index_acc.begin(); it != index_acc.end();) {
+      auto next_it = it;
+      ++next_it;
+
+      if (it->timestamp >= oldest_active_start_timestamp) {
+        it = next_it;
+        continue;
+      }
+
+      if ((next_it != index_acc.end() && it->vertex == next_it->vertex && it->value == next_it->value) ||
+          !AnyVersionHasLabelProperty(*it->vertex, label_property.first, label_property.second, it->value,
+                                      oldest_active_start_timestamp)) {
+        index_acc.remove(*it);
+      }
+      it = next_it;
+    }
+  }
+}
+
+InMemoryLabelPropertyIndex::Iterable::Iterator::Iterator(Iterable *self,
+                                                         utils::SkipList<Entry>::Iterator index_iterator)
+    : self_(self),
+      index_iterator_(index_iterator),
+      current_vertex_accessor_(nullptr, nullptr, nullptr, nullptr, self_->config_.items),
+      current_vertex_(nullptr) {
+  AdvanceUntilValid();
+}
+
+InMemoryLabelPropertyIndex::Iterable::Iterator &InMemoryLabelPropertyIndex::Iterable::Iterator::operator++() {
+  ++index_iterator_;
+  AdvanceUntilValid();
+  return *this;
+}
+
+void InMemoryLabelPropertyIndex::Iterable::Iterator::AdvanceUntilValid() {
+  for (; index_iterator_ != self_->index_accessor_.end(); ++index_iterator_) {
+    if (index_iterator_->vertex == current_vertex_) {
+      continue;
+    }
+
+    if (self_->lower_bound_) {
+      if (index_iterator_->value < self_->lower_bound_->value()) {
+        continue;
+      }
+      if (!self_->lower_bound_->IsInclusive() && index_iterator_->value == self_->lower_bound_->value()) {
+        continue;
+      }
+    }
+    if (self_->upper_bound_) {
+      if (self_->upper_bound_->value() < index_iterator_->value) {
+        index_iterator_ = self_->index_accessor_.end();
+        break;
+      }
+      if (!self_->upper_bound_->IsInclusive() && index_iterator_->value == self_->upper_bound_->value()) {
+        index_iterator_ = self_->index_accessor_.end();
+        break;
+      }
+    }
+
+    if (CurrentVersionHasLabelProperty(*index_iterator_->vertex, self_->label_, self_->property_,
+                                       index_iterator_->value, self_->transaction_, self_->view_)) {
+      current_vertex_ = index_iterator_->vertex;
+      current_vertex_accessor_ = VertexAccessor(current_vertex_, self_->transaction_, self_->indices_,
+                                                self_->constraints_, self_->config_.items);
+      break;
+    }
+  }
+}
+
+// These constants represent the smallest possible value of each type that is
+// contained in a `PropertyValue`. Note that numbers (integers and doubles) are
+// treated as the same "type" in `PropertyValue`.
+const PropertyValue kSmallestBool = PropertyValue(false);
+// NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
+static_assert(-std::numeric_limits<double>::infinity() < std::numeric_limits<int64_t>::min());
+const PropertyValue kSmallestNumber = PropertyValue(-std::numeric_limits<double>::infinity());
+const PropertyValue kSmallestString = PropertyValue("");
+const PropertyValue kSmallestList = PropertyValue(std::vector<PropertyValue>());
+const PropertyValue kSmallestMap = PropertyValue(std::map<std::string, PropertyValue>());
+const PropertyValue kSmallestTemporalData =
+    PropertyValue(TemporalData{static_cast<TemporalType>(0), std::numeric_limits<int64_t>::min()});
+
+InMemoryLabelPropertyIndex::Iterable::Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label,
+                                               PropertyId property,
+                                               const std::optional<utils::Bound<PropertyValue>> &lower_bound,
+                                               const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view,
+                                               Transaction *transaction, Indices *indices, Constraints *constraints,
+                                               const Config &config)
+    : index_accessor_(std::move(index_accessor)),
+      label_(label),
+      property_(property),
+      lower_bound_(lower_bound),
+      upper_bound_(upper_bound),
+      view_(view),
+      transaction_(transaction),
+      indices_(indices),
+      constraints_(constraints),
+      config_(config) {
+  // We have to fix the bounds that the user provided to us. If the user
+  // provided only one bound we should make sure that only values of that type
+  // are returned by the iterator. We ensure this by supplying either an
+  // inclusive lower bound of the same type, or an exclusive upper bound of the
+  // following type. If neither bound is set we yield all items in the index.
+
+  // First we statically verify that our assumptions about the `PropertyValue`
+  // type ordering holds.
+  static_assert(PropertyValue::Type::Bool < PropertyValue::Type::Int);
+  static_assert(PropertyValue::Type::Int < PropertyValue::Type::Double);
+  static_assert(PropertyValue::Type::Double < PropertyValue::Type::String);
+  static_assert(PropertyValue::Type::String < PropertyValue::Type::List);
+  static_assert(PropertyValue::Type::List < PropertyValue::Type::Map);
+
+  // Remove any bounds that are set to `Null` because that isn't a valid value.
+  if (lower_bound_ && lower_bound_->value().IsNull()) {
+    lower_bound_ = std::nullopt;
+  }
+  if (upper_bound_ && upper_bound_->value().IsNull()) {
+    upper_bound_ = std::nullopt;
+  }
+
+  // Check whether the bounds are of comparable types if both are supplied.
+  if (lower_bound_ && upper_bound_ &&
+      !PropertyValue::AreComparableTypes(lower_bound_->value().type(), upper_bound_->value().type())) {
+    bounds_valid_ = false;
+    return;
+  }
+
+  // Set missing bounds.
+  if (lower_bound_ && !upper_bound_) {
+    // Here we need to supply an upper bound. The upper bound is set to an
+    // exclusive lower bound of the following type.
+    switch (lower_bound_->value().type()) {
+      case PropertyValue::Type::Null:
+        // This shouldn't happen because of the nullopt-ing above.
+        LOG_FATAL("Invalid database state!");
+        break;
+      case PropertyValue::Type::Bool:
+        upper_bound_ = utils::MakeBoundExclusive(kSmallestNumber);
+        break;
+      case PropertyValue::Type::Int:
+      case PropertyValue::Type::Double:
+        // Both integers and doubles are treated as the same type in
+        // `PropertyValue` and they are interleaved when sorted.
+        upper_bound_ = utils::MakeBoundExclusive(kSmallestString);
+        break;
+      case PropertyValue::Type::String:
+        upper_bound_ = utils::MakeBoundExclusive(kSmallestList);
+        break;
+      case PropertyValue::Type::List:
+        upper_bound_ = utils::MakeBoundExclusive(kSmallestMap);
+        break;
+      case PropertyValue::Type::Map:
+        upper_bound_ = utils::MakeBoundExclusive(kSmallestTemporalData);
+        break;
+      case PropertyValue::Type::TemporalData:
+        // This is the last type in the order so we leave the upper bound empty.
+        break;
+    }
+  }
+  if (upper_bound_ && !lower_bound_) {
+    // Here we need to supply a lower bound. The lower bound is set to an
+    // inclusive lower bound of the current type.
+    switch (upper_bound_->value().type()) {
+      case PropertyValue::Type::Null:
+        // This shouldn't happen because of the nullopt-ing above.
+        LOG_FATAL("Invalid database state!");
+        break;
+      case PropertyValue::Type::Bool:
+        lower_bound_ = utils::MakeBoundInclusive(kSmallestBool);
+        break;
+      case PropertyValue::Type::Int:
+      case PropertyValue::Type::Double:
+        // Both integers and doubles are treated as the same type in
+        // `PropertyValue` and they are interleaved when sorted.
+        lower_bound_ = utils::MakeBoundInclusive(kSmallestNumber);
+        break;
+      case PropertyValue::Type::String:
+        lower_bound_ = utils::MakeBoundInclusive(kSmallestString);
+        break;
+      case PropertyValue::Type::List:
+        lower_bound_ = utils::MakeBoundInclusive(kSmallestList);
+        break;
+      case PropertyValue::Type::Map:
+        lower_bound_ = utils::MakeBoundInclusive(kSmallestMap);
+        break;
+      case PropertyValue::Type::TemporalData:
+        lower_bound_ = utils::MakeBoundInclusive(kSmallestTemporalData);
+        break;
+    }
+  }
+}
+
+InMemoryLabelPropertyIndex::Iterable::Iterator InMemoryLabelPropertyIndex::Iterable::begin() {
+  // If the bounds are set and don't have comparable types we don't yield any
+  // items from the index.
+  if (!bounds_valid_) return {this, index_accessor_.end()};
+  auto index_iterator = index_accessor_.begin();
+  if (lower_bound_) {
+    index_iterator = index_accessor_.find_equal_or_greater(lower_bound_->value());
+  }
+  return {this, index_iterator};
+}
+
+InMemoryLabelPropertyIndex::Iterable::Iterator InMemoryLabelPropertyIndex::Iterable::end() {
+  return {this, index_accessor_.end()};
+}
+
+uint64_t InMemoryLabelPropertyIndex::ApproximateVertexCount(LabelId label, PropertyId property) const {
+  auto it = index_.find({label, property});
+  MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(), property.AsUint());
+  return it->second.size();
+}
+
+uint64_t InMemoryLabelPropertyIndex::ApproximateVertexCount(LabelId label, PropertyId property,
+                                                            const PropertyValue &value) const {
+  auto it = index_.find({label, property});
+  MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(), property.AsUint());
+  auto acc = it->second.access();
+  if (!value.IsNull()) {
+    // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
+    return acc.estimate_count(value, utils::SkipListLayerForCountEstimation(acc.size()));
+  }
+  // The value `Null` won't ever appear in the index because it indicates that
+  // the property shouldn't exist. Instead, this value is used as an indicator
+  // to estimate the average number of equal elements in the list (for any
+  // given value).
+  return acc.estimate_average_number_of_equals(
+      [](const auto &first, const auto &second) { return first.value == second.value; },
+      // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
+      utils::SkipListLayerForAverageEqualsEstimation(acc.size()));
+}
+
+uint64_t InMemoryLabelPropertyIndex::ApproximateVertexCount(
+    LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower,
+    const std::optional<utils::Bound<PropertyValue>> &upper) const {
+  auto it = index_.find({label, property});
+  MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(), property.AsUint());
+  auto acc = it->second.access();
+  // NOLINTNEXTLINE(bugprone-narrowing-conversions,cppcoreguidelines-narrowing-conversions)
+  return acc.estimate_range_count(lower, upper, utils::SkipListLayerForCountEstimation(acc.size()));
+}
+
+std::vector<std::pair<LabelId, PropertyId>> InMemoryLabelPropertyIndex::ClearIndexStats() {
+  std::vector<std::pair<LabelId, PropertyId>> deleted_indexes;
+  deleted_indexes.reserve(stats_.size());
+  std::transform(stats_.begin(), stats_.end(), std::back_inserter(deleted_indexes),
+                 [](const auto &elem) { return elem.first; });
+  stats_.clear();
+  return deleted_indexes;
+}
+
+std::vector<std::pair<LabelId, PropertyId>> InMemoryLabelPropertyIndex::DeleteIndexStats(
+    const storage::LabelId &label) {
+  std::vector<std::pair<LabelId, PropertyId>> deleted_indexes;
+  for (auto it = stats_.cbegin(); it != stats_.cend();) {
+    if (it->first.first == label) {
+      deleted_indexes.push_back(it->first);
+      it = stats_.erase(it);
+    } else {
+      ++it;
+    }
+  }
+  return deleted_indexes;
+}
+
+void InMemoryLabelPropertyIndex::SetIndexStats(const std::pair<storage::LabelId, storage::PropertyId> &key,
+                                               const LabelPropertyIndexStats &stats) {
+  stats_[key] = stats;
+}
+
+std::optional<LabelPropertyIndexStats> InMemoryLabelPropertyIndex::GetIndexStats(
+    const std::pair<storage::LabelId, storage::PropertyId> &key) const {
+  if (auto it = stats_.find(key); it != stats_.end()) {
+    return it->second;
+  }
+  return {};
+}
+
+void InMemoryLabelPropertyIndex::RunGC() {
+  for (auto &index_entry : index_) {
+    index_entry.second.run_gc();
+  }
+}
+
+InMemoryLabelPropertyIndex::Iterable InMemoryLabelPropertyIndex::Vertices(
+    LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
+    const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction) {
+  auto it = index_.find({label, property});
+  MG_ASSERT(it != index_.end(), "Index for label {} and property {} doesn't exist", label.AsUint(), property.AsUint());
+  return {it->second.access(), label,    property,     lower_bound, upper_bound, view,
+          transaction,         indices_, constraints_, config_};
+}
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/inmemory/label_property_index.hpp b/src/storage/v2/inmemory/label_property_index.hpp
new file mode 100644
index 000000000..13db8109e
--- /dev/null
+++ b/src/storage/v2/inmemory/label_property_index.hpp
@@ -0,0 +1,141 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include "storage/v2/indices/label_property_index.hpp"
+
+namespace memgraph::storage {
+
+struct LabelPropertyIndexStats {
+  uint64_t count, distinct_values_count;
+  double statistic, avg_group_size, avg_degree;
+};
+
+/// TODO: andi. Too many copies, extract at one place
+using ParalellizedIndexCreationInfo =
+    std::pair<std::vector<std::pair<Gid, uint64_t>> /*vertex_recovery_info*/, uint64_t /*thread_count*/>;
+
+class InMemoryLabelPropertyIndex : public storage::LabelPropertyIndex {
+ private:
+  struct Entry {
+    PropertyValue value;
+    Vertex *vertex;
+    uint64_t timestamp;
+
+    bool operator<(const Entry &rhs) const;
+    bool operator==(const Entry &rhs) const;
+
+    bool operator<(const PropertyValue &rhs) const;
+    bool operator==(const PropertyValue &rhs) const;
+  };
+
+ public:
+  InMemoryLabelPropertyIndex(Indices *indices, Constraints *constraints, const Config &config);
+
+  /// @throw std::bad_alloc
+  bool CreateIndex(LabelId label, PropertyId property, utils::SkipList<Vertex>::Accessor vertices,
+                   const std::optional<ParalellizedIndexCreationInfo> &paralell_exec_info);
+
+  /// @throw std::bad_alloc
+  void UpdateOnAddLabel(LabelId added_label, Vertex *vertex_after_update, const Transaction &tx) override;
+
+  void UpdateOnRemoveLabel(LabelId removed_label, Vertex *vertex_before_update, const Transaction &tx) override {}
+
+  /// @throw std::bad_alloc
+  void UpdateOnSetProperty(PropertyId property, const PropertyValue &value, Vertex *vertex,
+                           const Transaction &tx) override;
+
+  bool DropIndex(LabelId label, PropertyId property) override;
+
+  bool IndexExists(LabelId label, PropertyId property) const override;
+
+  std::vector<std::pair<LabelId, PropertyId>> ListIndices() const override;
+
+  void RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp);
+
+  class Iterable {
+   public:
+    Iterable(utils::SkipList<Entry>::Accessor index_accessor, LabelId label, PropertyId property,
+             const std::optional<utils::Bound<PropertyValue>> &lower_bound,
+             const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction,
+             Indices *indices, Constraints *constraints, const Config &config);
+
+    class Iterator {
+     public:
+      Iterator(Iterable *self, utils::SkipList<Entry>::Iterator index_iterator);
+
+      VertexAccessor operator*() const { return current_vertex_accessor_; }
+
+      bool operator==(const Iterator &other) const { return index_iterator_ == other.index_iterator_; }
+      bool operator!=(const Iterator &other) const { return index_iterator_ != other.index_iterator_; }
+
+      Iterator &operator++();
+
+     private:
+      void AdvanceUntilValid();
+
+      Iterable *self_;
+      utils::SkipList<Entry>::Iterator index_iterator_;
+      VertexAccessor current_vertex_accessor_;
+      Vertex *current_vertex_;
+    };
+
+    Iterator begin();
+    Iterator end();
+
+   private:
+    utils::SkipList<Entry>::Accessor index_accessor_;
+    LabelId label_;
+    PropertyId property_;
+    std::optional<utils::Bound<PropertyValue>> lower_bound_;
+    std::optional<utils::Bound<PropertyValue>> upper_bound_;
+    bool bounds_valid_{true};
+    View view_;
+    Transaction *transaction_;
+    Indices *indices_;
+    Constraints *constraints_;
+    Config config_;
+  };
+
+  uint64_t ApproximateVertexCount(LabelId label, PropertyId property) const override;
+
+  /// Supplying a specific value into the count estimation function will return
+  /// an estimated count of nodes which have their property's value set to
+  /// `value`. If the `value` specified is `Null`, then an average number of
+  /// equal elements is returned.
+  uint64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const override;
+
+  uint64_t ApproximateVertexCount(LabelId label, PropertyId property,
+                                  const std::optional<utils::Bound<PropertyValue>> &lower,
+                                  const std::optional<utils::Bound<PropertyValue>> &upper) const override;
+
+  std::vector<std::pair<LabelId, PropertyId>> ClearIndexStats();
+
+  std::vector<std::pair<LabelId, PropertyId>> DeleteIndexStats(const storage::LabelId &label);
+
+  void SetIndexStats(const std::pair<storage::LabelId, storage::PropertyId> &key,
+                     const storage::LabelPropertyIndexStats &stats);
+
+  std::optional<storage::LabelPropertyIndexStats> GetIndexStats(
+      const std::pair<storage::LabelId, storage::PropertyId> &key) const;
+
+  void RunGC();
+
+  Iterable Vertices(LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
+                    const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view, Transaction *transaction);
+
+ private:
+  std::map<std::pair<LabelId, PropertyId>, utils::SkipList<Entry>> index_;
+  std::map<std::pair<LabelId, PropertyId>, storage::LabelPropertyIndexStats> stats_;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/inmemory/storage.cpp b/src/storage/v2/inmemory/storage.cpp
new file mode 100644
index 000000000..159fff10c
--- /dev/null
+++ b/src/storage/v2/inmemory/storage.cpp
@@ -0,0 +1,2075 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include "storage/v2/inmemory/storage.hpp"
+#include "storage/v2/constraints/constraints.hpp"
+#include "storage/v2/durability/durability.hpp"
+#include "storage/v2/durability/snapshot.hpp"
+#include "storage/v2/durability/wal.hpp"
+#include "storage/v2/edge_accessor.hpp"
+#include "storage/v2/storage_mode.hpp"
+#include "storage/v2/vertex_accessor.hpp"
+#include "utils/stat.hpp"
+
+/// REPLICATION ///
+#include "storage/v2/replication/replication_client.hpp"
+#include "storage/v2/replication/replication_server.hpp"
+#include "storage/v2/replication/rpc.hpp"
+#include "storage/v2/storage_error.hpp"
+
+namespace memgraph::storage {
+
+using OOMExceptionEnabler = utils::MemoryTracker::OutOfMemoryExceptionEnabler;
+
+namespace {
+inline constexpr uint16_t kEpochHistoryRetention = 1000;
+
+std::string RegisterReplicaErrorToString(InMemoryStorage::RegisterReplicaError error) {
+  switch (error) {
+    case InMemoryStorage::RegisterReplicaError::NAME_EXISTS:
+      return "NAME_EXISTS";
+    case InMemoryStorage::RegisterReplicaError::END_POINT_EXISTS:
+      return "END_POINT_EXISTS";
+    case InMemoryStorage::RegisterReplicaError::CONNECTION_FAILED:
+      return "CONNECTION_FAILED";
+    case InMemoryStorage::RegisterReplicaError::COULD_NOT_BE_PERSISTED:
+      return "COULD_NOT_BE_PERSISTED";
+  }
+}
+}  // namespace
+
+InMemoryStorage::InMemoryStorage(Config config)
+    : Storage(config, StorageMode::IN_MEMORY_TRANSACTIONAL),
+      snapshot_directory_(config.durability.storage_directory / durability::kSnapshotDirectory),
+      lock_file_path_(config.durability.storage_directory / durability::kLockFile),
+      wal_directory_(config.durability.storage_directory / durability::kWalDirectory),
+      uuid_(utils::GenerateUUID()),
+      epoch_id_(utils::GenerateUUID()),
+      global_locker_(file_retainer_.AddLocker()) {
+  if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::DISABLED ||
+      config_.durability.snapshot_on_exit || config_.durability.recover_on_startup) {
+    // Create the directory initially to crash the database in case of
+    // permission errors. This is done early to crash the database on startup
+    // instead of crashing the database for the first time during runtime (which
+    // could be an unpleasant surprise).
+    utils::EnsureDirOrDie(snapshot_directory_);
+    // Same reasoning as above.
+    utils::EnsureDirOrDie(wal_directory_);
+
+    // Verify that the user that started the process is the same user that is
+    // the owner of the storage directory.
+    durability::VerifyStorageDirectoryOwnerAndProcessUserOrDie(config_.durability.storage_directory);
+
+    // Create the lock file and open a handle to it. This will crash the
+    // database if it can't open the file for writing or if any other process is
+    // holding the file opened.
+    lock_file_handle_.Open(lock_file_path_, utils::OutputFile::Mode::OVERWRITE_EXISTING);
+    MG_ASSERT(lock_file_handle_.AcquireLock(),
+              "Couldn't acquire lock on the storage directory {}"
+              "!\nAnother Memgraph process is currently running with the same "
+              "storage directory, please stop it first before starting this "
+              "process!",
+              config_.durability.storage_directory);
+  }
+  if (config_.durability.recover_on_startup) {
+    auto info = durability::RecoverData(snapshot_directory_, wal_directory_, &uuid_, &epoch_id_, &epoch_history_,
+                                        &vertices_, &edges_, &edge_count_, name_id_mapper_.get(), &indices_,
+                                        &constraints_, config_, &wal_seq_num_);
+    if (info) {
+      vertex_id_ = info->next_vertex_id;
+      edge_id_ = info->next_edge_id;
+      timestamp_ = std::max(timestamp_, info->next_timestamp);
+      if (info->last_commit_timestamp) {
+        last_commit_timestamp_ = *info->last_commit_timestamp;
+      }
+    }
+  } else if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::DISABLED ||
+             config_.durability.snapshot_on_exit) {
+    bool files_moved = false;
+    auto backup_root = config_.durability.storage_directory / durability::kBackupDirectory;
+    for (const auto &[path, dirname, what] :
+         {std::make_tuple(snapshot_directory_, durability::kSnapshotDirectory, "snapshot"),
+          std::make_tuple(wal_directory_, durability::kWalDirectory, "WAL")}) {
+      if (!utils::DirExists(path)) continue;
+      auto backup_curr = backup_root / dirname;
+      std::error_code error_code;
+      for (const auto &item : std::filesystem::directory_iterator(path, error_code)) {
+        utils::EnsureDirOrDie(backup_root);
+        utils::EnsureDirOrDie(backup_curr);
+        std::error_code item_error_code;
+        std::filesystem::rename(item.path(), backup_curr / item.path().filename(), item_error_code);
+        MG_ASSERT(!item_error_code, "Couldn't move {} file {} because of: {}", what, item.path(),
+                  item_error_code.message());
+        files_moved = true;
+      }
+      MG_ASSERT(!error_code, "Couldn't backup {} files because of: {}", what, error_code.message());
+    }
+    if (files_moved) {
+      spdlog::warn(
+          "Since Memgraph was not supposed to recover on startup and "
+          "durability is enabled, your current durability files will likely "
+          "be overridden. To prevent important data loss, Memgraph has stored "
+          "those files into a .backup directory inside the storage directory.");
+    }
+  }
+  if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::DISABLED) {
+    snapshot_runner_.Run("Snapshot", config_.durability.snapshot_interval, [this] {
+      if (auto maybe_error = this->CreateSnapshot({true}); maybe_error.HasError()) {
+        switch (maybe_error.GetError()) {
+          case CreateSnapshotError::DisabledForReplica:
+            spdlog::warn(
+                utils::MessageWithLink("Snapshots are disabled for replicas.", "https://memgr.ph/replication"));
+            break;
+          case CreateSnapshotError::DisabledForAnalyticsPeriodicCommit:
+            spdlog::warn(utils::MessageWithLink("Periodic snapshots are disabled for analytical mode.",
+                                                "https://memgr.ph/durability"));
+            break;
+          case storage::InMemoryStorage::CreateSnapshotError::ReachedMaxNumTries:
+            spdlog::warn("Failed to create snapshot. Reached max number of tries. Please contact support");
+            break;
+        }
+      }
+    });
+  }
+  if (config_.gc.type == Config::Gc::Type::PERIODIC) {
+    gc_runner_.Run("Storage GC", config_.gc.interval, [this] { this->CollectGarbage<false>(); });
+  }
+
+  if (timestamp_ == kTimestampInitialId) {
+    commit_log_.emplace();
+  } else {
+    commit_log_.emplace(timestamp_);
+  }
+
+  if (config_.durability.restore_replication_state_on_startup) {
+    spdlog::info("Replication configuration will be stored and will be automatically restored in case of a crash.");
+    utils::EnsureDirOrDie(config_.durability.storage_directory / durability::kReplicationDirectory);
+    storage_ =
+        std::make_unique<kvstore::KVStore>(config_.durability.storage_directory / durability::kReplicationDirectory);
+
+    RestoreReplicationRole();
+
+    if (replication_role_ == replication::ReplicationRole::MAIN) {
+      RestoreReplicas();
+    }
+  } else {
+    spdlog::warn(
+        "Replicastion configuration will NOT be stored. When the server restarts, replication state will be "
+        "forgotten.");
+  }
+
+  if (config_.durability.snapshot_wal_mode == Config::Durability::SnapshotWalMode::DISABLED &&
+      replication_role_ == replication::ReplicationRole::MAIN) {
+    spdlog::warn(
+        "The instance has the MAIN replication role, but durability logs and snapshots are disabled. Please consider "
+        "enabling durability by using --storage-snapshot-interval-sec and --storage-wal-enabled flags because "
+        "without write-ahead logs this instance is not replicating any data.");
+  }
+}
+
+InMemoryStorage::~InMemoryStorage() {
+  if (config_.gc.type == Config::Gc::Type::PERIODIC) {
+    gc_runner_.Stop();
+  }
+  {
+    // Clear replication data
+    replication_server_.reset();
+    replication_clients_.WithLock([&](auto &clients) { clients.clear(); });
+  }
+  if (wal_file_) {
+    wal_file_->FinalizeWal();
+    wal_file_ = std::nullopt;
+  }
+  if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::DISABLED) {
+    snapshot_runner_.Stop();
+  }
+  if (config_.durability.snapshot_on_exit) {
+    if (auto maybe_error = this->CreateSnapshot({false}); maybe_error.HasError()) {
+      switch (maybe_error.GetError()) {
+        case CreateSnapshotError::DisabledForReplica:
+          spdlog::warn(utils::MessageWithLink("Snapshots are disabled for replicas.", "https://memgr.ph/replication"));
+          break;
+        case CreateSnapshotError::DisabledForAnalyticsPeriodicCommit:
+          spdlog::warn(utils::MessageWithLink("Periodic snapshots are disabled for analytical mode.",
+                                              "https://memgr.ph/replication"));
+          break;
+        case storage::InMemoryStorage::CreateSnapshotError::ReachedMaxNumTries:
+          spdlog::warn("Failed to create snapshot. Reached max number of tries. Please contact support");
+          break;
+      }
+    }
+  }
+}
+
+InMemoryStorage::InMemoryAccessor::InMemoryAccessor(InMemoryStorage *storage, IsolationLevel isolation_level,
+                                                    StorageMode storage_mode)
+    : Accessor(storage, isolation_level, storage_mode), config_(storage->config_.items) {}
+InMemoryStorage::InMemoryAccessor::InMemoryAccessor(InMemoryAccessor &&other) noexcept
+    : Accessor(std::move(other)), config_(other.config_) {}
+
+InMemoryStorage::InMemoryAccessor::~InMemoryAccessor() {
+  if (is_transaction_active_) {
+    Abort();
+  }
+
+  FinalizeTransaction();
+}
+
+VertexAccessor InMemoryStorage::InMemoryAccessor::CreateVertex() {
+  OOMExceptionEnabler oom_exception;
+  auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+  auto gid = mem_storage->vertex_id_.fetch_add(1, std::memory_order_acq_rel);
+  auto acc = mem_storage->vertices_.access();
+
+  auto *delta = CreateDeleteObjectDelta(&transaction_);
+  auto [it, inserted] = acc.insert(Vertex{storage::Gid::FromUint(gid), delta});
+  MG_ASSERT(inserted, "The vertex must be inserted here!");
+  MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
+
+  if (delta) {
+    delta->prev.Set(&*it);
+  }
+  return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
+}
+
+VertexAccessor InMemoryStorage::InMemoryAccessor::CreateVertex(storage::Gid gid) {
+  OOMExceptionEnabler oom_exception;
+  // NOTE: When we update the next `vertex_id_` here we perform a RMW
+  // (read-modify-write) operation that ISN'T atomic! But, that isn't an issue
+  // because this function is only called from the replication delta applier
+  // that runs single-threadedly and while this instance is set-up to apply
+  // threads (it is the replica), it is guaranteed that no other writes are
+  // possible.
+  auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+  mem_storage->vertex_id_.store(std::max(mem_storage->vertex_id_.load(std::memory_order_acquire), gid.AsUint() + 1),
+                                std::memory_order_release);
+  auto acc = mem_storage->vertices_.access();
+
+  auto *delta = CreateDeleteObjectDelta(&transaction_);
+  auto [it, inserted] = acc.insert(Vertex{gid, delta});
+  MG_ASSERT(inserted, "The vertex must be inserted here!");
+  MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
+  if (delta) {
+    delta->prev.Set(&*it);
+  }
+  return {&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_};
+}
+
+std::optional<VertexAccessor> InMemoryStorage::InMemoryAccessor::FindVertex(Gid gid, View view) {
+  auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+  auto acc = mem_storage->vertices_.access();
+  auto it = acc.find(gid);
+  if (it == acc.end()) return std::nullopt;
+  return VertexAccessor::Create(&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_, view);
+}
+
+Result<std::optional<VertexAccessor>> InMemoryStorage::InMemoryAccessor::DeleteVertex(VertexAccessor *vertex) {
+  MG_ASSERT(vertex->transaction_ == &transaction_,
+            "VertexAccessor must be from the same transaction as the storage "
+            "accessor when deleting a vertex!");
+  auto *vertex_ptr = vertex->vertex_;
+
+  std::lock_guard<utils::SpinLock> guard(vertex_ptr->lock);
+
+  if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
+
+  if (vertex_ptr->deleted) {
+    return std::optional<VertexAccessor>{};
+  }
+
+  if (!vertex_ptr->in_edges.empty() || !vertex_ptr->out_edges.empty()) return Error::VERTEX_HAS_EDGES;
+
+  CreateAndLinkDelta(&transaction_, vertex_ptr, Delta::RecreateObjectTag());
+  vertex_ptr->deleted = true;
+
+  // Need to inform the next CollectGarbage call that there are some
+  // non-transactional deletions that need to be collected
+  if (transaction_.storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
+    auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+    mem_storage->gc_full_scan_vertices_delete_ = true;
+  }
+
+  return std::make_optional<VertexAccessor>(vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_,
+                                            config_, true);
+}
+
+Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>>
+InMemoryStorage::InMemoryAccessor::DetachDeleteVertex(VertexAccessor *vertex) {
+  using ReturnType = std::pair<VertexAccessor, std::vector<EdgeAccessor>>;
+
+  MG_ASSERT(vertex->transaction_ == &transaction_,
+            "VertexAccessor must be from the same transaction as the storage "
+            "accessor when deleting a vertex!");
+  auto *vertex_ptr = vertex->vertex_;
+
+  std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> in_edges;
+  std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges;
+
+  {
+    std::lock_guard<utils::SpinLock> guard(vertex_ptr->lock);
+
+    if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
+
+    if (vertex_ptr->deleted) return std::optional<ReturnType>{};
+
+    in_edges = vertex_ptr->in_edges;
+    out_edges = vertex_ptr->out_edges;
+  }
+
+  std::vector<EdgeAccessor> deleted_edges;
+  for (const auto &item : in_edges) {
+    auto [edge_type, from_vertex, edge] = item;
+    EdgeAccessor e(edge, edge_type, from_vertex, vertex_ptr, &transaction_, &storage_->indices_,
+                   &storage_->constraints_, config_);
+    auto ret = DeleteEdge(&e);
+    if (ret.HasError()) {
+      MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
+      return ret.GetError();
+    }
+
+    if (ret.GetValue()) {
+      deleted_edges.push_back(*ret.GetValue());
+    }
+  }
+  for (const auto &item : out_edges) {
+    auto [edge_type, to_vertex, edge] = item;
+    EdgeAccessor e(edge, edge_type, vertex_ptr, to_vertex, &transaction_, &storage_->indices_, &storage_->constraints_,
+                   config_);
+    auto ret = DeleteEdge(&e);
+    if (ret.HasError()) {
+      MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
+      return ret.GetError();
+    }
+
+    if (ret.GetValue()) {
+      deleted_edges.push_back(*ret.GetValue());
+    }
+  }
+
+  std::lock_guard<utils::SpinLock> guard(vertex_ptr->lock);
+
+  // We need to check again for serialization errors because we unlocked the
+  // vertex. Some other transaction could have modified the vertex in the
+  // meantime if we didn't have any edges to delete.
+
+  if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
+
+  MG_ASSERT(!vertex_ptr->deleted, "Invalid database state!");
+
+  CreateAndLinkDelta(&transaction_, vertex_ptr, Delta::RecreateObjectTag());
+  vertex_ptr->deleted = true;
+
+  // Need to inform the next CollectGarbage call that there are some
+  // non-transactional deletions that need to be collected
+  if (transaction_.storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
+    auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+    mem_storage->gc_full_scan_vertices_delete_ = true;
+  }
+
+  return std::make_optional<ReturnType>(
+      VertexAccessor{vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_, config_, true},
+      std::move(deleted_edges));
+}
+
+Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdge(VertexAccessor *from, VertexAccessor *to,
+                                                                   EdgeTypeId edge_type) {
+  OOMExceptionEnabler oom_exception;
+  MG_ASSERT(from->transaction_ == to->transaction_,
+            "VertexAccessors must be from the same transaction when creating "
+            "an edge!");
+  MG_ASSERT(from->transaction_ == &transaction_,
+            "VertexAccessors must be from the same transaction in when "
+            "creating an edge!");
+
+  auto *from_vertex = from->vertex_;
+  auto *to_vertex = to->vertex_;
+
+  // Obtain the locks by `gid` order to avoid lock cycles.
+  std::unique_lock<utils::SpinLock> guard_from(from_vertex->lock, std::defer_lock);
+  std::unique_lock<utils::SpinLock> guard_to(to_vertex->lock, std::defer_lock);
+  if (from_vertex->gid < to_vertex->gid) {
+    guard_from.lock();
+    guard_to.lock();
+  } else if (from_vertex->gid > to_vertex->gid) {
+    guard_to.lock();
+    guard_from.lock();
+  } else {
+    // The vertices are the same vertex, only lock one.
+    guard_from.lock();
+  }
+
+  if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
+  if (from_vertex->deleted) return Error::DELETED_OBJECT;
+
+  if (to_vertex != from_vertex) {
+    if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
+    if (to_vertex->deleted) return Error::DELETED_OBJECT;
+  }
+
+  auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+  auto gid = storage::Gid::FromUint(mem_storage->edge_id_.fetch_add(1, std::memory_order_acq_rel));
+  EdgeRef edge(gid);
+  if (config_.properties_on_edges) {
+    auto acc = mem_storage->edges_.access();
+    auto *delta = CreateDeleteObjectDelta(&transaction_);
+    auto [it, inserted] = acc.insert(Edge(gid, delta));
+    MG_ASSERT(inserted, "The edge must be inserted here!");
+    MG_ASSERT(it != acc.end(), "Invalid Edge accessor!");
+    edge = EdgeRef(&*it);
+    if (delta) {
+      delta->prev.Set(&*it);
+    }
+  }
+
+  CreateAndLinkDelta(&transaction_, from_vertex, Delta::RemoveOutEdgeTag(), edge_type, to_vertex, edge);
+  from_vertex->out_edges.emplace_back(edge_type, to_vertex, edge);
+
+  CreateAndLinkDelta(&transaction_, to_vertex, Delta::RemoveInEdgeTag(), edge_type, from_vertex, edge);
+  to_vertex->in_edges.emplace_back(edge_type, from_vertex, edge);
+
+  // Increment edge count.
+  storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
+
+  return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
+                      &storage_->constraints_, config_);
+}
+
+Result<EdgeAccessor> InMemoryStorage::InMemoryAccessor::CreateEdge(VertexAccessor *from, VertexAccessor *to,
+                                                                   EdgeTypeId edge_type, storage::Gid gid) {
+  OOMExceptionEnabler oom_exception;
+  MG_ASSERT(from->transaction_ == to->transaction_,
+            "VertexAccessors must be from the same transaction when creating "
+            "an edge!");
+  MG_ASSERT(from->transaction_ == &transaction_,
+            "VertexAccessors must be from the same transaction in when "
+            "creating an edge!");
+
+  auto *from_vertex = from->vertex_;
+  auto *to_vertex = to->vertex_;
+
+  // Obtain the locks by `gid` order to avoid lock cycles.
+  std::unique_lock<utils::SpinLock> guard_from(from_vertex->lock, std::defer_lock);
+  std::unique_lock<utils::SpinLock> guard_to(to_vertex->lock, std::defer_lock);
+  if (from_vertex->gid < to_vertex->gid) {
+    guard_from.lock();
+    guard_to.lock();
+  } else if (from_vertex->gid > to_vertex->gid) {
+    guard_to.lock();
+    guard_from.lock();
+  } else {
+    // The vertices are the same vertex, only lock one.
+    guard_from.lock();
+  }
+
+  if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
+  if (from_vertex->deleted) return Error::DELETED_OBJECT;
+
+  if (to_vertex != from_vertex) {
+    if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
+    if (to_vertex->deleted) return Error::DELETED_OBJECT;
+  }
+
+  // NOTE: When we update the next `edge_id_` here we perform a RMW
+  // (read-modify-write) operation that ISN'T atomic! But, that isn't an issue
+  // because this function is only called from the replication delta applier
+  // that runs single-threadedly and while this instance is set-up to apply
+  // threads (it is the replica), it is guaranteed that no other writes are
+  // possible.
+  auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+  mem_storage->edge_id_.store(std::max(mem_storage->edge_id_.load(std::memory_order_acquire), gid.AsUint() + 1),
+                              std::memory_order_release);
+
+  EdgeRef edge(gid);
+  if (config_.properties_on_edges) {
+    auto acc = mem_storage->edges_.access();
+
+    auto *delta = CreateDeleteObjectDelta(&transaction_);
+    auto [it, inserted] = acc.insert(Edge(gid, delta));
+    MG_ASSERT(inserted, "The edge must be inserted here!");
+    MG_ASSERT(it != acc.end(), "Invalid Edge accessor!");
+    edge = EdgeRef(&*it);
+    if (delta) {
+      delta->prev.Set(&*it);
+    }
+  }
+
+  CreateAndLinkDelta(&transaction_, from_vertex, Delta::RemoveOutEdgeTag(), edge_type, to_vertex, edge);
+  from_vertex->out_edges.emplace_back(edge_type, to_vertex, edge);
+
+  CreateAndLinkDelta(&transaction_, to_vertex, Delta::RemoveInEdgeTag(), edge_type, from_vertex, edge);
+  to_vertex->in_edges.emplace_back(edge_type, from_vertex, edge);
+
+  // Increment edge count.
+  storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
+
+  return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
+                      &storage_->constraints_, config_);
+}
+
+Result<std::optional<EdgeAccessor>> InMemoryStorage::InMemoryAccessor::DeleteEdge(EdgeAccessor *edge) {
+  MG_ASSERT(edge->transaction_ == &transaction_,
+            "EdgeAccessor must be from the same transaction as the storage "
+            "accessor when deleting an edge!");
+  auto edge_ref = edge->edge_;
+  auto edge_type = edge->edge_type_;
+
+  std::unique_lock<utils::SpinLock> guard;
+  if (config_.properties_on_edges) {
+    auto *edge_ptr = edge_ref.ptr;
+    guard = std::unique_lock<utils::SpinLock>(edge_ptr->lock);
+
+    if (!PrepareForWrite(&transaction_, edge_ptr)) return Error::SERIALIZATION_ERROR;
+
+    if (edge_ptr->deleted) return std::optional<EdgeAccessor>{};
+  }
+
+  auto *from_vertex = edge->from_vertex_;
+  auto *to_vertex = edge->to_vertex_;
+
+  // Obtain the locks by `gid` order to avoid lock cycles.
+  std::unique_lock<utils::SpinLock> guard_from(from_vertex->lock, std::defer_lock);
+  std::unique_lock<utils::SpinLock> guard_to(to_vertex->lock, std::defer_lock);
+  if (from_vertex->gid < to_vertex->gid) {
+    guard_from.lock();
+    guard_to.lock();
+  } else if (from_vertex->gid > to_vertex->gid) {
+    guard_to.lock();
+    guard_from.lock();
+  } else {
+    // The vertices are the same vertex, only lock one.
+    guard_from.lock();
+  }
+
+  if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
+  MG_ASSERT(!from_vertex->deleted, "Invalid database state!");
+
+  if (to_vertex != from_vertex) {
+    if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
+    MG_ASSERT(!to_vertex->deleted, "Invalid database state!");
+  }
+
+  auto delete_edge_from_storage = [&edge_type, &edge_ref, this](auto *vertex, auto *edges) {
+    std::tuple<EdgeTypeId, Vertex *, EdgeRef> link(edge_type, vertex, edge_ref);
+    auto it = std::find(edges->begin(), edges->end(), link);
+    if (config_.properties_on_edges) {
+      MG_ASSERT(it != edges->end(), "Invalid database state!");
+    } else if (it == edges->end()) {
+      return false;
+    }
+    std::swap(*it, *edges->rbegin());
+    edges->pop_back();
+    return true;
+  };
+
+  auto op1 = delete_edge_from_storage(to_vertex, &from_vertex->out_edges);
+  auto op2 = delete_edge_from_storage(from_vertex, &to_vertex->in_edges);
+
+  if (config_.properties_on_edges) {
+    MG_ASSERT((op1 && op2), "Invalid database state!");
+  } else {
+    MG_ASSERT((op1 && op2) || (!op1 && !op2), "Invalid database state!");
+    if (!op1 && !op2) {
+      // The edge is already deleted.
+      return std::optional<EdgeAccessor>{};
+    }
+  }
+
+  if (config_.properties_on_edges) {
+    auto *edge_ptr = edge_ref.ptr;
+    CreateAndLinkDelta(&transaction_, edge_ptr, Delta::RecreateObjectTag());
+    edge_ptr->deleted = true;
+
+    // Need to inform the next CollectGarbage call that there are some
+    // non-transactional deletions that need to be collected
+    if (transaction_.storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
+      auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+      mem_storage->gc_full_scan_edges_delete_ = true;
+    }
+  }
+
+  CreateAndLinkDelta(&transaction_, from_vertex, Delta::AddOutEdgeTag(), edge_type, to_vertex, edge_ref);
+  CreateAndLinkDelta(&transaction_, to_vertex, Delta::AddInEdgeTag(), edge_type, from_vertex, edge_ref);
+
+  // Decrement edge count.
+  storage_->edge_count_.fetch_add(-1, std::memory_order_acq_rel);
+
+  return std::make_optional<EdgeAccessor>(edge_ref, edge_type, from_vertex, to_vertex, &transaction_,
+                                          &storage_->indices_, &storage_->constraints_, config_, true);
+}
+
+// NOLINTNEXTLINE(google-default-arguments)
+utils::BasicResult<StorageDataManipulationError, void> InMemoryStorage::InMemoryAccessor::Commit(
+    const std::optional<uint64_t> desired_commit_timestamp) {
+  MG_ASSERT(is_transaction_active_, "The transaction is already terminated!");
+  MG_ASSERT(!transaction_.must_abort, "The transaction can't be committed!");
+
+  auto could_replicate_all_sync_replicas = true;
+
+  auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+
+  if (transaction_.deltas.empty()) {
+    // We don't have to update the commit timestamp here because no one reads
+    // it.
+    mem_storage->commit_log_->MarkFinished(transaction_.start_timestamp);
+  } else {
+    // Validate that existence constraints are satisfied for all modified
+    // vertices.
+    for (const auto &delta : transaction_.deltas) {
+      auto prev = delta.prev.Get();
+      MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
+      if (prev.type != PreviousPtr::Type::VERTEX) {
+        continue;
+      }
+      // No need to take any locks here because we modified this vertex and no
+      // one else can touch it until we commit.
+      auto validation_result = storage_->constraints_.existence_constraints_->Validate(*prev.vertex);
+      if (validation_result) {
+        Abort();
+        return StorageDataManipulationError{*validation_result};
+      }
+    }
+
+    // Result of validating the vertex against unqiue constraints. It has to be
+    // declared outside of the critical section scope because its value is
+    // tested for Abort call which has to be done out of the scope.
+    std::optional<ConstraintViolation> unique_constraint_violation;
+
+    // Save these so we can mark them used in the commit log.
+    uint64_t start_timestamp = transaction_.start_timestamp;
+
+    {
+      std::unique_lock<utils::SpinLock> engine_guard(storage_->engine_lock_);
+      auto *mem_unique_constraints =
+          static_cast<InMemoryUniqueConstraints *>(storage_->constraints_.unique_constraints_.get());
+      commit_timestamp_.emplace(mem_storage->CommitTimestamp(desired_commit_timestamp));
+
+      // Before committing and validating vertices against unique constraints,
+      // we have to update unique constraints with the vertices that are going
+      // to be validated/committed.
+      for (const auto &delta : transaction_.deltas) {
+        auto prev = delta.prev.Get();
+        MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
+        if (prev.type != PreviousPtr::Type::VERTEX) {
+          continue;
+        }
+        mem_unique_constraints->UpdateBeforeCommit(prev.vertex, transaction_);
+      }
+
+      // Validate that unique constraints are satisfied for all modified
+      // vertices.
+      for (const auto &delta : transaction_.deltas) {
+        auto prev = delta.prev.Get();
+        MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
+        if (prev.type != PreviousPtr::Type::VERTEX) {
+          continue;
+        }
+
+        // No need to take any locks here because we modified this vertex and no
+        // one else can touch it until we commit.
+        unique_constraint_violation = mem_unique_constraints->Validate(*prev.vertex, transaction_, *commit_timestamp_);
+        if (unique_constraint_violation) {
+          break;
+        }
+      }
+
+      if (!unique_constraint_violation) {
+        // Write transaction to WAL while holding the engine lock to make sure
+        // that committed transactions are sorted by the commit timestamp in the
+        // WAL files. We supply the new commit timestamp to the function so that
+        // it knows what will be the final commit timestamp. The WAL must be
+        // written before actually committing the transaction (before setting
+        // the commit timestamp) so that no other transaction can see the
+        // modifications before they are written to disk.
+        // Replica can log only the write transaction received from Main
+        // so the Wal files are consistent
+        if (mem_storage->replication_role_ == replication::ReplicationRole::MAIN ||
+            desired_commit_timestamp.has_value()) {
+          could_replicate_all_sync_replicas =
+              mem_storage->AppendToWalDataManipulation(transaction_, *commit_timestamp_);
+        }
+
+        // Take committed_transactions lock while holding the engine lock to
+        // make sure that committed transactions are sorted by the commit
+        // timestamp in the list.
+        mem_storage->committed_transactions_.WithLock([&](auto & /*committed_transactions*/) {
+          // TODO: release lock, and update all deltas to have a local copy
+          // of the commit timestamp
+          MG_ASSERT(transaction_.commit_timestamp != nullptr, "Invalid database state!");
+          transaction_.commit_timestamp->store(*commit_timestamp_, std::memory_order_release);
+          // Replica can only update the last commit timestamp with
+          // the commits received from main.
+          if (mem_storage->replication_role_ == replication::ReplicationRole::MAIN ||
+              desired_commit_timestamp.has_value()) {
+            // Update the last commit timestamp
+            mem_storage->last_commit_timestamp_.store(*commit_timestamp_);
+          }
+          // Release engine lock because we don't have to hold it anymore
+          // and emplace back could take a long time.
+          engine_guard.unlock();
+        });
+
+        mem_storage->commit_log_->MarkFinished(start_timestamp);
+      }
+    }
+
+    if (unique_constraint_violation) {
+      Abort();
+      return StorageDataManipulationError{*unique_constraint_violation};
+    }
+  }
+  is_transaction_active_ = false;
+
+  if (!could_replicate_all_sync_replicas) {
+    return StorageDataManipulationError{ReplicationError{}};
+  }
+
+  return {};
+}
+
+void InMemoryStorage::InMemoryAccessor::Abort() {
+  MG_ASSERT(is_transaction_active_, "The transaction is already terminated!");
+
+  // We collect vertices and edges we've created here and then splice them into
+  // `deleted_vertices_` and `deleted_edges_` lists, instead of adding them one
+  // by one and acquiring lock every time.
+  std::list<Gid> my_deleted_vertices;
+  std::list<Gid> my_deleted_edges;
+
+  for (const auto &delta : transaction_.deltas) {
+    auto prev = delta.prev.Get();
+    switch (prev.type) {
+      case PreviousPtr::Type::VERTEX: {
+        auto *vertex = prev.vertex;
+        std::lock_guard<utils::SpinLock> guard(vertex->lock);
+        Delta *current = vertex->delta;
+        while (current != nullptr && current->timestamp->load(std::memory_order_acquire) ==
+                                         transaction_.transaction_id.load(std::memory_order_acquire)) {
+          switch (current->action) {
+            case Delta::Action::REMOVE_LABEL: {
+              auto it = std::find(vertex->labels.begin(), vertex->labels.end(), current->label);
+              MG_ASSERT(it != vertex->labels.end(), "Invalid database state!");
+              std::swap(*it, *vertex->labels.rbegin());
+              vertex->labels.pop_back();
+              break;
+            }
+            case Delta::Action::ADD_LABEL: {
+              auto it = std::find(vertex->labels.begin(), vertex->labels.end(), current->label);
+              MG_ASSERT(it == vertex->labels.end(), "Invalid database state!");
+              vertex->labels.push_back(current->label);
+              break;
+            }
+            case Delta::Action::SET_PROPERTY: {
+              vertex->properties.SetProperty(current->property.key, current->property.value);
+              break;
+            }
+            case Delta::Action::ADD_IN_EDGE: {
+              std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{current->vertex_edge.edge_type,
+                                                             current->vertex_edge.vertex, current->vertex_edge.edge};
+              auto it = std::find(vertex->in_edges.begin(), vertex->in_edges.end(), link);
+              MG_ASSERT(it == vertex->in_edges.end(), "Invalid database state!");
+              vertex->in_edges.push_back(link);
+              break;
+            }
+            case Delta::Action::ADD_OUT_EDGE: {
+              std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{current->vertex_edge.edge_type,
+                                                             current->vertex_edge.vertex, current->vertex_edge.edge};
+              auto it = std::find(vertex->out_edges.begin(), vertex->out_edges.end(), link);
+              MG_ASSERT(it == vertex->out_edges.end(), "Invalid database state!");
+              vertex->out_edges.push_back(link);
+              // Increment edge count. We only increment the count here because
+              // the information in `ADD_IN_EDGE` and `Edge/RECREATE_OBJECT` is
+              // redundant. Also, `Edge/RECREATE_OBJECT` isn't available when
+              // edge properties are disabled.
+              storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
+              break;
+            }
+            case Delta::Action::REMOVE_IN_EDGE: {
+              std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{current->vertex_edge.edge_type,
+                                                             current->vertex_edge.vertex, current->vertex_edge.edge};
+              auto it = std::find(vertex->in_edges.begin(), vertex->in_edges.end(), link);
+              MG_ASSERT(it != vertex->in_edges.end(), "Invalid database state!");
+              std::swap(*it, *vertex->in_edges.rbegin());
+              vertex->in_edges.pop_back();
+              break;
+            }
+            case Delta::Action::REMOVE_OUT_EDGE: {
+              std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{current->vertex_edge.edge_type,
+                                                             current->vertex_edge.vertex, current->vertex_edge.edge};
+              auto it = std::find(vertex->out_edges.begin(), vertex->out_edges.end(), link);
+              MG_ASSERT(it != vertex->out_edges.end(), "Invalid database state!");
+              std::swap(*it, *vertex->out_edges.rbegin());
+              vertex->out_edges.pop_back();
+              // Decrement edge count. We only decrement the count here because
+              // the information in `REMOVE_IN_EDGE` and `Edge/DELETE_OBJECT` is
+              // redundant. Also, `Edge/DELETE_OBJECT` isn't available when edge
+              // properties are disabled.
+              storage_->edge_count_.fetch_add(-1, std::memory_order_acq_rel);
+              break;
+            }
+            case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+            case Delta::Action::DELETE_OBJECT: {
+              vertex->deleted = true;
+              my_deleted_vertices.push_back(vertex->gid);
+              break;
+            }
+            case Delta::Action::RECREATE_OBJECT: {
+              vertex->deleted = false;
+              break;
+            }
+          }
+          current = current->next.load(std::memory_order_acquire);
+        }
+        vertex->delta = current;
+        if (current != nullptr) {
+          current->prev.Set(vertex);
+        }
+
+        break;
+      }
+      case PreviousPtr::Type::EDGE: {
+        auto *edge = prev.edge;
+        std::lock_guard<utils::SpinLock> guard(edge->lock);
+        Delta *current = edge->delta;
+        while (current != nullptr && current->timestamp->load(std::memory_order_acquire) ==
+                                         transaction_.transaction_id.load(std::memory_order_acquire)) {
+          switch (current->action) {
+            case Delta::Action::SET_PROPERTY: {
+              edge->properties.SetProperty(current->property.key, current->property.value);
+              break;
+            }
+            case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+            case Delta::Action::DELETE_OBJECT: {
+              edge->deleted = true;
+              my_deleted_edges.push_back(edge->gid);
+              break;
+            }
+            case Delta::Action::RECREATE_OBJECT: {
+              edge->deleted = false;
+              break;
+            }
+            case Delta::Action::REMOVE_LABEL:
+            case Delta::Action::ADD_LABEL:
+            case Delta::Action::ADD_IN_EDGE:
+            case Delta::Action::ADD_OUT_EDGE:
+            case Delta::Action::REMOVE_IN_EDGE:
+            case Delta::Action::REMOVE_OUT_EDGE: {
+              LOG_FATAL("Invalid database state!");
+              break;
+            }
+          }
+          current = current->next.load(std::memory_order_acquire);
+        }
+        edge->delta = current;
+        if (current != nullptr) {
+          current->prev.Set(edge);
+        }
+
+        break;
+      }
+      case PreviousPtr::Type::DELTA:
+      // pointer probably couldn't be set because allocation failed
+      case PreviousPtr::Type::NULLPTR:
+        break;
+    }
+  }
+
+  auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+  {
+    std::unique_lock<utils::SpinLock> engine_guard(storage_->engine_lock_);
+    uint64_t mark_timestamp = storage_->timestamp_;
+    // Take garbage_undo_buffers lock while holding the engine lock to make
+    // sure that entries are sorted by mark timestamp in the list.
+    mem_storage->garbage_undo_buffers_.WithLock([&](auto &garbage_undo_buffers) {
+      // Release engine lock because we don't have to hold it anymore and
+      // emplace back could take a long time.
+      engine_guard.unlock();
+      garbage_undo_buffers.emplace_back(mark_timestamp, std::move(transaction_.deltas));
+    });
+    mem_storage->deleted_vertices_.WithLock(
+        [&](auto &deleted_vertices) { deleted_vertices.splice(deleted_vertices.begin(), my_deleted_vertices); });
+    mem_storage->deleted_edges_.WithLock(
+        [&](auto &deleted_edges) { deleted_edges.splice(deleted_edges.begin(), my_deleted_edges); });
+  }
+
+  mem_storage->commit_log_->MarkFinished(transaction_.start_timestamp);
+  is_transaction_active_ = false;
+}
+
+void InMemoryStorage::InMemoryAccessor::FinalizeTransaction() {
+  if (commit_timestamp_) {
+    auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+    mem_storage->commit_log_->MarkFinished(*commit_timestamp_);
+    mem_storage->committed_transactions_.WithLock(
+        [&](auto &committed_transactions) { committed_transactions.emplace_back(std::move(transaction_)); });
+    commit_timestamp_.reset();
+  }
+}
+
+utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::CreateIndex(
+    LabelId label, const std::optional<uint64_t> desired_commit_timestamp) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+  auto *mem_label_index = static_cast<InMemoryLabelIndex *>(indices_.label_index_.get());
+  if (!mem_label_index->CreateIndex(label, vertices_.access(), std::nullopt)) {
+    return StorageIndexDefinitionError{IndexDefinitionError{}};
+  }
+  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
+  const auto success =
+      AppendToWalDataDefinition(durability::StorageGlobalOperation::LABEL_INDEX_CREATE, label, {}, commit_timestamp);
+  commit_log_->MarkFinished(commit_timestamp);
+  last_commit_timestamp_ = commit_timestamp;
+
+  // We don't care if there is a replication error because on main node the change will go through
+  memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveLabelIndices);
+
+  if (success) {
+    return {};
+  }
+
+  return StorageIndexDefinitionError{ReplicationError{}};
+}
+
+utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::CreateIndex(
+    LabelId label, PropertyId property, const std::optional<uint64_t> desired_commit_timestamp) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+  auto *mem_label_property_index = static_cast<InMemoryLabelPropertyIndex *>(indices_.label_property_index_.get());
+  if (!mem_label_property_index->CreateIndex(label, property, vertices_.access(), std::nullopt)) {
+    return StorageIndexDefinitionError{IndexDefinitionError{}};
+  }
+  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
+  auto success = AppendToWalDataDefinition(durability::StorageGlobalOperation::LABEL_PROPERTY_INDEX_CREATE, label,
+                                           {property}, commit_timestamp);
+  commit_log_->MarkFinished(commit_timestamp);
+  last_commit_timestamp_ = commit_timestamp;
+
+  // We don't care if there is a replication error because on main node the change will go through
+  memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveLabelPropertyIndices);
+
+  if (success) {
+    return {};
+  }
+
+  return StorageIndexDefinitionError{ReplicationError{}};
+}
+
+utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::DropIndex(
+    LabelId label, const std::optional<uint64_t> desired_commit_timestamp) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+  if (!indices_.label_index_->DropIndex(label)) {
+    return StorageIndexDefinitionError{IndexDefinitionError{}};
+  }
+  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
+  auto success =
+      AppendToWalDataDefinition(durability::StorageGlobalOperation::LABEL_INDEX_DROP, label, {}, commit_timestamp);
+  commit_log_->MarkFinished(commit_timestamp);
+  last_commit_timestamp_ = commit_timestamp;
+
+  // We don't care if there is a replication error because on main node the change will go through
+  memgraph::metrics::DecrementCounter(memgraph::metrics::ActiveLabelIndices);
+
+  if (success) {
+    return {};
+  }
+
+  return StorageIndexDefinitionError{ReplicationError{}};
+}
+
+utils::BasicResult<StorageIndexDefinitionError, void> InMemoryStorage::DropIndex(
+    LabelId label, PropertyId property, const std::optional<uint64_t> desired_commit_timestamp) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+  if (!indices_.label_property_index_->DropIndex(label, property)) {
+    return StorageIndexDefinitionError{IndexDefinitionError{}};
+  }
+  // For a description why using `timestamp_` is correct, see
+  // `CreateIndex(LabelId label)`.
+  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
+  auto success = AppendToWalDataDefinition(durability::StorageGlobalOperation::LABEL_PROPERTY_INDEX_DROP, label,
+                                           {property}, commit_timestamp);
+  commit_log_->MarkFinished(commit_timestamp);
+  last_commit_timestamp_ = commit_timestamp;
+
+  // We don't care if there is a replication error because on main node the change will go through
+  memgraph::metrics::DecrementCounter(memgraph::metrics::ActiveLabelPropertyIndices);
+
+  if (success) {
+    return {};
+  }
+
+  return StorageIndexDefinitionError{ReplicationError{}};
+}
+
+utils::BasicResult<StorageExistenceConstraintDefinitionError, void> InMemoryStorage::CreateExistenceConstraint(
+    LabelId label, PropertyId property, const std::optional<uint64_t> desired_commit_timestamp) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+
+  if (constraints_.existence_constraints_->ConstraintExists(label, property)) {
+    return StorageExistenceConstraintDefinitionError{ConstraintDefinitionError{}};
+  }
+
+  if (auto violation = ExistenceConstraints::ValidateVerticesOnConstraint(vertices_.access(), label, property);
+      violation.has_value()) {
+    return StorageExistenceConstraintDefinitionError{violation.value()};
+  }
+
+  constraints_.existence_constraints_->InsertConstraint(label, property);
+
+  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
+  auto success = AppendToWalDataDefinition(durability::StorageGlobalOperation::EXISTENCE_CONSTRAINT_CREATE, label,
+                                           {property}, commit_timestamp);
+  commit_log_->MarkFinished(commit_timestamp);
+  last_commit_timestamp_ = commit_timestamp;
+
+  if (success) {
+    return {};
+  }
+
+  return StorageExistenceConstraintDefinitionError{ReplicationError{}};
+}
+
+utils::BasicResult<StorageExistenceConstraintDroppingError, void> InMemoryStorage::DropExistenceConstraint(
+    LabelId label, PropertyId property, const std::optional<uint64_t> desired_commit_timestamp) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+  if (!constraints_.existence_constraints_->DropConstraint(label, property)) {
+    return StorageExistenceConstraintDroppingError{ConstraintDefinitionError{}};
+  }
+  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
+  auto success = AppendToWalDataDefinition(durability::StorageGlobalOperation::EXISTENCE_CONSTRAINT_DROP, label,
+                                           {property}, commit_timestamp);
+  commit_log_->MarkFinished(commit_timestamp);
+  last_commit_timestamp_ = commit_timestamp;
+
+  if (success) {
+    return {};
+  }
+
+  return StorageExistenceConstraintDroppingError{ReplicationError{}};
+}
+
+utils::BasicResult<StorageUniqueConstraintDefinitionError, UniqueConstraints::CreationStatus>
+InMemoryStorage::CreateUniqueConstraint(LabelId label, const std::set<PropertyId> &properties,
+                                        const std::optional<uint64_t> desired_commit_timestamp) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+  auto *mem_unique_constraints = static_cast<InMemoryUniqueConstraints *>(constraints_.unique_constraints_.get());
+  auto ret = mem_unique_constraints->CreateConstraint(label, properties, vertices_.access());
+  if (ret.HasError()) {
+    return StorageUniqueConstraintDefinitionError{ret.GetError()};
+  }
+  if (ret.GetValue() != UniqueConstraints::CreationStatus::SUCCESS) {
+    return ret.GetValue();
+  }
+  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
+  auto success = AppendToWalDataDefinition(durability::StorageGlobalOperation::UNIQUE_CONSTRAINT_CREATE, label,
+                                           properties, commit_timestamp);
+  commit_log_->MarkFinished(commit_timestamp);
+  last_commit_timestamp_ = commit_timestamp;
+
+  if (success) {
+    return UniqueConstraints::CreationStatus::SUCCESS;
+  }
+
+  return StorageUniqueConstraintDefinitionError{ReplicationError{}};
+}
+
+utils::BasicResult<StorageUniqueConstraintDroppingError, UniqueConstraints::DeletionStatus>
+InMemoryStorage::DropUniqueConstraint(LabelId label, const std::set<PropertyId> &properties,
+                                      const std::optional<uint64_t> desired_commit_timestamp) {
+  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
+  auto ret = constraints_.unique_constraints_->DropConstraint(label, properties);
+  if (ret != UniqueConstraints::DeletionStatus::SUCCESS) {
+    return ret;
+  }
+  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
+  auto success = AppendToWalDataDefinition(durability::StorageGlobalOperation::UNIQUE_CONSTRAINT_DROP, label,
+                                           properties, commit_timestamp);
+  commit_log_->MarkFinished(commit_timestamp);
+  last_commit_timestamp_ = commit_timestamp;
+
+  if (success) {
+    return UniqueConstraints::DeletionStatus::SUCCESS;
+  }
+
+  return StorageUniqueConstraintDroppingError{ReplicationError{}};
+}
+
+VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, View view) {
+  auto *mem_label_index = static_cast<InMemoryLabelIndex *>(storage_->indices_.label_index_.get());
+  return VerticesIterable(mem_label_index->Vertices(label, view, &transaction_));
+}
+
+VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, PropertyId property, View view) {
+  auto *mem_label_property_index =
+      static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
+  return VerticesIterable(
+      mem_label_property_index->Vertices(label, property, std::nullopt, std::nullopt, view, &transaction_));
+}
+
+VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(LabelId label, PropertyId property,
+                                                             const PropertyValue &value, View view) {
+  auto *mem_label_property_index =
+      static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
+  return VerticesIterable(mem_label_property_index->Vertices(label, property, utils::MakeBoundInclusive(value),
+                                                             utils::MakeBoundInclusive(value), view, &transaction_));
+}
+
+VerticesIterable InMemoryStorage::InMemoryAccessor::Vertices(
+    LabelId label, PropertyId property, const std::optional<utils::Bound<PropertyValue>> &lower_bound,
+    const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) {
+  auto *mem_label_property_index =
+      static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get());
+  return VerticesIterable(
+      mem_label_property_index->Vertices(label, property, lower_bound, upper_bound, view, &transaction_));
+}
+
+Transaction InMemoryStorage::CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) {
+  // We acquire the transaction engine lock here because we access (and
+  // modify) the transaction engine variables (`transaction_id` and
+  // `timestamp`) below.
+  uint64_t transaction_id = 0;
+  uint64_t start_timestamp = 0;
+  {
+    std::lock_guard<utils::SpinLock> guard(engine_lock_);
+    transaction_id = transaction_id_++;
+    // Replica should have only read queries and the write queries
+    // can come from main instance with any past timestamp.
+    // To preserve snapshot isolation we set the start timestamp
+    // of any query on replica to the last commited transaction
+    // which is timestamp_ as only commit of transaction with writes
+    // can change the value of it.
+    if (replication_role_ == replication::ReplicationRole::REPLICA) {
+      start_timestamp = timestamp_;
+    } else {
+      start_timestamp = timestamp_++;
+    }
+  }
+  return {transaction_id, start_timestamp, isolation_level, storage_mode};
+}
+
+template <bool force>
+void InMemoryStorage::CollectGarbage(std::unique_lock<utils::RWLock> main_guard) {
+  // NOTE: You do not need to consider cleanup of deleted object that occurred in
+  // different storage modes within the same CollectGarbage call. This is because
+  // SetStorageMode will ensure CollectGarbage is called before any new transactions
+  // with the new storage mode can start.
+
+  // SetStorageMode will pass its unique_lock of main_lock_. We will use that lock,
+  // as reacquiring the lock would cause  deadlock. Otherwise, we need to get our own
+  // lock.
+  if (!main_guard.owns_lock()) {
+    if constexpr (force) {
+      // We take the unique lock on the main storage lock, so we can forcefully clean
+      // everything we can
+      if (!main_lock_.try_lock()) {
+        CollectGarbage<false>();
+        return;
+      }
+    } else {
+      // Because the garbage collector iterates through the indices and constraints
+      // to clean them up, it must take the main lock for reading to make sure that
+      // the indices and constraints aren't concurrently being modified.
+      main_lock_.lock_shared();
+    }
+  } else {
+    MG_ASSERT(main_guard.mutex() == std::addressof(main_lock_), "main_guard should be only for the main_lock_");
+  }
+
+  utils::OnScopeExit lock_releaser{[&] {
+    if (!main_guard.owns_lock()) {
+      if constexpr (force) {
+        main_lock_.unlock();
+      } else {
+        main_lock_.unlock_shared();
+      }
+    } else {
+      main_guard.unlock();
+    }
+  }};
+
+  // Garbage collection must be performed in two phases. In the first phase,
+  // deltas that won't be applied by any transaction anymore are unlinked from
+  // the version chains. They cannot be deleted immediately, because there
+  // might be a transaction that still needs them to terminate the version
+  // chain traversal. They are instead marked for deletion and will be deleted
+  // in the second GC phase in this GC iteration or some of the following
+  // ones.
+  std::unique_lock<std::mutex> gc_guard(gc_lock_, std::try_to_lock);
+  if (!gc_guard.owns_lock()) {
+    return;
+  }
+
+  uint64_t oldest_active_start_timestamp = commit_log_->OldestActive();
+  // We don't move undo buffers of unlinked transactions to garbage_undo_buffers
+  // list immediately, because we would have to repeatedly take
+  // garbage_undo_buffers lock.
+  std::list<std::pair<uint64_t, std::list<Delta>>> unlinked_undo_buffers;
+
+  // We will only free vertices deleted up until now in this GC cycle, and we
+  // will do it after cleaning-up the indices. That way we are sure that all
+  // vertices that appear in an index also exist in main storage.
+  std::list<Gid> current_deleted_edges;
+  std::list<Gid> current_deleted_vertices;
+  deleted_vertices_->swap(current_deleted_vertices);
+  deleted_edges_->swap(current_deleted_edges);
+
+  auto const need_full_scan_vertices = gc_full_scan_vertices_delete_.exchange(false);
+  auto const need_full_scan_edges = gc_full_scan_edges_delete_.exchange(false);
+
+  // Flag that will be used to determine whether the Index GC should be run. It
+  // should be run when there were any items that were cleaned up (there were
+  // updates between this run of the GC and the previous run of the GC). This
+  // eliminates high CPU usage when the GC doesn't have to clean up anything.
+  bool run_index_cleanup = !committed_transactions_->empty() || !garbage_undo_buffers_->empty() ||
+                           need_full_scan_vertices || need_full_scan_edges;
+
+  while (true) {
+    // We don't want to hold the lock on committed transactions for too long,
+    // because that prevents other transactions from committing.
+    Transaction *transaction = nullptr;
+    {
+      auto committed_transactions_ptr = committed_transactions_.Lock();
+      if (committed_transactions_ptr->empty()) {
+        break;
+      }
+      transaction = &committed_transactions_ptr->front();
+    }
+
+    auto commit_timestamp = transaction->commit_timestamp->load(std::memory_order_acquire);
+    if (commit_timestamp >= oldest_active_start_timestamp) {
+      break;
+    }
+
+    // When unlinking a delta which is the first delta in its version chain,
+    // special care has to be taken to avoid the following race condition:
+    //
+    // [Vertex] --> [Delta A]
+    //
+    //    GC thread: Delta A is the first in its chain, it must be unlinked from
+    //               vertex and marked for deletion
+    //    TX thread: Update vertex and add Delta B with Delta A as next
+    //
+    // [Vertex] --> [Delta B] <--> [Delta A]
+    //
+    //    GC thread: Unlink delta from Vertex
+    //
+    // [Vertex] --> (nullptr)
+    //
+    // When processing a delta that is the first one in its chain, we
+    // obtain the corresponding vertex or edge lock, and then verify that this
+    // delta still is the first in its chain.
+    // When processing a delta that is in the middle of the chain we only
+    // process the final delta of the given transaction in that chain. We
+    // determine the owner of the chain (either a vertex or an edge), obtain the
+    // corresponding lock, and then verify that this delta is still in the same
+    // position as it was before taking the lock.
+    //
+    // Even though the delta chain is lock-free (both `next` and `prev`) the
+    // chain should not be modified without taking the lock from the object that
+    // owns the chain (either a vertex or an edge). Modifying the chain without
+    // taking the lock will cause subtle race conditions that will leave the
+    // chain in a broken state.
+    // The chain can be only read without taking any locks.
+
+    for (Delta &delta : transaction->deltas) {
+      while (true) {
+        auto prev = delta.prev.Get();
+        switch (prev.type) {
+          case PreviousPtr::Type::VERTEX: {
+            Vertex *vertex = prev.vertex;
+            std::lock_guard<utils::SpinLock> vertex_guard(vertex->lock);
+            if (vertex->delta != &delta) {
+              // Something changed, we're not the first delta in the chain
+              // anymore.
+              continue;
+            }
+            vertex->delta = nullptr;
+            if (vertex->deleted) {
+              current_deleted_vertices.push_back(vertex->gid);
+            }
+            break;
+          }
+          case PreviousPtr::Type::EDGE: {
+            Edge *edge = prev.edge;
+            std::lock_guard<utils::SpinLock> edge_guard(edge->lock);
+            if (edge->delta != &delta) {
+              // Something changed, we're not the first delta in the chain
+              // anymore.
+              continue;
+            }
+            edge->delta = nullptr;
+            if (edge->deleted) {
+              current_deleted_edges.push_back(edge->gid);
+            }
+            break;
+          }
+          case PreviousPtr::Type::DELTA: {
+            if (prev.delta->timestamp->load(std::memory_order_acquire) == commit_timestamp) {
+              // The delta that is newer than this one is also a delta from this
+              // transaction. We skip the current delta and will remove it as a
+              // part of the suffix later.
+              break;
+            }
+            std::unique_lock<utils::SpinLock> guard;
+            {
+              // We need to find the parent object in order to be able to use
+              // its lock.
+              auto parent = prev;
+              while (parent.type == PreviousPtr::Type::DELTA) {
+                parent = parent.delta->prev.Get();
+              }
+              switch (parent.type) {
+                case PreviousPtr::Type::VERTEX:
+                  guard = std::unique_lock<utils::SpinLock>(parent.vertex->lock);
+                  break;
+                case PreviousPtr::Type::EDGE:
+                  guard = std::unique_lock<utils::SpinLock>(parent.edge->lock);
+                  break;
+                case PreviousPtr::Type::DELTA:
+                case PreviousPtr::Type::NULLPTR:
+                  LOG_FATAL("Invalid database state!");
+              }
+            }
+            if (delta.prev.Get() != prev) {
+              // Something changed, we could now be the first delta in the
+              // chain.
+              continue;
+            }
+            Delta *prev_delta = prev.delta;
+            prev_delta->next.store(nullptr, std::memory_order_release);
+            break;
+          }
+          case PreviousPtr::Type::NULLPTR: {
+            LOG_FATAL("Invalid pointer!");
+          }
+        }
+        break;
+      }
+    }
+
+    committed_transactions_.WithLock([&](auto &committed_transactions) {
+      unlinked_undo_buffers.emplace_back(0, std::move(transaction->deltas));
+      committed_transactions.pop_front();
+    });
+  }
+
+  // After unlinking deltas from vertices, we refresh the indices. That way
+  // we're sure that none of the vertices from `current_deleted_vertices`
+  // appears in an index, and we can safely remove the from the main storage
+  // after the last currently active transaction is finished.
+  if (run_index_cleanup) {
+    // This operation is very expensive as it traverses through all of the items
+    // in every index every time.
+    indices_.RemoveObsoleteEntries(oldest_active_start_timestamp);
+    auto *mem_unique_constraints = static_cast<InMemoryUniqueConstraints *>(constraints_.unique_constraints_.get());
+    mem_unique_constraints->RemoveObsoleteEntries(oldest_active_start_timestamp);
+  }
+
+  {
+    std::unique_lock<utils::SpinLock> guard(engine_lock_);
+    uint64_t mark_timestamp = timestamp_;
+    // Take garbage_undo_buffers lock while holding the engine lock to make
+    // sure that entries are sorted by mark timestamp in the list.
+    garbage_undo_buffers_.WithLock([&](auto &garbage_undo_buffers) {
+      // Release engine lock because we don't have to hold it anymore and
+      // this could take a long time.
+      guard.unlock();
+      // TODO(mtomic): holding garbage_undo_buffers_ lock here prevents
+      // transactions from aborting until we're done marking, maybe we should
+      // add them one-by-one or something
+      for (auto &[timestamp, undo_buffer] : unlinked_undo_buffers) {
+        timestamp = mark_timestamp;
+      }
+      garbage_undo_buffers.splice(garbage_undo_buffers.end(), unlinked_undo_buffers);
+    });
+    for (auto vertex : current_deleted_vertices) {
+      garbage_vertices_.emplace_back(mark_timestamp, vertex);
+    }
+  }
+
+  garbage_undo_buffers_.WithLock([&](auto &undo_buffers) {
+    // if force is set to true we can simply delete all the leftover undos because
+    // no transaction is active
+    if constexpr (force) {
+      undo_buffers.clear();
+    } else {
+      while (!undo_buffers.empty() && undo_buffers.front().first <= oldest_active_start_timestamp) {
+        undo_buffers.pop_front();
+      }
+    }
+  });
+
+  {
+    auto vertex_acc = vertices_.access();
+    if constexpr (force) {
+      // if force is set to true, then we have unique_lock and no transactions are active
+      // so we can clean all of the deleted vertices
+      while (!garbage_vertices_.empty()) {
+        MG_ASSERT(vertex_acc.remove(garbage_vertices_.front().second), "Invalid database state!");
+        garbage_vertices_.pop_front();
+      }
+    } else {
+      while (!garbage_vertices_.empty() && garbage_vertices_.front().first < oldest_active_start_timestamp) {
+        MG_ASSERT(vertex_acc.remove(garbage_vertices_.front().second), "Invalid database state!");
+        garbage_vertices_.pop_front();
+      }
+    }
+  }
+  {
+    auto edge_acc = edges_.access();
+    for (auto edge : current_deleted_edges) {
+      MG_ASSERT(edge_acc.remove(edge), "Invalid database state!");
+    }
+  }
+
+  // EXPENSIVE full scan, is only run if an IN_MEMORY_ANALYTICAL transaction involved any deletions
+  // TODO: implement a fast internal iteration inside the skip_list (to avoid unnecessary find_node calls),
+  //  accessor.remove_if([](auto const & item){ return item.delta == nullptr && item.deleted;});
+  //  alternatively, an auxiliary data structure within skip_list to track these, hence a full scan wouldn't be needed
+  //  we will wait for evidence that this is needed before doing so.
+  if (need_full_scan_vertices) {
+    auto vertex_acc = vertices_.access();
+    for (auto &vertex : vertex_acc) {
+      // a deleted vertex which as no deltas must have come from IN_MEMORY_ANALYTICAL deletion
+      if (vertex.delta == nullptr && vertex.deleted) {
+        vertex_acc.remove(vertex);
+      }
+    }
+  }
+
+  // EXPENSIVE full scan, is only run if an IN_MEMORY_ANALYTICAL transaction involved any deletions
+  if (need_full_scan_edges) {
+    auto edge_acc = edges_.access();
+    for (auto &edge : edge_acc) {
+      // a deleted edge which as no deltas must have come from IN_MEMORY_ANALYTICAL deletion
+      if (edge.delta == nullptr && edge.deleted) {
+        edge_acc.remove(edge);
+      }
+    }
+  }
+}
+
+// tell the linker he can find the CollectGarbage definitions here
+template void InMemoryStorage::CollectGarbage<true>(std::unique_lock<utils::RWLock>);
+template void InMemoryStorage::CollectGarbage<false>(std::unique_lock<utils::RWLock>);
+
+StorageInfo InMemoryStorage::GetInfo() const {
+  auto vertex_count = vertices_.size();
+  auto edge_count = edge_count_.load(std::memory_order_acquire);
+  double average_degree = 0.0;
+  if (vertex_count) {
+    // NOLINTNEXTLINE(bugprone-narrowing-conversions, cppcoreguidelines-narrowing-conversions)
+    average_degree = 2.0 * static_cast<double>(edge_count) / vertex_count;
+  }
+  return {vertex_count, edge_count, average_degree, utils::GetMemoryUsage(),
+          utils::GetDirDiskUsage(config_.durability.storage_directory)};
+}
+
+bool InMemoryStorage::InitializeWalFile() {
+  if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL)
+    return false;
+  if (!wal_file_) {
+    wal_file_.emplace(wal_directory_, uuid_, epoch_id_, config_.items, name_id_mapper_.get(), wal_seq_num_++,
+                      &file_retainer_);
+  }
+  return true;
+}
+
+void InMemoryStorage::FinalizeWalFile() {
+  ++wal_unsynced_transactions_;
+  if (wal_unsynced_transactions_ >= config_.durability.wal_file_flush_every_n_tx) {
+    wal_file_->Sync();
+    wal_unsynced_transactions_ = 0;
+  }
+  if (wal_file_->GetSize() / 1024 >= config_.durability.wal_file_size_kibibytes) {
+    wal_file_->FinalizeWal();
+    wal_file_ = std::nullopt;
+    wal_unsynced_transactions_ = 0;
+  } else {
+    // Try writing the internal buffer if possible, if not
+    // the data should be written as soon as it's possible
+    // (triggered by the new transaction commit, or some
+    // reading thread EnabledFlushing)
+    wal_file_->TryFlushing();
+  }
+}
+
+bool InMemoryStorage::AppendToWalDataManipulation(const Transaction &transaction, uint64_t final_commit_timestamp) {
+  if (!InitializeWalFile()) {
+    return true;
+  }
+  // Traverse deltas and append them to the WAL file.
+  // A single transaction will always be contained in a single WAL file.
+  auto current_commit_timestamp = transaction.commit_timestamp->load(std::memory_order_acquire);
+
+  if (replication_role_.load() == replication::ReplicationRole::MAIN) {
+    replication_clients_.WithLock([&](auto &clients) {
+      for (auto &client : clients) {
+        client->StartTransactionReplication(wal_file_->SequenceNumber());
+      }
+    });
+  }
+
+  // Helper lambda that traverses the delta chain on order to find the first
+  // delta that should be processed and then appends all discovered deltas.
+  auto find_and_apply_deltas = [&](const auto *delta, const auto &parent, auto filter) {
+    while (true) {
+      auto *older = delta->next.load(std::memory_order_acquire);
+      if (older == nullptr || older->timestamp->load(std::memory_order_acquire) != current_commit_timestamp) break;
+      delta = older;
+    }
+    while (true) {
+      if (filter(delta->action)) {
+        wal_file_->AppendDelta(*delta, parent, final_commit_timestamp);
+        replication_clients_.WithLock([&](auto &clients) {
+          for (auto &client : clients) {
+            client->IfStreamingTransaction(
+                [&](auto &stream) { stream.AppendDelta(*delta, parent, final_commit_timestamp); });
+          }
+        });
+      }
+      auto prev = delta->prev.Get();
+      MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
+      if (prev.type != PreviousPtr::Type::DELTA) break;
+      delta = prev.delta;
+    }
+  };
+
+  // The deltas are ordered correctly in the `transaction.deltas` buffer, but we
+  // don't traverse them in that order. That is because for each delta we need
+  // information about the vertex or edge they belong to and that information
+  // isn't stored in the deltas themselves. In order to find out information
+  // about the corresponding vertex or edge it is necessary to traverse the
+  // delta chain for each delta until a vertex or edge is encountered. This
+  // operation is very expensive as the chain grows.
+  // Instead, we traverse the edges until we find a vertex or edge and traverse
+  // their delta chains. This approach has a drawback because we lose the
+  // correct order of the operations. Because of that, we need to traverse the
+  // deltas several times and we have to manually ensure that the stored deltas
+  // will be ordered correctly.
+
+  // 1. Process all Vertex deltas and store all operations that create vertices
+  // and modify vertex data.
+  for (const auto &delta : transaction.deltas) {
+    auto prev = delta.prev.Get();
+    MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
+    if (prev.type != PreviousPtr::Type::VERTEX) continue;
+    find_and_apply_deltas(&delta, *prev.vertex, [](auto action) {
+      switch (action) {
+        case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+        case Delta::Action::DELETE_OBJECT:
+        case Delta::Action::SET_PROPERTY:
+        case Delta::Action::ADD_LABEL:
+        case Delta::Action::REMOVE_LABEL:
+          return true;
+
+        case Delta::Action::RECREATE_OBJECT:
+        case Delta::Action::ADD_IN_EDGE:
+        case Delta::Action::ADD_OUT_EDGE:
+        case Delta::Action::REMOVE_IN_EDGE:
+        case Delta::Action::REMOVE_OUT_EDGE:
+          return false;
+      }
+    });
+  }
+  // 2. Process all Vertex deltas and store all operations that create edges.
+  for (const auto &delta : transaction.deltas) {
+    auto prev = delta.prev.Get();
+    MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
+    if (prev.type != PreviousPtr::Type::VERTEX) continue;
+    find_and_apply_deltas(&delta, *prev.vertex, [](auto action) {
+      switch (action) {
+        case Delta::Action::REMOVE_OUT_EDGE:
+          return true;
+        case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+        case Delta::Action::DELETE_OBJECT:
+        case Delta::Action::RECREATE_OBJECT:
+        case Delta::Action::SET_PROPERTY:
+        case Delta::Action::ADD_LABEL:
+        case Delta::Action::REMOVE_LABEL:
+        case Delta::Action::ADD_IN_EDGE:
+        case Delta::Action::ADD_OUT_EDGE:
+        case Delta::Action::REMOVE_IN_EDGE:
+          return false;
+      }
+    });
+  }
+  // 3. Process all Edge deltas and store all operations that modify edge data.
+  for (const auto &delta : transaction.deltas) {
+    auto prev = delta.prev.Get();
+    MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
+    if (prev.type != PreviousPtr::Type::EDGE) continue;
+    find_and_apply_deltas(&delta, *prev.edge, [](auto action) {
+      switch (action) {
+        case Delta::Action::SET_PROPERTY:
+          return true;
+        case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+        case Delta::Action::DELETE_OBJECT:
+        case Delta::Action::RECREATE_OBJECT:
+        case Delta::Action::ADD_LABEL:
+        case Delta::Action::REMOVE_LABEL:
+        case Delta::Action::ADD_IN_EDGE:
+        case Delta::Action::ADD_OUT_EDGE:
+        case Delta::Action::REMOVE_IN_EDGE:
+        case Delta::Action::REMOVE_OUT_EDGE:
+          return false;
+      }
+    });
+  }
+  // 4. Process all Vertex deltas and store all operations that delete edges.
+  for (const auto &delta : transaction.deltas) {
+    auto prev = delta.prev.Get();
+    MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
+    if (prev.type != PreviousPtr::Type::VERTEX) continue;
+    find_and_apply_deltas(&delta, *prev.vertex, [](auto action) {
+      switch (action) {
+        case Delta::Action::ADD_OUT_EDGE:
+          return true;
+        case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+        case Delta::Action::DELETE_OBJECT:
+        case Delta::Action::RECREATE_OBJECT:
+        case Delta::Action::SET_PROPERTY:
+        case Delta::Action::ADD_LABEL:
+        case Delta::Action::REMOVE_LABEL:
+        case Delta::Action::ADD_IN_EDGE:
+        case Delta::Action::REMOVE_IN_EDGE:
+        case Delta::Action::REMOVE_OUT_EDGE:
+          return false;
+      }
+    });
+  }
+  // 5. Process all Vertex deltas and store all operations that delete vertices.
+  for (const auto &delta : transaction.deltas) {
+    auto prev = delta.prev.Get();
+    MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
+    if (prev.type != PreviousPtr::Type::VERTEX) continue;
+    find_and_apply_deltas(&delta, *prev.vertex, [](auto action) {
+      switch (action) {
+        case Delta::Action::RECREATE_OBJECT:
+          return true;
+        case Delta::Action::DELETE_DESERIALIZED_OBJECT:
+        case Delta::Action::DELETE_OBJECT:
+        case Delta::Action::SET_PROPERTY:
+        case Delta::Action::ADD_LABEL:
+        case Delta::Action::REMOVE_LABEL:
+        case Delta::Action::ADD_IN_EDGE:
+        case Delta::Action::ADD_OUT_EDGE:
+        case Delta::Action::REMOVE_IN_EDGE:
+        case Delta::Action::REMOVE_OUT_EDGE:
+          return false;
+      }
+    });
+  }
+
+  // Add a delta that indicates that the transaction is fully written to the WAL
+  // file.
+  wal_file_->AppendTransactionEnd(final_commit_timestamp);
+
+  FinalizeWalFile();
+
+  auto finalized_on_all_replicas = true;
+  replication_clients_.WithLock([&](auto &clients) {
+    for (auto &client : clients) {
+      client->IfStreamingTransaction([&](auto &stream) { stream.AppendTransactionEnd(final_commit_timestamp); });
+      const auto finalized = client->FinalizeTransactionReplication();
+
+      if (client->Mode() == replication::ReplicationMode::SYNC) {
+        finalized_on_all_replicas = finalized && finalized_on_all_replicas;
+      }
+    }
+  });
+
+  return finalized_on_all_replicas;
+}
+
+bool InMemoryStorage::AppendToWalDataDefinition(durability::StorageGlobalOperation operation, LabelId label,
+                                                const std::set<PropertyId> &properties,
+                                                uint64_t final_commit_timestamp) {
+  if (!InitializeWalFile()) {
+    return true;
+  }
+
+  auto finalized_on_all_replicas = true;
+  wal_file_->AppendOperation(operation, label, properties, final_commit_timestamp);
+  {
+    if (replication_role_.load() == replication::ReplicationRole::MAIN) {
+      replication_clients_.WithLock([&](auto &clients) {
+        for (auto &client : clients) {
+          client->StartTransactionReplication(wal_file_->SequenceNumber());
+          client->IfStreamingTransaction(
+              [&](auto &stream) { stream.AppendOperation(operation, label, properties, final_commit_timestamp); });
+
+          const auto finalized = client->FinalizeTransactionReplication();
+          if (client->Mode() == replication::ReplicationMode::SYNC) {
+            finalized_on_all_replicas = finalized && finalized_on_all_replicas;
+          }
+        }
+      });
+    }
+  }
+  FinalizeWalFile();
+  return finalized_on_all_replicas;
+}
+
+utils::BasicResult<InMemoryStorage::CreateSnapshotError> InMemoryStorage::CreateSnapshot(
+    std::optional<bool> is_periodic) {
+  if (replication_role_.load() != replication::ReplicationRole::MAIN) {
+    return CreateSnapshotError::DisabledForReplica;
+  }
+
+  auto snapshot_creator = [this]() {
+    utils::Timer timer;
+
+    auto transaction = CreateTransaction(IsolationLevel::SNAPSHOT_ISOLATION, storage_mode_);
+    // Create snapshot.
+    durability::CreateSnapshot(&transaction, snapshot_directory_, wal_directory_,
+                               config_.durability.snapshot_retention_count, &vertices_, &edges_, name_id_mapper_.get(),
+                               &indices_, &constraints_, config_, uuid_, epoch_id_, epoch_history_, &file_retainer_);
+    // Finalize snapshot transaction.
+    commit_log_->MarkFinished(transaction.start_timestamp);
+
+    memgraph::metrics::Measure(memgraph::metrics::SnapshotCreationLatency_us,
+                               std::chrono::duration_cast<std::chrono::microseconds>(timer.Elapsed()).count());
+  };
+
+  std::lock_guard snapshot_guard(snapshot_lock_);
+
+  auto should_try_shared{true};
+  auto max_num_tries{10};
+  while (max_num_tries) {
+    if (should_try_shared) {
+      std::shared_lock<utils::RWLock> storage_guard(main_lock_);
+      if (storage_mode_ == memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL) {
+        snapshot_creator();
+        return {};
+      }
+    } else {
+      std::unique_lock main_guard{main_lock_};
+      if (storage_mode_ == memgraph::storage::StorageMode::IN_MEMORY_ANALYTICAL) {
+        if (is_periodic && *is_periodic) {
+          return CreateSnapshotError::DisabledForAnalyticsPeriodicCommit;
+        }
+        snapshot_creator();
+        return {};
+      }
+    }
+    should_try_shared = !should_try_shared;
+    max_num_tries--;
+  }
+
+  return CreateSnapshotError::ReachedMaxNumTries;
+}
+
+void InMemoryStorage::FreeMemory(std::unique_lock<utils::RWLock> main_guard) {
+  CollectGarbage<true>(std::move(main_guard));
+
+  // SkipList is already threadsafe
+  vertices_.run_gc();
+  edges_.run_gc();
+
+  static_cast<InMemoryLabelIndex *>(indices_.label_index_.get())->RunGC();
+  static_cast<InMemoryLabelPropertyIndex *>(indices_.label_property_index_.get())->RunGC();
+}
+
+uint64_t InMemoryStorage::CommitTimestamp(const std::optional<uint64_t> desired_commit_timestamp) {
+  if (!desired_commit_timestamp) {
+    return timestamp_++;
+  }
+  timestamp_ = std::max(timestamp_, *desired_commit_timestamp + 1);
+  return *desired_commit_timestamp;
+}
+
+bool InMemoryStorage::SetReplicaRole(io::network::Endpoint endpoint,
+                                     const replication::ReplicationServerConfig &config) {
+  // We don't want to restart the server if we're already a REPLICA
+  if (replication_role_ == replication::ReplicationRole::REPLICA) {
+    return false;
+  }
+
+  auto port = endpoint.port;  // assigning because we will move the endpoint
+  replication_server_ = std::make_unique<ReplicationServer>(this, std::move(endpoint), config);
+
+  if (ShouldStoreAndRestoreReplicationState()) {
+    // Only thing that matters here is the role saved as REPLICA and the listening port
+    auto data = replication::ReplicationStatusToJSON(
+        replication::ReplicationStatus{.name = replication::kReservedReplicationRoleName,
+                                       .ip_address = "",
+                                       .port = port,
+                                       .sync_mode = replication::ReplicationMode::SYNC,
+                                       .replica_check_frequency = std::chrono::seconds(0),
+                                       .ssl = std::nullopt,
+                                       .role = replication::ReplicationRole::REPLICA});
+
+    if (!storage_->Put(replication::kReservedReplicationRoleName, data.dump())) {
+      spdlog::error("Error when saving REPLICA replication role in settings.");
+      return false;
+    }
+  }
+
+  replication_role_.store(replication::ReplicationRole::REPLICA);
+  return true;
+}
+
+bool InMemoryStorage::SetMainReplicationRole() {
+  // We don't want to generate new epoch_id and do the
+  // cleanup if we're already a MAIN
+  if (replication_role_ == replication::ReplicationRole::MAIN) {
+    return false;
+  }
+
+  // Main instance does not need replication server
+  // This should be always called first so we finalize everything
+  replication_server_.reset(nullptr);
+
+  {
+    std::unique_lock engine_guard{engine_lock_};
+    if (wal_file_) {
+      wal_file_->FinalizeWal();
+      wal_file_.reset();
+    }
+
+    // Generate new epoch id and save the last one to the history.
+    if (epoch_history_.size() == kEpochHistoryRetention) {
+      epoch_history_.pop_front();
+    }
+    epoch_history_.emplace_back(std::move(epoch_id_), last_commit_timestamp_);
+    epoch_id_ = utils::GenerateUUID();
+  }
+
+  if (ShouldStoreAndRestoreReplicationState()) {
+    // Only thing that matters here is the role saved as MAIN
+    auto data = replication::ReplicationStatusToJSON(
+        replication::ReplicationStatus{.name = replication::kReservedReplicationRoleName,
+                                       .ip_address = "",
+                                       .port = 0,
+                                       .sync_mode = replication::ReplicationMode::SYNC,
+                                       .replica_check_frequency = std::chrono::seconds(0),
+                                       .ssl = std::nullopt,
+                                       .role = replication::ReplicationRole::MAIN});
+
+    if (!storage_->Put(replication::kReservedReplicationRoleName, data.dump())) {
+      spdlog::error("Error when saving MAIN replication role in settings.");
+      return false;
+    }
+  }
+
+  replication_role_.store(replication::ReplicationRole::MAIN);
+  return true;
+}
+
+utils::BasicResult<InMemoryStorage::RegisterReplicaError> InMemoryStorage::RegisterReplica(
+    std::string name, io::network::Endpoint endpoint, const replication::ReplicationMode replication_mode,
+    const replication::RegistrationMode registration_mode, const replication::ReplicationClientConfig &config) {
+  MG_ASSERT(replication_role_.load() == replication::ReplicationRole::MAIN,
+            "Only main instance can register a replica!");
+
+  const bool name_exists = replication_clients_.WithLock([&](auto &clients) {
+    return std::any_of(clients.begin(), clients.end(), [&name](const auto &client) { return client->Name() == name; });
+  });
+
+  if (name_exists) {
+    return RegisterReplicaError::NAME_EXISTS;
+  }
+
+  const auto end_point_exists = replication_clients_.WithLock([&endpoint](auto &clients) {
+    return std::any_of(clients.begin(), clients.end(),
+                       [&endpoint](const auto &client) { return client->Endpoint() == endpoint; });
+  });
+
+  if (end_point_exists) {
+    return RegisterReplicaError::END_POINT_EXISTS;
+  }
+
+  if (ShouldStoreAndRestoreReplicationState()) {
+    auto data = replication::ReplicationStatusToJSON(
+        replication::ReplicationStatus{.name = name,
+                                       .ip_address = endpoint.address,
+                                       .port = endpoint.port,
+                                       .sync_mode = replication_mode,
+                                       .replica_check_frequency = config.replica_check_frequency,
+                                       .ssl = config.ssl,
+                                       .role = replication::ReplicationRole::REPLICA});
+    if (!storage_->Put(name, data.dump())) {
+      spdlog::error("Error when saving replica {} in settings.", name);
+      return RegisterReplicaError::COULD_NOT_BE_PERSISTED;
+    }
+  }
+
+  auto client = std::make_unique<ReplicationClient>(std::move(name), this, endpoint, replication_mode, config);
+
+  if (client->State() == replication::ReplicaState::INVALID) {
+    if (replication::RegistrationMode::CAN_BE_INVALID != registration_mode) {
+      return RegisterReplicaError::CONNECTION_FAILED;
+    }
+
+    spdlog::warn("Connection failed when registering replica {}. Replica will still be registered.", client->Name());
+  }
+
+  return replication_clients_.WithLock([&](auto &clients) -> utils::BasicResult<InMemoryStorage::RegisterReplicaError> {
+    // Another thread could have added a client with same name while
+    // we were connecting to this client.
+    if (std::any_of(clients.begin(), clients.end(),
+                    [&](const auto &other_client) { return client->Name() == other_client->Name(); })) {
+      return RegisterReplicaError::NAME_EXISTS;
+    }
+
+    if (std::any_of(clients.begin(), clients.end(),
+                    [&client](const auto &other_client) { return client->Endpoint() == other_client->Endpoint(); })) {
+      return RegisterReplicaError::END_POINT_EXISTS;
+    }
+
+    clients.push_back(std::move(client));
+    return {};
+  });
+}
+
+bool InMemoryStorage::UnregisterReplica(const std::string &name) {
+  MG_ASSERT(replication_role_.load() == replication::ReplicationRole::MAIN,
+            "Only main instance can unregister a replica!");
+  if (ShouldStoreAndRestoreReplicationState()) {
+    if (!storage_->Delete(name)) {
+      spdlog::error("Error when removing replica {} from settings.", name);
+      return false;
+    }
+  }
+
+  return replication_clients_.WithLock([&](auto &clients) {
+    return std::erase_if(clients, [&](const auto &client) { return client->Name() == name; });
+  });
+}
+
+std::optional<replication::ReplicaState> InMemoryStorage::GetReplicaState(const std::string_view name) {
+  return replication_clients_.WithLock([&](auto &clients) -> std::optional<replication::ReplicaState> {
+    const auto client_it =
+        std::find_if(clients.cbegin(), clients.cend(), [name](auto &client) { return client->Name() == name; });
+    if (client_it == clients.cend()) {
+      return std::nullopt;
+    }
+    return (*client_it)->State();
+  });
+}
+
+replication::ReplicationRole InMemoryStorage::GetReplicationRole() const { return replication_role_; }
+
+std::vector<InMemoryStorage::ReplicaInfo> InMemoryStorage::ReplicasInfo() {
+  return replication_clients_.WithLock([](auto &clients) {
+    std::vector<InMemoryStorage::ReplicaInfo> replica_info;
+    replica_info.reserve(clients.size());
+    std::transform(
+        clients.begin(), clients.end(), std::back_inserter(replica_info), [](const auto &client) -> ReplicaInfo {
+          return {client->Name(), client->Mode(), client->Endpoint(), client->State(), client->GetTimestampInfo()};
+        });
+    return replica_info;
+  });
+}
+
+void InMemoryStorage::RestoreReplicationRole() {
+  if (!ShouldStoreAndRestoreReplicationState()) {
+    return;
+  }
+
+  spdlog::info("Restoring replication role.");
+
+  uint16_t port = replication::kDefaultReplicationPort;
+  for (const auto &[replica_name, replica_data] : *storage_) {
+    const auto maybe_replica_status = replication::JSONToReplicationStatus(nlohmann::json::parse(replica_data));
+    if (!maybe_replica_status.has_value()) {
+      LOG_FATAL("Cannot parse previously saved configuration of replica {}.", replica_name);
+    }
+
+    if (replica_name != replication::kReservedReplicationRoleName) {
+      continue;
+    }
+
+    auto replica_status = *maybe_replica_status;
+
+    if (!replica_status.role.has_value()) {
+      replication_role_.store(replication::ReplicationRole::MAIN);
+    } else {
+      replication_role_.store(*replica_status.role);
+      port = replica_status.port;
+    }
+
+    break;
+  }
+
+  if (replication_role_ == replication::ReplicationRole::REPLICA) {
+    io::network::Endpoint endpoint(replication::kDefaultReplicationServerIp, port);
+    replication_server_ =
+        std::make_unique<ReplicationServer>(this, std::move(endpoint), replication::ReplicationServerConfig{});
+  }
+
+  spdlog::info("Replication role restored to {}.",
+               replication_role_ == replication::ReplicationRole::MAIN ? "MAIN" : "REPLICA");
+}
+
+void InMemoryStorage::RestoreReplicas() {
+  if (!ShouldStoreAndRestoreReplicationState()) {
+    return;
+  }
+  spdlog::info("Restoring replicas.");
+
+  for (const auto &[replica_name, replica_data] : *storage_) {
+    spdlog::info("Restoring replica {}.", replica_name);
+
+    const auto maybe_replica_status = replication::JSONToReplicationStatus(nlohmann::json::parse(replica_data));
+    if (!maybe_replica_status.has_value()) {
+      LOG_FATAL("Cannot parse previously saved configuration of replica {}.", replica_name);
+    }
+
+    auto replica_status = *maybe_replica_status;
+    MG_ASSERT(replica_status.name == replica_name, "Expected replica name is '{}', but got '{}'", replica_status.name,
+              replica_name);
+
+    if (replica_name == replication::kReservedReplicationRoleName) {
+      continue;
+    }
+
+    auto ret =
+        RegisterReplica(std::move(replica_status.name), {std::move(replica_status.ip_address), replica_status.port},
+                        replica_status.sync_mode, replication::RegistrationMode::CAN_BE_INVALID,
+                        {
+                            .replica_check_frequency = replica_status.replica_check_frequency,
+                            .ssl = replica_status.ssl,
+                        });
+
+    if (ret.HasError()) {
+      MG_ASSERT(RegisterReplicaError::CONNECTION_FAILED != ret.GetError());
+      LOG_FATAL("Failure when restoring replica {}: {}.", replica_name, RegisterReplicaErrorToString(ret.GetError()));
+    }
+    spdlog::info("Replica {} restored.", replica_name);
+  }
+}
+
+bool InMemoryStorage::ShouldStoreAndRestoreReplicationState() const { return nullptr != storage_; }
+
+utils::FileRetainer::FileLockerAccessor::ret_type InMemoryStorage::IsPathLocked() {
+  auto locker_accessor = global_locker_.Access();
+  return locker_accessor.IsPathLocked(config_.durability.storage_directory);
+}
+
+utils::FileRetainer::FileLockerAccessor::ret_type InMemoryStorage::LockPath() {
+  auto locker_accessor = global_locker_.Access();
+  return locker_accessor.AddPath(config_.durability.storage_directory);
+}
+
+utils::FileRetainer::FileLockerAccessor::ret_type InMemoryStorage::UnlockPath() {
+  {
+    auto locker_accessor = global_locker_.Access();
+    const auto ret = locker_accessor.RemovePath(config_.durability.storage_directory);
+    if (ret.HasError() || !ret.GetValue()) {
+      // Exit without cleaning the queue
+      return ret;
+    }
+  }
+  // We use locker accessor in seperate scope so we don't produce deadlock
+  // after we call clean queue.
+  file_retainer_.CleanQueue();
+  return true;
+}
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/inmemory/storage.hpp b/src/storage/v2/inmemory/storage.hpp
new file mode 100644
index 000000000..b45fce0ef
--- /dev/null
+++ b/src/storage/v2/inmemory/storage.hpp
@@ -0,0 +1,522 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include "storage/v2/inmemory/label_index.hpp"
+#include "storage/v2/inmemory/label_property_index.hpp"
+#include "storage/v2/storage.hpp"
+
+/// REPLICATION ///
+#include "rpc/server.hpp"
+#include "storage/v2/replication/config.hpp"
+#include "storage/v2/replication/enums.hpp"
+#include "storage/v2/replication/replication_persistence_helper.hpp"
+#include "storage/v2/replication/rpc.hpp"
+#include "storage/v2/replication/serialization.hpp"
+
+namespace memgraph::storage {
+
+// The storage is based on this paper:
+// https://db.in.tum.de/~muehlbau/papers/mvcc.pdf
+// The paper implements a fully serializable storage, in our implementation we
+// only implement snapshot isolation for transactions.
+
+class InMemoryStorage final : public Storage {
+ public:
+  enum class RegisterReplicaError : uint8_t {
+    NAME_EXISTS,
+    END_POINT_EXISTS,
+    CONNECTION_FAILED,
+    COULD_NOT_BE_PERSISTED
+  };
+
+  struct TimestampInfo {
+    uint64_t current_timestamp_of_replica;
+    uint64_t current_number_of_timestamp_behind_master;
+  };
+
+  struct ReplicaInfo {
+    std::string name;
+    replication::ReplicationMode mode;
+    io::network::Endpoint endpoint;
+    replication::ReplicaState state;
+    TimestampInfo timestamp_info;
+  };
+
+  enum class CreateSnapshotError : uint8_t {
+    DisabledForReplica,
+    DisabledForAnalyticsPeriodicCommit,
+    ReachedMaxNumTries
+  };
+
+  /// @throw std::system_error
+  /// @throw std::bad_alloc
+  explicit InMemoryStorage(Config config = Config());
+
+  InMemoryStorage(const InMemoryStorage &) = delete;
+  InMemoryStorage(InMemoryStorage &&) = delete;
+  InMemoryStorage &operator=(const InMemoryStorage &) = delete;
+  InMemoryStorage &operator=(InMemoryStorage &&) = delete;
+
+  ~InMemoryStorage() override;
+
+  class InMemoryAccessor final : public Storage::Accessor {
+   private:
+    friend class InMemoryStorage;
+
+    explicit InMemoryAccessor(InMemoryStorage *storage, IsolationLevel isolation_level, StorageMode storage_mode);
+
+   public:
+    InMemoryAccessor(const InMemoryAccessor &) = delete;
+    InMemoryAccessor &operator=(const InMemoryAccessor &) = delete;
+    InMemoryAccessor &operator=(InMemoryAccessor &&other) = delete;
+
+    // NOTE: After the accessor is moved, all objects derived from it (accessors
+    // and iterators) are *invalid*. You have to get all derived objects again.
+    InMemoryAccessor(InMemoryAccessor &&other) noexcept;
+
+    ~InMemoryAccessor() override;
+
+    /// @throw std::bad_alloc
+    VertexAccessor CreateVertex() override;
+
+    std::optional<VertexAccessor> FindVertex(Gid gid, View view) override;
+
+    VerticesIterable Vertices(View view) override {
+      auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+      return VerticesIterable(AllVerticesIterable(mem_storage->vertices_.access(), &transaction_, view,
+                                                  &mem_storage->indices_, &mem_storage->constraints_,
+                                                  mem_storage->config_.items));
+    }
+
+    VerticesIterable Vertices(LabelId label, View view) override;
+
+    VerticesIterable Vertices(LabelId label, PropertyId property, View view) override;
+
+    VerticesIterable Vertices(LabelId label, PropertyId property, const PropertyValue &value, View view) override;
+
+    VerticesIterable Vertices(LabelId label, PropertyId property,
+                              const std::optional<utils::Bound<PropertyValue>> &lower_bound,
+                              const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) override;
+
+    /// Return approximate number of all vertices in the database.
+    /// Note that this is always an over-estimate and never an under-estimate.
+    uint64_t ApproximateVertexCount() const override {
+      auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+      return mem_storage->vertices_.size();
+    }
+
+    /// Return approximate number of vertices with the given label.
+    /// Note that this is always an over-estimate and never an under-estimate.
+    uint64_t ApproximateVertexCount(LabelId label) const override {
+      return static_cast<InMemoryStorage *>(storage_)->indices_.label_index_->ApproximateVertexCount(label);
+    }
+
+    /// Return approximate number of vertices with the given label and property.
+    /// Note that this is always an over-estimate and never an under-estimate.
+    uint64_t ApproximateVertexCount(LabelId label, PropertyId property) const override {
+      return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index_->ApproximateVertexCount(label,
+                                                                                                              property);
+    }
+
+    /// Return approximate number of vertices with the given label and the given
+    /// value for the given property. Note that this is always an over-estimate
+    /// and never an under-estimate.
+    uint64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const override {
+      return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index_->ApproximateVertexCount(
+          label, property, value);
+    }
+
+    /// Return approximate number of vertices with the given label and value for
+    /// the given property in the range defined by provided upper and lower
+    /// bounds.
+    uint64_t ApproximateVertexCount(LabelId label, PropertyId property,
+                                    const std::optional<utils::Bound<PropertyValue>> &lower,
+                                    const std::optional<utils::Bound<PropertyValue>> &upper) const override {
+      return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index_->ApproximateVertexCount(
+          label, property, lower, upper);
+    }
+
+    template <typename TResult, typename TIndex, typename TIndexKey>
+    std::optional<TResult> GetIndexStatsForIndex(TIndex *index, TIndexKey &&key) const {
+      return index->GetIndexStats(key);
+    }
+
+    std::optional<storage::LabelIndexStats> GetIndexStats(const storage::LabelId &label) const override {
+      return GetIndexStatsForIndex<storage::LabelIndexStats>(
+          static_cast<InMemoryLabelIndex *>(storage_->indices_.label_index_.get()), label);
+    }
+
+    std::optional<storage::LabelPropertyIndexStats> GetIndexStats(const storage::LabelId &label,
+                                                                  const storage::PropertyId &property) const override {
+      return GetIndexStatsForIndex<storage::LabelPropertyIndexStats>(
+          static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get()),
+          std::make_pair(label, property));
+    }
+
+    template <typename TIndex, typename TIndexKey, typename TIndexStats>
+    void SetIndexStatsForIndex(TIndex *index, TIndexKey &&key, TIndexStats &stats) const {
+      index->SetIndexStats(key, stats);
+    }
+
+    void SetIndexStats(const storage::LabelId &label, const LabelIndexStats &stats) override {
+      SetIndexStatsForIndex(static_cast<InMemoryLabelIndex *>(storage_->indices_.label_index_.get()), label, stats);
+    }
+
+    void SetIndexStats(const storage::LabelId &label, const storage::PropertyId &property,
+                       const LabelPropertyIndexStats &stats) override {
+      SetIndexStatsForIndex(static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get()),
+                            std::make_pair(label, property), stats);
+    }
+
+    template <typename TResult, typename TIndex>
+    std::vector<TResult> ClearIndexStatsForIndex(TIndex *index) const {
+      return index->ClearIndexStats();
+    }
+
+    std::vector<LabelId> ClearLabelIndexStats() override {
+      return ClearIndexStatsForIndex<LabelId>(static_cast<InMemoryLabelIndex *>(storage_->indices_.label_index_.get()));
+    }
+
+    std::vector<std::pair<LabelId, PropertyId>> ClearLabelPropertyIndexStats() override {
+      return ClearIndexStatsForIndex<std::pair<LabelId, PropertyId>>(
+          static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get()));
+    }
+
+    template <typename TResult, typename TIndex>
+    std::vector<TResult> DeleteIndexStatsForIndex(TIndex *index, const std::span<std::string> labels) {
+      std::vector<TResult> deleted_indexes;
+
+      for (const auto &label : labels) {
+        std::vector<TResult> loc_results = index->DeleteIndexStats(NameToLabel(label));
+        deleted_indexes.insert(deleted_indexes.end(), std::make_move_iterator(loc_results.begin()),
+                               std::make_move_iterator(loc_results.end()));
+      }
+      return deleted_indexes;
+    }
+
+    std::vector<std::pair<LabelId, PropertyId>> DeleteLabelPropertyIndexStats(
+        const std::span<std::string> labels) override {
+      return DeleteIndexStatsForIndex<std::pair<LabelId, PropertyId>>(
+          static_cast<InMemoryLabelPropertyIndex *>(storage_->indices_.label_property_index_.get()), labels);
+    }
+
+    std::vector<LabelId> DeleteLabelIndexStats(const std::span<std::string> labels) override {
+      return DeleteIndexStatsForIndex<LabelId>(static_cast<InMemoryLabelIndex *>(storage_->indices_.label_index_.get()),
+                                               labels);
+    }
+
+    /// @return Accessor to the deleted vertex if a deletion took place, std::nullopt otherwise
+    /// @throw std::bad_alloc
+    Result<std::optional<VertexAccessor>> DeleteVertex(VertexAccessor *vertex) override;
+
+    /// @return Accessor to the deleted vertex and deleted edges if a deletion took place, std::nullopt otherwise
+    /// @throw std::bad_alloc
+    Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachDeleteVertex(
+        VertexAccessor *vertex) override;
+
+    void PrefetchInEdges(const VertexAccessor &vertex_acc) override{};
+
+    void PrefetchOutEdges(const VertexAccessor &vertex_acc) override{};
+
+    /// @throw std::bad_alloc
+    Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type) override;
+
+    /// Accessor to the deleted edge if a deletion took place, std::nullopt otherwise
+    /// @throw std::bad_alloc
+    Result<std::optional<EdgeAccessor>> DeleteEdge(EdgeAccessor *edge) override;
+
+    bool LabelIndexExists(LabelId label) const override {
+      return static_cast<InMemoryStorage *>(storage_)->indices_.label_index_->IndexExists(label);
+    }
+
+    bool LabelPropertyIndexExists(LabelId label, PropertyId property) const override {
+      return static_cast<InMemoryStorage *>(storage_)->indices_.label_property_index_->IndexExists(label, property);
+    }
+
+    IndicesInfo ListAllIndices() const override {
+      const auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+      return mem_storage->ListAllIndices();
+    }
+
+    ConstraintsInfo ListAllConstraints() const override {
+      const auto *mem_storage = static_cast<InMemoryStorage *>(storage_);
+      return mem_storage->ListAllConstraints();
+    }
+
+    /// Returns void if the transaction has been committed.
+    /// Returns `StorageDataManipulationError` if an error occures. Error can be:
+    /// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
+    /// * `ConstraintViolation`: the changes made by this transaction violate an existence or unique constraint. In this
+    /// case the transaction is automatically aborted.
+    /// @throw std::bad_alloc
+    // NOLINTNEXTLINE(google-default-arguments)
+    utils::BasicResult<StorageDataManipulationError, void> Commit(
+        std::optional<uint64_t> desired_commit_timestamp = {}) override;
+
+    /// @throw std::bad_alloc
+    void Abort() override;
+
+    void FinalizeTransaction() override;
+
+   private:
+    /// @throw std::bad_alloc
+    VertexAccessor CreateVertex(storage::Gid gid);
+
+    /// @throw std::bad_alloc
+    Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type, storage::Gid gid);
+
+    Config::Items config_;
+  };
+
+  std::unique_ptr<Storage::Accessor> Access(std::optional<IsolationLevel> override_isolation_level) override {
+    return std::unique_ptr<InMemoryAccessor>(
+        new InMemoryAccessor{this, override_isolation_level.value_or(isolation_level_), storage_mode_});
+  }
+
+  /// Create an index.
+  /// Returns void if the index has been created.
+  /// Returns `StorageIndexDefinitionError` if an error occures. Error can be:
+  /// * `IndexDefinitionError`: the index already exists.
+  /// * `ReplicationError`:  there is at least one SYNC replica that has not confirmed receiving the transaction.
+  /// @throw std::bad_alloc
+  utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(
+      LabelId label, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  /// Create an index.
+  /// Returns void if the index has been created.
+  /// Returns `StorageIndexDefinitionError` if an error occures. Error can be:
+  /// * `ReplicationError`:  there is at least one SYNC replica that has not confirmed receiving the transaction.
+  /// * `IndexDefinitionError`: the index already exists.
+  /// @throw std::bad_alloc
+  utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(
+      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  /// Drop an existing index.
+  /// Returns void if the index has been dropped.
+  /// Returns `StorageIndexDefinitionError` if an error occures. Error can be:
+  /// * `ReplicationError`:  there is at least one SYNC replica that has not confirmed receiving the transaction.
+  /// * `IndexDefinitionError`: the index does not exist.
+  utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(
+      LabelId label, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  /// Drop an existing index.
+  /// Returns void if the index has been dropped.
+  /// Returns `StorageIndexDefinitionError` if an error occures. Error can be:
+  /// * `ReplicationError`:  there is at least one SYNC replica that has not confirmed receiving the transaction.
+  /// * `IndexDefinitionError`: the index does not exist.
+  utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(
+      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  /// Returns void if the existence constraint has been created.
+  /// Returns `StorageExistenceConstraintDefinitionError` if an error occures. Error can be:
+  /// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
+  /// * `ConstraintViolation`: there is already a vertex existing that would break this new constraint.
+  /// * `ConstraintDefinitionError`: the constraint already exists.
+  /// @throw std::bad_alloc
+  /// @throw std::length_error
+  utils::BasicResult<StorageExistenceConstraintDefinitionError, void> CreateExistenceConstraint(
+      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  /// Drop an existing existence constraint.
+  /// Returns void if the existence constraint has been dropped.
+  /// Returns `StorageExistenceConstraintDroppingError` if an error occures. Error can be:
+  /// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
+  /// * `ConstraintDefinitionError`: the constraint did not exists.
+  utils::BasicResult<StorageExistenceConstraintDroppingError, void> DropExistenceConstraint(
+      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  /// Create an unique constraint.
+  /// Returns `StorageUniqueConstraintDefinitionError` if an error occures. Error can be:
+  /// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
+  /// * `ConstraintViolation`: there are already vertices violating the constraint.
+  /// Returns `UniqueConstraints::CreationStatus` otherwise. Value can be:
+  /// * `SUCCESS` if the constraint was successfully created,
+  /// * `ALREADY_EXISTS` if the constraint already existed,
+  /// * `EMPTY_PROPERTIES` if the property set is empty, or
+  /// * `PROPERTIES_SIZE_LIMIT_EXCEEDED` if the property set exceeds the limit of maximum number of properties.
+  /// @throw std::bad_alloc
+  utils::BasicResult<StorageUniqueConstraintDefinitionError, UniqueConstraints::CreationStatus> CreateUniqueConstraint(
+      LabelId label, const std::set<PropertyId> &properties, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  /// Removes an existing unique constraint.
+  /// Returns `StorageUniqueConstraintDroppingError` if an error occures. Error can be:
+  /// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
+  /// Returns `UniqueConstraints::DeletionStatus` otherwise. Value can be:
+  /// * `SUCCESS` if constraint was successfully removed,
+  /// * `NOT_FOUND` if the specified constraint was not found,
+  /// * `EMPTY_PROPERTIES` if the property set is empty, or
+  /// * `PROPERTIES_SIZE_LIMIT_EXCEEDED` if the property set exceeds the limit of maximum number of properties.
+  utils::BasicResult<StorageUniqueConstraintDroppingError, UniqueConstraints::DeletionStatus> DropUniqueConstraint(
+      LabelId label, const std::set<PropertyId> &properties, std::optional<uint64_t> desired_commit_timestamp) override;
+
+  bool SetReplicaRole(io::network::Endpoint endpoint, const replication::ReplicationServerConfig &config);
+
+  bool SetMainReplicationRole();
+
+  /// @pre The instance should have a MAIN role
+  /// @pre Timeout can only be set for SYNC replication
+  utils::BasicResult<RegisterReplicaError, void> RegisterReplica(std::string name, io::network::Endpoint endpoint,
+                                                                 replication::ReplicationMode replication_mode,
+                                                                 replication::RegistrationMode registration_mode,
+                                                                 const replication::ReplicationClientConfig &config);
+  /// @pre The instance should have a MAIN role
+  bool UnregisterReplica(const std::string &name);
+
+  std::optional<replication::ReplicaState> GetReplicaState(std::string_view name);
+
+  replication::ReplicationRole GetReplicationRole() const;
+
+  std::vector<ReplicaInfo> ReplicasInfo();
+
+  void FreeMemory(std::unique_lock<utils::RWLock> main_guard) override;
+
+  utils::FileRetainer::FileLockerAccessor::ret_type IsPathLocked();
+  utils::FileRetainer::FileLockerAccessor::ret_type LockPath();
+  utils::FileRetainer::FileLockerAccessor::ret_type UnlockPath();
+
+  utils::BasicResult<CreateSnapshotError> CreateSnapshot(std::optional<bool> is_periodic);
+
+  Transaction CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) override;
+
+ private:
+  /// The force parameter determines the behaviour of the garbage collector.
+  /// If it's set to true, it will behave as a global operation, i.e. it can't
+  /// be part of a transaction, and no other transaction can be active at the same time.
+  /// This allows it to delete immediately vertices without worrying that some other
+  /// transaction is possibly using it. If there are active transactions when this method
+  /// is called with force set to true, it will fallback to the same method with the force
+  /// set to false.
+  /// If it's set to false, it will execute in parallel with other transactions, ensuring
+  /// that no object in use can be deleted.
+  /// @throw std::system_error
+  /// @throw std::bad_alloc
+  template <bool force>
+  void CollectGarbage(std::unique_lock<utils::RWLock> main_guard = {});
+
+  bool InitializeWalFile();
+  void FinalizeWalFile();
+
+  StorageInfo GetInfo() const override;
+
+  /// Return true in all cases excepted if any sync replicas have not sent confirmation.
+  [[nodiscard]] bool AppendToWalDataManipulation(const Transaction &transaction, uint64_t final_commit_timestamp);
+  /// Return true in all cases excepted if any sync replicas have not sent confirmation.
+  [[nodiscard]] bool AppendToWalDataDefinition(durability::StorageGlobalOperation operation, LabelId label,
+                                               const std::set<PropertyId> &properties, uint64_t final_commit_timestamp);
+
+  uint64_t CommitTimestamp(std::optional<uint64_t> desired_commit_timestamp = {});
+
+  void RestoreReplicas();
+
+  void RestoreReplicationRole();
+
+  bool ShouldStoreAndRestoreReplicationState() const;
+
+  // Main object storage
+  utils::SkipList<storage::Vertex> vertices_;
+  utils::SkipList<storage::Edge> edges_;
+
+  // Durability
+  std::filesystem::path snapshot_directory_;
+  std::filesystem::path lock_file_path_;
+  utils::OutputFile lock_file_handle_;
+  std::unique_ptr<kvstore::KVStore> storage_;
+  std::filesystem::path wal_directory_;
+
+  utils::Scheduler snapshot_runner_;
+  utils::SpinLock snapshot_lock_;
+
+  // UUID used to distinguish snapshots and to link snapshots to WALs
+  std::string uuid_;
+  // Sequence number used to keep track of the chain of WALs.
+  uint64_t wal_seq_num_{0};
+
+  // UUID to distinguish different main instance runs for replication process
+  // on SAME storage.
+  // Multiple instances can have same storage UUID and be MAIN at the same time.
+  // We cannot compare commit timestamps of those instances if one of them
+  // becomes the replica of the other so we use epoch_id_ as additional
+  // discriminating property.
+  // Example of this:
+  // We have 2 instances of the same storage, S1 and S2.
+  // S1 and S2 are MAIN and accept their own commits and write them to the WAL.
+  // At the moment when S1 commited a transaction with timestamp 20, and S2
+  // a different transaction with timestamp 15, we change S2's role to REPLICA
+  // and register it on S1.
+  // Without using the epoch_id, we don't know that S1 and S2 have completely
+  // different transactions, we think that the S2 is behind only by 5 commits.
+  std::string epoch_id_;
+  // History of the previous epoch ids.
+  // Each value consists of the epoch id along the last commit belonging to that
+  // epoch.
+  std::deque<std::pair<std::string, uint64_t>> epoch_history_;
+
+  std::optional<durability::WalFile> wal_file_;
+  uint64_t wal_unsynced_transactions_{0};
+
+  utils::FileRetainer file_retainer_;
+
+  // Global locker that is used for clients file locking
+  utils::FileRetainer::FileLocker global_locker_;
+
+  // TODO: This isn't really a commit log, it doesn't even care if a
+  // transaction commited or aborted. We could probably combine this with
+  // `timestamp_` in a sensible unit, something like TransactionClock or
+  // whatever.
+  std::optional<CommitLog> commit_log_;
+  utils::Synchronized<std::list<Transaction>, utils::SpinLock> committed_transactions_;
+  utils::Scheduler gc_runner_;
+  std::mutex gc_lock_;
+
+  // Undo buffers that were unlinked and now are waiting to be freed.
+  utils::Synchronized<std::list<std::pair<uint64_t, std::list<Delta>>>, utils::SpinLock> garbage_undo_buffers_;
+
+  // Vertices that are logically deleted but still have to be removed from
+  // indices before removing them from the main storage.
+  utils::Synchronized<std::list<Gid>, utils::SpinLock> deleted_vertices_;
+
+  // Vertices that are logically deleted and removed from indices and now wait
+  // to be removed from the main storage.
+  std::list<std::pair<uint64_t, Gid>> garbage_vertices_;
+
+  // Edges that are logically deleted and wait to be removed from the main
+  // storage.
+  utils::Synchronized<std::list<Gid>, utils::SpinLock> deleted_edges_;
+
+  // Flags to inform CollectGarbage that it needs to do the more expensive full scans
+  std::atomic<bool> gc_full_scan_vertices_delete_ = false;
+  std::atomic<bool> gc_full_scan_edges_delete_ = false;
+
+  std::atomic<uint64_t> last_commit_timestamp_{kTimestampInitialId};
+
+  class ReplicationServer;
+  std::unique_ptr<ReplicationServer> replication_server_{nullptr};
+
+  class ReplicationClient;
+  // We create ReplicationClient using unique_ptr so we can move
+  // newly created client into the vector.
+  // We cannot move the client directly because it contains ThreadPool
+  // which cannot be moved. Also, the move is necessary because
+  // we don't want to create the client directly inside the vector
+  // because that would require the lock on the list putting all
+  // commits (they iterate list of clients) to halt.
+  // This way we can initialize client in main thread which means
+  // that we can immediately notify the user if the initialization
+  // failed.
+  using ReplicationClientList = utils::Synchronized<std::vector<std::unique_ptr<ReplicationClient>>, utils::SpinLock>;
+  ReplicationClientList replication_clients_;
+
+  std::atomic<replication::ReplicationRole> replication_role_{replication::ReplicationRole::MAIN};
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/constraints.cpp b/src/storage/v2/inmemory/unique_constraints.cpp
similarity index 80%
rename from src/storage/v2/constraints.cpp
rename to src/storage/v2/inmemory/unique_constraints.cpp
index 12243f173..83507bc77 100644
--- a/src/storage/v2/constraints.cpp
+++ b/src/storage/v2/inmemory/unique_constraints.cpp
@@ -9,24 +9,17 @@
 // by the Apache License, Version 2.0, included in the file
 // licenses/APL.txt.
 
-#include "storage/v2/constraints.hpp"
-
-#include <algorithm>
-#include <atomic>
-#include <cstring>
-#include <map>
-
-#include "storage/v2/mvcc.hpp"
-#include "utils/logging.hpp"
+#include "storage/v2/inmemory/unique_constraints.hpp"
 
 namespace memgraph::storage {
+
 namespace {
 
 /// Helper function that determines position of the given `property` in the
 /// sorted `property_array` using binary search. In the case that `property`
 /// cannot be found, `std::nullopt` is returned.
 std::optional<size_t> FindPropertyPosition(const PropertyIdArray &property_array, PropertyId property) {
-  auto it = std::lower_bound(property_array.values, property_array.values + property_array.size, property);
+  const auto *it = std::lower_bound(property_array.values, property_array.values + property_array.size, property);
   if (it == property_array.values + property_array.size || *it != property) {
     return std::nullopt;
   }
@@ -84,6 +77,7 @@ bool LastCommittedVersionHasLabelProperty(const Vertex &vertex, LabelId label, c
         }
         break;
       }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
       case Delta::Action::DELETE_OBJECT: {
         MG_ASSERT(!deleted, "Invalid database state!");
         deleted = true;
@@ -198,6 +192,7 @@ bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, const std::
         deleted = false;
         break;
       }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
       case Delta::Action::DELETE_OBJECT: {
         MG_ASSERT(!deleted, "Invalid database state!");
         deleted = true;
@@ -225,30 +220,9 @@ bool AnyVersionHasLabelProperty(const Vertex &vertex, LabelId label, const std::
   return false;
 }
 
-/// Helper function that, given the set of `properties`, extracts corresponding
-/// property values from the `vertex`.
-/// @throw std::bad_alloc
-std::optional<std::vector<PropertyValue>> ExtractPropertyValues(const Vertex &vertex,
-                                                                const std::set<PropertyId> &properties) {
-  std::vector<PropertyValue> value_array;
-  value_array.reserve(properties.size());
-  for (const auto &prop : properties) {
-    auto value = vertex.properties.GetProperty(prop);
-    if (value.IsNull()) {
-      return std::nullopt;
-    }
-    value_array.emplace_back(std::move(value));
-  }
-  return std::move(value_array);
-}
-
 }  // namespace
 
-bool operator==(const ConstraintViolation &lhs, const ConstraintViolation &rhs) {
-  return lhs.type == rhs.type && lhs.label == rhs.label && lhs.properties == rhs.properties;
-}
-
-bool UniqueConstraints::Entry::operator<(const Entry &rhs) {
+bool InMemoryUniqueConstraints::Entry::operator<(const Entry &rhs) const {
   if (values < rhs.values) {
     return true;
   }
@@ -258,20 +232,20 @@ bool UniqueConstraints::Entry::operator<(const Entry &rhs) {
   return std::make_tuple(vertex, timestamp) < std::make_tuple(rhs.vertex, rhs.timestamp);
 }
 
-bool UniqueConstraints::Entry::operator==(const Entry &rhs) {
+bool InMemoryUniqueConstraints::Entry::operator==(const Entry &rhs) const {
   return values == rhs.values && vertex == rhs.vertex && timestamp == rhs.timestamp;
 }
 
-bool UniqueConstraints::Entry::operator<(const std::vector<PropertyValue> &rhs) { return values < rhs; }
+bool InMemoryUniqueConstraints::Entry::operator<(const std::vector<PropertyValue> &rhs) const { return values < rhs; }
 
-bool UniqueConstraints::Entry::operator==(const std::vector<PropertyValue> &rhs) { return values == rhs; }
+bool InMemoryUniqueConstraints::Entry::operator==(const std::vector<PropertyValue> &rhs) const { return values == rhs; }
 
-void UniqueConstraints::UpdateBeforeCommit(const Vertex *vertex, const Transaction &tx) {
+void InMemoryUniqueConstraints::UpdateBeforeCommit(const Vertex *vertex, const Transaction &tx) {
   for (auto &[label_props, storage] : constraints_) {
     if (!utils::Contains(vertex->labels, label_props.first)) {
       continue;
     }
-    auto values = ExtractPropertyValues(*vertex, label_props.second);
+    auto values = vertex->properties.ExtractPropertyValues(label_props.second);
     if (values) {
       auto acc = storage.access();
       acc.insert(Entry{std::move(*values), vertex, tx.start_timestamp});
@@ -279,8 +253,9 @@ void UniqueConstraints::UpdateBeforeCommit(const Vertex *vertex, const Transacti
   }
 }
 
-utils::BasicResult<ConstraintViolation, UniqueConstraints::CreationStatus> UniqueConstraints::CreateConstraint(
-    LabelId label, const std::set<PropertyId> &properties, utils::SkipList<Vertex>::Accessor vertices) {
+utils::BasicResult<ConstraintViolation, InMemoryUniqueConstraints::CreationStatus>
+InMemoryUniqueConstraints::CreateConstraint(LabelId label, const std::set<PropertyId> &properties,
+                                            utils::SkipList<Vertex>::Accessor vertices) {
   if (properties.empty()) {
     return CreationStatus::EMPTY_PROPERTIES;
   }
@@ -305,7 +280,7 @@ utils::BasicResult<ConstraintViolation, UniqueConstraints::CreationStatus> Uniqu
       if (vertex.deleted || !utils::Contains(vertex.labels, label)) {
         continue;
       }
-      auto values = ExtractPropertyValues(vertex, properties);
+      auto values = vertex.properties.ExtractPropertyValues(properties);
       if (!values) {
         continue;
       }
@@ -331,13 +306,11 @@ utils::BasicResult<ConstraintViolation, UniqueConstraints::CreationStatus> Uniqu
   return CreationStatus::SUCCESS;
 }
 
-UniqueConstraints::DeletionStatus UniqueConstraints::DropConstraint(LabelId label,
-                                                                    const std::set<PropertyId> &properties) {
-  if (properties.empty()) {
-    return UniqueConstraints::DeletionStatus::EMPTY_PROPERTIES;
-  }
-  if (properties.size() > kUniqueConstraintsMaxProperties) {
-    return UniqueConstraints::DeletionStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED;
+InMemoryUniqueConstraints::DeletionStatus InMemoryUniqueConstraints::DropConstraint(
+    LabelId label, const std::set<PropertyId> &properties) {
+  if (auto drop_properties_check_result = UniqueConstraints::CheckPropertiesBeforeDeletion(properties);
+      drop_properties_check_result != UniqueConstraints::DeletionStatus::SUCCESS) {
+    return drop_properties_check_result;
   }
   if (constraints_.erase({label, properties}) > 0) {
     return UniqueConstraints::DeletionStatus::SUCCESS;
@@ -345,8 +318,12 @@ UniqueConstraints::DeletionStatus UniqueConstraints::DropConstraint(LabelId labe
   return UniqueConstraints::DeletionStatus::NOT_FOUND;
 }
 
-std::optional<ConstraintViolation> UniqueConstraints::Validate(const Vertex &vertex, const Transaction &tx,
-                                                               uint64_t commit_timestamp) const {
+bool InMemoryUniqueConstraints::ConstraintExists(LabelId label, const std::set<PropertyId> &properties) const {
+  return constraints_.find({label, properties}) != constraints_.end();
+}
+
+std::optional<ConstraintViolation> InMemoryUniqueConstraints::Validate(const Vertex &vertex, const Transaction &tx,
+                                                                       uint64_t commit_timestamp) const {
   if (vertex.deleted) {
     return std::nullopt;
   }
@@ -357,7 +334,7 @@ std::optional<ConstraintViolation> UniqueConstraints::Validate(const Vertex &ver
       continue;
     }
 
-    auto value_array = ExtractPropertyValues(vertex, properties);
+    auto value_array = vertex.properties.ExtractPropertyValues(properties);
     if (!value_array) {
       continue;
     }
@@ -381,7 +358,7 @@ std::optional<ConstraintViolation> UniqueConstraints::Validate(const Vertex &ver
   return std::nullopt;
 }
 
-std::vector<std::pair<LabelId, std::set<PropertyId>>> UniqueConstraints::ListConstraints() const {
+std::vector<std::pair<LabelId, std::set<PropertyId>>> InMemoryUniqueConstraints::ListConstraints() const {
   std::vector<std::pair<LabelId, std::set<PropertyId>>> ret;
   ret.reserve(constraints_.size());
   for (const auto &[label_props, _] : constraints_) {
@@ -390,7 +367,7 @@ std::vector<std::pair<LabelId, std::set<PropertyId>>> UniqueConstraints::ListCon
   return ret;
 }
 
-void UniqueConstraints::RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp) {
+void InMemoryUniqueConstraints::RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp) {
   for (auto &[label_props, storage] : constraints_) {
     auto acc = storage.access();
     for (auto it = acc.begin(); it != acc.end();) {
@@ -412,4 +389,6 @@ void UniqueConstraints::RemoveObsoleteEntries(uint64_t oldest_active_start_times
   }
 }
 
+void InMemoryUniqueConstraints::Clear() { constraints_.clear(); }
+
 }  // namespace memgraph::storage
diff --git a/src/storage/v2/inmemory/unique_constraints.hpp b/src/storage/v2/inmemory/unique_constraints.hpp
new file mode 100644
index 000000000..401f1e036
--- /dev/null
+++ b/src/storage/v2/inmemory/unique_constraints.hpp
@@ -0,0 +1,101 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include "storage/v2/constraints/unique_constraints.hpp"
+
+namespace memgraph::storage {
+
+/// Utility class to store data in a fixed size array. The array is used
+/// instead of `std::vector` to avoid `std::bad_alloc` exception where not
+/// necessary.
+template <class T>
+struct FixedCapacityArray {
+  size_t size;
+  T values[kUniqueConstraintsMaxProperties];
+
+  explicit FixedCapacityArray(size_t array_size) : size(array_size) {
+    MG_ASSERT(size <= kUniqueConstraintsMaxProperties, "Invalid array size!");
+  }
+};
+
+using PropertyIdArray = FixedCapacityArray<PropertyId>;
+
+class InMemoryUniqueConstraints : public UniqueConstraints {
+ private:
+  struct Entry {
+    std::vector<PropertyValue> values;
+    const Vertex *vertex;
+    uint64_t timestamp;
+
+    bool operator<(const Entry &rhs) const;
+    bool operator==(const Entry &rhs) const;
+
+    bool operator<(const std::vector<PropertyValue> &rhs) const;
+    bool operator==(const std::vector<PropertyValue> &rhs) const;
+  };
+
+ public:
+  /// Indexes the given vertex for relevant labels and properties.
+  /// This method should be called before committing and validating vertices
+  /// against unique constraints.
+  /// @throw std::bad_alloc
+  void UpdateBeforeCommit(const Vertex *vertex, const Transaction &tx);
+
+  /// Creates unique constraint on the given `label` and a list of `properties`.
+  /// Returns constraint violation if there are multiple vertices with the same
+  /// label and property values. Returns `CreationStatus::ALREADY_EXISTS` if
+  /// constraint already existed, `CreationStatus::EMPTY_PROPERTIES` if the
+  /// given list of properties is empty,
+  /// `CreationStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED` if the list of properties
+  /// exceeds the maximum allowed number of properties, and
+  /// `CreationStatus::SUCCESS` on success.
+  /// @throw std::bad_alloc
+  utils::BasicResult<ConstraintViolation, CreationStatus> CreateConstraint(LabelId label,
+                                                                           const std::set<PropertyId> &properties,
+                                                                           utils::SkipList<Vertex>::Accessor vertices);
+
+  /// Deletes the specified constraint. Returns `DeletionStatus::NOT_FOUND` if
+  /// there is not such constraint in the storage,
+  /// `DeletionStatus::EMPTY_PROPERTIES` if the given set of `properties` is
+  /// empty, `DeletionStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED` if the given set
+  /// of `properties` exceeds the maximum allowed number of properties, and
+  /// `DeletionStatus::SUCCESS` on success.
+  DeletionStatus DropConstraint(LabelId label, const std::set<PropertyId> &properties) override;
+
+  bool ConstraintExists(LabelId label, const std::set<PropertyId> &properties) const override;
+
+  void UpdateOnRemoveLabel(LabelId removed_label, const Vertex &vertex_before_update,
+                           const uint64_t transaction_start_timestamp) override {}
+
+  void UpdateOnAddLabel(LabelId added_label, const Vertex &vertex_before_update,
+                        uint64_t transaction_start_timestamp) override{};
+
+  /// Validates the given vertex against unique constraints before committing.
+  /// This method should be called while commit lock is active with
+  /// `commit_timestamp` being a potential commit timestamp of the transaction.
+  /// @throw std::bad_alloc
+  std::optional<ConstraintViolation> Validate(const Vertex &vertex, const Transaction &tx,
+                                              uint64_t commit_timestamp) const;
+
+  std::vector<std::pair<LabelId, std::set<PropertyId>>> ListConstraints() const override;
+
+  /// GC method that removes outdated entries from constraints' storages.
+  void RemoveObsoleteEntries(uint64_t oldest_active_start_timestamp);
+
+  void Clear() override;
+
+ private:
+  std::map<std::pair<LabelId, std::set<PropertyId>>, utils::SkipList<Entry>> constraints_;
+};
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/mvcc.hpp b/src/storage/v2/mvcc.hpp
index 47f4aaca5..ed2c0560f 100644
--- a/src/storage/v2/mvcc.hpp
+++ b/src/storage/v2/mvcc.hpp
@@ -12,6 +12,7 @@
 #pragma once
 
 #include <atomic>
+#include <optional>
 
 #include "storage/v2/property_value.hpp"
 #include "storage/v2/transaction.hpp"
@@ -60,7 +61,9 @@ inline void ApplyDeltasForRead(Transaction *transaction, const Delta *delta, Vie
 
     // We shouldn't undo our older changes because the user requested a OLD view
     // of the database.
-    if (view == View::OLD && ts == commit_timestamp && cid < transaction->command_id) {
+    if (view == View::OLD && ts == commit_timestamp &&
+        (cid < transaction->command_id ||
+         (cid == transaction->command_id && delta->action == Delta::Action::DELETE_DESERIALIZED_OBJECT))) {
       break;
     }
 
@@ -80,7 +83,6 @@ inline void ApplyDeltasForRead(Transaction *transaction, const Delta *delta, Vie
 template <typename TObj>
 inline bool PrepareForWrite(Transaction *transaction, TObj *object) {
   if (object->delta == nullptr) return true;
-
   auto ts = object->delta->timestamp->load(std::memory_order_acquire);
   if (ts == transaction->transaction_id.load(std::memory_order_acquire) || ts < transaction->start_timestamp) {
     return true;
@@ -104,6 +106,20 @@ inline Delta *CreateDeleteObjectDelta(Transaction *transaction) {
                                            transaction->command_id);
 }
 
+/// TODO: what if in-memory analytical
+inline Delta *CreateDeleteDeserializedObjectDelta(Transaction *transaction, std::optional<std::string> old_disk_key) {
+  transaction->EnsureCommitTimestampExists();
+  return &transaction->deltas.emplace_back(Delta::DeleteDeserializedObjectTag(), transaction->commit_timestamp.get(),
+                                           old_disk_key);
+}
+
+/// TODO: what if in-memory analytical
+inline Delta *CreateDeleteDeserializedIndexObjectDelta(Transaction *transaction, std::list<Delta> &deltas,
+                                                       std::optional<std::string> old_disk_key) {
+  transaction->EnsureCommitTimestampExists();
+  return &deltas.emplace_back(Delta::DeleteDeserializedObjectTag(), transaction->commit_timestamp.get(), old_disk_key);
+}
+
 /// This function creates a delta in the transaction for the object and links
 /// the delta into the object's delta list.
 /// @throw std::bad_alloc
diff --git a/src/storage/v2/name_id_mapper.hpp b/src/storage/v2/name_id_mapper.hpp
index 4ec79cb78..bb91e3647 100644
--- a/src/storage/v2/name_id_mapper.hpp
+++ b/src/storage/v2/name_id_mapper.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -20,14 +20,14 @@
 
 namespace memgraph::storage {
 
-class NameIdMapper final {
+class NameIdMapper {
  private:
   struct MapNameToId {
     std::string name;
     uint64_t id;
 
-    bool operator<(const MapNameToId &other) { return name < other.name; }
-    bool operator==(const MapNameToId &other) { return name == other.name; }
+    bool operator<(const MapNameToId &other) const { return name < other.name; }
+    bool operator==(const MapNameToId &other) const { return name == other.name; }
 
     bool operator<(const std::string_view other) const { return name < other; }
     bool operator==(const std::string_view other) const { return name == other; }
@@ -37,16 +37,25 @@ class NameIdMapper final {
     uint64_t id;
     std::string name;
 
-    bool operator<(const MapIdToName &other) { return id < other.id; }
-    bool operator==(const MapIdToName &other) { return id == other.id; }
+    bool operator<(const MapIdToName &other) const { return id < other.id; }
+    bool operator==(const MapIdToName &other) const { return id == other.id; }
 
-    bool operator<(uint64_t other) { return id < other; }
-    bool operator==(uint64_t other) { return id == other; }
+    bool operator<(uint64_t other) const { return id < other; }
+    bool operator==(uint64_t other) const { return id == other; }
   };
 
  public:
+  explicit NameIdMapper() = default;
+
+  NameIdMapper(const NameIdMapper &) = delete;
+  NameIdMapper &operator=(const NameIdMapper &) = delete;
+  NameIdMapper(NameIdMapper &&) = delete;
+  NameIdMapper &operator=(NameIdMapper &&) = delete;
+
+  virtual ~NameIdMapper() = default;
+
   /// @throw std::bad_alloc if unable to insert a new mapping
-  uint64_t NameToId(const std::string_view name) {
+  virtual uint64_t NameToId(const std::string_view name) {
     auto name_to_id_acc = name_to_id_.access();
     auto found = name_to_id_acc.find(name);
     uint64_t id;
@@ -83,14 +92,22 @@ class NameIdMapper final {
   // Currently, we never delete anything from the `utils::SkipList` so the
   // references will always be valid. If you change this class to remove unused
   // names, be sure to change the signature of this function.
-  const std::string &IdToName(uint64_t id) const {
+  virtual const std::string &IdToName(uint64_t id) {
+    auto maybe_name = MaybeIdToName(id);
+    MG_ASSERT(maybe_name.has_value(), "Trying to get a name for an invalid ID!");
+    return maybe_name.value();
+  }
+
+ protected:
+  std::optional<std::reference_wrapper<const std::string>> MaybeIdToName(uint64_t id) const {
     auto id_to_name_acc = id_to_name_.access();
     auto result = id_to_name_acc.find(id);
-    MG_ASSERT(result != id_to_name_acc.end(), "Trying to get a name for an invalid ID!");
+    if (result == id_to_name_acc.end()) {
+      return std::nullopt;
+    }
     return result->name;
   }
 
- private:
   std::atomic<uint64_t> counter_{0};
   utils::SkipList<MapNameToId> name_to_id_;
   utils::SkipList<MapIdToName> id_to_name_;
diff --git a/src/storage/v2/property_store.cpp b/src/storage/v2/property_store.cpp
index d1bedc4e3..c7d0eea3a 100644
--- a/src/storage/v2/property_store.cpp
+++ b/src/storage/v2/property_store.cpp
@@ -11,9 +11,12 @@
 
 #include "storage/v2/property_store.hpp"
 
+#include <cstdint>
 #include <cstring>
+#include <iterator>
 #include <limits>
 #include <optional>
+#include <sstream>
 #include <tuple>
 #include <type_traits>
 #include <utility>
@@ -984,6 +987,39 @@ bool PropertyStore::HasProperty(PropertyId property) const {
   return FindSpecificProperty(&reader, property, nullptr) == DecodeExpectedPropertyStatus::EQUAL;
 }
 
+/// TODO: andi write a unit test for it
+bool PropertyStore::HasAllProperties(const std::set<PropertyId> &properties) const {
+  return std::all_of(properties.begin(), properties.end(), [this](const auto &prop) { return HasProperty(prop); });
+}
+
+/// TODO: andi write a unit test for it
+bool PropertyStore::HasAllPropertyValues(const std::vector<PropertyValue> &property_values) const {
+  /// TODO: andi extract this into a private method
+  auto property_map = Properties();
+  std::vector<PropertyValue> all_property_values;
+  transform(property_map.begin(), property_map.end(), back_inserter(all_property_values),
+            [](const auto &kv_entry) { return kv_entry.second; });
+
+  return std::all_of(
+      property_values.begin(), property_values.end(), [&all_property_values](const PropertyValue &value) {
+        return std::find(all_property_values.begin(), all_property_values.end(), value) != all_property_values.end();
+      });
+}
+
+std::optional<std::vector<PropertyValue>> PropertyStore::ExtractPropertyValues(
+    const std::set<PropertyId> &properties) const {
+  std::vector<PropertyValue> value_array;
+  value_array.reserve(properties.size());
+  for (const auto &prop : properties) {
+    auto value = GetProperty(prop);
+    if (value.IsNull()) {
+      return std::nullopt;
+    }
+    value_array.emplace_back(std::move(value));
+  }
+  return value_array;
+}
+
 bool PropertyStore::IsPropertyEqual(PropertyId property, const PropertyValue &value) const {
   uint64_t size;
   const uint8_t *data;
@@ -1234,4 +1270,41 @@ bool PropertyStore::ClearProperties() {
   return true;
 }
 
+std::string PropertyStore::StringBuffer() const {
+  uint64_t size = 0;
+  const uint8_t *data = nullptr;
+  std::tie(size, data) = GetSizeData(buffer_);
+  if (size % 8 != 0) {  // We are storing the data in the local buffer.
+    size = sizeof(buffer_) - 1;
+    data = &buffer_[1];
+  }
+  std::string arr(size, ' ');
+  for (uint i = 0; i < size; ++i) {
+    arr[i] = static_cast<char>(data[i]);
+  }
+  return arr;
+}
+
+void PropertyStore::SetBuffer(const std::string_view buffer) {
+  if (buffer.empty()) {
+    return;
+  }
+
+  uint64_t size = 0;
+  uint8_t *data = nullptr;
+  if (buffer.size() == sizeof(buffer_) - 1) {  // use local buffer
+    buffer_[0] = kUseLocalBuffer;
+    size = buffer.size() - 1;
+    data = &buffer_[1];
+  } else {
+    size = buffer.size();
+    data = new uint8_t[size];
+    SetSizeData(buffer_, size, data);
+  }
+
+  for (uint i = 0; i < size; ++i) {
+    data[i] = static_cast<uint8_t>(buffer[i]);
+  }
+}
+
 }  // namespace memgraph::storage
diff --git a/src/storage/v2/property_store.hpp b/src/storage/v2/property_store.hpp
index 68d44147b..2cf785a7b 100644
--- a/src/storage/v2/property_store.hpp
+++ b/src/storage/v2/property_store.hpp
@@ -12,6 +12,7 @@
 #pragma once
 
 #include <map>
+#include <set>
 
 #include "storage/v2/id_types.hpp"
 #include "storage/v2/property_value.hpp"
@@ -23,6 +24,12 @@ class PropertyStore {
                 "PropertyStore supports only architectures using little-endian.");
 
  public:
+  static PropertyStore CreateFromBuffer(std::string_view buffer) {
+    PropertyStore store;
+    store.SetBuffer(buffer);
+    return store;
+  }
+
   PropertyStore();
 
   PropertyStore(const PropertyStore &) = delete;
@@ -42,6 +49,19 @@ class PropertyStore {
   /// complexity of this function is O(n).
   bool HasProperty(PropertyId property) const;
 
+  /// Checks whether all properties in the set `properties` exist in the store. The time
+  /// complexity of this function is O(n^2).
+  bool HasAllProperties(const std::set<PropertyId> &properties) const;
+
+  /// Checks whether all property values in the vector `property_values` exist in the store. The time
+  /// complexity of this function is O(n^2).
+  /// TODO: andi Not so sure it is quadratic complexity
+  bool HasAllPropertyValues(const std::vector<PropertyValue> &property_values) const;
+
+  /// Extracts property values for all property ids in the set `properties`. The time
+  /// complexity of this function is O(n^2).
+  std::optional<std::vector<PropertyValue>> ExtractPropertyValues(const std::set<PropertyId> &properties) const;
+
   /// Checks whether the property `property` is equal to the specified value
   /// `value`. This function doesn't perform any memory allocations while
   /// performing the equality check. The time complexity of this function is
@@ -77,6 +97,12 @@ class PropertyStore {
   /// @throw std::bad_alloc
   bool ClearProperties();
 
+  /// Return property buffer as a string
+  std::string StringBuffer() const;
+
+  /// Sets buffer
+  void SetBuffer(std::string_view buffer);
+
  private:
   template <typename TContainer>
   bool DoInitProperties(const TContainer &properties);
diff --git a/src/storage/v2/property_value.hpp b/src/storage/v2/property_value.hpp
index 43481ac21..91ccc3b8b 100644
--- a/src/storage/v2/property_value.hpp
+++ b/src/storage/v2/property_value.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -300,6 +300,7 @@ inline bool operator==(const PropertyValue &first, const PropertyValue &second)
   }
 }
 
+/// NOLINTNEXTLINE(bugprone-exception-escape)
 inline bool operator<(const PropertyValue &first, const PropertyValue &second) noexcept {
   if (!PropertyValue::AreComparableTypes(first.type(), second.type())) return first.type() < second.type();
   switch (first.type()) {
@@ -330,6 +331,9 @@ inline bool operator<(const PropertyValue &first, const PropertyValue &second) n
   }
 }
 
+/// NOLINTNEXTLINE(bugprone-exception-escape)
+inline bool operator>(const PropertyValue &first, const PropertyValue &second) noexcept { return second < first; }
+
 inline PropertyValue::PropertyValue(const PropertyValue &other) : type_(other.type_) {
   switch (other.type_) {
     case Type::Null:
diff --git a/src/storage/v2/replication/replication_client.cpp b/src/storage/v2/replication/replication_client.cpp
index 64cd9b607..c100e2f39 100644
--- a/src/storage/v2/replication/replication_client.cpp
+++ b/src/storage/v2/replication/replication_client.cpp
@@ -30,9 +30,10 @@ template <typename>
 }  // namespace
 
 ////// ReplicationClient //////
-Storage::ReplicationClient::ReplicationClient(std::string name, Storage *storage, const io::network::Endpoint &endpoint,
-                                              const replication::ReplicationMode mode,
-                                              const replication::ReplicationClientConfig &config)
+InMemoryStorage::ReplicationClient::ReplicationClient(std::string name, InMemoryStorage *storage,
+                                                      const io::network::Endpoint &endpoint,
+                                                      const replication::ReplicationMode mode,
+                                                      const replication::ReplicationClientConfig &config)
     : name_(std::move(name)), storage_(storage), mode_(mode) {
   if (config.ssl) {
     rpc_context_.emplace(config.ssl->key_file, config.ssl->cert_file);
@@ -49,14 +50,14 @@ Storage::ReplicationClient::ReplicationClient(std::string name, Storage *storage
   }
 }
 
-void Storage::ReplicationClient::TryInitializeClientAsync() {
+void InMemoryStorage::ReplicationClient::TryInitializeClientAsync() {
   thread_pool_.AddTask([this] {
     rpc_client_->Abort();
     this->TryInitializeClientSync();
   });
 }
 
-void Storage::ReplicationClient::FrequentCheck() {
+void InMemoryStorage::ReplicationClient::FrequentCheck() {
   const auto is_success = std::invoke([this]() {
     try {
       auto stream{rpc_client_->Stream<replication::FrequentHeartbeatRpc>()};
@@ -82,7 +83,7 @@ void Storage::ReplicationClient::FrequentCheck() {
 }
 
 /// @throws rpc::RpcFailedException
-void Storage::ReplicationClient::InitializeClient() {
+void InMemoryStorage::ReplicationClient::InitializeClient() {
   uint64_t current_commit_timestamp{kTimestampInitialId};
 
   std::optional<std::string> epoch_id;
@@ -134,7 +135,7 @@ void Storage::ReplicationClient::InitializeClient() {
   }
 }
 
-void Storage::ReplicationClient::TryInitializeClientSync() {
+void InMemoryStorage::ReplicationClient::TryInitializeClientSync() {
   try {
     InitializeClient();
   } catch (const rpc::RpcFailedException &) {
@@ -145,19 +146,19 @@ void Storage::ReplicationClient::TryInitializeClientSync() {
   }
 }
 
-void Storage::ReplicationClient::HandleRpcFailure() {
+void InMemoryStorage::ReplicationClient::HandleRpcFailure() {
   spdlog::error(utils::MessageWithLink("Couldn't replicate data to {}.", name_, "https://memgr.ph/replication"));
   TryInitializeClientAsync();
 }
 
-replication::SnapshotRes Storage::ReplicationClient::TransferSnapshot(const std::filesystem::path &path) {
+replication::SnapshotRes InMemoryStorage::ReplicationClient::TransferSnapshot(const std::filesystem::path &path) {
   auto stream{rpc_client_->Stream<replication::SnapshotRpc>()};
   replication::Encoder encoder(stream.GetBuilder());
   encoder.WriteFile(path);
   return stream.AwaitResponse();
 }
 
-replication::WalFilesRes Storage::ReplicationClient::TransferWalFiles(
+replication::WalFilesRes InMemoryStorage::ReplicationClient::TransferWalFiles(
     const std::vector<std::filesystem::path> &wal_files) {
   MG_ASSERT(!wal_files.empty(), "Wal files list is empty!");
   auto stream{rpc_client_->Stream<replication::WalFilesRpc>(wal_files.size())};
@@ -170,7 +171,7 @@ replication::WalFilesRes Storage::ReplicationClient::TransferWalFiles(
   return stream.AwaitResponse();
 }
 
-void Storage::ReplicationClient::StartTransactionReplication(const uint64_t current_wal_seq_num) {
+void InMemoryStorage::ReplicationClient::StartTransactionReplication(const uint64_t current_wal_seq_num) {
   std::unique_lock guard(client_lock_);
   const auto status = replica_state_.load();
   switch (status) {
@@ -204,7 +205,8 @@ void Storage::ReplicationClient::StartTransactionReplication(const uint64_t curr
   }
 }
 
-void Storage::ReplicationClient::IfStreamingTransaction(const std::function<void(ReplicaStream &handler)> &callback) {
+void InMemoryStorage::ReplicationClient::IfStreamingTransaction(
+    const std::function<void(ReplicaStream &handler)> &callback) {
   // We can only check the state because it guarantees to be only
   // valid during a single transaction replication (if the assumption
   // that this and other transaction replication functions can only be
@@ -224,7 +226,7 @@ void Storage::ReplicationClient::IfStreamingTransaction(const std::function<void
   }
 }
 
-bool Storage::ReplicationClient::FinalizeTransactionReplication() {
+bool InMemoryStorage::ReplicationClient::FinalizeTransactionReplication() {
   // We can only check the state because it guarantees to be only
   // valid during a single transaction replication (if the assumption
   // that this and other transaction replication functions can only be
@@ -241,7 +243,7 @@ bool Storage::ReplicationClient::FinalizeTransactionReplication() {
   }
 }
 
-bool Storage::ReplicationClient::FinalizeTransactionReplicationInternal() {
+bool InMemoryStorage::ReplicationClient::FinalizeTransactionReplicationInternal() {
   MG_ASSERT(replica_stream_, "Missing stream for transaction deltas");
   try {
     auto response = replica_stream_->Finalize();
@@ -265,7 +267,7 @@ bool Storage::ReplicationClient::FinalizeTransactionReplicationInternal() {
   return false;
 }
 
-void Storage::ReplicationClient::RecoverReplica(uint64_t replica_commit) {
+void InMemoryStorage::ReplicationClient::RecoverReplica(uint64_t replica_commit) {
   while (true) {
     auto file_locker = storage_->file_retainer_.AddLocker();
 
@@ -327,7 +329,7 @@ void Storage::ReplicationClient::RecoverReplica(uint64_t replica_commit) {
   }
 }
 
-uint64_t Storage::ReplicationClient::ReplicateCurrentWal() {
+uint64_t InMemoryStorage::ReplicationClient::ReplicateCurrentWal() {
   const auto &wal_file = storage_->wal_file_;
   auto stream = TransferCurrentWalFile();
   stream.AppendFilename(wal_file->Path().filename());
@@ -361,7 +363,7 @@ uint64_t Storage::ReplicationClient::ReplicateCurrentWal() {
 /// recovery steps, so we can safely send it to the replica.
 /// We assume that the property of preserving at least 1 WAL before the snapshot
 /// is satisfied as we extract the timestamp information from it.
-std::vector<Storage::ReplicationClient::RecoveryStep> Storage::ReplicationClient::GetRecoverySteps(
+std::vector<InMemoryStorage::ReplicationClient::RecoveryStep> InMemoryStorage::ReplicationClient::GetRecoverySteps(
     const uint64_t replica_commit, utils::FileRetainer::FileLocker *file_locker) {
   // First check if we can recover using the current wal file only
   // otherwise save the seq_num of the current wal file
@@ -507,8 +509,8 @@ std::vector<Storage::ReplicationClient::RecoveryStep> Storage::ReplicationClient
   return recovery_steps;
 }
 
-Storage::TimestampInfo Storage::ReplicationClient::GetTimestampInfo() {
-  Storage::TimestampInfo info;
+InMemoryStorage::TimestampInfo InMemoryStorage::ReplicationClient::GetTimestampInfo() {
+  InMemoryStorage::TimestampInfo info;
   info.current_timestamp_of_replica = 0;
   info.current_number_of_timestamp_behind_master = 0;
 
@@ -536,65 +538,71 @@ Storage::TimestampInfo Storage::ReplicationClient::GetTimestampInfo() {
 }
 
 ////// ReplicaStream //////
-Storage::ReplicationClient::ReplicaStream::ReplicaStream(ReplicationClient *self,
-                                                         const uint64_t previous_commit_timestamp,
-                                                         const uint64_t current_seq_num)
+InMemoryStorage::ReplicationClient::ReplicaStream::ReplicaStream(ReplicationClient *self,
+                                                                 const uint64_t previous_commit_timestamp,
+                                                                 const uint64_t current_seq_num)
     : self_(self),
       stream_(self_->rpc_client_->Stream<replication::AppendDeltasRpc>(previous_commit_timestamp, current_seq_num)) {
   replication::Encoder encoder{stream_.GetBuilder()};
   encoder.WriteString(self_->storage_->epoch_id_);
 }
 
-void Storage::ReplicationClient::ReplicaStream::AppendDelta(const Delta &delta, const Vertex &vertex,
-                                                            uint64_t final_commit_timestamp) {
+void InMemoryStorage::ReplicationClient::ReplicaStream::AppendDelta(const Delta &delta, const Vertex &vertex,
+                                                                    uint64_t final_commit_timestamp) {
   replication::Encoder encoder(stream_.GetBuilder());
-  EncodeDelta(&encoder, &self_->storage_->name_id_mapper_, self_->storage_->config_.items, delta, vertex,
+  EncodeDelta(&encoder, self_->storage_->name_id_mapper_.get(), self_->storage_->config_.items, delta, vertex,
               final_commit_timestamp);
 }
 
-void Storage::ReplicationClient::ReplicaStream::AppendDelta(const Delta &delta, const Edge &edge,
-                                                            uint64_t final_commit_timestamp) {
+void InMemoryStorage::ReplicationClient::ReplicaStream::AppendDelta(const Delta &delta, const Edge &edge,
+                                                                    uint64_t final_commit_timestamp) {
   replication::Encoder encoder(stream_.GetBuilder());
-  EncodeDelta(&encoder, &self_->storage_->name_id_mapper_, delta, edge, final_commit_timestamp);
+  EncodeDelta(&encoder, self_->storage_->name_id_mapper_.get(), delta, edge, final_commit_timestamp);
 }
 
-void Storage::ReplicationClient::ReplicaStream::AppendTransactionEnd(uint64_t final_commit_timestamp) {
+void InMemoryStorage::ReplicationClient::ReplicaStream::AppendTransactionEnd(uint64_t final_commit_timestamp) {
   replication::Encoder encoder(stream_.GetBuilder());
   EncodeTransactionEnd(&encoder, final_commit_timestamp);
 }
 
-void Storage::ReplicationClient::ReplicaStream::AppendOperation(durability::StorageGlobalOperation operation,
-                                                                LabelId label, const std::set<PropertyId> &properties,
-                                                                uint64_t timestamp) {
+void InMemoryStorage::ReplicationClient::ReplicaStream::AppendOperation(durability::StorageGlobalOperation operation,
+                                                                        LabelId label,
+                                                                        const std::set<PropertyId> &properties,
+                                                                        uint64_t timestamp) {
   replication::Encoder encoder(stream_.GetBuilder());
-  EncodeOperation(&encoder, &self_->storage_->name_id_mapper_, operation, label, properties, timestamp);
+  EncodeOperation(&encoder, self_->storage_->name_id_mapper_.get(), operation, label, properties, timestamp);
 }
 
-replication::AppendDeltasRes Storage::ReplicationClient::ReplicaStream::Finalize() { return stream_.AwaitResponse(); }
+replication::AppendDeltasRes InMemoryStorage::ReplicationClient::ReplicaStream::Finalize() {
+  return stream_.AwaitResponse();
+}
 
 ////// CurrentWalHandler //////
-Storage::ReplicationClient::CurrentWalHandler::CurrentWalHandler(ReplicationClient *self)
+InMemoryStorage::ReplicationClient::CurrentWalHandler::CurrentWalHandler(ReplicationClient *self)
     : self_(self), stream_(self_->rpc_client_->Stream<replication::CurrentWalRpc>()) {}
 
-void Storage::ReplicationClient::CurrentWalHandler::AppendFilename(const std::string &filename) {
+void InMemoryStorage::ReplicationClient::CurrentWalHandler::AppendFilename(const std::string &filename) {
   replication::Encoder encoder(stream_.GetBuilder());
   encoder.WriteString(filename);
 }
 
-void Storage::ReplicationClient::CurrentWalHandler::AppendSize(const size_t size) {
+void InMemoryStorage::ReplicationClient::CurrentWalHandler::AppendSize(const size_t size) {
   replication::Encoder encoder(stream_.GetBuilder());
   encoder.WriteUint(size);
 }
 
-void Storage::ReplicationClient::CurrentWalHandler::AppendFileData(utils::InputFile *file) {
+void InMemoryStorage::ReplicationClient::CurrentWalHandler::AppendFileData(utils::InputFile *file) {
   replication::Encoder encoder(stream_.GetBuilder());
   encoder.WriteFileData(file);
 }
 
-void Storage::ReplicationClient::CurrentWalHandler::AppendBufferData(const uint8_t *buffer, const size_t buffer_size) {
+void InMemoryStorage::ReplicationClient::CurrentWalHandler::AppendBufferData(const uint8_t *buffer,
+                                                                             const size_t buffer_size) {
   replication::Encoder encoder(stream_.GetBuilder());
   encoder.WriteBuffer(buffer, buffer_size);
 }
 
-replication::CurrentWalRes Storage::ReplicationClient::CurrentWalHandler::Finalize() { return stream_.AwaitResponse(); }
+replication::CurrentWalRes InMemoryStorage::ReplicationClient::CurrentWalHandler::Finalize() {
+  return stream_.AwaitResponse();
+}
 }  // namespace memgraph::storage
diff --git a/src/storage/v2/replication/replication_client.hpp b/src/storage/v2/replication/replication_client.hpp
index 829f0ab60..367c13058 100644
--- a/src/storage/v2/replication/replication_client.hpp
+++ b/src/storage/v2/replication/replication_client.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -21,6 +21,7 @@
 #include "storage/v2/delta.hpp"
 #include "storage/v2/durability/wal.hpp"
 #include "storage/v2/id_types.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/mvcc.hpp"
 #include "storage/v2/name_id_mapper.hpp"
 #include "storage/v2/property_value.hpp"
@@ -28,7 +29,6 @@
 #include "storage/v2/replication/enums.hpp"
 #include "storage/v2/replication/rpc.hpp"
 #include "storage/v2/replication/serialization.hpp"
-#include "storage/v2/storage.hpp"
 #include "utils/file.hpp"
 #include "utils/file_locker.hpp"
 #include "utils/spin_lock.hpp"
@@ -37,9 +37,9 @@
 
 namespace memgraph::storage {
 
-class Storage::ReplicationClient {
+class InMemoryStorage::ReplicationClient {
  public:
-  ReplicationClient(std::string name, Storage *storage, const io::network::Endpoint &endpoint,
+  ReplicationClient(std::string name, InMemoryStorage *storage, const io::network::Endpoint &endpoint,
                     replication::ReplicationMode mode, const replication::ReplicationClientConfig &config = {});
 
   // Handler used for transfering the current transaction.
@@ -123,7 +123,7 @@ class Storage::ReplicationClient {
 
   const auto &Endpoint() const { return rpc_client_->Endpoint(); }
 
-  Storage::TimestampInfo GetTimestampInfo();
+  InMemoryStorage::TimestampInfo GetTimestampInfo();
 
  private:
   [[nodiscard]] bool FinalizeTransactionReplicationInternal();
@@ -150,7 +150,7 @@ class Storage::ReplicationClient {
   void HandleRpcFailure();
 
   std::string name_;
-  Storage *storage_;
+  InMemoryStorage *storage_;
   std::optional<communication::ClientContext> rpc_context_;
   std::optional<rpc::Client> rpc_client_;
 
diff --git a/src/storage/v2/replication/replication_server.cpp b/src/storage/v2/replication/replication_server.cpp
index 8a705460d..035978001 100644
--- a/src/storage/v2/replication/replication_server.cpp
+++ b/src/storage/v2/replication/replication_server.cpp
@@ -9,18 +9,22 @@
 // by the Apache License, Version 2.0, included in the file
 // licenses/APL.txt.
 
-#include "storage/v2/replication/replication_server.hpp"
 #include <atomic>
 #include <filesystem>
 
 #include "spdlog/spdlog.h"
+
+#include "storage/v2/delta.hpp"
 #include "storage/v2/durability/durability.hpp"
 #include "storage/v2/durability/paths.hpp"
 #include "storage/v2/durability/serialization.hpp"
 #include "storage/v2/durability/snapshot.hpp"
 #include "storage/v2/durability/version.hpp"
 #include "storage/v2/durability/wal.hpp"
+#include "storage/v2/edge_accessor.hpp"
+#include "storage/v2/inmemory/unique_constraints.hpp"
 #include "storage/v2/replication/config.hpp"
+#include "storage/v2/replication/replication_server.hpp"
 #include "storage/v2/transaction.hpp"
 #include "utils/exceptions.hpp"
 
@@ -40,8 +44,8 @@ std::pair<uint64_t, durability::WalDeltaData> ReadDelta(durability::BaseDecoder
 };
 }  // namespace
 
-Storage::ReplicationServer::ReplicationServer(Storage *storage, io::network::Endpoint endpoint,
-                                              const replication::ReplicationServerConfig &config)
+InMemoryStorage::ReplicationServer::ReplicationServer(InMemoryStorage *storage, io::network::Endpoint endpoint,
+                                                      const replication::ReplicationServerConfig &config)
     : storage_(storage) {
   // Create RPC server.
   if (config.ssl) {
@@ -88,22 +92,21 @@ Storage::ReplicationServer::ReplicationServer(Storage *storage, io::network::End
   rpc_server_->Start();
 }
 
-void Storage::ReplicationServer::HeartbeatHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
+void InMemoryStorage::ReplicationServer::HeartbeatHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
   replication::HeartbeatReq req;
   slk::Load(&req, req_reader);
   replication::HeartbeatRes res{true, storage_->last_commit_timestamp_.load(), storage_->epoch_id_};
   slk::Save(res, res_builder);
 }
 
-void Storage::ReplicationServer::FrequentHeartbeatHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
+void InMemoryStorage::ReplicationServer::FrequentHeartbeatHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
   replication::FrequentHeartbeatReq req;
   slk::Load(&req, req_reader);
   replication::FrequentHeartbeatRes res{true};
   slk::Save(res, res_builder);
 }
 
-void Storage::ReplicationServer::AppendDeltasHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
-  spdlog::debug("Started replication recovery from appending deltas!");
+void InMemoryStorage::ReplicationServer::AppendDeltasHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
   replication::AppendDeltasReq req;
   slk::Load(&req, req_reader);
 
@@ -152,8 +155,7 @@ void Storage::ReplicationServer::AppendDeltasHandler(slk::Reader *req_reader, sl
   spdlog::debug("Replication recovery from append deltas finished, replica is now up to date!");
 }
 
-void Storage::ReplicationServer::SnapshotHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
-  spdlog::debug("Started replication recovery from received snapshot file!");
+void InMemoryStorage::ReplicationServer::SnapshotHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
   replication::SnapshotReq req;
   slk::Load(&req, req_reader);
 
@@ -171,14 +173,16 @@ void Storage::ReplicationServer::SnapshotHandler(slk::Reader *req_reader, slk::B
   storage_->vertices_.clear();
   storage_->edges_.clear();
 
-  storage_->constraints_ = Constraints();
-  storage_->indices_.label_index = LabelIndex(&storage_->indices_, &storage_->constraints_, storage_->config_.items);
-  storage_->indices_.label_property_index =
-      LabelPropertyIndex(&storage_->indices_, &storage_->constraints_, storage_->config_.items);
+  storage_->constraints_.existence_constraints_ = std::make_unique<ExistenceConstraints>();
+  storage_->constraints_.unique_constraints_ = std::make_unique<InMemoryUniqueConstraints>();
+  storage_->indices_.label_index_ =
+      std::make_unique<InMemoryLabelIndex>(&storage_->indices_, &storage_->constraints_, storage_->config_);
+  storage_->indices_.label_property_index_ =
+      std::make_unique<InMemoryLabelPropertyIndex>(&storage_->indices_, &storage_->constraints_, storage_->config_);
   try {
     spdlog::debug("Loading snapshot");
     auto recovered_snapshot = durability::LoadSnapshot(*maybe_snapshot_path, &storage_->vertices_, &storage_->edges_,
-                                                       &storage_->epoch_history_, &storage_->name_id_mapper_,
+                                                       &storage_->epoch_history_, storage_->name_id_mapper_.get(),
                                                        &storage_->edge_count_, storage_->config_);
     spdlog::debug("Snapshot loaded successfully");
     // If this step is present it should always be the first step of
@@ -224,8 +228,7 @@ void Storage::ReplicationServer::SnapshotHandler(slk::Reader *req_reader, slk::B
   spdlog::debug("Replication recovery from snapshot finished!");
 }
 
-void Storage::ReplicationServer::WalFilesHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
-  spdlog::debug("Started replication recovery from received WAL files!");
+void InMemoryStorage::ReplicationServer::WalFilesHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
   replication::WalFilesReq req;
   slk::Load(&req, req_reader);
 
@@ -245,8 +248,7 @@ void Storage::ReplicationServer::WalFilesHandler(slk::Reader *req_reader, slk::B
   spdlog::debug("Replication recovery from WAL files ended successfully, replica is now up to date!");
 }
 
-void Storage::ReplicationServer::CurrentWalHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
-  spdlog::debug("Started replication recovery from current WAL!");
+void InMemoryStorage::ReplicationServer::CurrentWalHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
   replication::CurrentWalReq req;
   slk::Load(&req, req_reader);
 
@@ -261,7 +263,7 @@ void Storage::ReplicationServer::CurrentWalHandler(slk::Reader *req_reader, slk:
   spdlog::debug("Replication recovery from current WAL ended successfully, replica is now up to date!");
 }
 
-void Storage::ReplicationServer::LoadWal(replication::Decoder *decoder) {
+void InMemoryStorage::ReplicationServer::LoadWal(replication::Decoder *decoder) {
   const auto temp_wal_directory = std::filesystem::temp_directory_path() / "memgraph" / durability::kWalDirectory;
   utils::EnsureDir(temp_wal_directory);
   auto maybe_wal_path = decoder->ReadFile(temp_wal_directory);
@@ -306,7 +308,7 @@ void Storage::ReplicationServer::LoadWal(replication::Decoder *decoder) {
   }
 }
 
-void Storage::ReplicationServer::TimestampHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
+void InMemoryStorage::ReplicationServer::TimestampHandler(slk::Reader *req_reader, slk::Builder *res_builder) {
   replication::TimestampReq req;
   slk::Load(&req, req_reader);
 
@@ -314,25 +316,29 @@ void Storage::ReplicationServer::TimestampHandler(slk::Reader *req_reader, slk::
   slk::Save(res, res_builder);
 }
 
-Storage::ReplicationServer::~ReplicationServer() {
+InMemoryStorage::ReplicationServer::~ReplicationServer() {
   if (rpc_server_) {
     rpc_server_->Shutdown();
     rpc_server_->AwaitShutdown();
   }
 }
-uint64_t Storage::ReplicationServer::ReadAndApplyDelta(durability::BaseDecoder *decoder) {
-  spdlog::debug("Reading and applying missing transaction deltas!");
+uint64_t InMemoryStorage::ReplicationServer::ReadAndApplyDelta(durability::BaseDecoder *decoder) {
   auto edge_acc = storage_->edges_.access();
   auto vertex_acc = storage_->vertices_.access();
 
-  std::optional<std::pair<uint64_t, storage::Storage::Accessor>> commit_timestamp_and_accessor;
+  std::optional<std::pair<uint64_t, std::unique_ptr<storage::Storage::Accessor>>> commit_timestamp_and_accessor;
   auto get_transaction = [this, &commit_timestamp_and_accessor](uint64_t commit_timestamp) {
     if (!commit_timestamp_and_accessor) {
-      commit_timestamp_and_accessor.emplace(commit_timestamp, storage_->Access());
+      commit_timestamp_and_accessor.emplace(commit_timestamp, storage_->Access(std::optional<IsolationLevel>{}));
     } else if (commit_timestamp_and_accessor->first != commit_timestamp) {
       throw utils::BasicException("Received more than one transaction!");
     }
-    return &commit_timestamp_and_accessor->second;
+    // TODO: Rethink this if we would reuse ReplicationServer for on disk storage.
+    if (auto *inmemoryAcc =
+            dynamic_cast<storage::InMemoryStorage::InMemoryAccessor *>(commit_timestamp_and_accessor->second.get())) {
+      return inmemoryAcc;
+    }
+    throw utils::BasicException("Received transaction for not supported storage!");
   };
 
   uint64_t applied_deltas = 0;
@@ -470,6 +476,7 @@ uint64_t Storage::ReplicationServer::ReadAndApplyDelta(durability::BaseDecoder *
                 is_visible = true;
                 break;
               }
+              case Delta::Action::DELETE_DESERIALIZED_OBJECT:
               case Delta::Action::DELETE_OBJECT: {
                 is_visible = false;
                 break;
@@ -503,7 +510,7 @@ uint64_t Storage::ReplicationServer::ReadAndApplyDelta(durability::BaseDecoder *
         spdlog::trace("       Transaction end");
         if (!commit_timestamp_and_accessor || commit_timestamp_and_accessor->first != timestamp)
           throw utils::BasicException("Invalid data!");
-        auto ret = commit_timestamp_and_accessor->second.Commit(commit_timestamp_and_accessor->first);
+        auto ret = commit_timestamp_and_accessor->second->Commit(commit_timestamp_and_accessor->first);
         if (ret.HasError()) throw utils::BasicException("Invalid transaction!");
         commit_timestamp_and_accessor = std::nullopt;
         break;
diff --git a/src/storage/v2/replication/replication_server.hpp b/src/storage/v2/replication/replication_server.hpp
index 083d1c6cf..6be1ad23c 100644
--- a/src/storage/v2/replication/replication_server.hpp
+++ b/src/storage/v2/replication/replication_server.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -11,13 +11,16 @@
 
 #pragma once
 
-#include "storage/v2/storage.hpp"
+#include "rpc/server.hpp"
+#include "slk/streams.hpp"
+#include "storage/v2/inmemory/storage.hpp"
+#include "storage/v2/replication/replication_client.hpp"
 
 namespace memgraph::storage {
 
-class Storage::ReplicationServer {
+class InMemoryStorage::ReplicationServer {
  public:
-  explicit ReplicationServer(Storage *storage, io::network::Endpoint endpoint,
+  explicit ReplicationServer(InMemoryStorage *storage, io::network::Endpoint endpoint,
                              const replication::ReplicationServerConfig &config);
   ReplicationServer(const ReplicationServer &) = delete;
   ReplicationServer(ReplicationServer &&) = delete;
@@ -42,7 +45,7 @@ class Storage::ReplicationServer {
   std::optional<communication::ServerContext> rpc_server_context_;
   std::optional<rpc::Server> rpc_server_;
 
-  Storage *storage_;
+  InMemoryStorage *storage_;
 };
 
 }  // namespace memgraph::storage
diff --git a/src/storage/v2/storage.cpp b/src/storage/v2/storage.cpp
index 4c65350b2..8b6555e8a 100644
--- a/src/storage/v2/storage.cpp
+++ b/src/storage/v2/storage.cpp
@@ -9,49 +9,22 @@
 // by the Apache License, Version 2.0, included in the file
 // licenses/APL.txt.
 
+#include "spdlog/spdlog.h"
+
+#include "storage/v2/disk/name_id_mapper.hpp"
 #include "storage/v2/storage.hpp"
-#include <algorithm>
-#include <atomic>
-#include <memory>
-#include <mutex>
-#include <variant>
-
-#include <gflags/gflags.h>
-#include <spdlog/spdlog.h>
-
-#include "io/network/endpoint.hpp"
-#include "storage/v2/durability/durability.hpp"
-#include "storage/v2/durability/metadata.hpp"
-#include "storage/v2/durability/paths.hpp"
-#include "storage/v2/durability/snapshot.hpp"
-#include "storage/v2/durability/wal.hpp"
-#include "storage/v2/edge_accessor.hpp"
-#include "storage/v2/indices.hpp"
-#include "storage/v2/mvcc.hpp"
-#include "storage/v2/replication/config.hpp"
-#include "storage/v2/replication/enums.hpp"
-#include "storage/v2/replication/replication_persistence_helper.hpp"
-#include "storage/v2/storage_mode.hpp"
 #include "storage/v2/transaction.hpp"
 #include "storage/v2/vertex_accessor.hpp"
 #include "utils/event_counter.hpp"
 #include "utils/event_histogram.hpp"
+#include "utils/exceptions.hpp"
 #include "utils/file.hpp"
 #include "utils/logging.hpp"
-#include "utils/memory_tracker.hpp"
-#include "utils/message.hpp"
-#include "utils/rw_lock.hpp"
-#include "utils/spin_lock.hpp"
 #include "utils/stat.hpp"
 #include "utils/timer.hpp"
+#include "utils/typeinfo.hpp"
 #include "utils/uuid.hpp"
 
-/// REPLICATION ///
-#include "storage/v2/replication/replication_client.hpp"
-#include "storage/v2/replication/replication_server.hpp"
-#include "storage/v2/replication/rpc.hpp"
-#include "storage/v2/storage_error.hpp"
-
 namespace memgraph::metrics {
 extern const Event SnapshotCreationLatency_us;
 
@@ -61,434 +34,23 @@ extern const Event ActiveLabelPropertyIndices;
 
 namespace memgraph::storage {
 
+class InMemoryStorage;
+
 using OOMExceptionEnabler = utils::MemoryTracker::OutOfMemoryExceptionEnabler;
 
-namespace {
-inline constexpr uint16_t kEpochHistoryRetention = 1000;
-
-std::string RegisterReplicaErrorToString(Storage::RegisterReplicaError error) {
-  switch (error) {
-    case Storage::RegisterReplicaError::NAME_EXISTS:
-      return "NAME_EXISTS";
-    case Storage::RegisterReplicaError::END_POINT_EXISTS:
-      return "END_POINT_EXISTS";
-    case Storage::RegisterReplicaError::CONNECTION_FAILED:
-      return "CONNECTION_FAILED";
-    case Storage::RegisterReplicaError::COULD_NOT_BE_PERSISTED:
-      return "COULD_NOT_BE_PERSISTED";
-  }
-}
-}  // namespace
-
-auto AdvanceToVisibleVertex(utils::SkipList<Vertex>::Iterator it, utils::SkipList<Vertex>::Iterator end,
-                            std::optional<VertexAccessor> *vertex, Transaction *tx, View view, Indices *indices,
-                            Constraints *constraints, Config::Items config) {
-  while (it != end) {
-    *vertex = VertexAccessor::Create(&*it, tx, indices, constraints, config, view);
-    if (!*vertex) {
-      ++it;
-      continue;
-    }
-    break;
-  }
-  return it;
-}
-
-AllVerticesIterable::Iterator::Iterator(AllVerticesIterable *self, utils::SkipList<Vertex>::Iterator it)
-    : self_(self),
-      it_(AdvanceToVisibleVertex(it, self->vertices_accessor_.end(), &self->vertex_, self->transaction_, self->view_,
-                                 self->indices_, self_->constraints_, self->config_)) {}
-
-VertexAccessor AllVerticesIterable::Iterator::operator*() const { return *self_->vertex_; }
-
-AllVerticesIterable::Iterator &AllVerticesIterable::Iterator::operator++() {
-  ++it_;
-  it_ = AdvanceToVisibleVertex(it_, self_->vertices_accessor_.end(), &self_->vertex_, self_->transaction_, self_->view_,
-                               self_->indices_, self_->constraints_, self_->config_);
-  return *this;
-}
-
-VerticesIterable::VerticesIterable(AllVerticesIterable vertices) : type_(Type::ALL) {
-  new (&all_vertices_) AllVerticesIterable(std::move(vertices));
-}
-
-VerticesIterable::VerticesIterable(LabelIndex::Iterable vertices) : type_(Type::BY_LABEL) {
-  new (&vertices_by_label_) LabelIndex::Iterable(std::move(vertices));
-}
-
-VerticesIterable::VerticesIterable(LabelPropertyIndex::Iterable vertices) : type_(Type::BY_LABEL_PROPERTY) {
-  new (&vertices_by_label_property_) LabelPropertyIndex::Iterable(std::move(vertices));
-}
-
-VerticesIterable::VerticesIterable(VerticesIterable &&other) noexcept : type_(other.type_) {
-  switch (other.type_) {
-    case Type::ALL:
-      new (&all_vertices_) AllVerticesIterable(std::move(other.all_vertices_));
-      break;
-    case Type::BY_LABEL:
-      new (&vertices_by_label_) LabelIndex::Iterable(std::move(other.vertices_by_label_));
-      break;
-    case Type::BY_LABEL_PROPERTY:
-      new (&vertices_by_label_property_) LabelPropertyIndex::Iterable(std::move(other.vertices_by_label_property_));
-      break;
-  }
-}
-
-VerticesIterable &VerticesIterable::operator=(VerticesIterable &&other) noexcept {
-  switch (type_) {
-    case Type::ALL:
-      all_vertices_.AllVerticesIterable::~AllVerticesIterable();
-      break;
-    case Type::BY_LABEL:
-      vertices_by_label_.LabelIndex::Iterable::~Iterable();
-      break;
-    case Type::BY_LABEL_PROPERTY:
-      vertices_by_label_property_.LabelPropertyIndex::Iterable::~Iterable();
-      break;
-  }
-  type_ = other.type_;
-  switch (other.type_) {
-    case Type::ALL:
-      new (&all_vertices_) AllVerticesIterable(std::move(other.all_vertices_));
-      break;
-    case Type::BY_LABEL:
-      new (&vertices_by_label_) LabelIndex::Iterable(std::move(other.vertices_by_label_));
-      break;
-    case Type::BY_LABEL_PROPERTY:
-      new (&vertices_by_label_property_) LabelPropertyIndex::Iterable(std::move(other.vertices_by_label_property_));
-      break;
-  }
-  return *this;
-}
-
-VerticesIterable::~VerticesIterable() {
-  switch (type_) {
-    case Type::ALL:
-      all_vertices_.AllVerticesIterable::~AllVerticesIterable();
-      break;
-    case Type::BY_LABEL:
-      vertices_by_label_.LabelIndex::Iterable::~Iterable();
-      break;
-    case Type::BY_LABEL_PROPERTY:
-      vertices_by_label_property_.LabelPropertyIndex::Iterable::~Iterable();
-      break;
-  }
-}
-
-VerticesIterable::Iterator VerticesIterable::begin() {
-  switch (type_) {
-    case Type::ALL:
-      return Iterator(all_vertices_.begin());
-    case Type::BY_LABEL:
-      return Iterator(vertices_by_label_.begin());
-    case Type::BY_LABEL_PROPERTY:
-      return Iterator(vertices_by_label_property_.begin());
-  }
-}
-
-VerticesIterable::Iterator VerticesIterable::end() {
-  switch (type_) {
-    case Type::ALL:
-      return Iterator(all_vertices_.end());
-    case Type::BY_LABEL:
-      return Iterator(vertices_by_label_.end());
-    case Type::BY_LABEL_PROPERTY:
-      return Iterator(vertices_by_label_property_.end());
-  }
-}
-
-VerticesIterable::Iterator::Iterator(AllVerticesIterable::Iterator it) : type_(Type::ALL) {
-  new (&all_it_) AllVerticesIterable::Iterator(std::move(it));
-}
-
-VerticesIterable::Iterator::Iterator(LabelIndex::Iterable::Iterator it) : type_(Type::BY_LABEL) {
-  new (&by_label_it_) LabelIndex::Iterable::Iterator(std::move(it));
-}
-
-VerticesIterable::Iterator::Iterator(LabelPropertyIndex::Iterable::Iterator it) : type_(Type::BY_LABEL_PROPERTY) {
-  new (&by_label_property_it_) LabelPropertyIndex::Iterable::Iterator(std::move(it));
-}
-
-VerticesIterable::Iterator::Iterator(const VerticesIterable::Iterator &other) : type_(other.type_) {
-  switch (other.type_) {
-    case Type::ALL:
-      new (&all_it_) AllVerticesIterable::Iterator(other.all_it_);
-      break;
-    case Type::BY_LABEL:
-      new (&by_label_it_) LabelIndex::Iterable::Iterator(other.by_label_it_);
-      break;
-    case Type::BY_LABEL_PROPERTY:
-      new (&by_label_property_it_) LabelPropertyIndex::Iterable::Iterator(other.by_label_property_it_);
-      break;
-  }
-}
-
-VerticesIterable::Iterator &VerticesIterable::Iterator::operator=(const VerticesIterable::Iterator &other) {
-  Destroy();
-  type_ = other.type_;
-  switch (other.type_) {
-    case Type::ALL:
-      new (&all_it_) AllVerticesIterable::Iterator(other.all_it_);
-      break;
-    case Type::BY_LABEL:
-      new (&by_label_it_) LabelIndex::Iterable::Iterator(other.by_label_it_);
-      break;
-    case Type::BY_LABEL_PROPERTY:
-      new (&by_label_property_it_) LabelPropertyIndex::Iterable::Iterator(other.by_label_property_it_);
-      break;
-  }
-  return *this;
-}
-
-VerticesIterable::Iterator::Iterator(VerticesIterable::Iterator &&other) noexcept : type_(other.type_) {
-  switch (other.type_) {
-    case Type::ALL:
-      new (&all_it_) AllVerticesIterable::Iterator(std::move(other.all_it_));
-      break;
-    case Type::BY_LABEL:
-      new (&by_label_it_) LabelIndex::Iterable::Iterator(std::move(other.by_label_it_));
-      break;
-    case Type::BY_LABEL_PROPERTY:
-      new (&by_label_property_it_) LabelPropertyIndex::Iterable::Iterator(std::move(other.by_label_property_it_));
-      break;
-  }
-}
-
-VerticesIterable::Iterator &VerticesIterable::Iterator::operator=(VerticesIterable::Iterator &&other) noexcept {
-  Destroy();
-  type_ = other.type_;
-  switch (other.type_) {
-    case Type::ALL:
-      new (&all_it_) AllVerticesIterable::Iterator(std::move(other.all_it_));
-      break;
-    case Type::BY_LABEL:
-      new (&by_label_it_) LabelIndex::Iterable::Iterator(std::move(other.by_label_it_));
-      break;
-    case Type::BY_LABEL_PROPERTY:
-      new (&by_label_property_it_) LabelPropertyIndex::Iterable::Iterator(std::move(other.by_label_property_it_));
-      break;
-  }
-  return *this;
-}
-
-VerticesIterable::Iterator::~Iterator() { Destroy(); }
-
-void VerticesIterable::Iterator::Destroy() noexcept {
-  switch (type_) {
-    case Type::ALL:
-      all_it_.AllVerticesIterable::Iterator::~Iterator();
-      break;
-    case Type::BY_LABEL:
-      by_label_it_.LabelIndex::Iterable::Iterator::~Iterator();
-      break;
-    case Type::BY_LABEL_PROPERTY:
-      by_label_property_it_.LabelPropertyIndex::Iterable::Iterator::~Iterator();
-      break;
-  }
-}
-
-VertexAccessor VerticesIterable::Iterator::operator*() const {
-  switch (type_) {
-    case Type::ALL:
-      return *all_it_;
-    case Type::BY_LABEL:
-      return *by_label_it_;
-    case Type::BY_LABEL_PROPERTY:
-      return *by_label_property_it_;
-  }
-}
-
-VerticesIterable::Iterator &VerticesIterable::Iterator::operator++() {
-  switch (type_) {
-    case Type::ALL:
-      ++all_it_;
-      break;
-    case Type::BY_LABEL:
-      ++by_label_it_;
-      break;
-    case Type::BY_LABEL_PROPERTY:
-      ++by_label_property_it_;
-      break;
-  }
-  return *this;
-}
-
-bool VerticesIterable::Iterator::operator==(const Iterator &other) const {
-  switch (type_) {
-    case Type::ALL:
-      return all_it_ == other.all_it_;
-    case Type::BY_LABEL:
-      return by_label_it_ == other.by_label_it_;
-    case Type::BY_LABEL_PROPERTY:
-      return by_label_property_it_ == other.by_label_property_it_;
-  }
-}
-
-Storage::Storage(Config config)
-    : indices_(&constraints_, config.items),
-      isolation_level_(config.transaction.isolation_level),
-      storage_mode_(StorageMode::IN_MEMORY_TRANSACTIONAL),
-      config_(config),
-      snapshot_directory_(config_.durability.storage_directory / durability::kSnapshotDirectory),
-      wal_directory_(config_.durability.storage_directory / durability::kWalDirectory),
-      lock_file_path_(config_.durability.storage_directory / durability::kLockFile),
-      uuid_(utils::GenerateUUID()),
-      epoch_id_(utils::GenerateUUID()),
-      global_locker_(file_retainer_.AddLocker()) {
-  if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::DISABLED ||
-      config_.durability.snapshot_on_exit || config_.durability.recover_on_startup) {
-    // Create the directory initially to crash the database in case of
-    // permission errors. This is done early to crash the database on startup
-    // instead of crashing the database for the first time during runtime (which
-    // could be an unpleasant surprise).
-    utils::EnsureDirOrDie(snapshot_directory_);
-    // Same reasoning as above.
-    utils::EnsureDirOrDie(wal_directory_);
-
-    // Verify that the user that started the process is the same user that is
-    // the owner of the storage directory.
-    durability::VerifyStorageDirectoryOwnerAndProcessUserOrDie(config_.durability.storage_directory);
-
-    // Create the lock file and open a handle to it. This will crash the
-    // database if it can't open the file for writing or if any other process is
-    // holding the file opened.
-    lock_file_handle_.Open(lock_file_path_, utils::OutputFile::Mode::OVERWRITE_EXISTING);
-    MG_ASSERT(lock_file_handle_.AcquireLock(),
-              "Couldn't acquire lock on the storage directory {}"
-              "!\nAnother Memgraph process is currently running with the same "
-              "storage directory, please stop it first before starting this "
-              "process!",
-              config_.durability.storage_directory);
-  }
-  if (config_.durability.recover_on_startup) {
-    auto info = durability::RecoverData(snapshot_directory_, wal_directory_, &uuid_, &epoch_id_, &epoch_history_,
-                                        &vertices_, &edges_, &edge_count_, &name_id_mapper_, &indices_, &constraints_,
-                                        config_, &wal_seq_num_);
-    if (info) {
-      vertex_id_ = info->next_vertex_id;
-      edge_id_ = info->next_edge_id;
-      timestamp_ = std::max(timestamp_, info->next_timestamp);
-      if (info->last_commit_timestamp) {
-        last_commit_timestamp_ = *info->last_commit_timestamp;
-      }
-    }
-  } else if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::DISABLED ||
-             config_.durability.snapshot_on_exit) {
-    bool files_moved = false;
-    auto backup_root = config_.durability.storage_directory / durability::kBackupDirectory;
-    for (const auto &[path, dirname, what] :
-         {std::make_tuple(snapshot_directory_, durability::kSnapshotDirectory, "snapshot"),
-          std::make_tuple(wal_directory_, durability::kWalDirectory, "WAL")}) {
-      if (!utils::DirExists(path)) continue;
-      auto backup_curr = backup_root / dirname;
-      std::error_code error_code;
-      for (const auto &item : std::filesystem::directory_iterator(path, error_code)) {
-        utils::EnsureDirOrDie(backup_root);
-        utils::EnsureDirOrDie(backup_curr);
-        std::error_code item_error_code;
-        std::filesystem::rename(item.path(), backup_curr / item.path().filename(), item_error_code);
-        MG_ASSERT(!item_error_code, "Couldn't move {} file {} because of: {}", what, item.path(),
-                  item_error_code.message());
-        files_moved = true;
-      }
-      MG_ASSERT(!error_code, "Couldn't backup {} files because of: {}", what, error_code.message());
-    }
-    if (files_moved) {
-      spdlog::warn(
-          "Since Memgraph was not supposed to recover on startup and "
-          "durability is enabled, your current durability files will likely "
-          "be overridden. To prevent important data loss, Memgraph has stored "
-          "those files into a .backup directory inside the storage directory.");
-    }
-  }
-  if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::DISABLED) {
-    snapshot_runner_.Run("Snapshot", config_.durability.snapshot_interval, [this] {
-      if (auto maybe_error = this->CreateSnapshot({true}); maybe_error.HasError()) {
-        switch (maybe_error.GetError()) {
-          case CreateSnapshotError::DisabledForReplica:
-            spdlog::warn(
-                utils::MessageWithLink("Snapshots are disabled for replicas.", "https://memgr.ph/replication"));
-            break;
-          case CreateSnapshotError::DisabledForAnalyticsPeriodicCommit:
-            spdlog::warn(utils::MessageWithLink("Periodic snapshots are disabled for analytical mode.",
-                                                "https://memgr.ph/durability"));
-            break;
-          case storage::Storage::CreateSnapshotError::ReachedMaxNumTries:
-            spdlog::warn("Failed to create snapshot. Reached max number of tries. Please contact support");
-            break;
+Storage::Storage(Config config, StorageMode storage_mode)
+    : name_id_mapper_(std::invoke([config, storage_mode]() -> std::unique_ptr<NameIdMapper> {
+        if (storage_mode == StorageMode::ON_DISK_TRANSACTIONAL) {
+          return std::make_unique<DiskNameIdMapper>(config.disk.name_id_mapper_directory,
+                                                    config.disk.id_name_mapper_directory);
         }
-      }
-    });
-  }
-  if (config_.gc.type == Config::Gc::Type::PERIODIC) {
-    gc_runner_.Run("Storage GC", config_.gc.interval, [this] { this->CollectGarbage<false>(); });
-  }
-
-  if (timestamp_ == kTimestampInitialId) {
-    commit_log_.emplace();
-  } else {
-    commit_log_.emplace(timestamp_);
-  }
-
-  if (config_.durability.restore_replication_state_on_startup) {
-    spdlog::info("Replication configuration will be stored and will be automatically restored in case of a crash.");
-    utils::EnsureDirOrDie(config_.durability.storage_directory / durability::kReplicationDirectory);
-    storage_ =
-        std::make_unique<kvstore::KVStore>(config_.durability.storage_directory / durability::kReplicationDirectory);
-
-    RestoreReplicationRole();
-
-    if (replication_role_ == replication::ReplicationRole::MAIN) {
-      RestoreReplicas();
-    }
-  } else {
-    spdlog::warn(
-        "Replicastion configuration will NOT be stored. When the server restarts, replication state will be "
-        "forgotten.");
-  }
-
-  if (config_.durability.snapshot_wal_mode == Config::Durability::SnapshotWalMode::DISABLED &&
-      replication_role_ == replication::ReplicationRole::MAIN) {
-    spdlog::warn(
-        "The instance has the MAIN replication role, but durability logs and snapshots are disabled. Please consider "
-        "enabling durability by using --storage-snapshot-interval-sec and --storage-wal-enabled flags because "
-        "without write-ahead logs this instance is not replicating any data.");
-  }
-}
-
-Storage::~Storage() {
-  if (config_.gc.type == Config::Gc::Type::PERIODIC) {
-    gc_runner_.Stop();
-  }
-  {
-    // Clear replication data
-    replication_server_.reset();
-    replication_clients_.WithLock([&](auto &clients) { clients.clear(); });
-  }
-  if (wal_file_) {
-    wal_file_->FinalizeWal();
-    wal_file_ = std::nullopt;
-  }
-  if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::DISABLED) {
-    snapshot_runner_.Stop();
-  }
-  if (config_.durability.snapshot_on_exit) {
-    if (auto maybe_error = this->CreateSnapshot({false}); maybe_error.HasError()) {
-      switch (maybe_error.GetError()) {
-        case CreateSnapshotError::DisabledForReplica:
-          spdlog::warn(utils::MessageWithLink("Snapshots are disabled for replicas.", "https://memgr.ph/replication"));
-          break;
-        case CreateSnapshotError::DisabledForAnalyticsPeriodicCommit:
-          spdlog::warn(utils::MessageWithLink("Periodic snapshots are disabled for analytical mode.",
-                                              "https://memgr.ph/replication"));
-          break;
-        case storage::Storage::CreateSnapshotError::ReachedMaxNumTries:
-          spdlog::warn("Failed to create snapshot. Reached max number of tries. Please contact support");
-          break;
-      }
-    }
-  }
-}
+        return std::make_unique<NameIdMapper>();
+      })),
+      config_(config),
+      isolation_level_(config.transaction.isolation_level),
+      storage_mode_(storage_mode),
+      indices_(&constraints_, config, storage_mode),
+      constraints_(config, storage_mode) {}
 
 Storage::Accessor::Accessor(Storage *storage, IsolationLevel isolation_level, StorageMode storage_mode)
     : storage_(storage),
@@ -498,7 +60,7 @@ Storage::Accessor::Accessor(Storage *storage, IsolationLevel isolation_level, St
       storage_guard_(storage_->main_lock_),
       transaction_(storage->CreateTransaction(isolation_level, storage_mode)),
       is_transaction_active_(true),
-      config_(storage->config_.items) {}
+      creation_storage_mode_(storage_mode) {}
 
 Storage::Accessor::Accessor(Accessor &&other) noexcept
     : storage_(other.storage_),
@@ -506,1817 +68,37 @@ Storage::Accessor::Accessor(Accessor &&other) noexcept
       transaction_(std::move(other.transaction_)),
       commit_timestamp_(other.commit_timestamp_),
       is_transaction_active_(other.is_transaction_active_),
-      config_(other.config_) {
+      creation_storage_mode_(other.creation_storage_mode_) {
   // Don't allow the other accessor to abort our transaction in destructor.
   other.is_transaction_active_ = false;
   other.commit_timestamp_.reset();
 }
 
-Storage::Accessor::~Accessor() {
-  if (is_transaction_active_) {
-    Abort();
-  }
-
-  FinalizeTransaction();
-}
-
-VertexAccessor Storage::Accessor::CreateVertex() {
-  OOMExceptionEnabler oom_exception;
-  auto gid = storage_->vertex_id_.fetch_add(1, std::memory_order_acq_rel);
-  auto acc = storage_->vertices_.access();
-
-  auto *delta = CreateDeleteObjectDelta(&transaction_);
-  auto [it, inserted] = acc.insert(Vertex{storage::Gid::FromUint(gid), delta});
-  MG_ASSERT(inserted, "The vertex must be inserted here!");
-  MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
-
-  if (delta) {
-    delta->prev.Set(&*it);
-  }
-
-  return VertexAccessor(&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_);
-}
-
-VertexAccessor Storage::Accessor::CreateVertex(storage::Gid gid) {
-  OOMExceptionEnabler oom_exception;
-  // NOTE: When we update the next `vertex_id_` here we perform a RMW
-  // (read-modify-write) operation that ISN'T atomic! But, that isn't an issue
-  // because this function is only called from the replication delta applier
-  // that runs single-threadedly and while this instance is set-up to apply
-  // threads (it is the replica), it is guaranteed that no other writes are
-  // possible.
-  storage_->vertex_id_.store(std::max(storage_->vertex_id_.load(std::memory_order_acquire), gid.AsUint() + 1),
-                             std::memory_order_release);
-  auto acc = storage_->vertices_.access();
-
-  auto *delta = CreateDeleteObjectDelta(&transaction_);
-  auto [it, inserted] = acc.insert(Vertex{gid, delta});
-  MG_ASSERT(inserted, "The vertex must be inserted here!");
-  MG_ASSERT(it != acc.end(), "Invalid Vertex accessor!");
-  if (delta) {
-    delta->prev.Set(&*it);
-  }
-  return VertexAccessor(&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_);
-}
-
-std::optional<VertexAccessor> Storage::Accessor::FindVertex(Gid gid, View view) {
-  auto acc = storage_->vertices_.access();
-  auto it = acc.find(gid);
-  if (it == acc.end()) return std::nullopt;
-  return VertexAccessor::Create(&*it, &transaction_, &storage_->indices_, &storage_->constraints_, config_, view);
-}
-
-Result<std::optional<VertexAccessor>> Storage::Accessor::DeleteVertex(VertexAccessor *vertex) {
-  MG_ASSERT(vertex->transaction_ == &transaction_,
-            "VertexAccessor must be from the same transaction as the storage "
-            "accessor when deleting a vertex!");
-  auto *vertex_ptr = vertex->vertex_;
-
-  std::lock_guard<utils::SpinLock> guard(vertex_ptr->lock);
-
-  if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
-
-  if (vertex_ptr->deleted) {
-    return std::optional<VertexAccessor>{};
-  }
-
-  if (!vertex_ptr->in_edges.empty() || !vertex_ptr->out_edges.empty()) return Error::VERTEX_HAS_EDGES;
-
-  CreateAndLinkDelta(&transaction_, vertex_ptr, Delta::RecreateObjectTag());
-  vertex_ptr->deleted = true;
-
-  // Need to inform the next CollectGarbage call that there are some
-  // non-transactional deletions that need to be collected
-  if (transaction_.storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
-    storage_->gc_full_scan_vertices_delete_ = true;
-  }
-
-  return std::make_optional<VertexAccessor>(vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_,
-                                            config_, true);
-}
-
-Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> Storage::Accessor::DetachDeleteVertex(
-    VertexAccessor *vertex) {
-  using ReturnType = std::pair<VertexAccessor, std::vector<EdgeAccessor>>;
-
-  MG_ASSERT(vertex->transaction_ == &transaction_,
-            "VertexAccessor must be from the same transaction as the storage "
-            "accessor when deleting a vertex!");
-  auto *vertex_ptr = vertex->vertex_;
-
-  std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> in_edges;
-  std::vector<std::tuple<EdgeTypeId, Vertex *, EdgeRef>> out_edges;
-
-  {
-    std::lock_guard<utils::SpinLock> guard(vertex_ptr->lock);
-
-    if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
-
-    if (vertex_ptr->deleted) return std::optional<ReturnType>{};
-
-    in_edges = vertex_ptr->in_edges;
-    out_edges = vertex_ptr->out_edges;
-  }
-
-  std::vector<EdgeAccessor> deleted_edges;
-  for (const auto &item : in_edges) {
-    auto [edge_type, from_vertex, edge] = item;
-    EdgeAccessor e(edge, edge_type, from_vertex, vertex_ptr, &transaction_, &storage_->indices_,
-                   &storage_->constraints_, config_);
-    auto ret = DeleteEdge(&e);
-    if (ret.HasError()) {
-      MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
-      return ret.GetError();
-    }
-
-    if (ret.GetValue()) {
-      deleted_edges.push_back(*ret.GetValue());
-    }
-  }
-  for (const auto &item : out_edges) {
-    auto [edge_type, to_vertex, edge] = item;
-    EdgeAccessor e(edge, edge_type, vertex_ptr, to_vertex, &transaction_, &storage_->indices_, &storage_->constraints_,
-                   config_);
-    auto ret = DeleteEdge(&e);
-    if (ret.HasError()) {
-      MG_ASSERT(ret.GetError() == Error::SERIALIZATION_ERROR, "Invalid database state!");
-      return ret.GetError();
-    }
-
-    if (ret.GetValue()) {
-      deleted_edges.push_back(*ret.GetValue());
-    }
-  }
-
-  std::lock_guard<utils::SpinLock> guard(vertex_ptr->lock);
-
-  // We need to check again for serialization errors because we unlocked the
-  // vertex. Some other transaction could have modified the vertex in the
-  // meantime if we didn't have any edges to delete.
-
-  if (!PrepareForWrite(&transaction_, vertex_ptr)) return Error::SERIALIZATION_ERROR;
-
-  MG_ASSERT(!vertex_ptr->deleted, "Invalid database state!");
-
-  CreateAndLinkDelta(&transaction_, vertex_ptr, Delta::RecreateObjectTag());
-  vertex_ptr->deleted = true;
-
-  // Need to inform the next CollectGarbage call that there are some
-  // non-transactional deletions that need to be collected
-  if (transaction_.storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
-    storage_->gc_full_scan_vertices_delete_ = true;
-  }
-
-  return std::make_optional<ReturnType>(
-      VertexAccessor{vertex_ptr, &transaction_, &storage_->indices_, &storage_->constraints_, config_, true},
-      std::move(deleted_edges));
-}
-
-Result<EdgeAccessor> Storage::Accessor::CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type) {
-  OOMExceptionEnabler oom_exception;
-  MG_ASSERT(from->transaction_ == to->transaction_,
-            "VertexAccessors must be from the same transaction when creating "
-            "an edge!");
-  MG_ASSERT(from->transaction_ == &transaction_,
-            "VertexAccessors must be from the same transaction in when "
-            "creating an edge!");
-
-  auto from_vertex = from->vertex_;
-  auto to_vertex = to->vertex_;
-
-  // Obtain the locks by `gid` order to avoid lock cycles.
-  std::unique_lock<utils::SpinLock> guard_from(from_vertex->lock, std::defer_lock);
-  std::unique_lock<utils::SpinLock> guard_to(to_vertex->lock, std::defer_lock);
-  if (from_vertex->gid < to_vertex->gid) {
-    guard_from.lock();
-    guard_to.lock();
-  } else if (from_vertex->gid > to_vertex->gid) {
-    guard_to.lock();
-    guard_from.lock();
-  } else {
-    // The vertices are the same vertex, only lock one.
-    guard_from.lock();
-  }
-
-  if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
-  if (from_vertex->deleted) return Error::DELETED_OBJECT;
-
-  if (to_vertex != from_vertex) {
-    if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
-    if (to_vertex->deleted) return Error::DELETED_OBJECT;
-  }
-
-  auto gid = storage::Gid::FromUint(storage_->edge_id_.fetch_add(1, std::memory_order_acq_rel));
-  EdgeRef edge(gid);
-  if (config_.properties_on_edges) {
-    auto acc = storage_->edges_.access();
-
-    auto *delta = CreateDeleteObjectDelta(&transaction_);
-    auto [it, inserted] = acc.insert(Edge(gid, delta));
-    MG_ASSERT(inserted, "The edge must be inserted here!");
-    MG_ASSERT(it != acc.end(), "Invalid Edge accessor!");
-    edge = EdgeRef(&*it);
-    if (delta) {
-      delta->prev.Set(&*it);
-    }
-  }
-
-  CreateAndLinkDelta(&transaction_, from_vertex, Delta::RemoveOutEdgeTag(), edge_type, to_vertex, edge);
-  from_vertex->out_edges.emplace_back(edge_type, to_vertex, edge);
-
-  CreateAndLinkDelta(&transaction_, to_vertex, Delta::RemoveInEdgeTag(), edge_type, from_vertex, edge);
-  to_vertex->in_edges.emplace_back(edge_type, from_vertex, edge);
-
-  // Increment edge count.
-  storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
-
-  return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
-                      &storage_->constraints_, config_);
-}
-
-Result<EdgeAccessor> Storage::Accessor::CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type,
-                                                   storage::Gid gid) {
-  OOMExceptionEnabler oom_exception;
-  MG_ASSERT(from->transaction_ == to->transaction_,
-            "VertexAccessors must be from the same transaction when creating "
-            "an edge!");
-  MG_ASSERT(from->transaction_ == &transaction_,
-            "VertexAccessors must be from the same transaction in when "
-            "creating an edge!");
-
-  auto from_vertex = from->vertex_;
-  auto to_vertex = to->vertex_;
-
-  // Obtain the locks by `gid` order to avoid lock cycles.
-  std::unique_lock<utils::SpinLock> guard_from(from_vertex->lock, std::defer_lock);
-  std::unique_lock<utils::SpinLock> guard_to(to_vertex->lock, std::defer_lock);
-  if (from_vertex->gid < to_vertex->gid) {
-    guard_from.lock();
-    guard_to.lock();
-  } else if (from_vertex->gid > to_vertex->gid) {
-    guard_to.lock();
-    guard_from.lock();
-  } else {
-    // The vertices are the same vertex, only lock one.
-    guard_from.lock();
-  }
-
-  if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
-  if (from_vertex->deleted) return Error::DELETED_OBJECT;
-
-  if (to_vertex != from_vertex) {
-    if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
-    if (to_vertex->deleted) return Error::DELETED_OBJECT;
-  }
-
-  // NOTE: When we update the next `edge_id_` here we perform a RMW
-  // (read-modify-write) operation that ISN'T atomic! But, that isn't an issue
-  // because this function is only called from the replication delta applier
-  // that runs single-threadedly and while this instance is set-up to apply
-  // threads (it is the replica), it is guaranteed that no other writes are
-  // possible.
-  storage_->edge_id_.store(std::max(storage_->edge_id_.load(std::memory_order_acquire), gid.AsUint() + 1),
-                           std::memory_order_release);
-
-  EdgeRef edge(gid);
-  if (config_.properties_on_edges) {
-    auto acc = storage_->edges_.access();
-
-    auto *delta = CreateDeleteObjectDelta(&transaction_);
-    auto [it, inserted] = acc.insert(Edge(gid, delta));
-    MG_ASSERT(inserted, "The edge must be inserted here!");
-    MG_ASSERT(it != acc.end(), "Invalid Edge accessor!");
-    edge = EdgeRef(&*it);
-    if (delta) {
-      delta->prev.Set(&*it);
-    }
-  }
-
-  CreateAndLinkDelta(&transaction_, from_vertex, Delta::RemoveOutEdgeTag(), edge_type, to_vertex, edge);
-  from_vertex->out_edges.emplace_back(edge_type, to_vertex, edge);
-
-  CreateAndLinkDelta(&transaction_, to_vertex, Delta::RemoveInEdgeTag(), edge_type, from_vertex, edge);
-  to_vertex->in_edges.emplace_back(edge_type, from_vertex, edge);
-
-  // Increment edge count.
-  storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
-
-  return EdgeAccessor(edge, edge_type, from_vertex, to_vertex, &transaction_, &storage_->indices_,
-                      &storage_->constraints_, config_);
-}
-
-Result<std::optional<EdgeAccessor>> Storage::Accessor::DeleteEdge(EdgeAccessor *edge) {
-  MG_ASSERT(edge->transaction_ == &transaction_,
-            "EdgeAccessor must be from the same transaction as the storage "
-            "accessor when deleting an edge!");
-  auto edge_ref = edge->edge_;
-  auto edge_type = edge->edge_type_;
-
-  std::unique_lock<utils::SpinLock> guard;
-  if (config_.properties_on_edges) {
-    auto edge_ptr = edge_ref.ptr;
-    guard = std::unique_lock<utils::SpinLock>(edge_ptr->lock);
-
-    if (!PrepareForWrite(&transaction_, edge_ptr)) return Error::SERIALIZATION_ERROR;
-
-    if (edge_ptr->deleted) return std::optional<EdgeAccessor>{};
-  }
-
-  auto *from_vertex = edge->from_vertex_;
-  auto *to_vertex = edge->to_vertex_;
-
-  // Obtain the locks by `gid` order to avoid lock cycles.
-  std::unique_lock<utils::SpinLock> guard_from(from_vertex->lock, std::defer_lock);
-  std::unique_lock<utils::SpinLock> guard_to(to_vertex->lock, std::defer_lock);
-  if (from_vertex->gid < to_vertex->gid) {
-    guard_from.lock();
-    guard_to.lock();
-  } else if (from_vertex->gid > to_vertex->gid) {
-    guard_to.lock();
-    guard_from.lock();
-  } else {
-    // The vertices are the same vertex, only lock one.
-    guard_from.lock();
-  }
-
-  if (!PrepareForWrite(&transaction_, from_vertex)) return Error::SERIALIZATION_ERROR;
-  MG_ASSERT(!from_vertex->deleted, "Invalid database state!");
-
-  if (to_vertex != from_vertex) {
-    if (!PrepareForWrite(&transaction_, to_vertex)) return Error::SERIALIZATION_ERROR;
-    MG_ASSERT(!to_vertex->deleted, "Invalid database state!");
-  }
-
-  auto delete_edge_from_storage = [&edge_type, &edge_ref, this](auto *vertex, auto *edges) {
-    std::tuple<EdgeTypeId, Vertex *, EdgeRef> link(edge_type, vertex, edge_ref);
-    auto it = std::find(edges->begin(), edges->end(), link);
-    if (config_.properties_on_edges) {
-      MG_ASSERT(it != edges->end(), "Invalid database state!");
-    } else if (it == edges->end()) {
-      return false;
-    }
-    std::swap(*it, *edges->rbegin());
-    edges->pop_back();
-    return true;
-  };
-
-  auto op1 = delete_edge_from_storage(to_vertex, &from_vertex->out_edges);
-  auto op2 = delete_edge_from_storage(from_vertex, &to_vertex->in_edges);
-
-  if (config_.properties_on_edges) {
-    MG_ASSERT((op1 && op2), "Invalid database state!");
-  } else {
-    MG_ASSERT((op1 && op2) || (!op1 && !op2), "Invalid database state!");
-    if (!op1 && !op2) {
-      // The edge is already deleted.
-      return std::optional<EdgeAccessor>{};
-    }
-  }
-
-  if (config_.properties_on_edges) {
-    auto *edge_ptr = edge_ref.ptr;
-    CreateAndLinkDelta(&transaction_, edge_ptr, Delta::RecreateObjectTag());
-    edge_ptr->deleted = true;
-
-    // Need to inform the next CollectGarbage call that there are some
-    // non-transactional deletions that need to be collected
-    if (transaction_.storage_mode == StorageMode::IN_MEMORY_ANALYTICAL) {
-      storage_->gc_full_scan_edges_delete_ = true;
-    }
-  }
-
-  CreateAndLinkDelta(&transaction_, from_vertex, Delta::AddOutEdgeTag(), edge_type, to_vertex, edge_ref);
-  CreateAndLinkDelta(&transaction_, to_vertex, Delta::AddInEdgeTag(), edge_type, from_vertex, edge_ref);
-
-  // Decrement edge count.
-  storage_->edge_count_.fetch_add(-1, std::memory_order_acq_rel);
-
-  return std::make_optional<EdgeAccessor>(edge_ref, edge_type, from_vertex, to_vertex, &transaction_,
-                                          &storage_->indices_, &storage_->constraints_, config_, true);
-}
-
-const std::string &Storage::Accessor::LabelToName(LabelId label) const { return storage_->LabelToName(label); }
-
-const std::string &Storage::Accessor::PropertyToName(PropertyId property) const {
-  return storage_->PropertyToName(property);
-}
-
-const std::string &Storage::Accessor::EdgeTypeToName(EdgeTypeId edge_type) const {
-  return storage_->EdgeTypeToName(edge_type);
-}
-
-LabelId Storage::Accessor::NameToLabel(const std::string_view name) { return storage_->NameToLabel(name); }
-
-PropertyId Storage::Accessor::NameToProperty(const std::string_view name) { return storage_->NameToProperty(name); }
-
-EdgeTypeId Storage::Accessor::NameToEdgeType(const std::string_view name) { return storage_->NameToEdgeType(name); }
-
-void Storage::Accessor::AdvanceCommand() { ++transaction_.command_id; }
-
-utils::BasicResult<StorageDataManipulationError, void> Storage::Accessor::Commit(
-    const std::optional<uint64_t> desired_commit_timestamp) {
-  MG_ASSERT(is_transaction_active_, "The transaction is already terminated!");
-  MG_ASSERT(!transaction_.must_abort, "The transaction can't be committed!");
-
-  auto could_replicate_all_sync_replicas = true;
-
-  if (transaction_.deltas.empty()) {
-    // We don't have to update the commit timestamp here because no one reads
-    // it.
-    storage_->commit_log_->MarkFinished(transaction_.start_timestamp);
-  } else {
-    // Validate that existence constraints are satisfied for all modified
-    // vertices.
-    for (const auto &delta : transaction_.deltas) {
-      auto prev = delta.prev.Get();
-      MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
-      if (prev.type != PreviousPtr::Type::VERTEX) {
-        continue;
-      }
-      // No need to take any locks here because we modified this vertex and no
-      // one else can touch it until we commit.
-      auto validation_result = ValidateExistenceConstraints(*prev.vertex, storage_->constraints_);
-      if (validation_result) {
-        Abort();
-        return StorageDataManipulationError{*validation_result};
-      }
-    }
-
-    // Result of validating the vertex against unqiue constraints. It has to be
-    // declared outside of the critical section scope because its value is
-    // tested for Abort call which has to be done out of the scope.
-    std::optional<ConstraintViolation> unique_constraint_violation;
-
-    // Save these so we can mark them used in the commit log.
-    uint64_t start_timestamp = transaction_.start_timestamp;
-
-    {
-      std::unique_lock<utils::SpinLock> engine_guard(storage_->engine_lock_);
-      commit_timestamp_.emplace(storage_->CommitTimestamp(desired_commit_timestamp));
-
-      // Before committing and validating vertices against unique constraints,
-      // we have to update unique constraints with the vertices that are going
-      // to be validated/committed.
-      for (const auto &delta : transaction_.deltas) {
-        auto prev = delta.prev.Get();
-        MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
-        if (prev.type != PreviousPtr::Type::VERTEX) {
-          continue;
-        }
-        storage_->constraints_.unique_constraints.UpdateBeforeCommit(prev.vertex, transaction_);
-      }
-
-      // Validate that unique constraints are satisfied for all modified
-      // vertices.
-      for (const auto &delta : transaction_.deltas) {
-        auto prev = delta.prev.Get();
-        MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
-        if (prev.type != PreviousPtr::Type::VERTEX) {
-          continue;
-        }
-
-        // No need to take any locks here because we modified this vertex and no
-        // one else can touch it until we commit.
-        unique_constraint_violation =
-            storage_->constraints_.unique_constraints.Validate(*prev.vertex, transaction_, *commit_timestamp_);
-        if (unique_constraint_violation) {
-          break;
-        }
-      }
-
-      if (!unique_constraint_violation) {
-        // Write transaction to WAL while holding the engine lock to make sure
-        // that committed transactions are sorted by the commit timestamp in the
-        // WAL files. We supply the new commit timestamp to the function so that
-        // it knows what will be the final commit timestamp. The WAL must be
-        // written before actually committing the transaction (before setting
-        // the commit timestamp) so that no other transaction can see the
-        // modifications before they are written to disk.
-        // Replica can log only the write transaction received from Main
-        // so the Wal files are consistent
-        if (storage_->replication_role_ == replication::ReplicationRole::MAIN || desired_commit_timestamp.has_value()) {
-          could_replicate_all_sync_replicas = storage_->AppendToWalDataManipulation(transaction_, *commit_timestamp_);
-        }
-
-        // Take committed_transactions lock while holding the engine lock to
-        // make sure that committed transactions are sorted by the commit
-        // timestamp in the list.
-        storage_->committed_transactions_.WithLock([&](auto &committed_transactions) {
-          // TODO: release lock, and update all deltas to have a local copy
-          // of the commit timestamp
-          MG_ASSERT(transaction_.commit_timestamp != nullptr, "Invalid database state!");
-          transaction_.commit_timestamp->store(*commit_timestamp_, std::memory_order_release);
-          // Replica can only update the last commit timestamp with
-          // the commits received from main.
-          if (storage_->replication_role_ == replication::ReplicationRole::MAIN ||
-              desired_commit_timestamp.has_value()) {
-            // Update the last commit timestamp
-            storage_->last_commit_timestamp_.store(*commit_timestamp_);
-          }
-          // Release engine lock because we don't have to hold it anymore
-          // and emplace back could take a long time.
-          engine_guard.unlock();
-        });
-
-        storage_->commit_log_->MarkFinished(start_timestamp);
-      }
-    }
-
-    if (unique_constraint_violation) {
-      Abort();
-      return StorageDataManipulationError{*unique_constraint_violation};
-    }
-  }
-  is_transaction_active_ = false;
-
-  if (!could_replicate_all_sync_replicas) {
-    return StorageDataManipulationError{ReplicationError{}};
-  }
-
-  return {};
-}
-
-void Storage::Accessor::Abort() {
-  MG_ASSERT(is_transaction_active_, "The transaction is already terminated!");
-
-  // We collect vertices and edges we've created here and then splice them into
-  // `deleted_vertices_` and `deleted_edges_` lists, instead of adding them one
-  // by one and acquiring lock every time.
-  std::list<Gid> my_deleted_vertices;
-  std::list<Gid> my_deleted_edges;
-
-  for (const auto &delta : transaction_.deltas) {
-    auto prev = delta.prev.Get();
-    switch (prev.type) {
-      case PreviousPtr::Type::VERTEX: {
-        auto vertex = prev.vertex;
-        std::lock_guard<utils::SpinLock> guard(vertex->lock);
-        Delta *current = vertex->delta;
-        while (current != nullptr && current->timestamp->load(std::memory_order_acquire) ==
-                                         transaction_.transaction_id.load(std::memory_order_acquire)) {
-          switch (current->action) {
-            case Delta::Action::REMOVE_LABEL: {
-              auto it = std::find(vertex->labels.begin(), vertex->labels.end(), current->label);
-              MG_ASSERT(it != vertex->labels.end(), "Invalid database state!");
-              std::swap(*it, *vertex->labels.rbegin());
-              vertex->labels.pop_back();
-              break;
-            }
-            case Delta::Action::ADD_LABEL: {
-              auto it = std::find(vertex->labels.begin(), vertex->labels.end(), current->label);
-              MG_ASSERT(it == vertex->labels.end(), "Invalid database state!");
-              vertex->labels.push_back(current->label);
-              break;
-            }
-            case Delta::Action::SET_PROPERTY: {
-              vertex->properties.SetProperty(current->property.key, current->property.value);
-              break;
-            }
-            case Delta::Action::ADD_IN_EDGE: {
-              std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{current->vertex_edge.edge_type,
-                                                             current->vertex_edge.vertex, current->vertex_edge.edge};
-              auto it = std::find(vertex->in_edges.begin(), vertex->in_edges.end(), link);
-              MG_ASSERT(it == vertex->in_edges.end(), "Invalid database state!");
-              vertex->in_edges.push_back(link);
-              break;
-            }
-            case Delta::Action::ADD_OUT_EDGE: {
-              std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{current->vertex_edge.edge_type,
-                                                             current->vertex_edge.vertex, current->vertex_edge.edge};
-              auto it = std::find(vertex->out_edges.begin(), vertex->out_edges.end(), link);
-              MG_ASSERT(it == vertex->out_edges.end(), "Invalid database state!");
-              vertex->out_edges.push_back(link);
-              // Increment edge count. We only increment the count here because
-              // the information in `ADD_IN_EDGE` and `Edge/RECREATE_OBJECT` is
-              // redundant. Also, `Edge/RECREATE_OBJECT` isn't available when
-              // edge properties are disabled.
-              storage_->edge_count_.fetch_add(1, std::memory_order_acq_rel);
-              break;
-            }
-            case Delta::Action::REMOVE_IN_EDGE: {
-              std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{current->vertex_edge.edge_type,
-                                                             current->vertex_edge.vertex, current->vertex_edge.edge};
-              auto it = std::find(vertex->in_edges.begin(), vertex->in_edges.end(), link);
-              MG_ASSERT(it != vertex->in_edges.end(), "Invalid database state!");
-              std::swap(*it, *vertex->in_edges.rbegin());
-              vertex->in_edges.pop_back();
-              break;
-            }
-            case Delta::Action::REMOVE_OUT_EDGE: {
-              std::tuple<EdgeTypeId, Vertex *, EdgeRef> link{current->vertex_edge.edge_type,
-                                                             current->vertex_edge.vertex, current->vertex_edge.edge};
-              auto it = std::find(vertex->out_edges.begin(), vertex->out_edges.end(), link);
-              MG_ASSERT(it != vertex->out_edges.end(), "Invalid database state!");
-              std::swap(*it, *vertex->out_edges.rbegin());
-              vertex->out_edges.pop_back();
-              // Decrement edge count. We only decrement the count here because
-              // the information in `REMOVE_IN_EDGE` and `Edge/DELETE_OBJECT` is
-              // redundant. Also, `Edge/DELETE_OBJECT` isn't available when edge
-              // properties are disabled.
-              storage_->edge_count_.fetch_add(-1, std::memory_order_acq_rel);
-              break;
-            }
-            case Delta::Action::DELETE_OBJECT: {
-              vertex->deleted = true;
-              my_deleted_vertices.push_back(vertex->gid);
-              break;
-            }
-            case Delta::Action::RECREATE_OBJECT: {
-              vertex->deleted = false;
-              break;
-            }
-          }
-          current = current->next.load(std::memory_order_acquire);
-        }
-        vertex->delta = current;
-        if (current != nullptr) {
-          current->prev.Set(vertex);
-        }
-
-        break;
-      }
-      case PreviousPtr::Type::EDGE: {
-        auto edge = prev.edge;
-        std::lock_guard<utils::SpinLock> guard(edge->lock);
-        Delta *current = edge->delta;
-        while (current != nullptr && current->timestamp->load(std::memory_order_acquire) ==
-                                         transaction_.transaction_id.load(std::memory_order_acquire)) {
-          switch (current->action) {
-            case Delta::Action::SET_PROPERTY: {
-              edge->properties.SetProperty(current->property.key, current->property.value);
-              break;
-            }
-            case Delta::Action::DELETE_OBJECT: {
-              edge->deleted = true;
-              my_deleted_edges.push_back(edge->gid);
-              break;
-            }
-            case Delta::Action::RECREATE_OBJECT: {
-              edge->deleted = false;
-              break;
-            }
-            case Delta::Action::REMOVE_LABEL:
-            case Delta::Action::ADD_LABEL:
-            case Delta::Action::ADD_IN_EDGE:
-            case Delta::Action::ADD_OUT_EDGE:
-            case Delta::Action::REMOVE_IN_EDGE:
-            case Delta::Action::REMOVE_OUT_EDGE: {
-              LOG_FATAL("Invalid database state!");
-              break;
-            }
-          }
-          current = current->next.load(std::memory_order_acquire);
-        }
-        edge->delta = current;
-        if (current != nullptr) {
-          current->prev.Set(edge);
-        }
-
-        break;
-      }
-      case PreviousPtr::Type::DELTA:
-      // pointer probably couldn't be set because allocation failed
-      case PreviousPtr::Type::NULLPTR:
-        break;
-    }
-  }
-
-  {
-    std::unique_lock<utils::SpinLock> engine_guard(storage_->engine_lock_);
-    uint64_t mark_timestamp = storage_->timestamp_;
-    // Take garbage_undo_buffers lock while holding the engine lock to make
-    // sure that entries are sorted by mark timestamp in the list.
-    storage_->garbage_undo_buffers_.WithLock([&](auto &garbage_undo_buffers) {
-      // Release engine lock because we don't have to hold it anymore and
-      // emplace back could take a long time.
-      engine_guard.unlock();
-      garbage_undo_buffers.emplace_back(mark_timestamp, std::move(transaction_.deltas));
-    });
-    storage_->deleted_vertices_.WithLock(
-        [&](auto &deleted_vertices) { deleted_vertices.splice(deleted_vertices.begin(), my_deleted_vertices); });
-    storage_->deleted_edges_.WithLock(
-        [&](auto &deleted_edges) { deleted_edges.splice(deleted_edges.begin(), my_deleted_edges); });
-  }
-
-  storage_->commit_log_->MarkFinished(transaction_.start_timestamp);
-  is_transaction_active_ = false;
-}
-
-void Storage::Accessor::FinalizeTransaction() {
-  if (commit_timestamp_) {
-    storage_->commit_log_->MarkFinished(*commit_timestamp_);
-    storage_->committed_transactions_.WithLock(
-        [&](auto &committed_transactions) { committed_transactions.emplace_back(std::move(transaction_)); });
-    commit_timestamp_.reset();
-  }
-}
-
-std::optional<uint64_t> Storage::Accessor::GetTransactionId() const {
-  if (is_transaction_active_) {
-    return transaction_.transaction_id.load(std::memory_order_acquire);
-  }
-  return {};
-}
-
-const std::string &Storage::LabelToName(LabelId label) const { return name_id_mapper_.IdToName(label.AsUint()); }
-
-const std::string &Storage::PropertyToName(PropertyId property) const {
-  return name_id_mapper_.IdToName(property.AsUint());
-}
-
-const std::string &Storage::EdgeTypeToName(EdgeTypeId edge_type) const {
-  return name_id_mapper_.IdToName(edge_type.AsUint());
-}
-
-LabelId Storage::NameToLabel(const std::string_view name) { return LabelId::FromUint(name_id_mapper_.NameToId(name)); }
-
-PropertyId Storage::NameToProperty(const std::string_view name) {
-  return PropertyId::FromUint(name_id_mapper_.NameToId(name));
-}
-
-EdgeTypeId Storage::NameToEdgeType(const std::string_view name) {
-  return EdgeTypeId::FromUint(name_id_mapper_.NameToId(name));
-}
-
-utils::BasicResult<StorageIndexDefinitionError, void> Storage::CreateIndex(
-    LabelId label, const std::optional<uint64_t> desired_commit_timestamp) {
-  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
-  if (!indices_.label_index.CreateIndex(label, vertices_.access())) {
-    return StorageIndexDefinitionError{IndexDefinitionError{}};
-  }
-  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
-  const auto success =
-      AppendToWalDataDefinition(durability::StorageGlobalOperation::LABEL_INDEX_CREATE, label, {}, commit_timestamp);
-  commit_log_->MarkFinished(commit_timestamp);
-  last_commit_timestamp_ = commit_timestamp;
-
-  // We don't care if there is a replication error because on main node the change will go through
-  memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveLabelIndices);
-
-  if (success) {
-    return {};
-  }
-
-  return StorageIndexDefinitionError{ReplicationError{}};
-}
-
-utils::BasicResult<StorageIndexDefinitionError, void> Storage::CreateIndex(
-    LabelId label, PropertyId property, const std::optional<uint64_t> desired_commit_timestamp) {
-  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
-  if (!indices_.label_property_index.CreateIndex(label, property, vertices_.access())) {
-    return StorageIndexDefinitionError{IndexDefinitionError{}};
-  }
-  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
-  auto success = AppendToWalDataDefinition(durability::StorageGlobalOperation::LABEL_PROPERTY_INDEX_CREATE, label,
-                                           {property}, commit_timestamp);
-  commit_log_->MarkFinished(commit_timestamp);
-  last_commit_timestamp_ = commit_timestamp;
-
-  // We don't care if there is a replication error because on main node the change will go through
-  memgraph::metrics::IncrementCounter(memgraph::metrics::ActiveLabelPropertyIndices);
-
-  if (success) {
-    return {};
-  }
-
-  return StorageIndexDefinitionError{ReplicationError{}};
-}
-
-utils::BasicResult<StorageIndexDefinitionError, void> Storage::DropIndex(
-    LabelId label, const std::optional<uint64_t> desired_commit_timestamp) {
-  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
-  if (!indices_.label_index.DropIndex(label)) {
-    return StorageIndexDefinitionError{IndexDefinitionError{}};
-  }
-  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
-  auto success =
-      AppendToWalDataDefinition(durability::StorageGlobalOperation::LABEL_INDEX_DROP, label, {}, commit_timestamp);
-  commit_log_->MarkFinished(commit_timestamp);
-  last_commit_timestamp_ = commit_timestamp;
-
-  // We don't care if there is a replication error because on main node the change will go through
-  memgraph::metrics::DecrementCounter(memgraph::metrics::ActiveLabelIndices);
-
-  if (success) {
-    return {};
-  }
-
-  return StorageIndexDefinitionError{ReplicationError{}};
-}
-
-utils::BasicResult<StorageIndexDefinitionError, void> Storage::DropIndex(
-    LabelId label, PropertyId property, const std::optional<uint64_t> desired_commit_timestamp) {
-  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
-  if (!indices_.label_property_index.DropIndex(label, property)) {
-    return StorageIndexDefinitionError{IndexDefinitionError{}};
-  }
-  // For a description why using `timestamp_` is correct, see
-  // `CreateIndex(LabelId label)`.
-  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
-  auto success = AppendToWalDataDefinition(durability::StorageGlobalOperation::LABEL_PROPERTY_INDEX_DROP, label,
-                                           {property}, commit_timestamp);
-  commit_log_->MarkFinished(commit_timestamp);
-  last_commit_timestamp_ = commit_timestamp;
-
-  // We don't care if there is a replication error because on main node the change will go through
-  memgraph::metrics::DecrementCounter(memgraph::metrics::ActiveLabelPropertyIndices);
-
-  if (success) {
-    return {};
-  }
-
-  return StorageIndexDefinitionError{ReplicationError{}};
-}
-
 IndicesInfo Storage::ListAllIndices() const {
   std::shared_lock<utils::RWLock> storage_guard_(main_lock_);
-  return {indices_.label_index.ListIndices(), indices_.label_property_index.ListIndices()};
-}
-
-utils::BasicResult<StorageExistenceConstraintDefinitionError, void> Storage::CreateExistenceConstraint(
-    LabelId label, PropertyId property, const std::optional<uint64_t> desired_commit_timestamp) {
-  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
-  auto ret = storage::CreateExistenceConstraint(&constraints_, label, property, vertices_.access());
-  if (ret.HasError()) {
-    return StorageExistenceConstraintDefinitionError{ret.GetError()};
-  }
-  if (!ret.GetValue()) {
-    return StorageExistenceConstraintDefinitionError{ConstraintDefinitionError{}};
-  }
-
-  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
-  auto success = AppendToWalDataDefinition(durability::StorageGlobalOperation::EXISTENCE_CONSTRAINT_CREATE, label,
-                                           {property}, commit_timestamp);
-  commit_log_->MarkFinished(commit_timestamp);
-  last_commit_timestamp_ = commit_timestamp;
-
-  if (success) {
-    return {};
-  }
-
-  return StorageExistenceConstraintDefinitionError{ReplicationError{}};
-}
-
-utils::BasicResult<StorageExistenceConstraintDroppingError, void> Storage::DropExistenceConstraint(
-    LabelId label, PropertyId property, const std::optional<uint64_t> desired_commit_timestamp) {
-  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
-  if (!storage::DropExistenceConstraint(&constraints_, label, property)) {
-    return StorageExistenceConstraintDroppingError{ConstraintDefinitionError{}};
-  }
-  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
-  auto success = AppendToWalDataDefinition(durability::StorageGlobalOperation::EXISTENCE_CONSTRAINT_DROP, label,
-                                           {property}, commit_timestamp);
-  commit_log_->MarkFinished(commit_timestamp);
-  last_commit_timestamp_ = commit_timestamp;
-
-  if (success) {
-    return {};
-  }
-
-  return StorageExistenceConstraintDroppingError{ReplicationError{}};
-}
-
-utils::BasicResult<StorageUniqueConstraintDefinitionError, UniqueConstraints::CreationStatus>
-Storage::CreateUniqueConstraint(LabelId label, const std::set<PropertyId> &properties,
-                                const std::optional<uint64_t> desired_commit_timestamp) {
-  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
-  auto ret = constraints_.unique_constraints.CreateConstraint(label, properties, vertices_.access());
-  if (ret.HasError()) {
-    return StorageUniqueConstraintDefinitionError{ret.GetError()};
-  }
-  if (ret.GetValue() != UniqueConstraints::CreationStatus::SUCCESS) {
-    return ret.GetValue();
-  }
-  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
-  auto success = AppendToWalDataDefinition(durability::StorageGlobalOperation::UNIQUE_CONSTRAINT_CREATE, label,
-                                           properties, commit_timestamp);
-  commit_log_->MarkFinished(commit_timestamp);
-  last_commit_timestamp_ = commit_timestamp;
-
-  if (success) {
-    return UniqueConstraints::CreationStatus::SUCCESS;
-  }
-
-  return StorageUniqueConstraintDefinitionError{ReplicationError{}};
-}
-
-utils::BasicResult<StorageUniqueConstraintDroppingError, UniqueConstraints::DeletionStatus>
-Storage::DropUniqueConstraint(LabelId label, const std::set<PropertyId> &properties,
-                              const std::optional<uint64_t> desired_commit_timestamp) {
-  std::unique_lock<utils::RWLock> storage_guard(main_lock_);
-  auto ret = constraints_.unique_constraints.DropConstraint(label, properties);
-  if (ret != UniqueConstraints::DeletionStatus::SUCCESS) {
-    return ret;
-  }
-  const auto commit_timestamp = CommitTimestamp(desired_commit_timestamp);
-  auto success = AppendToWalDataDefinition(durability::StorageGlobalOperation::UNIQUE_CONSTRAINT_DROP, label,
-                                           properties, commit_timestamp);
-  commit_log_->MarkFinished(commit_timestamp);
-  last_commit_timestamp_ = commit_timestamp;
-
-  if (success) {
-    return UniqueConstraints::DeletionStatus::SUCCESS;
-  }
-
-  return StorageUniqueConstraintDroppingError{ReplicationError{}};
+  return {indices_.label_index_->ListIndices(), indices_.label_property_index_->ListIndices()};
 }
 
 ConstraintsInfo Storage::ListAllConstraints() const {
   std::shared_lock<utils::RWLock> storage_guard_(main_lock_);
-  return {ListExistenceConstraints(constraints_), constraints_.unique_constraints.ListConstraints()};
+  return {constraints_.existence_constraints_->ListConstraints(), constraints_.unique_constraints_->ListConstraints()};
 }
 
-StorageInfo Storage::GetInfo() const {
-  auto vertex_count = vertices_.size();
-  auto edge_count = edge_count_.load(std::memory_order_acquire);
-  double average_degree = 0.0;
-  if (vertex_count) {
-    average_degree = 2.0 * static_cast<double>(edge_count) / vertex_count;
-  }
-  return {vertex_count, edge_count, average_degree, utils::GetMemoryUsage(),
-          utils::GetDirDiskUsage(config_.durability.storage_directory)};
-}
-
-VerticesIterable Storage::Accessor::Vertices(LabelId label, View view) {
-  return VerticesIterable(storage_->indices_.label_index.Vertices(label, view, &transaction_));
-}
-
-VerticesIterable Storage::Accessor::Vertices(LabelId label, PropertyId property, View view) {
-  return VerticesIterable(storage_->indices_.label_property_index.Vertices(label, property, std::nullopt, std::nullopt,
-                                                                           view, &transaction_));
-}
-
-VerticesIterable Storage::Accessor::Vertices(LabelId label, PropertyId property, const PropertyValue &value,
-                                             View view) {
-  return VerticesIterable(storage_->indices_.label_property_index.Vertices(
-      label, property, utils::MakeBoundInclusive(value), utils::MakeBoundInclusive(value), view, &transaction_));
-}
-
-VerticesIterable Storage::Accessor::Vertices(LabelId label, PropertyId property,
-                                             const std::optional<utils::Bound<PropertyValue>> &lower_bound,
-                                             const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) {
-  return VerticesIterable(
-      storage_->indices_.label_property_index.Vertices(label, property, lower_bound, upper_bound, view, &transaction_));
-}
-
-Transaction Storage::CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) {
-  // We acquire the transaction engine lock here because we access (and
-  // modify) the transaction engine variables (`transaction_id` and
-  // `timestamp`) below.
-  uint64_t transaction_id;
-  uint64_t start_timestamp;
-  {
-    std::lock_guard<utils::SpinLock> guard(engine_lock_);
-    transaction_id = transaction_id_++;
-    // Replica should have only read queries and the write queries
-    // can come from main instance with any past timestamp.
-    // To preserve snapshot isolation we set the start timestamp
-    // of any query on replica to the last commited transaction
-    // which is timestamp_ as only commit of transaction with writes
-    // can change the value of it.
-    if (replication_role_ == replication::ReplicationRole::REPLICA) {
-      start_timestamp = timestamp_;
-    } else {
-      start_timestamp = timestamp_++;
-    }
-  }
-  return {transaction_id, start_timestamp, isolation_level, storage_mode};
-}
-
-template <bool force>
-void Storage::CollectGarbage(std::unique_lock<utils::RWLock> main_guard) {
-  // NOTE: You do not need to consider cleanup of deleted object that occurred in
-  // different storage modes within the same CollectGarbage call. This is because
-  // SetStorageMode will ensure CollectGarbage is called before any new transactions
-  // with the new storage mode can start.
-
-  // SetStorageMode will pass its unique_lock of main_lock_. We will use that lock,
-  // as reacquiring the lock would cause  deadlock. Otherwise, we need to get our own
-  // lock.
-  if (!main_guard.owns_lock()) {
-    if constexpr (force) {
-      // We take the unique lock on the main storage lock, so we can forcefully clean
-      // everything we can
-      if (!main_lock_.try_lock()) {
-        CollectGarbage<false>();
-        return;
-      }
-    } else {
-      // Because the garbage collector iterates through the indices and constraints
-      // to clean them up, it must take the main lock for reading to make sure that
-      // the indices and constraints aren't concurrently being modified.
-      main_lock_.lock_shared();
-    }
-  } else {
-    MG_ASSERT(main_guard.mutex() == std::addressof(main_lock_), "main_guard should be only for the main_lock_");
-  }
-
-  utils::OnScopeExit lock_releaser{[&] {
-    if (!main_guard.owns_lock()) {
-      if constexpr (force) {
-        main_lock_.unlock();
-      } else {
-        main_lock_.unlock_shared();
-      }
-    } else {
-      main_guard.unlock();
-    }
-  }};
-
-  // Garbage collection must be performed in two phases. In the first phase,
-  // deltas that won't be applied by any transaction anymore are unlinked from
-  // the version chains. They cannot be deleted immediately, because there
-  // might be a transaction that still needs them to terminate the version
-  // chain traversal. They are instead marked for deletion and will be deleted
-  // in the second GC phase in this GC iteration or some of the following
-  // ones.
-  std::unique_lock<std::mutex> gc_guard(gc_lock_, std::try_to_lock);
-  if (!gc_guard.owns_lock()) {
-    return;
-  }
-
-  uint64_t oldest_active_start_timestamp = commit_log_->OldestActive();
-  // We don't move undo buffers of unlinked transactions to garbage_undo_buffers
-  // list immediately, because we would have to repeatedly take
-  // garbage_undo_buffers lock.
-  std::list<std::pair<uint64_t, std::list<Delta>>> unlinked_undo_buffers;
-
-  // We will only free vertices deleted up until now in this GC cycle, and we
-  // will do it after cleaning-up the indices. That way we are sure that all
-  // vertices that appear in an index also exist in main storage.
-  std::list<Gid> current_deleted_edges;
-  std::list<Gid> current_deleted_vertices;
-  deleted_vertices_->swap(current_deleted_vertices);
-  deleted_edges_->swap(current_deleted_edges);
-
-  auto const need_full_scan_vertices = gc_full_scan_vertices_delete_.exchange(false);
-  auto const need_full_scan_edges = gc_full_scan_edges_delete_.exchange(false);
-
-  // Flag that will be used to determine whether the Index GC should be run. It
-  // should be run when there were any items that were cleaned up (there were
-  // updates between this run of the GC and the previous run of the GC). This
-  // eliminates high CPU usage when the GC doesn't have to clean up anything.
-  bool run_index_cleanup = !committed_transactions_->empty() || !garbage_undo_buffers_->empty() ||
-                           need_full_scan_vertices || need_full_scan_edges;
-
-  while (true) {
-    // We don't want to hold the lock on committed transactions for too long,
-    // because that prevents other transactions from committing.
-    Transaction *transaction;
-    {
-      auto committed_transactions_ptr = committed_transactions_.Lock();
-      if (committed_transactions_ptr->empty()) {
-        break;
-      }
-      transaction = &committed_transactions_ptr->front();
-    }
-
-    auto commit_timestamp = transaction->commit_timestamp->load(std::memory_order_acquire);
-    if (commit_timestamp >= oldest_active_start_timestamp) {
-      break;
-    }
-
-    // When unlinking a delta which is the first delta in its version chain,
-    // special care has to be taken to avoid the following race condition:
-    //
-    // [Vertex] --> [Delta A]
-    //
-    //    GC thread: Delta A is the first in its chain, it must be unlinked from
-    //               vertex and marked for deletion
-    //    TX thread: Update vertex and add Delta B with Delta A as next
-    //
-    // [Vertex] --> [Delta B] <--> [Delta A]
-    //
-    //    GC thread: Unlink delta from Vertex
-    //
-    // [Vertex] --> (nullptr)
-    //
-    // When processing a delta that is the first one in its chain, we
-    // obtain the corresponding vertex or edge lock, and then verify that this
-    // delta still is the first in its chain.
-    // When processing a delta that is in the middle of the chain we only
-    // process the final delta of the given transaction in that chain. We
-    // determine the owner of the chain (either a vertex or an edge), obtain the
-    // corresponding lock, and then verify that this delta is still in the same
-    // position as it was before taking the lock.
-    //
-    // Even though the delta chain is lock-free (both `next` and `prev`) the
-    // chain should not be modified without taking the lock from the object that
-    // owns the chain (either a vertex or an edge). Modifying the chain without
-    // taking the lock will cause subtle race conditions that will leave the
-    // chain in a broken state.
-    // The chain can be only read without taking any locks.
-
-    for (Delta &delta : transaction->deltas) {
-      while (true) {
-        auto prev = delta.prev.Get();
-        switch (prev.type) {
-          case PreviousPtr::Type::VERTEX: {
-            Vertex *vertex = prev.vertex;
-            std::lock_guard<utils::SpinLock> vertex_guard(vertex->lock);
-            if (vertex->delta != &delta) {
-              // Something changed, we're not the first delta in the chain
-              // anymore.
-              continue;
-            }
-            vertex->delta = nullptr;
-            if (vertex->deleted) {
-              current_deleted_vertices.push_back(vertex->gid);
-            }
-            break;
-          }
-          case PreviousPtr::Type::EDGE: {
-            Edge *edge = prev.edge;
-            std::lock_guard<utils::SpinLock> edge_guard(edge->lock);
-            if (edge->delta != &delta) {
-              // Something changed, we're not the first delta in the chain
-              // anymore.
-              continue;
-            }
-            edge->delta = nullptr;
-            if (edge->deleted) {
-              current_deleted_edges.push_back(edge->gid);
-            }
-            break;
-          }
-          case PreviousPtr::Type::DELTA: {
-            if (prev.delta->timestamp->load(std::memory_order_acquire) == commit_timestamp) {
-              // The delta that is newer than this one is also a delta from this
-              // transaction. We skip the current delta and will remove it as a
-              // part of the suffix later.
-              break;
-            }
-            std::unique_lock<utils::SpinLock> guard;
-            {
-              // We need to find the parent object in order to be able to use
-              // its lock.
-              auto parent = prev;
-              while (parent.type == PreviousPtr::Type::DELTA) {
-                parent = parent.delta->prev.Get();
-              }
-              switch (parent.type) {
-                case PreviousPtr::Type::VERTEX:
-                  guard = std::unique_lock<utils::SpinLock>(parent.vertex->lock);
-                  break;
-                case PreviousPtr::Type::EDGE:
-                  guard = std::unique_lock<utils::SpinLock>(parent.edge->lock);
-                  break;
-                case PreviousPtr::Type::DELTA:
-                case PreviousPtr::Type::NULLPTR:
-                  LOG_FATAL("Invalid database state!");
-              }
-            }
-            if (delta.prev.Get() != prev) {
-              // Something changed, we could now be the first delta in the
-              // chain.
-              continue;
-            }
-            Delta *prev_delta = prev.delta;
-            prev_delta->next.store(nullptr, std::memory_order_release);
-            break;
-          }
-          case PreviousPtr::Type::NULLPTR: {
-            LOG_FATAL("Invalid pointer!");
-          }
-        }
-        break;
-      }
-    }
-
-    committed_transactions_.WithLock([&](auto &committed_transactions) {
-      unlinked_undo_buffers.emplace_back(0, std::move(transaction->deltas));
-      committed_transactions.pop_front();
-    });
-  }
-
-  // After unlinking deltas from vertices, we refresh the indices. That way
-  // we're sure that none of the vertices from `current_deleted_vertices`
-  // appears in an index, and we can safely remove the from the main storage
-  // after the last currently active transaction is finished.
-  if (run_index_cleanup) {
-    // This operation is very expensive as it traverses through all of the items
-    // in every index every time.
-    RemoveObsoleteEntries(&indices_, oldest_active_start_timestamp);
-    constraints_.unique_constraints.RemoveObsoleteEntries(oldest_active_start_timestamp);
-  }
-
-  {
-    std::unique_lock<utils::SpinLock> guard(engine_lock_);
-    uint64_t mark_timestamp = timestamp_;
-    // Take garbage_undo_buffers lock while holding the engine lock to make
-    // sure that entries are sorted by mark timestamp in the list.
-    garbage_undo_buffers_.WithLock([&](auto &garbage_undo_buffers) {
-      // Release engine lock because we don't have to hold it anymore and
-      // this could take a long time.
-      guard.unlock();
-      // TODO(mtomic): holding garbage_undo_buffers_ lock here prevents
-      // transactions from aborting until we're done marking, maybe we should
-      // add them one-by-one or something
-      for (auto &[timestamp, undo_buffer] : unlinked_undo_buffers) {
-        timestamp = mark_timestamp;
-      }
-      garbage_undo_buffers.splice(garbage_undo_buffers.end(), unlinked_undo_buffers);
-    });
-    for (auto vertex : current_deleted_vertices) {
-      garbage_vertices_.emplace_back(mark_timestamp, vertex);
-    }
-  }
-
-  garbage_undo_buffers_.WithLock([&](auto &undo_buffers) {
-    // if force is set to true we can simply delete all the leftover undos because
-    // no transaction is active
-    if constexpr (force) {
-      undo_buffers.clear();
-    } else {
-      while (!undo_buffers.empty() && undo_buffers.front().first <= oldest_active_start_timestamp) {
-        undo_buffers.pop_front();
-      }
-    }
-  });
-
-  {
-    auto vertex_acc = vertices_.access();
-    if constexpr (force) {
-      // if force is set to true, then we have unique_lock and no transactions are active
-      // so we can clean all of the deleted vertices
-      while (!garbage_vertices_.empty()) {
-        MG_ASSERT(vertex_acc.remove(garbage_vertices_.front().second), "Invalid database state!");
-        garbage_vertices_.pop_front();
-      }
-    } else {
-      while (!garbage_vertices_.empty() && garbage_vertices_.front().first < oldest_active_start_timestamp) {
-        MG_ASSERT(vertex_acc.remove(garbage_vertices_.front().second), "Invalid database state!");
-        garbage_vertices_.pop_front();
-      }
-    }
-  }
-  {
-    auto edge_acc = edges_.access();
-    for (auto edge : current_deleted_edges) {
-      MG_ASSERT(edge_acc.remove(edge), "Invalid database state!");
-    }
-  }
-
-  // EXPENSIVE full scan, is only run if an IN_MEMORY_ANALYTICAL transaction involved any deletions
-  // TODO: implement a fast internal iteration inside the skip_list (to avoid unnecessary find_node calls),
-  //  accessor.remove_if([](auto const & item){ return item.delta == nullptr && item.deleted;});
-  //  alternatively, an auxiliary data structure within skip_list to track these, hence a full scan wouldn't be needed
-  //  we will wait for evidence that this is needed before doing so.
-  if (need_full_scan_vertices) {
-    auto vertex_acc = vertices_.access();
-    for (auto &vertex : vertex_acc) {
-      // a deleted vertex which as no deltas must have come from IN_MEMORY_ANALYTICAL deletion
-      if (vertex.delta == nullptr && vertex.deleted) {
-        vertex_acc.remove(vertex);
-      }
-    }
-  }
-
-  // EXPENSIVE full scan, is only run if an IN_MEMORY_ANALYTICAL transaction involved any deletions
-  if (need_full_scan_edges) {
-    auto edge_acc = edges_.access();
-    for (auto &edge : edge_acc) {
-      // a deleted edge which as no deltas must have come from IN_MEMORY_ANALYTICAL deletion
-      if (edge.delta == nullptr && edge.deleted) {
-        edge_acc.remove(edge);
-      }
-    }
+/// Main lock is taken by the caller.
+void Storage::SetStorageMode(StorageMode storage_mode) {
+  std::unique_lock main_guard{main_lock_};
+  MG_ASSERT(
+      (storage_mode_ == StorageMode::IN_MEMORY_ANALYTICAL || storage_mode_ == StorageMode::IN_MEMORY_TRANSACTIONAL) &&
+      (storage_mode == StorageMode::IN_MEMORY_ANALYTICAL || storage_mode == StorageMode::IN_MEMORY_TRANSACTIONAL));
+  if (storage_mode_ != storage_mode) {
+    storage_mode_ = storage_mode;
+    FreeMemory(std::move(main_guard));
   }
 }
 
-// tell the linker it can find the CollectGarbage definitions here
-template void Storage::CollectGarbage<true>(std::unique_lock<utils::RWLock>);
-template void Storage::CollectGarbage<false>(std::unique_lock<utils::RWLock>);
+IsolationLevel Storage::GetIsolationLevel() const noexcept { return isolation_level_; }
 
-bool Storage::InitializeWalFile() {
-  if (config_.durability.snapshot_wal_mode != Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL)
-    return false;
-  if (!wal_file_) {
-    wal_file_.emplace(wal_directory_, uuid_, epoch_id_, config_.items, &name_id_mapper_, wal_seq_num_++,
-                      &file_retainer_);
-  }
-  return true;
-}
-
-void Storage::FinalizeWalFile() {
-  ++wal_unsynced_transactions_;
-  if (wal_unsynced_transactions_ >= config_.durability.wal_file_flush_every_n_tx) {
-    wal_file_->Sync();
-    wal_unsynced_transactions_ = 0;
-  }
-  if (wal_file_->GetSize() / 1024 >= config_.durability.wal_file_size_kibibytes) {
-    wal_file_->FinalizeWal();
-    wal_file_ = std::nullopt;
-    wal_unsynced_transactions_ = 0;
-  } else {
-    // Try writing the internal buffer if possible, if not
-    // the data should be written as soon as it's possible
-    // (triggered by the new transaction commit, or some
-    // reading thread EnabledFlushing)
-    wal_file_->TryFlushing();
-  }
-}
-
-bool Storage::AppendToWalDataManipulation(const Transaction &transaction, uint64_t final_commit_timestamp) {
-  if (!InitializeWalFile()) {
-    return true;
-  }
-  // Traverse deltas and append them to the WAL file.
-  // A single transaction will always be contained in a single WAL file.
-  auto current_commit_timestamp = transaction.commit_timestamp->load(std::memory_order_acquire);
-
-  if (replication_role_.load() == replication::ReplicationRole::MAIN) {
-    replication_clients_.WithLock([&](auto &clients) {
-      for (auto &client : clients) {
-        client->StartTransactionReplication(wal_file_->SequenceNumber());
-      }
-    });
-  }
-
-  // Helper lambda that traverses the delta chain on order to find the first
-  // delta that should be processed and then appends all discovered deltas.
-  auto find_and_apply_deltas = [&](const auto *delta, const auto &parent, auto filter) {
-    while (true) {
-      auto older = delta->next.load(std::memory_order_acquire);
-      if (older == nullptr || older->timestamp->load(std::memory_order_acquire) != current_commit_timestamp) break;
-      delta = older;
-    }
-    while (true) {
-      if (filter(delta->action)) {
-        wal_file_->AppendDelta(*delta, parent, final_commit_timestamp);
-        replication_clients_.WithLock([&](auto &clients) {
-          for (auto &client : clients) {
-            client->IfStreamingTransaction(
-                [&](auto &stream) { stream.AppendDelta(*delta, parent, final_commit_timestamp); });
-          }
-        });
-      }
-      auto prev = delta->prev.Get();
-      MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
-      if (prev.type != PreviousPtr::Type::DELTA) break;
-      delta = prev.delta;
-    }
-  };
-
-  // The deltas are ordered correctly in the `transaction.deltas` buffer, but we
-  // don't traverse them in that order. That is because for each delta we need
-  // information about the vertex or edge they belong to and that information
-  // isn't stored in the deltas themselves. In order to find out information
-  // about the corresponding vertex or edge it is necessary to traverse the
-  // delta chain for each delta until a vertex or edge is encountered. This
-  // operation is very expensive as the chain grows.
-  // Instead, we traverse the edges until we find a vertex or edge and traverse
-  // their delta chains. This approach has a drawback because we lose the
-  // correct order of the operations. Because of that, we need to traverse the
-  // deltas several times and we have to manually ensure that the stored deltas
-  // will be ordered correctly.
-
-  // 1. Process all Vertex deltas and store all operations that create vertices
-  // and modify vertex data.
-  for (const auto &delta : transaction.deltas) {
-    auto prev = delta.prev.Get();
-    MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
-    if (prev.type != PreviousPtr::Type::VERTEX) continue;
-    find_and_apply_deltas(&delta, *prev.vertex, [](auto action) {
-      switch (action) {
-        case Delta::Action::DELETE_OBJECT:
-        case Delta::Action::SET_PROPERTY:
-        case Delta::Action::ADD_LABEL:
-        case Delta::Action::REMOVE_LABEL:
-          return true;
-
-        case Delta::Action::RECREATE_OBJECT:
-        case Delta::Action::ADD_IN_EDGE:
-        case Delta::Action::ADD_OUT_EDGE:
-        case Delta::Action::REMOVE_IN_EDGE:
-        case Delta::Action::REMOVE_OUT_EDGE:
-          return false;
-      }
-    });
-  }
-  // 2. Process all Vertex deltas and store all operations that create edges.
-  for (const auto &delta : transaction.deltas) {
-    auto prev = delta.prev.Get();
-    MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
-    if (prev.type != PreviousPtr::Type::VERTEX) continue;
-    find_and_apply_deltas(&delta, *prev.vertex, [](auto action) {
-      switch (action) {
-        case Delta::Action::REMOVE_OUT_EDGE:
-          return true;
-
-        case Delta::Action::DELETE_OBJECT:
-        case Delta::Action::RECREATE_OBJECT:
-        case Delta::Action::SET_PROPERTY:
-        case Delta::Action::ADD_LABEL:
-        case Delta::Action::REMOVE_LABEL:
-        case Delta::Action::ADD_IN_EDGE:
-        case Delta::Action::ADD_OUT_EDGE:
-        case Delta::Action::REMOVE_IN_EDGE:
-          return false;
-      }
-    });
-  }
-  // 3. Process all Edge deltas and store all operations that modify edge data.
-  for (const auto &delta : transaction.deltas) {
-    auto prev = delta.prev.Get();
-    MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
-    if (prev.type != PreviousPtr::Type::EDGE) continue;
-    find_and_apply_deltas(&delta, *prev.edge, [](auto action) {
-      switch (action) {
-        case Delta::Action::SET_PROPERTY:
-          return true;
-
-        case Delta::Action::DELETE_OBJECT:
-        case Delta::Action::RECREATE_OBJECT:
-        case Delta::Action::ADD_LABEL:
-        case Delta::Action::REMOVE_LABEL:
-        case Delta::Action::ADD_IN_EDGE:
-        case Delta::Action::ADD_OUT_EDGE:
-        case Delta::Action::REMOVE_IN_EDGE:
-        case Delta::Action::REMOVE_OUT_EDGE:
-          return false;
-      }
-    });
-  }
-  // 4. Process all Vertex deltas and store all operations that delete edges.
-  for (const auto &delta : transaction.deltas) {
-    auto prev = delta.prev.Get();
-    MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
-    if (prev.type != PreviousPtr::Type::VERTEX) continue;
-    find_and_apply_deltas(&delta, *prev.vertex, [](auto action) {
-      switch (action) {
-        case Delta::Action::ADD_OUT_EDGE:
-          return true;
-
-        case Delta::Action::DELETE_OBJECT:
-        case Delta::Action::RECREATE_OBJECT:
-        case Delta::Action::SET_PROPERTY:
-        case Delta::Action::ADD_LABEL:
-        case Delta::Action::REMOVE_LABEL:
-        case Delta::Action::ADD_IN_EDGE:
-        case Delta::Action::REMOVE_IN_EDGE:
-        case Delta::Action::REMOVE_OUT_EDGE:
-          return false;
-      }
-    });
-  }
-  // 5. Process all Vertex deltas and store all operations that delete vertices.
-  for (const auto &delta : transaction.deltas) {
-    auto prev = delta.prev.Get();
-    MG_ASSERT(prev.type != PreviousPtr::Type::NULLPTR, "Invalid pointer!");
-    if (prev.type != PreviousPtr::Type::VERTEX) continue;
-    find_and_apply_deltas(&delta, *prev.vertex, [](auto action) {
-      switch (action) {
-        case Delta::Action::RECREATE_OBJECT:
-          return true;
-
-        case Delta::Action::DELETE_OBJECT:
-        case Delta::Action::SET_PROPERTY:
-        case Delta::Action::ADD_LABEL:
-        case Delta::Action::REMOVE_LABEL:
-        case Delta::Action::ADD_IN_EDGE:
-        case Delta::Action::ADD_OUT_EDGE:
-        case Delta::Action::REMOVE_IN_EDGE:
-        case Delta::Action::REMOVE_OUT_EDGE:
-          return false;
-      }
-    });
-  }
-
-  // Add a delta that indicates that the transaction is fully written to the WAL
-  // file.
-  wal_file_->AppendTransactionEnd(final_commit_timestamp);
-
-  FinalizeWalFile();
-
-  auto finalized_on_all_replicas = true;
-  replication_clients_.WithLock([&](auto &clients) {
-    for (auto &client : clients) {
-      client->IfStreamingTransaction([&](auto &stream) { stream.AppendTransactionEnd(final_commit_timestamp); });
-      const auto finalized = client->FinalizeTransactionReplication();
-
-      if (client->Mode() == replication::ReplicationMode::SYNC) {
-        finalized_on_all_replicas = finalized && finalized_on_all_replicas;
-      }
-    }
-  });
-
-  return finalized_on_all_replicas;
-}
-
-bool Storage::AppendToWalDataDefinition(durability::StorageGlobalOperation operation, LabelId label,
-                                        const std::set<PropertyId> &properties, uint64_t final_commit_timestamp) {
-  if (!InitializeWalFile()) {
-    return true;
-  }
-
-  auto finalized_on_all_replicas = true;
-  wal_file_->AppendOperation(operation, label, properties, final_commit_timestamp);
-  {
-    if (replication_role_.load() == replication::ReplicationRole::MAIN) {
-      replication_clients_.WithLock([&](auto &clients) {
-        for (auto &client : clients) {
-          client->StartTransactionReplication(wal_file_->SequenceNumber());
-          client->IfStreamingTransaction(
-              [&](auto &stream) { stream.AppendOperation(operation, label, properties, final_commit_timestamp); });
-
-          const auto finalized = client->FinalizeTransactionReplication();
-          if (client->Mode() == replication::ReplicationMode::SYNC) {
-            finalized_on_all_replicas = finalized && finalized_on_all_replicas;
-          }
-        }
-      });
-    }
-  }
-  FinalizeWalFile();
-  return finalized_on_all_replicas;
-}
-
-utils::BasicResult<Storage::CreateSnapshotError> Storage::CreateSnapshot(std::optional<bool> is_periodic) {
-  if (replication_role_.load() != replication::ReplicationRole::MAIN) {
-    return CreateSnapshotError::DisabledForReplica;
-  }
-
-  auto snapshot_creator = [this]() {
-    utils::Timer timer;
-
-    auto transaction = CreateTransaction(IsolationLevel::SNAPSHOT_ISOLATION, storage_mode_);
-    // Create snapshot.
-    durability::CreateSnapshot(&transaction, snapshot_directory_, wal_directory_,
-                               config_.durability.snapshot_retention_count, &vertices_, &edges_, &name_id_mapper_,
-                               &indices_, &constraints_, config_, uuid_, epoch_id_, epoch_history_, &file_retainer_);
-    // Finalize snapshot transaction.
-    commit_log_->MarkFinished(transaction.start_timestamp);
-
-    memgraph::metrics::Measure(memgraph::metrics::SnapshotCreationLatency_us,
-                               std::chrono::duration_cast<std::chrono::microseconds>(timer.Elapsed()).count());
-  };
-
-  std::lock_guard snapshot_guard(snapshot_lock_);
-
-  auto should_try_shared{true};
-  auto max_num_tries{10};
-  while (max_num_tries) {
-    if (should_try_shared) {
-      std::shared_lock<utils::RWLock> storage_guard(main_lock_);
-      if (storage_mode_ == memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL) {
-        snapshot_creator();
-        return {};
-      }
-    } else {
-      std::unique_lock main_guard{main_lock_};
-      if (storage_mode_ == memgraph::storage::StorageMode::IN_MEMORY_ANALYTICAL) {
-        if (is_periodic && *is_periodic) {
-          return CreateSnapshotError::DisabledForAnalyticsPeriodicCommit;
-        }
-        snapshot_creator();
-        return {};
-      }
-    }
-    should_try_shared = !should_try_shared;
-    max_num_tries--;
-  }
-
-  return CreateSnapshotError::ReachedMaxNumTries;
-}
-
-utils::FileRetainer::FileLockerAccessor::ret_type Storage::IsPathLocked() {
-  auto locker_accessor = global_locker_.Access();
-  return locker_accessor.IsPathLocked(config_.durability.storage_directory);
-}
-
-utils::FileRetainer::FileLockerAccessor::ret_type Storage::LockPath() {
-  auto locker_accessor = global_locker_.Access();
-  return locker_accessor.AddPath(config_.durability.storage_directory);
-}
-
-utils::FileRetainer::FileLockerAccessor::ret_type Storage::UnlockPath() {
-  {
-    auto locker_accessor = global_locker_.Access();
-    const auto ret = locker_accessor.RemovePath(config_.durability.storage_directory);
-    if (ret.HasError() || !ret.GetValue()) {
-      // Exit without cleaning the queue
-      return ret;
-    }
-  }
-
-  // We use locker accessor in seperate scope so we don't produce deadlock
-  // after we call clean queue.
-  file_retainer_.CleanQueue();
-  return true;
-}
-
-void Storage::FreeMemory(std::unique_lock<utils::RWLock> main_guard) {
-  CollectGarbage<true>(std::move(main_guard));
-
-  // SkipList is already threadsafe
-  vertices_.run_gc();
-  edges_.run_gc();
-  indices_.label_index.RunGC();
-  indices_.label_property_index.RunGC();
-}
-
-uint64_t Storage::CommitTimestamp(const std::optional<uint64_t> desired_commit_timestamp) {
-  if (!desired_commit_timestamp) {
-    return timestamp_++;
-  } else {
-    timestamp_ = std::max(timestamp_, *desired_commit_timestamp + 1);
-    return *desired_commit_timestamp;
-  }
-}
-
-bool Storage::SetReplicaRole(io::network::Endpoint endpoint, const replication::ReplicationServerConfig &config) {
-  spdlog::trace("Setting role to replica...");
-  // We don't want to restart the server if we're already a REPLICA
-  if (replication_role_ == replication::ReplicationRole::REPLICA) {
-    return false;
-  }
-
-  auto port = endpoint.port;  // assigning because we will move the endpoint
-  replication_server_ = std::make_unique<ReplicationServer>(this, std::move(endpoint), config);
-
-  if (ShouldStoreAndRestoreReplicationState()) {
-    // Only thing that matters here is the role saved as REPLICA and the listening port
-    auto data = replication::ReplicationStatusToJSON(
-        replication::ReplicationStatus{.name = replication::kReservedReplicationRoleName,
-                                       .ip_address = "",
-                                       .port = port,
-                                       .sync_mode = replication::ReplicationMode::SYNC,
-                                       .replica_check_frequency = std::chrono::seconds(0),
-                                       .ssl = std::nullopt,
-                                       .role = replication::ReplicationRole::REPLICA});
-
-    if (!storage_->Put(replication::kReservedReplicationRoleName, data.dump())) {
-      spdlog::error("Error when saving REPLICA replication role in settings.");
-      return false;
-    }
-  }
-
-  replication_role_.store(replication::ReplicationRole::REPLICA);
-  return true;
-}
-
-bool Storage::SetMainReplicationRole() {
-  spdlog::trace("Setting main role...");
-  // We don't want to generate new epoch_id and do the
-  // cleanup if we're already a MAIN
-  if (replication_role_ == replication::ReplicationRole::MAIN) {
-    return false;
-  }
-
-  // Main instance does not need replication server
-  // This should be always called first so we finalize everything
-  replication_server_.reset(nullptr);
-
-  {
-    std::unique_lock engine_guard{engine_lock_};
-    if (wal_file_) {
-      wal_file_->FinalizeWal();
-      wal_file_.reset();
-    }
-
-    // Generate new epoch id and save the last one to the history.
-    if (epoch_history_.size() == kEpochHistoryRetention) {
-      epoch_history_.pop_front();
-    }
-    epoch_history_.emplace_back(std::move(epoch_id_), last_commit_timestamp_);
-    epoch_id_ = utils::GenerateUUID();
-  }
-
-  if (ShouldStoreAndRestoreReplicationState()) {
-    // Only thing that matters here is the role saved as MAIN
-    auto data = replication::ReplicationStatusToJSON(
-        replication::ReplicationStatus{.name = replication::kReservedReplicationRoleName,
-                                       .ip_address = "",
-                                       .port = 0,
-                                       .sync_mode = replication::ReplicationMode::SYNC,
-                                       .replica_check_frequency = std::chrono::seconds(0),
-                                       .ssl = std::nullopt,
-                                       .role = replication::ReplicationRole::MAIN});
-
-    if (!storage_->Put(replication::kReservedReplicationRoleName, data.dump())) {
-      spdlog::error("Error when saving MAIN replication role in settings.");
-      return false;
-    }
-  }
-  spdlog::info("Instance is now in a MAIN role.");
-  replication_role_.store(replication::ReplicationRole::MAIN);
-
-  return true;
-}
-
-utils::BasicResult<Storage::RegisterReplicaError> Storage::RegisterReplica(
-    std::string name, io::network::Endpoint endpoint, const replication::ReplicationMode replication_mode,
-    const replication::RegistrationMode registration_mode, const replication::ReplicationClientConfig &config) {
-
-  MG_ASSERT(replication_role_.load() == replication::ReplicationRole::MAIN,
-            "Only main instance can register a replica!");
-  spdlog::trace("Registering replica...");
-
-  const bool name_exists = replication_clients_.WithLock([&](auto &clients) {
-    return std::any_of(clients.begin(), clients.end(), [&name](const auto &client) { return client->Name() == name; });
-  });
-
-  if (name_exists) {
-    return RegisterReplicaError::NAME_EXISTS;
-  }
-
-  const auto end_point_exists = replication_clients_.WithLock([&endpoint](auto &clients) {
-    return std::any_of(clients.begin(), clients.end(),
-                       [&endpoint](const auto &client) { return client->Endpoint() == endpoint; });
-  });
-
-  if (end_point_exists) {
-    return RegisterReplicaError::END_POINT_EXISTS;
-  }
-
-  if (ShouldStoreAndRestoreReplicationState()) {
-    auto data = replication::ReplicationStatusToJSON(
-        replication::ReplicationStatus{.name = name,
-                                       .ip_address = endpoint.address,
-                                       .port = endpoint.port,
-                                       .sync_mode = replication_mode,
-                                       .replica_check_frequency = config.replica_check_frequency,
-                                       .ssl = config.ssl,
-                                       .role = replication::ReplicationRole::REPLICA});
-    if (!storage_->Put(name, data.dump())) {
-      spdlog::error("Error when saving replica {} in settings.", name);
-      return RegisterReplicaError::COULD_NOT_BE_PERSISTED;
-    }
-  }
-
-  auto client = std::make_unique<ReplicationClient>(std::move(name), this, endpoint, replication_mode, config);
-
-  if (client->State() == replication::ReplicaState::INVALID) {
-    if (replication::RegistrationMode::CAN_BE_INVALID != registration_mode) {
-      return RegisterReplicaError::CONNECTION_FAILED;
-    }
-
-    spdlog::warn("Connection failed when registering replica {}. Replica will still be registered.", client->Name());
-  }
-
-  return replication_clients_.WithLock([&](auto &clients) -> utils::BasicResult<Storage::RegisterReplicaError> {
-    // Another thread could have added a client with same name while
-    // we were connecting to this client.
-    if (std::any_of(clients.begin(), clients.end(),
-                    [&](const auto &other_client) { return client->Name() == other_client->Name(); })) {
-      return RegisterReplicaError::NAME_EXISTS;
-    }
-
-    if (std::any_of(clients.begin(), clients.end(),
-                    [&client](const auto &other_client) { return client->Endpoint() == other_client->Endpoint(); })) {
-      return RegisterReplicaError::END_POINT_EXISTS;
-    }
-
-    clients.push_back(std::move(client));
-    return {};
-  });
-  spdlog::info("Replica {} registered.", name);
-}
-
-bool Storage::UnregisterReplica(const std::string &name) {
-
-  spdlog::trace("Unregistering replica...");
-  MG_ASSERT(replication_role_.load() == replication::ReplicationRole::MAIN,
-            "Only main instance can unregister a replica!");
-  if (ShouldStoreAndRestoreReplicationState()) {
-    if (!storage_->Delete(name)) {
-      spdlog::error("Error when removing replica {} from settings.", name);
-      return false;
-    }
-  }
-
-  return replication_clients_.WithLock([&](auto &clients) {
-    return std::erase_if(clients, [&](const auto &client) { return client->Name() == name; });
-  });
-}
-
-std::optional<replication::ReplicaState> Storage::GetReplicaState(const std::string_view name) {
-  spdlog::trace("Getting replica state...");
-  return replication_clients_.WithLock([&](auto &clients) -> std::optional<replication::ReplicaState> {
-    const auto client_it =
-        std::find_if(clients.cbegin(), clients.cend(), [name](auto &client) { return client->Name() == name; });
-    if (client_it == clients.cend()) {
-      return std::nullopt;
-    }
-    return (*client_it)->State();
-  });
-}
-
-replication::ReplicationRole Storage::GetReplicationRole() const { return replication_role_; }
-
-std::vector<Storage::ReplicaInfo> Storage::ReplicasInfo() {
-  spdlog::trace("Getting replicas info...");
-  return replication_clients_.WithLock([](auto &clients) {
-    std::vector<Storage::ReplicaInfo> replica_info;
-    replica_info.reserve(clients.size());
-    std::transform(
-        clients.begin(), clients.end(), std::back_inserter(replica_info), [](const auto &client) -> ReplicaInfo {
-          return {client->Name(), client->Mode(), client->Endpoint(), client->State(), client->GetTimestampInfo()};
-        });
-    return replica_info;
-  });
-}
+StorageMode Storage::GetStorageMode() const { return storage_mode_; }
 
 utils::BasicResult<Storage::SetIsolationLevelError> Storage::SetIsolationLevel(IsolationLevel isolation_level) {
   std::unique_lock main_guard{main_lock_};
@@ -2328,97 +110,15 @@ utils::BasicResult<Storage::SetIsolationLevelError> Storage::SetIsolationLevel(I
   return {};
 }
 
-void Storage::RestoreReplicationRole() {
-  if (!ShouldStoreAndRestoreReplicationState()) {
-    return;
+StorageMode Storage::Accessor::GetCreationStorageMode() const { return creation_storage_mode_; }
+
+std::optional<uint64_t> Storage::Accessor::GetTransactionId() const {
+  if (is_transaction_active_) {
+    return transaction_.transaction_id.load(std::memory_order_acquire);
   }
-
-  spdlog::info("Restoring replication role.");
-
-  uint16_t port = replication::kDefaultReplicationPort;
-  for (const auto &[replica_name, replica_data] : *storage_) {
-    const auto maybe_replica_status = replication::JSONToReplicationStatus(nlohmann::json::parse(replica_data));
-    if (!maybe_replica_status.has_value()) {
-      LOG_FATAL("Cannot parse previously saved configuration of replica {}.", replica_name);
-    }
-
-    if (replica_name != replication::kReservedReplicationRoleName) {
-      continue;
-    }
-
-    auto replica_status = *maybe_replica_status;
-
-    if (!replica_status.role.has_value()) {
-      replication_role_.store(replication::ReplicationRole::MAIN);
-    } else {
-      replication_role_.store(*replica_status.role);
-      port = replica_status.port;
-    }
-
-    break;
-  }
-
-  if (replication_role_ == replication::ReplicationRole::REPLICA) {
-    io::network::Endpoint endpoint(replication::kDefaultReplicationServerIp, port);
-    replication_server_ =
-        std::make_unique<ReplicationServer>(this, std::move(endpoint), replication::ReplicationServerConfig{});
-  }
-
-  spdlog::info("Replication role restored to {}.",
-               replication_role_ == replication::ReplicationRole::MAIN ? "MAIN" : "REPLICA");
+  return {};
 }
 
-IsolationLevel Storage::GetIsolationLevel() const noexcept { return isolation_level_; }
-
-void Storage::SetStorageMode(StorageMode storage_mode) {
-  std::unique_lock main_guard{main_lock_};
-  // Only if we change storage_mode do we want to force storage cleanup
-  if (storage_mode_ != storage_mode) {
-    storage_mode_ = storage_mode;
-    FreeMemory(std::move(main_guard));
-  }
-}
-
-StorageMode Storage::GetStorageMode() { return storage_mode_; }
-
-void Storage::RestoreReplicas() {
-  if (!ShouldStoreAndRestoreReplicationState()) {
-    return;
-  }
-  spdlog::info("Restoring replicas.");
-
-  for (const auto &[replica_name, replica_data] : *storage_) {
-    spdlog::info("Restoring replica {}.", replica_name);
-
-    const auto maybe_replica_status = replication::JSONToReplicationStatus(nlohmann::json::parse(replica_data));
-    if (!maybe_replica_status.has_value()) {
-      LOG_FATAL("Cannot parse previously saved configuration of replica {}.", replica_name);
-    }
-
-    auto replica_status = *maybe_replica_status;
-    MG_ASSERT(replica_status.name == replica_name, "Expected replica name is '{}', but got '{}'", replica_status.name,
-              replica_name);
-
-    if (replica_name == replication::kReservedReplicationRoleName) {
-      continue;
-    }
-
-    auto ret =
-        RegisterReplica(std::move(replica_status.name), {std::move(replica_status.ip_address), replica_status.port},
-                        replica_status.sync_mode, replication::RegistrationMode::CAN_BE_INVALID,
-                        {
-                            .replica_check_frequency = replica_status.replica_check_frequency,
-                            .ssl = replica_status.ssl,
-                        });
-
-    if (ret.HasError()) {
-      MG_ASSERT(RegisterReplicaError::CONNECTION_FAILED != ret.GetError());
-      LOG_FATAL("Failure when restoring replica {}: {}.", replica_name, RegisterReplicaErrorToString(ret.GetError()));
-    }
-    spdlog::info("Replica {} restored.", replica_name);
-  }
-}
-
-bool Storage::ShouldStoreAndRestoreReplicationState() const { return nullptr != storage_; }
+void Storage::Accessor::AdvanceCommand() { ++transaction_.command_id; }
 
 }  // namespace memgraph::storage
diff --git a/src/storage/v2/storage.hpp b/src/storage/v2/storage.hpp
index 207ee5290..34985ccad 100644
--- a/src/storage/v2/storage.hpp
+++ b/src/storage/v2/storage.hpp
@@ -11,176 +11,50 @@
 
 #pragma once
 
-#include <atomic>
-#include <cstdint>
-#include <filesystem>
-#include <optional>
-#include <shared_mutex>
 #include <span>
-#include <variant>
 
 #include "io/network/endpoint.hpp"
 #include "kvstore/kvstore.hpp"
+#include "query/exceptions.hpp"
+#include "storage/v2/all_vertices_iterable.hpp"
 #include "storage/v2/commit_log.hpp"
 #include "storage/v2/config.hpp"
-#include "storage/v2/constraints.hpp"
-#include "storage/v2/durability/metadata.hpp"
+#include "storage/v2/durability/paths.hpp"
 #include "storage/v2/durability/wal.hpp"
-#include "storage/v2/edge.hpp"
 #include "storage/v2/edge_accessor.hpp"
-#include "storage/v2/indices.hpp"
-#include "storage/v2/isolation_level.hpp"
+#include "storage/v2/indices/indices.hpp"
 #include "storage/v2/mvcc.hpp"
-#include "storage/v2/name_id_mapper.hpp"
-#include "storage/v2/result.hpp"
+#include "storage/v2/storage_error.hpp"
 #include "storage/v2/storage_mode.hpp"
-#include "storage/v2/transaction.hpp"
-#include "storage/v2/vertex.hpp"
-#include "storage/v2/vertex_accessor.hpp"
-#include "utils/file_locker.hpp"
-#include "utils/on_scope_exit.hpp"
-#include "utils/result.hpp"
-#include "utils/rw_lock.hpp"
+#include "storage/v2/vertices_iterable.hpp"
+#include "utils/event_counter.hpp"
+#include "utils/event_histogram.hpp"
 #include "utils/scheduler.hpp"
-#include "utils/skip_list.hpp"
-#include "utils/synchronized.hpp"
+#include "utils/timer.hpp"
 #include "utils/uuid.hpp"
 
-/// REPLICATION ///
-#include "rpc/server.hpp"
-#include "storage/v2/replication/config.hpp"
-#include "storage/v2/replication/enums.hpp"
-#include "storage/v2/replication/replication_persistence_helper.hpp"
-#include "storage/v2/replication/rpc.hpp"
-#include "storage/v2/replication/serialization.hpp"
-#include "storage/v2/storage_error.hpp"
+namespace memgraph::metrics {
+extern const Event SnapshotCreationLatency_us;
+
+extern const Event ActiveLabelIndices;
+extern const Event ActiveLabelPropertyIndices;
+}  // namespace memgraph::metrics
 
 namespace memgraph::storage {
 
-// The storage is based on this paper:
-// https://db.in.tum.de/~muehlbau/papers/mvcc.pdf
-// The paper implements a fully serializable storage, in our implementation we
-// only implement snapshot isolation for transactions.
+struct Transaction;
+class EdgeAccessor;
 
-/// Iterable for iterating through all vertices of a Storage.
-///
-/// An instance of this will be usually be wrapped inside VerticesIterable for
-/// generic, public use.
-class AllVerticesIterable final {
-  utils::SkipList<Vertex>::Accessor vertices_accessor_;
-  Transaction *transaction_;
-  View view_;
-  Indices *indices_;
-  Constraints *constraints_;
-  Config::Items config_;
-  std::optional<VertexAccessor> vertex_;
-
- public:
-  class Iterator final {
-    AllVerticesIterable *self_;
-    utils::SkipList<Vertex>::Iterator it_;
-
-   public:
-    Iterator(AllVerticesIterable *self, utils::SkipList<Vertex>::Iterator it);
-
-    VertexAccessor operator*() const;
-
-    Iterator &operator++();
-
-    bool operator==(const Iterator &other) const { return self_ == other.self_ && it_ == other.it_; }
-
-    bool operator!=(const Iterator &other) const { return !(*this == other); }
-  };
-
-  AllVerticesIterable(utils::SkipList<Vertex>::Accessor vertices_accessor, Transaction *transaction, View view,
-                      Indices *indices, Constraints *constraints, Config::Items config)
-      : vertices_accessor_(std::move(vertices_accessor)),
-        transaction_(transaction),
-        view_(view),
-        indices_(indices),
-        constraints_(constraints),
-        config_(config) {}
-
-  Iterator begin() { return Iterator(this, vertices_accessor_.begin()); }
-  Iterator end() { return Iterator(this, vertices_accessor_.end()); }
-};
-
-/// Generic access to different kinds of vertex iterations.
-///
-/// This class should be the primary type used by the client code to iterate
-/// over vertices inside a Storage instance.
-class VerticesIterable final {
-  enum class Type { ALL, BY_LABEL, BY_LABEL_PROPERTY };
-
-  Type type_;
-  union {
-    AllVerticesIterable all_vertices_;
-    LabelIndex::Iterable vertices_by_label_;
-    LabelPropertyIndex::Iterable vertices_by_label_property_;
-  };
-
- public:
-  explicit VerticesIterable(AllVerticesIterable);
-  explicit VerticesIterable(LabelIndex::Iterable);
-  explicit VerticesIterable(LabelPropertyIndex::Iterable);
-
-  VerticesIterable(const VerticesIterable &) = delete;
-  VerticesIterable &operator=(const VerticesIterable &) = delete;
-
-  VerticesIterable(VerticesIterable &&) noexcept;
-  VerticesIterable &operator=(VerticesIterable &&) noexcept;
-
-  ~VerticesIterable();
-
-  class Iterator final {
-    Type type_;
-    union {
-      AllVerticesIterable::Iterator all_it_;
-      LabelIndex::Iterable::Iterator by_label_it_;
-      LabelPropertyIndex::Iterable::Iterator by_label_property_it_;
-    };
-
-    void Destroy() noexcept;
-
-   public:
-    explicit Iterator(AllVerticesIterable::Iterator);
-    explicit Iterator(LabelIndex::Iterable::Iterator);
-    explicit Iterator(LabelPropertyIndex::Iterable::Iterator);
-
-    Iterator(const Iterator &);
-    Iterator &operator=(const Iterator &);
-
-    Iterator(Iterator &&) noexcept;
-    Iterator &operator=(Iterator &&) noexcept;
-
-    ~Iterator();
-
-    VertexAccessor operator*() const;
-
-    Iterator &operator++();
-
-    bool operator==(const Iterator &other) const;
-    bool operator!=(const Iterator &other) const { return !(*this == other); }
-  };
-
-  Iterator begin();
-  Iterator end();
-};
-
-/// Structure used to return information about existing indices in the storage.
 struct IndicesInfo {
   std::vector<LabelId> label;
   std::vector<std::pair<LabelId, PropertyId>> label_property;
 };
 
-/// Structure used to return information about existing constraints in the
-/// storage.
 struct ConstraintsInfo {
   std::vector<std::pair<LabelId, PropertyId>> existence;
   std::vector<std::pair<LabelId, std::set<PropertyId>>> unique;
 };
 
-/// Structure used to return information about the storage.
 struct StorageInfo {
   uint64_t vertex_count;
   uint64_t edge_count;
@@ -189,541 +63,244 @@ struct StorageInfo {
   uint64_t disk_usage;
 };
 
-class Storage final {
+class Storage {
  public:
-  /// @throw std::system_error
-  /// @throw std::bad_alloc
-  explicit Storage(Config config = Config());
+  Storage(Config config, StorageMode storage_mode);
 
-  ~Storage();
+  Storage(const Storage &) = delete;
+  Storage(Storage &&) = delete;
+  Storage &operator=(const Storage &) = delete;
+  Storage &operator=(Storage &&) = delete;
 
-  class Accessor final {
-   private:
-    friend class Storage;
-
-    explicit Accessor(Storage *storage, IsolationLevel isolation_level, StorageMode storage_mode);
+  virtual ~Storage() {}
 
+  class Accessor {
    public:
+    Accessor(Storage *storage, IsolationLevel isolation_level, StorageMode storage_mode);
     Accessor(const Accessor &) = delete;
     Accessor &operator=(const Accessor &) = delete;
     Accessor &operator=(Accessor &&other) = delete;
 
-    // NOTE: After the accessor is moved, all objects derived from it (accessors
-    // and iterators) are *invalid*. You have to get all derived objects again.
     Accessor(Accessor &&other) noexcept;
 
-    ~Accessor();
+    virtual ~Accessor() {}
 
-    /// @throw std::bad_alloc
-    VertexAccessor CreateVertex();
+    virtual VertexAccessor CreateVertex() = 0;
 
-    std::optional<VertexAccessor> FindVertex(Gid gid, View view);
+    virtual std::optional<VertexAccessor> FindVertex(Gid gid, View view) = 0;
 
-    VerticesIterable Vertices(View view) {
-      return VerticesIterable(AllVerticesIterable(storage_->vertices_.access(), &transaction_, view,
-                                                  &storage_->indices_, &storage_->constraints_,
-                                                  storage_->config_.items));
-    }
+    virtual VerticesIterable Vertices(View view) = 0;
 
-    VerticesIterable Vertices(LabelId label, View view);
+    virtual VerticesIterable Vertices(LabelId label, View view) = 0;
 
-    VerticesIterable Vertices(LabelId label, PropertyId property, View view);
+    virtual VerticesIterable Vertices(LabelId label, PropertyId property, View view) = 0;
 
-    VerticesIterable Vertices(LabelId label, PropertyId property, const PropertyValue &value, View view);
+    virtual VerticesIterable Vertices(LabelId label, PropertyId property, const PropertyValue &value, View view) = 0;
 
-    VerticesIterable Vertices(LabelId label, PropertyId property,
-                              const std::optional<utils::Bound<PropertyValue>> &lower_bound,
-                              const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view);
+    virtual VerticesIterable Vertices(LabelId label, PropertyId property,
+                                      const std::optional<utils::Bound<PropertyValue>> &lower_bound,
+                                      const std::optional<utils::Bound<PropertyValue>> &upper_bound, View view) = 0;
 
-    /// Return approximate number of all vertices in the database.
-    /// Note that this is always an over-estimate and never an under-estimate.
-    int64_t ApproximateVertexCount() const { return storage_->vertices_.size(); }
+    virtual uint64_t ApproximateVertexCount() const = 0;
 
-    /// Return approximate number of vertices with the given label.
-    /// Note that this is always an over-estimate and never an under-estimate.
-    int64_t ApproximateVertexCount(LabelId label) const {
-      return storage_->indices_.label_index.ApproximateVertexCount(label);
-    }
+    virtual uint64_t ApproximateVertexCount(LabelId label) const = 0;
 
-    /// Return approximate number of vertices with the given label and property.
-    /// Note that this is always an over-estimate and never an under-estimate.
-    int64_t ApproximateVertexCount(LabelId label, PropertyId property) const {
-      return storage_->indices_.label_property_index.ApproximateVertexCount(label, property);
-    }
+    virtual uint64_t ApproximateVertexCount(LabelId label, PropertyId property) const = 0;
 
-    /// Return approximate number of vertices with the given label and the given
-    /// value for the given property. Note that this is always an over-estimate
-    /// and never an under-estimate.
-    int64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const {
-      return storage_->indices_.label_property_index.ApproximateVertexCount(label, property, value);
-    }
+    virtual uint64_t ApproximateVertexCount(LabelId label, PropertyId property, const PropertyValue &value) const = 0;
 
-    /// Return approximate number of vertices with the given label and value for
-    /// the given property in the range defined by provided upper and lower
-    /// bounds.
-    int64_t ApproximateVertexCount(LabelId label, PropertyId property,
-                                   const std::optional<utils::Bound<PropertyValue>> &lower,
-                                   const std::optional<utils::Bound<PropertyValue>> &upper) const {
-      return storage_->indices_.label_property_index.ApproximateVertexCount(label, property, lower, upper);
-    }
+    virtual uint64_t ApproximateVertexCount(LabelId label, PropertyId property,
+                                            const std::optional<utils::Bound<PropertyValue>> &lower,
+                                            const std::optional<utils::Bound<PropertyValue>> &upper) const = 0;
 
-    template <typename TResult, typename TIndex, typename TIndexKey>
-    std::optional<TResult> GetIndexStatsForIndex(TIndex &index, TIndexKey &&key) const {
-      return index.GetIndexStats(key);
-    }
+    virtual std::optional<storage::LabelIndexStats> GetIndexStats(const storage::LabelId &label) const = 0;
 
-    std::optional<storage::LabelIndexStats> GetIndexStats(const storage::LabelId &label) const {
-      return GetIndexStatsForIndex<storage::LabelIndexStats>(storage_->indices_.label_index, label);
-    }
+    virtual std::optional<storage::LabelPropertyIndexStats> GetIndexStats(
+        const storage::LabelId &label, const storage::PropertyId &property) const = 0;
 
-    std::optional<storage::LabelPropertyIndexStats> GetIndexStats(const storage::LabelId &label,
-                                                                  const storage::PropertyId &property) const {
-      return GetIndexStatsForIndex<storage::LabelPropertyIndexStats>(storage_->indices_.label_property_index,
-                                                                     std::make_pair(label, property));
-    }
+    virtual void SetIndexStats(const storage::LabelId &label, const LabelIndexStats &stats) = 0;
 
-    template <typename TIndex, typename TIndexKey, typename TIndexStats>
-    void SetIndexStatsForIndex(TIndex &index, TIndexKey &&key, TIndexStats &stats) const {
-      index.SetIndexStats(key, stats);
-    }
+    virtual void SetIndexStats(const storage::LabelId &label, const storage::PropertyId &property,
+                               const LabelPropertyIndexStats &stats) = 0;
 
-    void SetIndexStats(const storage::LabelId &label, const LabelIndexStats &stats) {
-      SetIndexStatsForIndex(storage_->indices_.label_index, label, stats);
-    }
+    virtual std::vector<std::pair<LabelId, PropertyId>> ClearLabelPropertyIndexStats() = 0;
 
-    void SetIndexStats(const storage::LabelId &label, const storage::PropertyId &property,
-                       const LabelPropertyIndexStats &stats) {
-      SetIndexStatsForIndex(storage_->indices_.label_property_index, std::make_pair(label, property), stats);
-    }
+    virtual std::vector<LabelId> ClearLabelIndexStats() = 0;
 
-    template <typename TResult, typename TIndex>
-    std::vector<TResult> ClearIndexStatsForIndex(TIndex &index) const {
-      return index.ClearIndexStats();
-    }
+    virtual std::vector<std::pair<LabelId, PropertyId>> DeleteLabelPropertyIndexStats(
+        std::span<std::string> labels) = 0;
 
-    std::vector<std::pair<LabelId, PropertyId>> ClearLabelPropertyIndexStats() {
-      return ClearIndexStatsForIndex<std::pair<LabelId, PropertyId>>(storage_->indices_.label_property_index);
-    }
+    virtual std::vector<LabelId> DeleteLabelIndexStats(std::span<std::string> labels) = 0;
 
-    std::vector<LabelId> ClearLabelIndexStats() {
-      return ClearIndexStatsForIndex<LabelId>(storage_->indices_.label_index);
-    }
+    virtual Result<std::optional<VertexAccessor>> DeleteVertex(VertexAccessor *vertex) = 0;
 
-    template <typename TResult, typename TIndex>
-    std::vector<TResult> DeleteIndexStatsForIndex(TIndex &index, const std::span<std::string> labels) {
-      std::vector<TResult> deleted_indexes;
+    virtual Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachDeleteVertex(
+        VertexAccessor *vertex) = 0;
 
-      for (const auto &label : labels) {
-        std::vector<TResult> loc_results = index.DeleteIndexStats(NameToLabel(label));
-        deleted_indexes.insert(deleted_indexes.end(), std::make_move_iterator(loc_results.begin()),
-                               std::make_move_iterator(loc_results.end()));
-      }
-      return deleted_indexes;
-    }
+    virtual void PrefetchInEdges(const VertexAccessor &vertex_acc) = 0;
 
-    std::vector<std::pair<LabelId, PropertyId>> DeleteLabelPropertyIndexStats(const std::span<std::string> labels) {
-      return DeleteIndexStatsForIndex<std::pair<LabelId, PropertyId>>(storage_->indices_.label_property_index, labels);
-    }
+    virtual void PrefetchOutEdges(const VertexAccessor &vertex_acc) = 0;
 
-    std::vector<LabelId> DeleteLabelIndexStats(const std::span<std::string> labels) {
-      return DeleteIndexStatsForIndex<LabelId>(storage_->indices_.label_index, labels);
-    }
+    virtual Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type) = 0;
 
-    /// @return Accessor to the deleted vertex if a deletion took place, std::nullopt otherwise
-    /// @throw std::bad_alloc
-    Result<std::optional<VertexAccessor>> DeleteVertex(VertexAccessor *vertex);
+    virtual Result<std::optional<EdgeAccessor>> DeleteEdge(EdgeAccessor *edge) = 0;
 
-    /// @return Accessor to the deleted vertex and deleted edges if a deletion took place, std::nullopt otherwise
-    /// @throw std::bad_alloc
-    Result<std::optional<std::pair<VertexAccessor, std::vector<EdgeAccessor>>>> DetachDeleteVertex(
-        VertexAccessor *vertex);
+    virtual bool LabelIndexExists(LabelId label) const = 0;
 
-    /// @throw std::bad_alloc
-    Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type);
+    virtual bool LabelPropertyIndexExists(LabelId label, PropertyId property) const = 0;
 
-    /// Accessor to the deleted edge if a deletion took place, std::nullopt otherwise
-    /// @throw std::bad_alloc
-    Result<std::optional<EdgeAccessor>> DeleteEdge(EdgeAccessor *edge);
+    virtual IndicesInfo ListAllIndices() const = 0;
 
-    const std::string &LabelToName(LabelId label) const;
-    const std::string &PropertyToName(PropertyId property) const;
-    const std::string &EdgeTypeToName(EdgeTypeId edge_type) const;
+    virtual ConstraintsInfo ListAllConstraints() const = 0;
 
-    /// @throw std::bad_alloc if unable to insert a new mapping
-    LabelId NameToLabel(std::string_view name);
+    // NOLINTNEXTLINE(google-default-arguments)
+    virtual utils::BasicResult<StorageDataManipulationError, void> Commit(
+        std::optional<uint64_t> desired_commit_timestamp = {}) = 0;
 
-    /// @throw std::bad_alloc if unable to insert a new mapping
-    PropertyId NameToProperty(std::string_view name);
+    virtual void Abort() = 0;
 
-    /// @throw std::bad_alloc if unable to insert a new mapping
-    EdgeTypeId NameToEdgeType(std::string_view name);
-
-    bool LabelIndexExists(LabelId label) const { return storage_->indices_.label_index.IndexExists(label); }
-
-    bool LabelPropertyIndexExists(LabelId label, PropertyId property) const {
-      return storage_->indices_.label_property_index.IndexExists(label, property);
-    }
-
-    IndicesInfo ListAllIndices() const {
-      return {storage_->indices_.label_index.ListIndices(), storage_->indices_.label_property_index.ListIndices()};
-    }
-
-    ConstraintsInfo ListAllConstraints() const {
-      return {ListExistenceConstraints(storage_->constraints_),
-              storage_->constraints_.unique_constraints.ListConstraints()};
-    }
-
-    void AdvanceCommand();
-
-    /// Returns void if the transaction has been committed.
-    /// Returns `StorageDataManipulationError` if an error occures. Error can be:
-    /// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
-    /// * `ConstraintViolation`: the changes made by this transaction violate an existence or unique constraint. In this
-    /// case the transaction is automatically aborted.
-    /// @throw std::bad_alloc
-    utils::BasicResult<StorageDataManipulationError, void> Commit(
-        std::optional<uint64_t> desired_commit_timestamp = {});
-
-    /// @throw std::bad_alloc
-    void Abort();
-
-    void FinalizeTransaction();
+    virtual void FinalizeTransaction() = 0;
 
     std::optional<uint64_t> GetTransactionId() const;
 
-   private:
-    /// @throw std::bad_alloc
-    VertexAccessor CreateVertex(storage::Gid gid);
+    void AdvanceCommand();
 
-    /// @throw std::bad_alloc
-    Result<EdgeAccessor> CreateEdge(VertexAccessor *from, VertexAccessor *to, EdgeTypeId edge_type, storage::Gid gid);
+    const std::string &LabelToName(LabelId label) const { return storage_->LabelToName(label); }
 
+    const std::string &PropertyToName(PropertyId property) const { return storage_->PropertyToName(property); }
+
+    const std::string &EdgeTypeToName(EdgeTypeId edge_type) const { return storage_->EdgeTypeToName(edge_type); }
+
+    LabelId NameToLabel(std::string_view name) { return storage_->NameToLabel(name); }
+
+    PropertyId NameToProperty(std::string_view name) { return storage_->NameToProperty(name); }
+
+    EdgeTypeId NameToEdgeType(std::string_view name) { return storage_->NameToEdgeType(name); }
+
+    StorageMode GetCreationStorageMode() const;
+
+   protected:
     Storage *storage_;
     std::shared_lock<utils::RWLock> storage_guard_;
     Transaction transaction_;
     std::optional<uint64_t> commit_timestamp_;
     bool is_transaction_active_;
-    Config::Items config_;
+
+   private:
+    StorageMode creation_storage_mode_;
   };
 
-  Accessor Access(std::optional<IsolationLevel> override_isolation_level = {}) {
-    return Accessor{this, override_isolation_level.value_or(isolation_level_), storage_mode_};
+  const std::string &LabelToName(LabelId label) const { return name_id_mapper_->IdToName(label.AsUint()); }
+
+  const std::string &PropertyToName(PropertyId property) const { return name_id_mapper_->IdToName(property.AsUint()); }
+
+  const std::string &EdgeTypeToName(EdgeTypeId edge_type) const {
+    return name_id_mapper_->IdToName(edge_type.AsUint());
   }
 
-  const std::string &LabelToName(LabelId label) const;
-  const std::string &PropertyToName(PropertyId property) const;
-  const std::string &EdgeTypeToName(EdgeTypeId edge_type) const;
+  LabelId NameToLabel(const std::string_view name) const { return LabelId::FromUint(name_id_mapper_->NameToId(name)); }
 
-  /// @throw std::bad_alloc if unable to insert a new mapping
-  LabelId NameToLabel(std::string_view name);
+  PropertyId NameToProperty(const std::string_view name) const {
+    return PropertyId::FromUint(name_id_mapper_->NameToId(name));
+  }
 
-  /// @throw std::bad_alloc if unable to insert a new mapping
-  PropertyId NameToProperty(std::string_view name);
+  EdgeTypeId NameToEdgeType(const std::string_view name) const {
+    return EdgeTypeId::FromUint(name_id_mapper_->NameToId(name));
+  }
 
-  /// @throw std::bad_alloc if unable to insert a new mapping
-  EdgeTypeId NameToEdgeType(std::string_view name);
+  void SetStorageMode(StorageMode storage_mode);
 
-  /// Create an index.
-  /// Returns void if the index has been created.
-  /// Returns `StorageIndexDefinitionError` if an error occures. Error can be:
-  /// * `IndexDefinitionError`: the index already exists.
-  /// * `ReplicationError`:  there is at least one SYNC replica that has not confirmed receiving the transaction.
-  /// @throw std::bad_alloc
-  utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(
-      LabelId label, std::optional<uint64_t> desired_commit_timestamp = {});
+  StorageMode GetStorageMode() const;
 
-  /// Create an index.
-  /// Returns void if the index has been created.
-  /// Returns `StorageIndexDefinitionError` if an error occures. Error can be:
-  /// * `ReplicationError`:  there is at least one SYNC replica that has not confirmed receiving the transaction.
-  /// * `IndexDefinitionError`: the index already exists.
-  /// @throw std::bad_alloc
-  utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(
-      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp = {});
+  virtual void FreeMemory(std::unique_lock<utils::RWLock> main_guard) = 0;
+  void FreeMemory() { FreeMemory({}); }
 
-  /// Drop an existing index.
-  /// Returns void if the index has been dropped.
-  /// Returns `StorageIndexDefinitionError` if an error occures. Error can be:
-  /// * `ReplicationError`:  there is at least one SYNC replica that has not confirmed receiving the transaction.
-  /// * `IndexDefinitionError`: the index does not exist.
-  utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(
-      LabelId label, std::optional<uint64_t> desired_commit_timestamp = {});
+  virtual std::unique_ptr<Accessor> Access(std::optional<IsolationLevel> override_isolation_level) = 0;
+  std::unique_ptr<Accessor> Access() { return Access(std::optional<IsolationLevel>{}); }
 
-  /// Drop an existing index.
-  /// Returns void if the index has been dropped.
-  /// Returns `StorageIndexDefinitionError` if an error occures. Error can be:
-  /// * `ReplicationError`:  there is at least one SYNC replica that has not confirmed receiving the transaction.
-  /// * `IndexDefinitionError`: the index does not exist.
-  utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(
-      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp = {});
+  virtual utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(
+      LabelId label, std::optional<uint64_t> desired_commit_timestamp) = 0;
+
+  utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label) {
+    return CreateIndex(label, std::optional<uint64_t>{});
+  }
+
+  virtual utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(
+      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp) = 0;
+
+  utils::BasicResult<StorageIndexDefinitionError, void> CreateIndex(LabelId label, PropertyId property) {
+    return CreateIndex(label, property, std::optional<uint64_t>{});
+  }
+
+  virtual utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(
+      LabelId label, std::optional<uint64_t> desired_commit_timestamp) = 0;
+
+  utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(LabelId label) {
+    return DropIndex(label, std::optional<uint64_t>{});
+  }
+
+  virtual utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(
+      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp) = 0;
+
+  utils::BasicResult<StorageIndexDefinitionError, void> DropIndex(LabelId label, PropertyId property) {
+    return DropIndex(label, property, std::optional<uint64_t>{});
+  }
 
   IndicesInfo ListAllIndices() const;
 
-  /// Returns void if the existence constraint has been created.
-  /// Returns `StorageExistenceConstraintDefinitionError` if an error occures. Error can be:
-  /// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
-  /// * `ConstraintViolation`: there is already a vertex existing that would break this new constraint.
-  /// * `ConstraintDefinitionError`: the constraint already exists.
-  /// @throw std::bad_alloc
-  /// @throw std::length_error
-  utils::BasicResult<StorageExistenceConstraintDefinitionError, void> CreateExistenceConstraint(
-      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp = {});
+  virtual utils::BasicResult<StorageExistenceConstraintDefinitionError, void> CreateExistenceConstraint(
+      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp) = 0;
 
-  /// Drop an existing existence constraint.
-  /// Returns void if the existence constraint has been dropped.
-  /// Returns `StorageExistenceConstraintDroppingError` if an error occures. Error can be:
-  /// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
-  /// * `ConstraintDefinitionError`: the constraint did not exists.
-  utils::BasicResult<StorageExistenceConstraintDroppingError, void> DropExistenceConstraint(
-      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp = {});
+  virtual utils::BasicResult<StorageExistenceConstraintDroppingError, void> DropExistenceConstraint(
+      LabelId label, PropertyId property, std::optional<uint64_t> desired_commit_timestamp) = 0;
 
-  /// Create an unique constraint.
-  /// Returns `StorageUniqueConstraintDefinitionError` if an error occures. Error can be:
-  /// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
-  /// * `ConstraintViolation`: there are already vertices violating the constraint.
-  /// Returns `UniqueConstraints::CreationStatus` otherwise. Value can be:
-  /// * `SUCCESS` if the constraint was successfully created,
-  /// * `ALREADY_EXISTS` if the constraint already existed,
-  /// * `EMPTY_PROPERTIES` if the property set is empty, or
-  /// * `PROPERTIES_SIZE_LIMIT_EXCEEDED` if the property set exceeds the limit of maximum number of properties.
-  /// @throw std::bad_alloc
-  utils::BasicResult<StorageUniqueConstraintDefinitionError, UniqueConstraints::CreationStatus> CreateUniqueConstraint(
-      LabelId label, const std::set<PropertyId> &properties, std::optional<uint64_t> desired_commit_timestamp = {});
+  virtual utils::BasicResult<StorageUniqueConstraintDefinitionError, UniqueConstraints::CreationStatus>
+  CreateUniqueConstraint(LabelId label, const std::set<PropertyId> &properties,
+                         std::optional<uint64_t> desired_commit_timestamp) = 0;
 
-  /// Removes an existing unique constraint.
-  /// Returns `StorageUniqueConstraintDroppingError` if an error occures. Error can be:
-  /// * `ReplicationError`: there is at least one SYNC replica that has not confirmed receiving the transaction.
-  /// Returns `UniqueConstraints::DeletionStatus` otherwise. Value can be:
-  /// * `SUCCESS` if constraint was successfully removed,
-  /// * `NOT_FOUND` if the specified constraint was not found,
-  /// * `EMPTY_PROPERTIES` if the property set is empty, or
-  /// * `PROPERTIES_SIZE_LIMIT_EXCEEDED` if the property set exceeds the limit of maximum number of properties.
-  utils::BasicResult<StorageUniqueConstraintDroppingError, UniqueConstraints::DeletionStatus> DropUniqueConstraint(
-      LabelId label, const std::set<PropertyId> &properties, std::optional<uint64_t> desired_commit_timestamp = {});
+  virtual utils::BasicResult<StorageUniqueConstraintDroppingError, UniqueConstraints::DeletionStatus>
+  DropUniqueConstraint(LabelId label, const std::set<PropertyId> &properties,
+                       std::optional<uint64_t> desired_commit_timestamp) = 0;
 
   ConstraintsInfo ListAllConstraints() const;
 
-  StorageInfo GetInfo() const;
-
-  utils::FileRetainer::FileLockerAccessor::ret_type IsPathLocked();
-  utils::FileRetainer::FileLockerAccessor::ret_type LockPath();
-  utils::FileRetainer::FileLockerAccessor::ret_type UnlockPath();
-
-  bool SetReplicaRole(io::network::Endpoint endpoint, const replication::ReplicationServerConfig &config = {});
-
-  bool SetMainReplicationRole();
-
-  enum class RegisterReplicaError : uint8_t {
-    NAME_EXISTS,
-    END_POINT_EXISTS,
-    CONNECTION_FAILED,
-    COULD_NOT_BE_PERSISTED
-  };
-
-  /// @pre The instance should have a MAIN role
-  /// @pre Timeout can only be set for SYNC replication
-  utils::BasicResult<RegisterReplicaError, void> RegisterReplica(
-      std::string name, io::network::Endpoint endpoint, replication::ReplicationMode replication_mode,
-      replication::RegistrationMode registration_mode, const replication::ReplicationClientConfig &config = {});
-  /// @pre The instance should have a MAIN role
-  bool UnregisterReplica(const std::string &name);
-
-  std::optional<replication::ReplicaState> GetReplicaState(std::string_view name);
-
-  replication::ReplicationRole GetReplicationRole() const;
-
-  struct TimestampInfo {
-    uint64_t current_timestamp_of_replica;
-    uint64_t current_number_of_timestamp_behind_master;
-  };
-
-  struct ReplicaInfo {
-    std::string name;
-    replication::ReplicationMode mode;
-    io::network::Endpoint endpoint;
-    replication::ReplicaState state;
-    TimestampInfo timestamp_info;
-  };
-
-  std::vector<ReplicaInfo> ReplicasInfo();
-
-  void FreeMemory(std::unique_lock<utils::RWLock> main_guard = {});
-
   enum class SetIsolationLevelError : uint8_t { DisabledForAnalyticalMode };
 
   utils::BasicResult<SetIsolationLevelError> SetIsolationLevel(IsolationLevel isolation_level);
   IsolationLevel GetIsolationLevel() const noexcept;
 
-  void SetStorageMode(StorageMode storage_mode);
+  virtual StorageInfo GetInfo() const = 0;
 
-  StorageMode GetStorageMode();
-
-  enum class CreateSnapshotError : uint8_t {
-    DisabledForReplica,
-    DisabledForAnalyticsPeriodicCommit,
-    ReachedMaxNumTries
-  };
-
-  utils::BasicResult<CreateSnapshotError> CreateSnapshot(std::optional<bool> is_periodic);
-
- private:
-  Transaction CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode);
-
-  /// The force parameter determines the behaviour of the garbage collector.
-  /// If it's set to true, it will behave as a global operation, i.e. it can't
-  /// be part of a transaction, and no other transaction can be active at the same time.
-  /// This allows it to delete immediately vertices without worrying that some other
-  /// transaction is possibly using it. If there are active transactions when this method
-  /// is called with force set to true, it will fallback to the same method with the force
-  /// set to false.
-  /// If it's set to false, it will execute in parallel with other transactions, ensuring
-  /// that no object in use can be deleted.
-  /// @throw std::system_error
-  /// @throw std::bad_alloc
-  template <bool force>
-  void CollectGarbage(std::unique_lock<utils::RWLock> main_guard = {});
-
-  bool InitializeWalFile();
-  void FinalizeWalFile();
-
-  /// Return true in all cases excepted if any sync replicas have not sent confirmation.
-  [[nodiscard]] bool AppendToWalDataManipulation(const Transaction &transaction, uint64_t final_commit_timestamp);
-  /// Return true in all cases excepted if any sync replicas have not sent confirmation.
-  [[nodiscard]] bool AppendToWalDataDefinition(durability::StorageGlobalOperation operation, LabelId label,
-                                               const std::set<PropertyId> &properties, uint64_t final_commit_timestamp);
-
-  uint64_t CommitTimestamp(std::optional<uint64_t> desired_commit_timestamp = {});
-
-  void RestoreReplicationRole();
-
-  void RestoreReplicas();
-
-  bool ShouldStoreAndRestoreReplicationState() const;
+  virtual Transaction CreateTransaction(IsolationLevel isolation_level, StorageMode storage_mode) = 0;
 
   // Main storage lock.
-  //
   // Accessors take a shared lock when starting, so it is possible to block
   // creation of new accessors by taking a unique lock. This is used when doing
   // operations on storage that affect the global state, for example index
   // creation.
   mutable utils::RWLock main_lock_{utils::RWLock::Priority::WRITE};
 
-  // Main object storage
-  utils::SkipList<storage::Vertex> vertices_;
-  utils::SkipList<storage::Edge> edges_;
-  std::atomic<uint64_t> vertex_id_{0};
-  std::atomic<uint64_t> edge_id_{0};
   // Even though the edge count is already kept in the `edges_` SkipList, the
   // list is used only when properties are enabled for edges. Because of that we
   // keep a separate count of edges that is always updated.
   std::atomic<uint64_t> edge_count_{0};
 
-  NameIdMapper name_id_mapper_;
-
-  Constraints constraints_;
-  Indices indices_;
+  std::unique_ptr<NameIdMapper> name_id_mapper_;
+  Config config_;
 
   // Transaction engine
   utils::SpinLock engine_lock_;
   uint64_t timestamp_{kTimestampInitialId};
   uint64_t transaction_id_{kTransactionInitialId};
-  // TODO: This isn't really a commit log, it doesn't even care if a
-  // transaction commited or aborted. We could probably combine this with
-  // `timestamp_` in a sensible unit, something like TransactionClock or
-  // whatever.
-  std::optional<CommitLog> commit_log_;
 
-  utils::Synchronized<std::list<Transaction>, utils::SpinLock> committed_transactions_;
   IsolationLevel isolation_level_;
   StorageMode storage_mode_;
 
-  Config config_;
-  utils::Scheduler gc_runner_;
-  std::mutex gc_lock_;
+  Indices indices_;
+  Constraints constraints_;
 
-  // Undo buffers that were unlinked and now are waiting to be freed.
-  utils::Synchronized<std::list<std::pair<uint64_t, std::list<Delta>>>, utils::SpinLock> garbage_undo_buffers_;
-
-  // Vertices that are logically deleted but still have to be removed from
-  // indices before removing them from the main storage.
-  utils::Synchronized<std::list<Gid>, utils::SpinLock> deleted_vertices_;
-
-  // Vertices that are logically deleted and removed from indices and now wait
-  // to be removed from the main storage.
-  std::list<std::pair<uint64_t, Gid>> garbage_vertices_;
-
-  // Edges that are logically deleted and wait to be removed from the main
-  // storage.
-  utils::Synchronized<std::list<Gid>, utils::SpinLock> deleted_edges_;
-
-  // Flags to inform CollectGarbage that it needs to do the more expensive full scans
-  std::atomic<bool> gc_full_scan_vertices_delete_ = false;
-  std::atomic<bool> gc_full_scan_edges_delete_ = false;
-
-  // Durability
-  std::filesystem::path snapshot_directory_;
-  std::filesystem::path wal_directory_;
-  std::filesystem::path lock_file_path_;
-  utils::OutputFile lock_file_handle_;
-  std::unique_ptr<kvstore::KVStore> storage_;
-
-  utils::Scheduler snapshot_runner_;
-  utils::SpinLock snapshot_lock_;
-
-  // UUID used to distinguish snapshots and to link snapshots to WALs
-  std::string uuid_;
-  // Sequence number used to keep track of the chain of WALs.
-  uint64_t wal_seq_num_{0};
-
-  // UUID to distinguish different main instance runs for replication process
-  // on SAME storage.
-  // Multiple instances can have same storage UUID and be MAIN at the same time.
-  // We cannot compare commit timestamps of those instances if one of them
-  // becomes the replica of the other so we use epoch_id_ as additional
-  // discriminating property.
-  // Example of this:
-  // We have 2 instances of the same storage, S1 and S2.
-  // S1 and S2 are MAIN and accept their own commits and write them to the WAL.
-  // At the moment when S1 commited a transaction with timestamp 20, and S2
-  // a different transaction with timestamp 15, we change S2's role to REPLICA
-  // and register it on S1.
-  // Without using the epoch_id, we don't know that S1 and S2 have completely
-  // different transactions, we think that the S2 is behind only by 5 commits.
-  std::string epoch_id_;
-  // History of the previous epoch ids.
-  // Each value consists of the epoch id along the last commit belonging to that
-  // epoch.
-  std::deque<std::pair<std::string, uint64_t>> epoch_history_;
-
-  std::optional<durability::WalFile> wal_file_;
-  uint64_t wal_unsynced_transactions_{0};
-
-  utils::FileRetainer file_retainer_;
-
-  // Global locker that is used for clients file locking
-  utils::FileRetainer::FileLocker global_locker_;
-
-  // Last commited timestamp
-  std::atomic<uint64_t> last_commit_timestamp_{kTimestampInitialId};
-
-  class ReplicationServer;
-  std::unique_ptr<ReplicationServer> replication_server_{nullptr};
-
-  class ReplicationClient;
-  // We create ReplicationClient using unique_ptr so we can move
-  // newly created client into the vector.
-  // We cannot move the client directly because it contains ThreadPool
-  // which cannot be moved. Also, the move is necessary because
-  // we don't want to create the client directly inside the vector
-  // because that would require the lock on the list putting all
-  // commits (they iterate list of clients) to halt.
-  // This way we can initialize client in main thread which means
-  // that we can immediately notify the user if the initialization
-  // failed.
-  using ReplicationClientList = utils::Synchronized<std::vector<std::unique_ptr<ReplicationClient>>, utils::SpinLock>;
-  ReplicationClientList replication_clients_;
-
-  std::atomic<replication::ReplicationRole> replication_role_{replication::ReplicationRole::MAIN};
+  std::atomic<uint64_t> vertex_id_{0};
+  std::atomic<uint64_t> edge_id_{0};
 };
 
 }  // namespace memgraph::storage
diff --git a/src/storage/v2/storage_error.hpp b/src/storage/v2/storage_error.hpp
index 1e071f748..b9ce0fb71 100644
--- a/src/storage/v2/storage_error.hpp
+++ b/src/storage/v2/storage_error.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -11,28 +11,38 @@
 
 #pragma once
 
-#include "storage/v2/constraints.hpp"
+#include "storage/v2/constraints/constraints.hpp"
 
+#include <iterator>
 #include <variant>
 
 namespace memgraph::storage {
 
 struct ReplicationError {};
 
-using StorageDataManipulationError = std::variant<ConstraintViolation, ReplicationError>;
+struct IndexPersistenceError {};
+
+struct ConstraintsPersistenceError {};
+
+struct SerializationError {};
+inline bool operator==(const SerializationError & /*err1*/, const SerializationError & /*err2*/) { return true; }
+
+using StorageDataManipulationError = std::variant<ConstraintViolation, ReplicationError, SerializationError>;
 
 struct IndexDefinitionError {};
-using StorageIndexDefinitionError = std::variant<IndexDefinitionError, ReplicationError>;
+using StorageIndexDefinitionError = std::variant<IndexDefinitionError, ReplicationError, IndexPersistenceError>;
 
 struct ConstraintDefinitionError {};
 
 using StorageExistenceConstraintDefinitionError =
-    std::variant<ConstraintViolation, ConstraintDefinitionError, ReplicationError>;
+    std::variant<ConstraintViolation, ConstraintDefinitionError, ReplicationError, ConstraintsPersistenceError>;
 
-using StorageExistenceConstraintDroppingError = std::variant<ConstraintDefinitionError, ReplicationError>;
+using StorageExistenceConstraintDroppingError =
+    std::variant<ConstraintDefinitionError, ReplicationError, ConstraintsPersistenceError>;
 
-using StorageUniqueConstraintDefinitionError = std::variant<ConstraintViolation, ReplicationError>;
+using StorageUniqueConstraintDefinitionError =
+    std::variant<ConstraintViolation, ConstraintDefinitionError, ReplicationError, ConstraintsPersistenceError>;
 
-using StorageUniqueConstraintDroppingError = std::variant<ReplicationError>;
+using StorageUniqueConstraintDroppingError = std::variant<ReplicationError, ConstraintsPersistenceError>;
 
 }  // namespace memgraph::storage
diff --git a/src/storage/v2/storage_mode.cpp b/src/storage/v2/storage_mode.cpp
index a05017f1d..73a886d6a 100644
--- a/src/storage/v2/storage_mode.cpp
+++ b/src/storage/v2/storage_mode.cpp
@@ -19,6 +19,8 @@ std::string_view StorageModeToString(memgraph::storage::StorageMode storage_mode
       return "IN_MEMORY_ANALYTICAL";
     case memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL:
       return "IN_MEMORY_TRANSACTIONAL";
+    case memgraph::storage::StorageMode::ON_DISK_TRANSACTIONAL:
+      return "ON_DISK_TRANSACTIONAL";
   }
 }
 
diff --git a/src/storage/v2/storage_mode.hpp b/src/storage/v2/storage_mode.hpp
index 2da6435cd..2ab348c59 100644
--- a/src/storage/v2/storage_mode.hpp
+++ b/src/storage/v2/storage_mode.hpp
@@ -16,7 +16,7 @@
 
 namespace memgraph::storage {
 
-enum class StorageMode : std::uint8_t { IN_MEMORY_ANALYTICAL, IN_MEMORY_TRANSACTIONAL };
+enum class StorageMode : std::uint8_t { IN_MEMORY_ANALYTICAL, IN_MEMORY_TRANSACTIONAL, ON_DISK_TRANSACTIONAL };
 
 std::string_view StorageModeToString(memgraph::storage::StorageMode storage_mode);
 
diff --git a/src/storage/v2/vertex.hpp b/src/storage/v2/vertex.hpp
index 83f517c46..5ec612f68 100644
--- a/src/storage/v2/vertex.hpp
+++ b/src/storage/v2/vertex.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -25,7 +25,8 @@ namespace memgraph::storage {
 
 struct Vertex {
   Vertex(Gid gid, Delta *delta) : gid(gid), deleted(false), delta(delta) {
-    MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT,
+    MG_ASSERT(delta == nullptr || delta->action == Delta::Action::DELETE_OBJECT ||
+                  delta->action == Delta::Action::DELETE_DESERIALIZED_OBJECT,
               "Vertex must be created with an initial DELETE_OBJECT delta!");
   }
 
diff --git a/src/storage/v2/vertex_accessor.cpp b/src/storage/v2/vertex_accessor.cpp
index 9682565f9..9bf3c8fa9 100644
--- a/src/storage/v2/vertex_accessor.cpp
+++ b/src/storage/v2/vertex_accessor.cpp
@@ -15,7 +15,7 @@
 
 #include "storage/v2/edge_accessor.hpp"
 #include "storage/v2/id_types.hpp"
-#include "storage/v2/indices.hpp"
+#include "storage/v2/indices/indices.hpp"
 #include "storage/v2/mvcc.hpp"
 #include "storage/v2/property_value.hpp"
 #include "utils/logging.hpp"
@@ -48,6 +48,7 @@ std::pair<bool, bool> IsVisible(Vertex *vertex, Transaction *transaction, View v
         deleted = false;
         break;
       }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
       case Delta::Action::DELETE_OBJECT: {
         exists = false;
         break;
@@ -79,34 +80,37 @@ Result<bool> VertexAccessor::AddLabel(LabelId label) {
   std::lock_guard<utils::SpinLock> guard(vertex_->lock);
 
   if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
-
   if (vertex_->deleted) return Error::DELETED_OBJECT;
-
   if (std::find(vertex_->labels.begin(), vertex_->labels.end(), label) != vertex_->labels.end()) return false;
 
   CreateAndLinkDelta(transaction_, vertex_, Delta::RemoveLabelTag(), label);
-
   vertex_->labels.push_back(label);
 
-  UpdateOnAddLabel(indices_, label, vertex_, *transaction_);
+  /// TODO: some by pointers, some by reference => not good, make it better
+  constraints_->unique_constraints_->UpdateOnAddLabel(label, *vertex_, transaction_->start_timestamp);
+  indices_->UpdateOnAddLabel(label, vertex_, *transaction_);
 
   return true;
 }
 
+/// TODO: move to after update and change naming to vertex after update
 Result<bool> VertexAccessor::RemoveLabel(LabelId label) {
   std::lock_guard<utils::SpinLock> guard(vertex_->lock);
 
   if (!PrepareForWrite(transaction_, vertex_)) return Error::SERIALIZATION_ERROR;
-
   if (vertex_->deleted) return Error::DELETED_OBJECT;
 
   auto it = std::find(vertex_->labels.begin(), vertex_->labels.end(), label);
   if (it == vertex_->labels.end()) return false;
 
   CreateAndLinkDelta(transaction_, vertex_, Delta::AddLabelTag(), label);
-
   std::swap(*it, *vertex_->labels.rbegin());
   vertex_->labels.pop_back();
+
+  /// TODO: some by pointers, some by reference => not good, make it better
+  constraints_->unique_constraints_->UpdateOnRemoveLabel(label, *vertex_, transaction_->start_timestamp);
+  indices_->UpdateOnRemoveLabel(label, vertex_, *transaction_);
+
   return true;
 }
 
@@ -137,6 +141,7 @@ Result<bool> VertexAccessor::HasLabel(LabelId label, View view) const {
         }
         break;
       }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
       case Delta::Action::DELETE_OBJECT: {
         exists = false;
         break;
@@ -186,6 +191,7 @@ Result<std::vector<LabelId>> VertexAccessor::Labels(View view) const {
         labels.push_back(delta.label);
         break;
       }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
       case Delta::Action::DELETE_OBJECT: {
         exists = false;
         break;
@@ -226,7 +232,7 @@ Result<PropertyValue> VertexAccessor::SetProperty(PropertyId property, const Pro
   CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), property, current_value);
   vertex_->properties.SetProperty(property, value);
 
-  UpdateOnSetProperty(indices_, property, value, vertex_, *transaction_);
+  indices_->UpdateOnSetProperty(property, value, vertex_, *transaction_);
 
   return std::move(current_value);
 }
@@ -242,7 +248,7 @@ Result<bool> VertexAccessor::InitProperties(const std::map<storage::PropertyId,
   if (!vertex_->properties.InitProperties(properties)) return false;
   for (const auto &[property, value] : properties) {
     CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), property, PropertyValue());
-    UpdateOnSetProperty(indices_, property, value, vertex_, *transaction_);
+    indices_->UpdateOnSetProperty(property, value, vertex_, *transaction_);
   }
 
   return true;
@@ -258,7 +264,7 @@ Result<std::map<PropertyId, PropertyValue>> VertexAccessor::ClearProperties() {
   auto properties = vertex_->properties.Properties();
   for (const auto &property : properties) {
     CreateAndLinkDelta(transaction_, vertex_, Delta::SetPropertyTag(), property.first, property.second);
-    UpdateOnSetProperty(indices_, property.first, PropertyValue(), vertex_, *transaction_);
+    indices_->UpdateOnSetProperty(property.first, PropertyValue(), vertex_, *transaction_);
   }
 
   vertex_->properties.ClearProperties();
@@ -285,6 +291,7 @@ Result<PropertyValue> VertexAccessor::GetProperty(PropertyId property, View view
         }
         break;
       }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
       case Delta::Action::DELETE_OBJECT: {
         exists = false;
         break;
@@ -335,6 +342,7 @@ Result<std::map<PropertyId, PropertyValue>> VertexAccessor::Properties(View view
         }
         break;
       }
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
       case Delta::Action::DELETE_OBJECT: {
         exists = false;
         break;
@@ -410,6 +418,7 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::InEdges(View view, const std::
             in_edges.pop_back();
             break;
           }
+          case Delta::Action::DELETE_DESERIALIZED_OBJECT:
           case Delta::Action::DELETE_OBJECT: {
             exists = false;
             break;
@@ -490,6 +499,7 @@ Result<std::vector<EdgeAccessor>> VertexAccessor::OutEdges(View view, const std:
             out_edges.pop_back();
             break;
           }
+          case Delta::Action::DELETE_DESERIALIZED_OBJECT:
           case Delta::Action::DELETE_OBJECT: {
             exists = false;
             break;
@@ -536,6 +546,7 @@ Result<size_t> VertexAccessor::InDegree(View view) const {
       case Delta::Action::REMOVE_IN_EDGE:
         --degree;
         break;
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
       case Delta::Action::DELETE_OBJECT:
         exists = false;
         break;
@@ -574,6 +585,7 @@ Result<size_t> VertexAccessor::OutDegree(View view) const {
       case Delta::Action::REMOVE_OUT_EDGE:
         --degree;
         break;
+      case Delta::Action::DELETE_DESERIALIZED_OBJECT:
       case Delta::Action::DELETE_OBJECT:
         exists = false;
         break;
diff --git a/src/storage/v2/vertex_accessor.hpp b/src/storage/v2/vertex_accessor.hpp
index 79a391708..db1a6a6ef 100644
--- a/src/storage/v2/vertex_accessor.hpp
+++ b/src/storage/v2/vertex_accessor.hpp
@@ -24,8 +24,8 @@ namespace memgraph::storage {
 
 class EdgeAccessor;
 class Storage;
-struct Indices;
 struct Constraints;
+struct Indices;
 
 class VertexAccessor final {
  private:
@@ -106,7 +106,6 @@ class VertexAccessor final {
   }
   bool operator!=(const VertexAccessor &other) const noexcept { return !(*this == other); }
 
- private:
   Vertex *vertex_;
   Transaction *transaction_;
   Indices *indices_;
diff --git a/src/storage/v2/vertices_iterable.cpp b/src/storage/v2/vertices_iterable.cpp
new file mode 100644
index 000000000..34d6c76b2
--- /dev/null
+++ b/src/storage/v2/vertices_iterable.cpp
@@ -0,0 +1,250 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include "storage/v2/vertices_iterable.hpp"
+
+namespace memgraph::storage {
+
+VerticesIterable::VerticesIterable(AllVerticesIterable vertices) : type_(Type::ALL) {
+  new (&all_vertices_) AllVerticesIterable(std::move(vertices));
+}
+
+VerticesIterable::VerticesIterable(InMemoryLabelIndex::Iterable vertices) : type_(Type::BY_LABEL_IN_MEMORY) {
+  new (&in_memory_vertices_by_label_) InMemoryLabelIndex::Iterable(std::move(vertices));
+}
+
+VerticesIterable::VerticesIterable(InMemoryLabelPropertyIndex::Iterable vertices)
+    : type_(Type::BY_LABEL_PROPERTY_IN_MEMORY) {
+  new (&in_memory_vertices_by_label_property_) InMemoryLabelPropertyIndex::Iterable(std::move(vertices));
+}
+
+VerticesIterable::VerticesIterable(VerticesIterable &&other) noexcept : type_(other.type_) {
+  switch (other.type_) {
+    case Type::ALL:
+      new (&all_vertices_) AllVerticesIterable(std::move(other.all_vertices_));
+      break;
+    case Type::BY_LABEL_IN_MEMORY:
+      new (&in_memory_vertices_by_label_) InMemoryLabelIndex::Iterable(std::move(other.in_memory_vertices_by_label_));
+      break;
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      new (&in_memory_vertices_by_label_property_)
+          InMemoryLabelPropertyIndex::Iterable(std::move(other.in_memory_vertices_by_label_property_));
+      break;
+  }
+}
+
+VerticesIterable &VerticesIterable::operator=(VerticesIterable &&other) noexcept {
+  switch (type_) {
+    case Type::ALL:
+      all_vertices_.AllVerticesIterable::~AllVerticesIterable();
+      break;
+    case Type::BY_LABEL_IN_MEMORY:
+      in_memory_vertices_by_label_.InMemoryLabelIndex::Iterable::~Iterable();
+      break;
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      in_memory_vertices_by_label_property_.InMemoryLabelPropertyIndex::Iterable::~Iterable();
+      break;
+  }
+  type_ = other.type_;
+  switch (other.type_) {
+    case Type::ALL:
+      new (&all_vertices_) AllVerticesIterable(std::move(other.all_vertices_));
+      break;
+    case Type::BY_LABEL_IN_MEMORY:
+      new (&in_memory_vertices_by_label_) InMemoryLabelIndex::Iterable(std::move(other.in_memory_vertices_by_label_));
+      break;
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      new (&in_memory_vertices_by_label_property_)
+          InMemoryLabelPropertyIndex::Iterable(std::move(other.in_memory_vertices_by_label_property_));
+      break;
+  }
+  return *this;
+}
+
+VerticesIterable::~VerticesIterable() {
+  switch (type_) {
+    case Type::ALL:
+      all_vertices_.AllVerticesIterable::~AllVerticesIterable();
+      break;
+    case Type::BY_LABEL_IN_MEMORY:
+      in_memory_vertices_by_label_.InMemoryLabelIndex::Iterable::~Iterable();
+      break;
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      in_memory_vertices_by_label_property_.InMemoryLabelPropertyIndex::Iterable::~Iterable();
+      break;
+  }
+}
+
+VerticesIterable::Iterator VerticesIterable::begin() {
+  switch (type_) {
+    case Type::ALL:
+      return Iterator(all_vertices_.begin());
+    case Type::BY_LABEL_IN_MEMORY:
+      return Iterator(in_memory_vertices_by_label_.begin());
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      return Iterator(in_memory_vertices_by_label_property_.begin());
+  }
+}
+
+VerticesIterable::Iterator VerticesIterable::end() {
+  switch (type_) {
+    case Type::ALL:
+      return Iterator(all_vertices_.end());
+    case Type::BY_LABEL_IN_MEMORY:
+      return Iterator(in_memory_vertices_by_label_.end());
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      return Iterator(in_memory_vertices_by_label_property_.end());
+  }
+}
+
+VerticesIterable::Iterator::Iterator(AllVerticesIterable::Iterator it) : type_(Type::ALL) {
+  // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+  new (&all_it_) AllVerticesIterable::Iterator(std::move(it));
+}
+
+VerticesIterable::Iterator::Iterator(InMemoryLabelIndex::Iterable::Iterator it) : type_(Type::BY_LABEL_IN_MEMORY) {
+  // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+  new (&in_memory_by_label_it_) InMemoryLabelIndex::Iterable::Iterator(std::move(it));
+}
+
+VerticesIterable::Iterator::Iterator(InMemoryLabelPropertyIndex::Iterable::Iterator it)
+    : type_(Type::BY_LABEL_PROPERTY_IN_MEMORY) {
+  // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+  new (&in_memory_by_label_property_it_) InMemoryLabelPropertyIndex::Iterable::Iterator(std::move(it));
+}
+
+VerticesIterable::Iterator::Iterator(const VerticesIterable::Iterator &other) : type_(other.type_) {
+  switch (other.type_) {
+    case Type::ALL:
+      new (&all_it_) AllVerticesIterable::Iterator(other.all_it_);
+      break;
+    case Type::BY_LABEL_IN_MEMORY:
+      new (&in_memory_by_label_it_) InMemoryLabelIndex::Iterable::Iterator(other.in_memory_by_label_it_);
+      break;
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      new (&in_memory_by_label_property_it_)
+          InMemoryLabelPropertyIndex::Iterable::Iterator(other.in_memory_by_label_property_it_);
+      break;
+  }
+}
+
+// NOLINTNEXTLINE(cert-oop54-cpp)
+VerticesIterable::Iterator &VerticesIterable::Iterator::operator=(const VerticesIterable::Iterator &other) {
+  Destroy();
+  type_ = other.type_;
+  switch (other.type_) {
+    case Type::ALL:
+      new (&all_it_) AllVerticesIterable::Iterator(other.all_it_);
+      break;
+    case Type::BY_LABEL_IN_MEMORY:
+      new (&in_memory_by_label_it_) InMemoryLabelIndex::Iterable::Iterator(other.in_memory_by_label_it_);
+      break;
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      new (&in_memory_by_label_property_it_)
+          InMemoryLabelPropertyIndex::Iterable::Iterator(other.in_memory_by_label_property_it_);
+      break;
+  }
+  return *this;
+}
+
+VerticesIterable::Iterator::Iterator(VerticesIterable::Iterator &&other) noexcept : type_(other.type_) {
+  switch (other.type_) {
+    case Type::ALL:
+      // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+      new (&all_it_) AllVerticesIterable::Iterator(std::move(other.all_it_));
+      break;
+    case Type::BY_LABEL_IN_MEMORY:
+      // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+      new (&in_memory_by_label_it_) InMemoryLabelIndex::Iterable::Iterator(std::move(other.in_memory_by_label_it_));
+      break;
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      new (&in_memory_by_label_property_it_)
+          // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+          InMemoryLabelPropertyIndex::Iterable::Iterator(std::move(other.in_memory_by_label_property_it_));
+      break;
+  }
+}
+
+VerticesIterable::Iterator &VerticesIterable::Iterator::operator=(VerticesIterable::Iterator &&other) noexcept {
+  Destroy();
+  type_ = other.type_;
+  switch (other.type_) {
+    case Type::ALL:
+      // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+      new (&all_it_) AllVerticesIterable::Iterator(std::move(other.all_it_));
+      break;
+    case Type::BY_LABEL_IN_MEMORY:
+      // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+      new (&in_memory_by_label_it_) InMemoryLabelIndex::Iterable::Iterator(std::move(other.in_memory_by_label_it_));
+      break;
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      new (&in_memory_by_label_property_it_)
+          // NOLINTNEXTLINE(hicpp-move-const-arg,performance-move-const-arg)
+          InMemoryLabelPropertyIndex::Iterable::Iterator(std::move(other.in_memory_by_label_property_it_));
+      break;
+  }
+  return *this;
+}
+
+VerticesIterable::Iterator::~Iterator() { Destroy(); }
+
+void VerticesIterable::Iterator::Destroy() noexcept {
+  switch (type_) {
+    case Type::ALL:
+      all_it_.AllVerticesIterable::Iterator::~Iterator();
+      break;
+    case Type::BY_LABEL_IN_MEMORY:
+      in_memory_by_label_it_.InMemoryLabelIndex::Iterable::Iterator::~Iterator();
+      break;
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      in_memory_by_label_property_it_.InMemoryLabelPropertyIndex::Iterable::Iterator::~Iterator();
+      break;
+  }
+}
+
+VertexAccessor VerticesIterable::Iterator::operator*() const {
+  switch (type_) {
+    case Type::ALL:
+      return *all_it_;
+    case Type::BY_LABEL_IN_MEMORY:
+      return *in_memory_by_label_it_;
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      return *in_memory_by_label_property_it_;
+  }
+}
+
+VerticesIterable::Iterator &VerticesIterable::Iterator::operator++() {
+  switch (type_) {
+    case Type::ALL:
+      ++all_it_;
+      break;
+    case Type::BY_LABEL_IN_MEMORY:
+      ++in_memory_by_label_it_;
+      break;
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      ++in_memory_by_label_property_it_;
+      break;
+  }
+  return *this;
+}
+
+bool VerticesIterable::Iterator::operator==(const Iterator &other) const {
+  switch (type_) {
+    case Type::ALL:
+      return all_it_ == other.all_it_;
+    case Type::BY_LABEL_IN_MEMORY:
+      return in_memory_by_label_it_ == other.in_memory_by_label_it_;
+    case Type::BY_LABEL_PROPERTY_IN_MEMORY:
+      return in_memory_by_label_property_it_ == other.in_memory_by_label_property_it_;
+  }
+}
+
+}  // namespace memgraph::storage
diff --git a/src/storage/v2/vertices_iterable.hpp b/src/storage/v2/vertices_iterable.hpp
new file mode 100644
index 000000000..7cd1fe208
--- /dev/null
+++ b/src/storage/v2/vertices_iterable.hpp
@@ -0,0 +1,78 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include "storage/v2/all_vertices_iterable.hpp"
+#include "storage/v2/inmemory/label_index.hpp"
+#include "storage/v2/inmemory/label_property_index.hpp"
+
+namespace memgraph::storage {
+
+class VerticesIterable final {
+  enum class Type { ALL, BY_LABEL_IN_MEMORY, BY_LABEL_PROPERTY_IN_MEMORY };
+
+  Type type_;
+  union {
+    AllVerticesIterable all_vertices_;
+    InMemoryLabelIndex::Iterable in_memory_vertices_by_label_;
+    InMemoryLabelPropertyIndex::Iterable in_memory_vertices_by_label_property_;
+  };
+
+ public:
+  explicit VerticesIterable(AllVerticesIterable);
+  explicit VerticesIterable(InMemoryLabelIndex::Iterable);
+  explicit VerticesIterable(InMemoryLabelPropertyIndex::Iterable);
+
+  VerticesIterable(const VerticesIterable &) = delete;
+  VerticesIterable &operator=(const VerticesIterable &) = delete;
+
+  VerticesIterable(VerticesIterable &&) noexcept;
+  VerticesIterable &operator=(VerticesIterable &&) noexcept;
+
+  ~VerticesIterable();
+
+  class Iterator final {
+    Type type_;
+    union {
+      AllVerticesIterable::Iterator all_it_;
+      InMemoryLabelIndex::Iterable::Iterator in_memory_by_label_it_;
+      InMemoryLabelPropertyIndex::Iterable::Iterator in_memory_by_label_property_it_;
+    };
+
+    void Destroy() noexcept;
+
+   public:
+    explicit Iterator(AllVerticesIterable::Iterator);
+    explicit Iterator(InMemoryLabelIndex::Iterable::Iterator);
+    explicit Iterator(InMemoryLabelPropertyIndex::Iterable::Iterator);
+
+    Iterator(const Iterator &);
+    Iterator &operator=(const Iterator &);
+
+    Iterator(Iterator &&) noexcept;
+    Iterator &operator=(Iterator &&) noexcept;
+
+    ~Iterator();
+
+    VertexAccessor operator*() const;
+
+    Iterator &operator++();
+
+    bool operator==(const Iterator &other) const;
+    bool operator!=(const Iterator &other) const { return !(*this == other); }
+  };
+
+  Iterator begin();
+  Iterator end();
+};
+
+}  // namespace memgraph::storage
diff --git a/src/utils/disk_utils.hpp b/src/utils/disk_utils.hpp
new file mode 100644
index 000000000..0372a38bb
--- /dev/null
+++ b/src/utils/disk_utils.hpp
@@ -0,0 +1,28 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include "storage/v2/delta.hpp"
+
+namespace memgraph::utils {
+
+inline std::optional<std::string> GetOldDiskKeyOrNull(storage::Delta *head) {
+  while (head->next != nullptr) {
+    head = head->next;
+  }
+  if (head->action == storage::Delta::Action::DELETE_DESERIALIZED_OBJECT) {
+    return head->old_disk_key;
+  }
+  return std::nullopt;
+}
+
+}  // namespace memgraph::utils
diff --git a/src/utils/logging.hpp b/src/utils/logging.hpp
index 87517b05a..0b8eaa639 100644
--- a/src/utils/logging.hpp
+++ b/src/utils/logging.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -75,4 +75,15 @@ void Fatal(const char *msg, const Args &...msg_args) {
 #endif
 
 inline void RedirectToStderr() { spdlog::set_default_logger(spdlog::stderr_color_mt("stderr")); }
+
+// /// Use it for operations that must successfully finish.
+inline void AssertRocksDBStatus(const auto &status) { MG_ASSERT(status.ok(), "rocksdb: {}", status.ToString()); }
+
+inline bool CheckRocksDBStatus(const auto &status) {
+  if (!status.ok()) [[unlikely]] {
+    spdlog::error("rocksdb: {}", status.ToString());
+  }
+  return status.ok();
+}
+
 }  // namespace memgraph::logging
diff --git a/src/utils/math.hpp b/src/utils/math.hpp
index ab0e7a05d..250952a29 100644
--- a/src/utils/math.hpp
+++ b/src/utils/math.hpp
@@ -81,11 +81,9 @@ bool LessThanDecimal(T a, T b) {
   return (b - a) > std::numeric_limits<T>::epsilon();
 }
 
-/*
- * return 0 if a == b
- * return 1 if a > b
- * return -1 if a < b
- */
+/// @return 0 if a == b
+/// @return 1 if a > b
+/// @return -1 if a < b
 template <FloatingPoint T>
 int CompareDecimal(T a, T b) {
   if (ApproxEqualDecimal(a, b)) return 0;
diff --git a/src/utils/memory.hpp b/src/utils/memory.hpp
index 62b1f1d17..b764c83af 100644
--- a/src/utils/memory.hpp
+++ b/src/utils/memory.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
diff --git a/src/utils/rocksdb_serialization.hpp b/src/utils/rocksdb_serialization.hpp
new file mode 100644
index 000000000..5dbe3178b
--- /dev/null
+++ b/src/utils/rocksdb_serialization.hpp
@@ -0,0 +1,291 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include <cstdint>
+#include <iomanip>
+#include <iterator>
+#include <numeric>
+#include <string>
+
+#include "storage/v2/edge_accessor.hpp"
+#include "storage/v2/id_types.hpp"
+#include "storage/v2/property_store.hpp"
+#include "storage/v2/vertex.hpp"
+#include "storage/v2/vertex_accessor.hpp"
+#include "utils/exceptions.hpp"
+#include "utils/string.hpp"
+
+namespace memgraph::utils {
+
+static constexpr const char *outEdgeDirection = "0";
+static constexpr const char *inEdgeDirection = "1";
+
+/// TODO: try to move this to hpp files so that we can follow jump on readings
+
+inline std::string SerializeIdType(const auto &id) { return std::to_string(id.AsUint()); }
+
+inline bool SerializedVertexHasLabels(const std::string &labels) { return !labels.empty(); }
+
+template <typename Collection>
+inline std::vector<std::string> TransformIDsToString(const Collection &labels) {
+  std::vector<std::string> transformed_labels{};
+  std::transform(labels.begin(), labels.end(), std::back_inserter(transformed_labels),
+                 [](const auto &label) { return SerializeIdType(label); });
+  return transformed_labels;
+}
+
+inline std::vector<storage::LabelId> TransformFromStringLabels(const std::vector<std::string> &labels) {
+  std::vector<storage::LabelId> transformed_labels;
+  std::transform(labels.begin(), labels.end(), std::back_inserter(transformed_labels),
+                 [](const auto &label) { return storage::LabelId::FromUint(std::stoull(label)); });
+  return transformed_labels;
+}
+
+inline std::string SerializeLabels(const std::vector<std::string> &labels) {
+  if (labels.empty()) {
+    return "";
+  }
+  std::string result = labels[0];
+  std::string ser_labels =
+      std::accumulate(std::next(labels.begin()), labels.end(), result,
+                      [](const std::string &join, const auto &label_id) { return join + "," + label_id; });
+  return ser_labels;
+}
+
+inline std::string SerializeProperties(const storage::PropertyStore &properties) { return properties.StringBuffer(); }
+
+/// TODO: andi Probably it is better to add delimiter between label,property and the rest of labels
+/// TODO: reuse PutIndexingLabelAndPropertiesFirst
+inline std::string PutIndexingLabelAndPropertyFirst(const std::string &indexing_label,
+                                                    const std::string &indexing_property,
+                                                    const std::vector<std::string> &vertex_labels) {
+  std::string result = indexing_label + "," + indexing_property;
+  for (const auto &label : vertex_labels) {
+    if (label != indexing_label) {
+      result += "," + label;
+    }
+  }
+  return result;
+}
+
+inline std::string PutIndexingLabelAndPropertiesFirst(const std::string &target_label,
+                                                      const std::vector<std::string> &target_properties) {
+  std::string result = target_label;
+  for (const auto &target_property : target_properties) {
+    result += "," + target_property;
+  }
+  return result;
+}
+
+inline std::string SerializeVertexAsValueForAuxiliaryStorages(storage::LabelId label_to_remove,
+                                                              const std::vector<storage::LabelId> &vertex_labels,
+                                                              const storage::PropertyStore &property_store) {
+  std::vector<storage::LabelId> labels_without_target;
+  std::copy_if(vertex_labels.begin(), vertex_labels.end(), std::back_inserter(labels_without_target),
+               [&label_to_remove](const auto &label) { return label_to_remove != label; });
+  std::string result = SerializeLabels(TransformIDsToString(vertex_labels)) + "|";
+  return result + SerializeProperties(property_store);
+}
+
+inline std::string ExtractGidFromKey(const std::string &key) {
+  std::vector<std::string> key_vector = utils::Split(key, "|");
+  return key_vector[1];
+}
+
+inline storage::PropertyStore DeserializePropertiesFromAuxiliaryStorages(const std::string &value) {
+  std::vector<std::string> value_vector = utils::Split(value, "|");
+  std::string properties_str = value_vector[1];
+  return storage::PropertyStore::CreateFromBuffer(properties_str);
+}
+
+inline std::string SerializeVertex(const storage::Vertex &vertex) {
+  std::string result = utils::SerializeLabels(TransformIDsToString(vertex.labels)) + "|";
+  result += utils::SerializeIdType(vertex.gid);
+  return result;
+}
+
+inline std::vector<storage::LabelId> DeserializeLabelsFromMainDiskStorage(const std::string &key) {
+  std::vector<std::string> key_vector = utils::Split(key, "|");
+  std::string labels_str = key_vector[0];
+  if (SerializedVertexHasLabels(labels_str)) {
+    return TransformFromStringLabels(utils::Split(labels_str, ","));
+  }
+  return {};
+}
+
+inline std::vector<std::string> ExtractLabelsFromMainDiskStorage(const std::string &key) {
+  std::vector<std::string> key_vector = utils::Split(key, "|");
+  std::string labels_str = key_vector[0];
+  return utils::Split(labels_str, ",");
+}
+
+inline storage::PropertyStore DeserializePropertiesFromMainDiskStorage(const std::string_view value) {
+  return storage::PropertyStore::CreateFromBuffer(value);
+}
+
+inline std::string ExtractGidFromMainDiskStorage(const std::string &key) { return ExtractGidFromKey(key); }
+
+inline std::string ExtractGidFromUniqueConstraintStorage(const std::string &key) { return ExtractGidFromKey(key); }
+
+/// Serialize vertex to string as a key in unique constraint index KV store.
+/// target_label, target_property_1, target_property_2, ... GID |
+/// commit_timestamp
+inline std::string SerializeVertexAsKeyForUniqueConstraint(const storage::LabelId &constraint_label,
+                                                           const std::set<storage::PropertyId> &constraint_properties,
+                                                           const std::string &gid) {
+  auto key_for_indexing = PutIndexingLabelAndPropertiesFirst(SerializeIdType(constraint_label),
+                                                             TransformIDsToString(constraint_properties));
+  return key_for_indexing + "|" + gid;
+}
+
+inline std::string SerializeVertexAsValueForUniqueConstraint(const storage::LabelId &constraint_label,
+                                                             const std::vector<storage::LabelId> &vertex_labels,
+                                                             const storage::PropertyStore &property_store) {
+  return SerializeVertexAsValueForAuxiliaryStorages(constraint_label, vertex_labels, property_store);
+}
+
+inline storage::LabelId DeserializeConstraintLabelFromUniqueConstraintStorage(const std::string &key) {
+  std::vector<std::string> key_vector = utils::Split(key, "|");
+  std::vector<std::string> constraint_key = utils::Split(key_vector[0], ",");
+  /// TODO: andi Change this to deserialization method directly into the LabelId class
+  return storage::LabelId::FromUint(std::stoull(constraint_key[0]));
+}
+
+inline storage::PropertyStore DeserializePropertiesFromUniqueConstraintStorage(const std::string &value) {
+  return DeserializePropertiesFromAuxiliaryStorages(value);
+}
+
+inline std::string SerializeVertexAsKeyForLabelIndex(const std::string &indexing_label, const std::string &gid) {
+  return indexing_label + "|" + gid;
+}
+
+inline std::string SerializeVertexAsKeyForLabelIndex(storage::LabelId label, storage::Gid gid) {
+  return SerializeVertexAsKeyForLabelIndex(SerializeIdType(label), utils::SerializeIdType(gid));
+}
+
+inline std::string ExtractGidFromLabelIndexStorage(const std::string &key) { return ExtractGidFromKey(key); }
+
+inline std::string SerializeVertexAsValueForLabelIndex(storage::LabelId indexing_label,
+                                                       const std::vector<storage::LabelId> &vertex_labels,
+                                                       const storage::PropertyStore &property_store) {
+  return SerializeVertexAsValueForAuxiliaryStorages(indexing_label, vertex_labels, property_store);
+}
+
+inline std::vector<storage::LabelId> DeserializeLabelsFromLabelIndexStorage(const std::string &value) {
+  const auto value_splitted = utils::Split(value, "|");
+  return TransformFromStringLabels(utils::Split(value_splitted[0], ","));
+}
+
+inline storage::PropertyStore DeserializePropertiesFromLabelIndexStorage(const std::string &value) {
+  return DeserializePropertiesFromAuxiliaryStorages(value);
+}
+
+inline std::string SerializeVertexAsKeyForLabelPropertyIndex(const std::string &indexing_label,
+                                                             const std::string &indexing_property,
+                                                             const std::string &gid) {
+  return indexing_label + "|" + indexing_property + "|" + gid;
+}
+
+inline std::string SerializeVertexAsKeyForLabelPropertyIndex(storage::LabelId label, storage::PropertyId property,
+                                                             storage::Gid gid) {
+  return SerializeVertexAsKeyForLabelPropertyIndex(SerializeIdType(label), SerializeIdType(property),
+                                                   utils::SerializeIdType(gid));
+}
+
+inline std::string SerializeVertexAsValueForLabelPropertyIndex(storage::LabelId indexing_label,
+                                                               const std::vector<storage::LabelId> &vertex_labels,
+                                                               const storage::PropertyStore &property_store) {
+  return SerializeVertexAsValueForAuxiliaryStorages(indexing_label, vertex_labels, property_store);
+}
+
+inline std::string ExtractGidFromLabelPropertyIndexStorage(const std::string &key) {
+  std::vector<std::string> key_vector = utils::Split(key, "|");
+  return key_vector[2];
+}
+
+/// TODO: refactor into one method with label index storage
+inline std::vector<storage::LabelId> DeserializeLabelsFromLabelPropertyIndexStorage(const std::string &value) {
+  const auto value_splitted = utils::Split(value, "|");
+  return TransformFromStringLabels(utils::Split(value_splitted[0], ","));
+}
+
+inline storage::PropertyStore DeserializePropertiesFromLabelPropertyIndexStorage(const std::string &value) {
+  return DeserializePropertiesFromAuxiliaryStorages(value);
+}
+
+/// Serialize edge as two KV entries
+/// vertex_gid_1 | vertex_gid_2 | direction | edge_type | GID | commit_timestamp
+inline std::string SerializeEdge(storage::EdgeAccessor *edge_acc) {
+  // Serialized objects
+  auto from_gid = utils::SerializeIdType(edge_acc->FromVertex().Gid());
+  auto to_gid = utils::SerializeIdType(edge_acc->ToVertex().Gid());
+  auto edge_type = utils::SerializeIdType(edge_acc->EdgeType());
+  auto edge_gid = utils::SerializeIdType(edge_acc->Gid());
+  // source->destination key
+  std::string src_dest_key = from_gid + "|";
+  src_dest_key += to_gid + "|";
+  src_dest_key += outEdgeDirection;
+  src_dest_key += "|" + edge_type + "|";
+  src_dest_key += edge_gid;
+  return src_dest_key;
+}
+
+/// Serialize edge as two KV entries
+/// vertex_gid_1 | vertex_gid_2 | direction | edge_type | GID | commit_timestamp
+/// @tparam src_vertex_gid, dest_vertex_gid: Gid of the source and destination vertices
+/// @tparam edge: Edge to be serialized
+/// @tparam edge_type_id: EdgeTypeId of the edge
+inline std::string SerializeEdge(storage::Gid src_vertex_gid, storage::Gid dest_vertex_gid,
+                                 storage::EdgeTypeId edge_type_id, const storage::EdgeRef edge_ref,
+                                 bool properties_on_edges) {
+  // Serialized objects
+  auto from_gid = utils::SerializeIdType(src_vertex_gid);
+  auto to_gid = utils::SerializeIdType(dest_vertex_gid);
+  auto edge_type = utils::SerializeIdType(edge_type_id);
+  std::string edge_gid;
+
+  if (properties_on_edges) {
+    edge_gid = utils::SerializeIdType(edge_ref.ptr->gid);
+  } else {
+    edge_gid = utils::SerializeIdType(edge_ref.gid);
+  }
+
+  // source->destination key
+  std::string src_dest_key = from_gid + "|";
+  src_dest_key += to_gid + "|";
+  src_dest_key += outEdgeDirection;
+  src_dest_key += "|" + edge_type + "|";
+  src_dest_key += edge_gid;
+  return src_dest_key;
+}
+
+/// TODO: (andi): This can potentially be a problem on big-endian machines.
+inline void PutFixed64(std::string *dst, uint64_t value) {
+  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
+  dst->append(const_cast<const char *>(reinterpret_cast<char *>(&value)), sizeof(value));
+}
+
+inline uint64_t DecodeFixed64(const char *ptr) {
+  // Load the raw bytes
+  uint64_t result = 0;
+  memcpy(&result, ptr, sizeof(result));  // gcc optimizes this to a plain load
+  return result;
+}
+
+inline std::string StringTimestamp(uint64_t ts) {
+  std::string ret;
+  PutFixed64(&ret, ts);
+  return ret;
+}
+
+}  // namespace memgraph::utils
diff --git a/src/utils/skip_list.hpp b/src/utils/skip_list.hpp
index 69326ab5e..2a7090678 100644
--- a/src/utils/skip_list.hpp
+++ b/src/utils/skip_list.hpp
@@ -21,13 +21,17 @@
 #include <random>
 #include <utility>
 
+#include "spdlog/spdlog.h"
 #include "utils/bound.hpp"
 #include "utils/linux.hpp"
 #include "utils/logging.hpp"
 #include "utils/memory.hpp"
+#include "utils/memory_tracker.hpp"
 #include "utils/on_scope_exit.hpp"
+#include "utils/readable_size.hpp"
 #include "utils/spin_lock.hpp"
 #include "utils/stack.hpp"
+#include "utils/stat.hpp"
 
 // This code heavily depends on atomic operations. For a more detailed
 // description of how exactly atomic operations work, see:
@@ -345,9 +349,6 @@ class SkipListGc final {
   MemoryResource *GetMemoryResource() const { return memory_; }
 
   void Clear() {
-#ifndef NDEBUG
-    MG_ASSERT(alive_accessors_ == 0, "The SkipList can't be cleared while there are existing accessors!");
-#endif
     // Delete all allocated blocks.
     Block *head = head_.load(std::memory_order_acquire);
     while (head != nullptr) {
@@ -890,7 +891,7 @@ class SkipList final {
   MemoryResource *GetMemoryResource() const { return gc_.GetMemoryResource(); }
 
   /// This function removes all elements from the list.
-  /// NOTE: The function *isn't* thread-safe. It must be called while there are
+  /// NOTE: The function *isn't* thread-safe. It must be called only if there are
   /// no more active accessors using the list.
   void clear() {
     TNode *curr = head_->nexts[0].load(std::memory_order_acquire);
diff --git a/tests/benchmark/expansion.cpp b/tests/benchmark/expansion.cpp
index 5ff412eea..5b7988f3a 100644
--- a/tests/benchmark/expansion.cpp
+++ b/tests/benchmark/expansion.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -15,46 +15,44 @@
 #include "query/config.hpp"
 #include "query/interpreter.hpp"
 #include "query/typed_value.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/isolation_level.hpp"
-#include "storage/v2/storage.hpp"
 
 class ExpansionBenchFixture : public benchmark::Fixture {
  protected:
-  std::optional<memgraph::storage::Storage> db;
   std::optional<memgraph::query::InterpreterContext> interpreter_context;
   std::optional<memgraph::query::Interpreter> interpreter;
   std::filesystem::path data_directory{std::filesystem::temp_directory_path() / "expansion-benchmark"};
 
   void SetUp(const benchmark::State &state) override {
-    db.emplace();
+    interpreter_context.emplace(memgraph::storage::Config{}, memgraph::query::InterpreterConfig{}, data_directory);
+    auto *db = interpreter_context->db.get();
 
     auto label = db->NameToLabel("Starting");
 
     {
       auto dba = db->Access();
-      for (int i = 0; i < state.range(0); i++) dba.CreateVertex();
+      for (int i = 0; i < state.range(0); i++) dba->CreateVertex();
 
       // the fixed part is one vertex expanding to 1000 others
-      auto start = dba.CreateVertex();
+      auto start = dba->CreateVertex();
       MG_ASSERT(start.AddLabel(label).HasValue());
-      auto edge_type = dba.NameToEdgeType("edge_type");
+      auto edge_type = dba->NameToEdgeType("edge_type");
       for (int i = 0; i < 1000; i++) {
-        auto dest = dba.CreateVertex();
-        MG_ASSERT(dba.CreateEdge(&start, &dest, edge_type).HasValue());
+        auto dest = dba->CreateVertex();
+        MG_ASSERT(dba->CreateEdge(&start, &dest, edge_type).HasValue());
       }
-      MG_ASSERT(!dba.Commit().HasError());
+      MG_ASSERT(!dba->Commit().HasError());
     }
 
     MG_ASSERT(!db->CreateIndex(label).HasError());
 
-    interpreter_context.emplace(&*db, memgraph::query::InterpreterConfig{}, data_directory);
     interpreter.emplace(&*interpreter_context);
   }
 
   void TearDown(const benchmark::State &) override {
     interpreter = std::nullopt;
     interpreter_context = std::nullopt;
-    db = std::nullopt;
     std::filesystem::remove_all(data_directory);
   }
 };
@@ -63,7 +61,7 @@ BENCHMARK_DEFINE_F(ExpansionBenchFixture, Match)(benchmark::State &state) {
   auto query = "MATCH (s:Starting) return s";
 
   while (state.KeepRunning()) {
-    ResultStreamFaker results(&*db);
+    ResultStreamFaker results(interpreter_context->db.get());
     interpreter->Prepare(query, {}, nullptr);
     interpreter->PullAll(&results);
   }
@@ -78,7 +76,7 @@ BENCHMARK_DEFINE_F(ExpansionBenchFixture, Expand)(benchmark::State &state) {
   auto query = "MATCH (s:Starting) WITH s MATCH (s)--(d) RETURN count(d)";
 
   while (state.KeepRunning()) {
-    ResultStreamFaker results(&*db);
+    ResultStreamFaker results(interpreter_context->db.get());
     interpreter->Prepare(query, {}, nullptr);
     interpreter->PullAll(&results);
   }
diff --git a/tests/benchmark/query/eval.cpp b/tests/benchmark/query/eval.cpp
index 72e185793..5e5acea7f 100644
--- a/tests/benchmark/query/eval.cpp
+++ b/tests/benchmark/query/eval.cpp
@@ -14,6 +14,7 @@
 #include "query/db_accessor.hpp"
 #include "query/interpret/eval.hpp"
 #include "query/interpreter.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/storage.hpp"
 
 // The following classes are wrappers for memgraph::utils::MemoryResource, so that we can
@@ -38,9 +39,9 @@ static void MapLiteral(benchmark::State &state) {
   memgraph::query::SymbolTable symbol_table;
   TMemory memory;
   memgraph::query::Frame frame(symbol_table.max_position(), memory.get());
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   std::unordered_map<memgraph::query::PropertyIx, memgraph::query::Expression *> elements;
   for (int64_t i = 0; i < state.range(0); ++i) {
     elements.emplace(ast.GetPropertyIx("prop" + std::to_string(i)), ast.Create<memgraph::query::PrimitiveLiteral>(i));
@@ -69,9 +70,9 @@ static void AdditionOperator(benchmark::State &state) {
   memgraph::query::SymbolTable symbol_table;
   TMemory memory;
   memgraph::query::Frame frame(symbol_table.max_position(), memory.get());
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   memgraph::query::Expression *expr = ast.Create<memgraph::query::PrimitiveLiteral>(0);
   for (int64_t i = 0; i < state.range(0); ++i) {
     expr = ast.Create<memgraph::query::AdditionOperator>(expr, ast.Create<memgraph::query::PrimitiveLiteral>(i));
diff --git a/tests/benchmark/query/execution.cpp b/tests/benchmark/query/execution.cpp
index 69aedaff9..1d1f26d3f 100644
--- a/tests/benchmark/query/execution.cpp
+++ b/tests/benchmark/query/execution.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -30,7 +30,7 @@
 #include "query/frontend/semantic/required_privileges.hpp"
 #include "query/frontend/semantic/symbol_generator.hpp"
 #include "query/interpreter.hpp"
-#include "storage/v2/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 // The following classes are wrappers for memgraph::utils::MemoryResource, so that we can
 // use BENCHMARK_TEMPLATE
@@ -62,8 +62,8 @@ class PoolResource final {
 
 static void AddVertices(memgraph::storage::Storage *db, int vertex_count) {
   auto dba = db->Access();
-  for (int i = 0; i < vertex_count; i++) dba.CreateVertex();
-  MG_ASSERT(!dba.Commit().HasError());
+  for (int i = 0; i < vertex_count; i++) dba->CreateVertex();
+  MG_ASSERT(!dba->Commit().HasError());
 }
 
 static const char *kStartLabel = "start";
@@ -71,17 +71,17 @@ static const char *kStartLabel = "start";
 static void AddStarGraph(memgraph::storage::Storage *db, int spoke_count, int depth) {
   {
     auto dba = db->Access();
-    auto center_vertex = dba.CreateVertex();
-    MG_ASSERT(center_vertex.AddLabel(dba.NameToLabel(kStartLabel)).HasValue());
+    auto center_vertex = dba->CreateVertex();
+    MG_ASSERT(center_vertex.AddLabel(dba->NameToLabel(kStartLabel)).HasValue());
     for (int i = 0; i < spoke_count; ++i) {
       auto prev_vertex = center_vertex;
       for (int j = 0; j < depth; ++j) {
-        auto dest = dba.CreateVertex();
-        MG_ASSERT(dba.CreateEdge(&prev_vertex, &dest, dba.NameToEdgeType("Type")).HasValue());
+        auto dest = dba->CreateVertex();
+        MG_ASSERT(dba->CreateEdge(&prev_vertex, &dest, dba->NameToEdgeType("Type")).HasValue());
         prev_vertex = dest;
       }
     }
-    MG_ASSERT(!dba.Commit().HasError());
+    MG_ASSERT(!dba->Commit().HasError());
   }
   MG_ASSERT(!db->CreateIndex(db->NameToLabel(kStartLabel)).HasError());
 }
@@ -91,19 +91,19 @@ static void AddTree(memgraph::storage::Storage *db, int vertex_count) {
     auto dba = db->Access();
     std::vector<memgraph::storage::VertexAccessor> vertices;
     vertices.reserve(vertex_count);
-    auto root = dba.CreateVertex();
-    MG_ASSERT(root.AddLabel(dba.NameToLabel(kStartLabel)).HasValue());
+    auto root = dba->CreateVertex();
+    MG_ASSERT(root.AddLabel(dba->NameToLabel(kStartLabel)).HasValue());
     vertices.push_back(root);
     // NOLINTNEXTLINE(cert-msc32-c,cert-msc51-cpp)
     std::mt19937_64 rg(42);
     for (int i = 1; i < vertex_count; ++i) {
-      auto v = dba.CreateVertex();
+      auto v = dba->CreateVertex();
       std::uniform_int_distribution<> dis(0U, vertices.size() - 1U);
       auto &parent = vertices.at(dis(rg));
-      MG_ASSERT(dba.CreateEdge(&parent, &v, dba.NameToEdgeType("Type")).HasValue());
+      MG_ASSERT(dba->CreateEdge(&parent, &v, dba->NameToEdgeType("Type")).HasValue());
       vertices.push_back(v);
     }
-    MG_ASSERT(!dba.Commit().HasError());
+    MG_ASSERT(!dba->Commit().HasError());
   }
   MG_ASSERT(!db->CreateIndex(db->NameToLabel(kStartLabel)).HasError());
 }
@@ -124,16 +124,16 @@ template <class TMemory>
 static void Distinct(benchmark::State &state) {
   memgraph::query::AstStorage ast;
   memgraph::query::Parameters parameters;
-  memgraph::storage::Storage db;
-  AddVertices(&db, state.range(0));
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  AddVertices(db.get(), state.range(0));
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto query_string = "MATCH (s) RETURN DISTINCT s";
   auto *cypher_query = ParseCypherQuery(query_string, &ast);
   auto symbol_table = memgraph::query::MakeSymbolTable(cypher_query);
   auto context = memgraph::query::plan::MakePlanningContext(&ast, &symbol_table, cypher_query, &dba);
   auto plan_and_cost = memgraph::query::plan::MakeLogicalPlan(&context, parameters, false);
-  ResultStreamFaker results(&db);
+  ResultStreamFaker results(db.get());
   // We need to only set the memory for temporary (per pull) evaluations
   TMemory per_pull_memory;
   memgraph::query::EvaluationContext evaluation_context{per_pull_memory.get()};
@@ -174,12 +174,12 @@ template <class TMemory>
 static void ExpandVariable(benchmark::State &state) {
   memgraph::query::AstStorage ast;
   memgraph::query::Parameters parameters;
-  memgraph::storage::Storage db;
-  AddStarGraph(&db, state.range(0), state.range(1));
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  AddStarGraph(db.get(), state.range(0), state.range(1));
   memgraph::query::SymbolTable symbol_table;
   auto expand_variable = MakeExpandVariable(memgraph::query::EdgeAtom::Type::DEPTH_FIRST, &symbol_table);
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // We need to only set the memory for temporary (per pull) evaluations
   TMemory per_pull_memory;
   memgraph::query::EvaluationContext evaluation_context{per_pull_memory.get()};
@@ -213,12 +213,12 @@ template <class TMemory>
 static void ExpandBfs(benchmark::State &state) {
   memgraph::query::AstStorage ast;
   memgraph::query::Parameters parameters;
-  memgraph::storage::Storage db;
-  AddTree(&db, state.range(0));
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  AddTree(db.get(), state.range(0));
   memgraph::query::SymbolTable symbol_table;
   auto expand_variable = MakeExpandVariable(memgraph::query::EdgeAtom::Type::BREADTH_FIRST, &symbol_table);
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // We need to only set the memory for temporary (per pull) evaluations
   TMemory per_pull_memory;
   memgraph::query::EvaluationContext evaluation_context{per_pull_memory.get()};
@@ -246,14 +246,14 @@ template <class TMemory>
 static void ExpandShortest(benchmark::State &state) {
   memgraph::query::AstStorage ast;
   memgraph::query::Parameters parameters;
-  memgraph::storage::Storage db;
-  AddTree(&db, state.range(0));
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  AddTree(db.get(), state.range(0));
   memgraph::query::SymbolTable symbol_table;
   auto expand_variable = MakeExpandVariable(memgraph::query::EdgeAtom::Type::BREADTH_FIRST, &symbol_table);
   expand_variable.common_.existing_node = true;
   auto dest_symbol = expand_variable.common_.node_symbol;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // We need to only set the memory for temporary (per pull) evaluations
   TMemory per_pull_memory;
   memgraph::query::EvaluationContext evaluation_context{per_pull_memory.get()};
@@ -284,8 +284,8 @@ template <class TMemory>
 static void ExpandWeightedShortest(benchmark::State &state) {
   memgraph::query::AstStorage ast;
   memgraph::query::Parameters parameters;
-  memgraph::storage::Storage db;
-  AddTree(&db, state.range(0));
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  AddTree(db.get(), state.range(0));
   memgraph::query::SymbolTable symbol_table;
   auto expand_variable = MakeExpandVariable(memgraph::query::EdgeAtom::Type::WEIGHTED_SHORTEST_PATH, &symbol_table);
   expand_variable.common_.existing_node = true;
@@ -293,8 +293,8 @@ static void ExpandWeightedShortest(benchmark::State &state) {
       symbol_table.CreateSymbol("edge", false), symbol_table.CreateSymbol("vertex", false),
       ast.Create<memgraph::query::PrimitiveLiteral>(1)};
   auto dest_symbol = expand_variable.common_.node_symbol;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // We need to only set the memory for temporary (per pull) evaluations
   TMemory per_pull_memory;
   memgraph::query::EvaluationContext evaluation_context{per_pull_memory.get()};
@@ -327,8 +327,8 @@ template <class TMemory>
 static void Accumulate(benchmark::State &state) {
   memgraph::query::AstStorage ast;
   memgraph::query::Parameters parameters;
-  memgraph::storage::Storage db;
-  AddVertices(&db, state.range(1));
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  AddVertices(db.get(), state.range(1));
   memgraph::query::SymbolTable symbol_table;
   auto scan_all = std::make_shared<memgraph::query::plan::ScanAll>(nullptr, symbol_table.CreateSymbol("v", false));
   std::vector<memgraph::query::Symbol> symbols;
@@ -338,8 +338,8 @@ static void Accumulate(benchmark::State &state) {
   }
   memgraph::query::plan::Accumulate accumulate(scan_all, symbols,
                                                /* advance_command= */ false);
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // We need to only set the memory for temporary (per pull) evaluations
   TMemory per_pull_memory;
   memgraph::query::EvaluationContext evaluation_context{per_pull_memory.get()};
@@ -368,8 +368,8 @@ template <class TMemory>
 static void Aggregate(benchmark::State &state) {
   memgraph::query::AstStorage ast;
   memgraph::query::Parameters parameters;
-  memgraph::storage::Storage db;
-  AddVertices(&db, state.range(1));
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  AddVertices(db.get(), state.range(1));
   memgraph::query::SymbolTable symbol_table;
   auto scan_all = std::make_shared<memgraph::query::plan::ScanAll>(nullptr, symbol_table.CreateSymbol("v", false));
   std::vector<memgraph::query::Symbol> symbols;
@@ -387,8 +387,8 @@ static void Aggregate(benchmark::State &state) {
                             symbol_table.CreateSymbol("out" + std::to_string(i), false)});
   }
   memgraph::query::plan::Aggregate aggregate(scan_all, aggregations, group_by, symbols);
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // We need to only set the memory for temporary (per pull) evaluations
   TMemory per_pull_memory;
   memgraph::query::EvaluationContext evaluation_context{per_pull_memory.get()};
@@ -421,8 +421,8 @@ template <class TMemory>
 static void OrderBy(benchmark::State &state) {
   memgraph::query::AstStorage ast;
   memgraph::query::Parameters parameters;
-  memgraph::storage::Storage db;
-  AddVertices(&db, state.range(1));
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  AddVertices(db.get(), state.range(1));
   memgraph::query::SymbolTable symbol_table;
   auto scan_all = std::make_shared<memgraph::query::plan::ScanAll>(nullptr, symbol_table.CreateSymbol("v", false));
   std::vector<memgraph::query::Symbol> symbols;
@@ -437,8 +437,8 @@ static void OrderBy(benchmark::State &state) {
     sort_items.push_back({memgraph::query::Ordering::ASC, ast.Create<memgraph::query::PrimitiveLiteral>(rand_value)});
   }
   memgraph::query::plan::OrderBy order_by(scan_all, sort_items, symbols);
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // We need to only set the memory for temporary (per pull) evaluations
   TMemory per_pull_memory;
   memgraph::query::EvaluationContext evaluation_context{per_pull_memory.get()};
@@ -467,16 +467,16 @@ template <class TMemory>
 static void Unwind(benchmark::State &state) {
   memgraph::query::AstStorage ast;
   memgraph::query::Parameters parameters;
-  memgraph::storage::Storage db;
-  AddVertices(&db, state.range(0));
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  AddVertices(db.get(), state.range(0));
   memgraph::query::SymbolTable symbol_table;
   auto scan_all = std::make_shared<memgraph::query::plan::ScanAll>(nullptr, symbol_table.CreateSymbol("v", false));
   auto list_sym = symbol_table.CreateSymbol("list", false);
   auto *list_expr = ast.Create<memgraph::query::Identifier>("list")->MapTo(list_sym);
   auto out_sym = symbol_table.CreateSymbol("out", false);
   memgraph::query::plan::Unwind unwind(scan_all, list_expr, out_sym);
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // We need to only set the memory for temporary (per pull) evaluations
   TMemory per_pull_memory;
   memgraph::query::EvaluationContext evaluation_context{per_pull_memory.get()};
@@ -503,7 +503,7 @@ template <class TMemory>
 // NOLINTNEXTLINE(google-runtime-references)
 static void Foreach(benchmark::State &state) {
   memgraph::query::AstStorage ast;
-  memgraph::storage::Storage db;
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
   memgraph::query::SymbolTable symbol_table;
   auto list_sym = symbol_table.CreateSymbol("list", false);
   auto *list_expr = ast.Create<memgraph::query::Identifier>("list")->MapTo(list_sym);
@@ -512,8 +512,8 @@ static void Foreach(benchmark::State &state) {
       std::make_shared<memgraph::query::plan::CreateNode>(nullptr, memgraph::query::plan::NodeCreationInfo{});
   auto foreach = std::make_shared<memgraph::query::plan::Foreach>(nullptr, std::move(create_node), list_expr, out_sym);
 
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   TMemory per_pull_memory;
   memgraph::query::EvaluationContext evaluation_context{per_pull_memory.get()};
   while (state.KeepRunning()) {
diff --git a/tests/benchmark/query/planner.cpp b/tests/benchmark/query/planner.cpp
index 4fe4ee28c..2beb4fc41 100644
--- a/tests/benchmark/query/planner.cpp
+++ b/tests/benchmark/query/planner.cpp
@@ -17,7 +17,7 @@
 #include "query/plan/cost_estimator.hpp"
 #include "query/plan/planner.hpp"
 #include "query/plan/vertex_count_cache.hpp"
-#include "storage/v2/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 // Add chained MATCH (node1) -- (node2), MATCH (node2) -- (node3) ... clauses.
 static memgraph::query::CypherQuery *AddChainedMatches(int num_matches, memgraph::query::AstStorage &storage) {
@@ -43,9 +43,9 @@ static memgraph::query::CypherQuery *AddChainedMatches(int num_matches, memgraph
 }
 
 static void BM_PlanChainedMatches(benchmark::State &state) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   while (state.KeepRunning()) {
     state.PauseTiming();
     memgraph::query::AstStorage storage;
@@ -98,24 +98,24 @@ static auto CreateIndexedVertices(int index_count, int vertex_count, memgraph::s
   auto dba = db->Access();
   for (int vi = 0; vi < vertex_count; ++vi) {
     for (int index = 0; index < index_count; ++index) {
-      auto vertex = dba.CreateVertex();
+      auto vertex = dba->CreateVertex();
       MG_ASSERT(vertex.AddLabel(label).HasValue());
       MG_ASSERT(vertex.SetProperty(prop, memgraph::storage::PropertyValue(index)).HasValue());
     }
   }
-  MG_ASSERT(!dba.Commit().HasError());
+  MG_ASSERT(!dba->Commit().HasError());
   return std::make_pair("label", "prop");
 }
 
 static void BM_PlanAndEstimateIndexedMatching(benchmark::State &state) {
-  memgraph::storage::Storage db;
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
   std::string label;
   std::string prop;
   int index_count = state.range(0);
   int vertex_count = state.range(1);
-  std::tie(label, prop) = CreateIndexedVertices(index_count, vertex_count, &db);
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  std::tie(label, prop) = CreateIndexedVertices(index_count, vertex_count, db.get());
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   memgraph::query::Parameters parameters;
   while (state.KeepRunning()) {
     state.PauseTiming();
@@ -137,14 +137,14 @@ static void BM_PlanAndEstimateIndexedMatching(benchmark::State &state) {
 }
 
 static void BM_PlanAndEstimateIndexedMatchingWithCachedCounts(benchmark::State &state) {
-  memgraph::storage::Storage db;
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
   std::string label;
   std::string prop;
   int index_count = state.range(0);
   int vertex_count = state.range(1);
-  std::tie(label, prop) = CreateIndexedVertices(index_count, vertex_count, &db);
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  std::tie(label, prop) = CreateIndexedVertices(index_count, vertex_count, db.get());
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto vertex_counts = memgraph::query::plan::MakeVertexCountCache(&dba);
   memgraph::query::Parameters parameters;
   while (state.KeepRunning()) {
diff --git a/tests/benchmark/storage_v2_gc.cpp b/tests/benchmark/storage_v2_gc.cpp
index a8f023ced..3941fcbe1 100644
--- a/tests/benchmark/storage_v2_gc.cpp
+++ b/tests/benchmark/storage_v2_gc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,6 +13,7 @@
 
 #include <gflags/gflags.h>
 
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/storage.hpp"
 #include "utils/timer.hpp"
 
@@ -43,12 +44,12 @@ void UpdateLabelFunc(int thread_id, memgraph::storage::Storage *storage,
   for (int iter = 0; iter < num_iterations; ++iter) {
     auto acc = storage->Access();
     memgraph::storage::Gid gid = vertices.at(vertex_dist(gen));
-    std::optional<memgraph::storage::VertexAccessor> vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     MG_ASSERT(vertex.has_value(), "Vertex with GID {} doesn't exist", gid.AsUint());
     if (vertex->AddLabel(memgraph::storage::LabelId::FromUint(label_dist(gen))).HasValue()) {
-      MG_ASSERT(!acc.Commit().HasError());
+      MG_ASSERT(!acc->Commit().HasError());
     } else {
-      acc.Abort();
+      acc->Abort();
     }
   }
 }
@@ -57,21 +58,21 @@ int main(int argc, char *argv[]) {
   gflags::ParseCommandLineFlags(&argc, &argv, true);
 
   for (const auto &config : TestConfigurations) {
-    memgraph::storage::Storage storage(config.second);
+    std::unique_ptr<memgraph::storage::Storage> storage(new memgraph::storage::InMemoryStorage(config.second));
     std::vector<memgraph::storage::Gid> vertices;
     {
-      auto acc = storage.Access();
+      auto acc = storage->Access();
       for (int i = 0; i < FLAGS_num_vertices; ++i) {
-        vertices.push_back(acc.CreateVertex().Gid());
+        vertices.push_back(acc->CreateVertex().Gid());
       }
-      MG_ASSERT(!acc.Commit().HasError());
+      MG_ASSERT(!acc->Commit().HasError());
     }
 
     memgraph::utils::Timer timer;
     std::vector<std::thread> threads;
     threads.reserve(FLAGS_num_threads);
     for (int i = 0; i < FLAGS_num_threads; ++i) {
-      threads.emplace_back(UpdateLabelFunc, i, &storage, vertices, FLAGS_num_iterations);
+      threads.emplace_back(UpdateLabelFunc, i, storage.get(), vertices, FLAGS_num_iterations);
     }
 
     for (int i = 0; i < FLAGS_num_threads; ++i) {
diff --git a/tests/concurrent/storage_indices.cpp b/tests/concurrent/storage_indices.cpp
index 8c50ef18b..4757be189 100644
--- a/tests/concurrent/storage_indices.cpp
+++ b/tests/concurrent/storage_indices.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -15,7 +15,7 @@
 #include <fmt/format.h>
 #include <gtest/gtest.h>
 
-#include "storage/v2/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/storage_error.hpp"
 #include "utils/thread.hpp"
 
@@ -27,10 +27,10 @@ const uint64_t kVerifierBatchSize = 10;
 const uint64_t kMutatorBatchSize = 1000;
 
 TEST(Storage, LabelIndex) {
-  auto store = memgraph::storage::Storage();
+  std::unique_ptr<memgraph::storage::Storage> store{new memgraph::storage::InMemoryStorage()};
 
-  auto label = store.NameToLabel("label");
-  ASSERT_FALSE(store.CreateIndex(label).HasError());
+  auto label = store->NameToLabel("label");
+  ASSERT_FALSE(store->CreateIndex(label).HasError());
 
   std::vector<std::thread> verifiers;
   verifiers.reserve(kNumVerifiers);
@@ -41,17 +41,17 @@ TEST(Storage, LabelIndex) {
       gids.reserve(kNumIterations * kVerifierBatchSize);
       for (uint64_t i = 0; i < kNumIterations; ++i) {
         for (uint64_t j = 0; j < kVerifierBatchSize; ++j) {
-          auto acc = store.Access();
-          auto vertex = acc.CreateVertex();
+          auto acc = store->Access();
+          auto vertex = acc->CreateVertex();
           gids.emplace(vertex.Gid(), false);
           auto ret = vertex.AddLabel(label);
           ASSERT_TRUE(ret.HasValue());
           ASSERT_TRUE(*ret);
-          ASSERT_FALSE(acc.Commit().HasError());
+          ASSERT_FALSE(acc->Commit().HasError());
         }
         {
-          auto acc = store.Access();
-          auto vertices = acc.Vertices(label, memgraph::storage::View::OLD);
+          auto acc = store->Access();
+          auto vertices = acc->Vertices(label, memgraph::storage::View::OLD);
           for (auto vertex : vertices) {
             auto it = gids.find(vertex.Gid());
             if (it != gids.end()) {
@@ -78,20 +78,20 @@ TEST(Storage, LabelIndex) {
       gids.resize(kMutatorBatchSize);
       while (mutators_run.load(std::memory_order_acquire)) {
         for (uint64_t i = 0; i < kMutatorBatchSize; ++i) {
-          auto acc = store.Access();
-          auto vertex = acc.CreateVertex();
+          auto acc = store->Access();
+          auto vertex = acc->CreateVertex();
           gids[i] = vertex.Gid();
           auto ret = vertex.AddLabel(label);
           ASSERT_TRUE(ret.HasValue());
           ASSERT_TRUE(*ret);
-          ASSERT_FALSE(acc.Commit().HasError());
+          ASSERT_FALSE(acc->Commit().HasError());
         }
         for (uint64_t i = 0; i < kMutatorBatchSize; ++i) {
-          auto acc = store.Access();
-          auto vertex = acc.FindVertex(gids[i], memgraph::storage::View::OLD);
+          auto acc = store->Access();
+          auto vertex = acc->FindVertex(gids[i], memgraph::storage::View::OLD);
           ASSERT_TRUE(vertex);
-          ASSERT_TRUE(acc.DeleteVertex(&*vertex).HasValue());
-          ASSERT_FALSE(acc.Commit().HasError());
+          ASSERT_TRUE(acc->DeleteVertex(&*vertex).HasValue());
+          ASSERT_FALSE(acc->Commit().HasError());
         }
       }
     });
@@ -108,11 +108,11 @@ TEST(Storage, LabelIndex) {
 }
 
 TEST(Storage, LabelPropertyIndex) {
-  auto store = memgraph::storage::Storage();
+  std::unique_ptr<memgraph::storage::Storage> store{new memgraph::storage::InMemoryStorage()};
 
-  auto label = store.NameToLabel("label");
-  auto prop = store.NameToProperty("prop");
-  ASSERT_FALSE(store.CreateIndex(label, prop).HasError());
+  auto label = store->NameToLabel("label");
+  auto prop = store->NameToProperty("prop");
+  ASSERT_FALSE(store->CreateIndex(label, prop).HasError());
 
   std::vector<std::thread> verifiers;
   verifiers.reserve(kNumVerifiers);
@@ -123,8 +123,8 @@ TEST(Storage, LabelPropertyIndex) {
       gids.reserve(kNumIterations * kVerifierBatchSize);
       for (uint64_t i = 0; i < kNumIterations; ++i) {
         for (uint64_t j = 0; j < kVerifierBatchSize; ++j) {
-          auto acc = store.Access();
-          auto vertex = acc.CreateVertex();
+          auto acc = store->Access();
+          auto vertex = acc->CreateVertex();
           gids.emplace(vertex.Gid(), false);
           {
             auto ret = vertex.AddLabel(label);
@@ -136,11 +136,11 @@ TEST(Storage, LabelPropertyIndex) {
             ASSERT_TRUE(old_value.HasValue());
             ASSERT_TRUE(old_value->IsNull());
           }
-          ASSERT_FALSE(acc.Commit().HasError());
+          ASSERT_FALSE(acc->Commit().HasError());
         }
         {
-          auto acc = store.Access();
-          auto vertices = acc.Vertices(label, prop, memgraph::storage::View::OLD);
+          auto acc = store->Access();
+          auto vertices = acc->Vertices(label, prop, memgraph::storage::View::OLD);
           for (auto vertex : vertices) {
             auto it = gids.find(vertex.Gid());
             if (it != gids.end()) {
@@ -167,8 +167,8 @@ TEST(Storage, LabelPropertyIndex) {
       gids.resize(kMutatorBatchSize);
       while (mutators_run.load(std::memory_order_acquire)) {
         for (uint64_t i = 0; i < kMutatorBatchSize; ++i) {
-          auto acc = store.Access();
-          auto vertex = acc.CreateVertex();
+          auto acc = store->Access();
+          auto vertex = acc->CreateVertex();
           gids[i] = vertex.Gid();
           {
             auto ret = vertex.AddLabel(label);
@@ -180,14 +180,14 @@ TEST(Storage, LabelPropertyIndex) {
             ASSERT_TRUE(old_value.HasValue());
             ASSERT_TRUE(old_value->IsNull());
           }
-          ASSERT_FALSE(acc.Commit().HasError());
+          ASSERT_FALSE(acc->Commit().HasError());
         }
         for (uint64_t i = 0; i < kMutatorBatchSize; ++i) {
-          auto acc = store.Access();
-          auto vertex = acc.FindVertex(gids[i], memgraph::storage::View::OLD);
+          auto acc = store->Access();
+          auto vertex = acc->FindVertex(gids[i], memgraph::storage::View::OLD);
           ASSERT_TRUE(vertex);
-          ASSERT_TRUE(acc.DeleteVertex(&*vertex).HasValue());
-          ASSERT_FALSE(acc.Commit().HasError());
+          ASSERT_TRUE(acc->DeleteVertex(&*vertex).HasValue());
+          ASSERT_FALSE(acc->Commit().HasError());
         }
       }
     });
diff --git a/tests/concurrent/storage_unique_constraints.cpp b/tests/concurrent/storage_unique_constraints.cpp
index 63ad00abb..f2f73930e 100644
--- a/tests/concurrent/storage_unique_constraints.cpp
+++ b/tests/concurrent/storage_unique_constraints.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,8 +13,8 @@
 
 #include <gtest/gtest.h>
 
-#include "storage/v2/constraints.hpp"
-#include "storage/v2/storage.hpp"
+#include "storage/v2/constraints/constraints.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 const int kNumThreads = 8;
 
@@ -27,23 +27,23 @@ using memgraph::storage::PropertyValue;
 class StorageUniqueConstraints : public ::testing::Test {
  protected:
   StorageUniqueConstraints()
-      : label(storage.NameToLabel("label")),
-        prop1(storage.NameToProperty("prop1")),
-        prop2(storage.NameToProperty("prop2")),
-        prop3(storage.NameToProperty("prop3")) {}
+      : label(storage->NameToLabel("label")),
+        prop1(storage->NameToProperty("prop1")),
+        prop2(storage->NameToProperty("prop2")),
+        prop3(storage->NameToProperty("prop3")) {}
 
   void SetUp() override {
     // Create initial vertices.
-    auto acc = storage.Access();
+    auto acc = storage->Access();
     // NOLINTNEXTLINE(modernize-loop-convert)
     for (int i = 0; i < kNumThreads; ++i) {
-      auto vertex = acc.CreateVertex();
+      auto vertex = acc->CreateVertex();
       gids[i] = vertex.Gid();
     }
-    ASSERT_OK(acc.Commit());
+    ASSERT_OK(acc->Commit());
   }
 
-  memgraph::storage::Storage storage;
+  std::unique_ptr<memgraph::storage::Storage> storage{new memgraph::storage::InMemoryStorage()};
   LabelId label;
   PropertyId prop1;
   PropertyId prop2;
@@ -56,7 +56,7 @@ void SetProperties(memgraph::storage::Storage *storage, memgraph::storage::Gid g
                    bool *commit_status) {
   ASSERT_EQ(properties.size(), values.size());
   auto acc = storage->Access();
-  auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+  auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
   ASSERT_TRUE(vertex);
   int value = 0;
   for (int iter = 0; iter < 40000; ++iter) {
@@ -67,37 +67,37 @@ void SetProperties(memgraph::storage::Storage *storage, memgraph::storage::Gid g
   for (size_t i = 0; i < properties.size(); ++i) {
     ASSERT_OK(vertex->SetProperty(properties[i], values[i]));
   }
-  *commit_status = !acc.Commit().HasError();
+  *commit_status = !acc->Commit().HasError();
 }
 
 void AddLabel(memgraph::storage::Storage *storage, memgraph::storage::Gid gid, LabelId label, bool *commit_status) {
   auto acc = storage->Access();
-  auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+  auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
   ASSERT_TRUE(vertex);
   for (int iter = 0; iter < 40000; ++iter) {
     ASSERT_OK(vertex->AddLabel(label));
     ASSERT_OK(vertex->RemoveLabel(label));
   }
   ASSERT_OK(vertex->AddLabel(label));
-  *commit_status = !acc.Commit().HasError();
+  *commit_status = !acc->Commit().HasError();
 }
 
 TEST_F(StorageUniqueConstraints, ChangeProperties) {
   {
-    auto res = storage.CreateUniqueConstraint(label, {prop1, prop2, prop3});
+    auto res = storage->CreateUniqueConstraint(label, {prop1, prop2, prop3}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), memgraph::storage::UniqueConstraints::CreationStatus::SUCCESS);
   }
 
   {
-    auto acc = storage.Access();
+    auto acc = storage->Access();
     // NOLINTNEXTLINE(modernize-loop-convert)
     for (int i = 0; i < kNumThreads; ++i) {
-      auto vertex = acc.FindVertex(gids[i], memgraph::storage::View::OLD);
+      auto vertex = acc->FindVertex(gids[i], memgraph::storage::View::OLD);
       ASSERT_TRUE(vertex);
       ASSERT_OK(vertex->AddLabel(label));
     }
-    ASSERT_OK(acc.Commit());
+    ASSERT_OK(acc->Commit());
   }
 
   std::vector<PropertyId> properties{prop1, prop2, prop3};
@@ -111,7 +111,7 @@ TEST_F(StorageUniqueConstraints, ChangeProperties) {
       std::vector<std::thread> threads;
       threads.reserve(kNumThreads);
       for (int i = 0; i < kNumThreads; ++i) {
-        threads.emplace_back(SetProperties, &storage, gids[i], properties, values, &status[i]);
+        threads.emplace_back(SetProperties, storage.get(), gids[i], properties, values, &status[i]);
       }
       int count_ok = 0;
       for (int i = 0; i < kNumThreads; ++i) {
@@ -131,7 +131,7 @@ TEST_F(StorageUniqueConstraints, ChangeProperties) {
       std::vector<std::thread> threads;
       threads.reserve(kNumThreads);
       for (int i = 0; i < kNumThreads; ++i) {
-        threads.emplace_back(SetProperties, &storage, gids[i], properties, values, &status[i]);
+        threads.emplace_back(SetProperties, storage.get(), gids[i], properties, values, &status[i]);
       }
       int count_ok = 0;
       for (int i = 0; i < kNumThreads; ++i) {
@@ -152,7 +152,7 @@ TEST_F(StorageUniqueConstraints, ChangeProperties) {
       threads.reserve(kNumThreads);
       for (int i = 0; i < kNumThreads; ++i) {
         std::vector<PropertyValue> values{PropertyValue(value++), PropertyValue(value++), PropertyValue(value++)};
-        threads.emplace_back(SetProperties, &storage, gids[i], properties, values, &status[i]);
+        threads.emplace_back(SetProperties, storage.get(), gids[i], properties, values, &status[i]);
       }
       int count_ok = 0;
       for (int i = 0; i < kNumThreads; ++i) {
@@ -166,7 +166,7 @@ TEST_F(StorageUniqueConstraints, ChangeProperties) {
 
 TEST_F(StorageUniqueConstraints, ChangeLabels) {
   {
-    auto res = storage.CreateUniqueConstraint(label, {prop1, prop2, prop3});
+    auto res = storage->CreateUniqueConstraint(label, {prop1, prop2, prop3}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), memgraph::storage::UniqueConstraints::CreationStatus::SUCCESS);
   }
@@ -177,29 +177,29 @@ TEST_F(StorageUniqueConstraints, ChangeLabels) {
   // succeed, as the others should result with constraint violation.
 
   {
-    auto acc = storage.Access();
+    auto acc = storage->Access();
     // NOLINTNEXTLINE(modernize-loop-convert)
     for (int i = 0; i < kNumThreads; ++i) {
-      auto vertex = acc.FindVertex(gids[i], memgraph::storage::View::OLD);
+      auto vertex = acc->FindVertex(gids[i], memgraph::storage::View::OLD);
       ASSERT_TRUE(vertex);
       ASSERT_OK(vertex->SetProperty(prop1, PropertyValue(1)));
       ASSERT_OK(vertex->SetProperty(prop2, PropertyValue(2)));
       ASSERT_OK(vertex->SetProperty(prop3, PropertyValue(3)));
     }
-    ASSERT_OK(acc.Commit());
+    ASSERT_OK(acc->Commit());
   }
 
   for (int iter = 0; iter < 20; ++iter) {
     // Clear labels.
     {
-      auto acc = storage.Access();
+      auto acc = storage->Access();
       // NOLINTNEXTLINE(modernize-loop-convert)
       for (int i = 0; i < kNumThreads; ++i) {
-        auto vertex = acc.FindVertex(gids[i], memgraph::storage::View::OLD);
+        auto vertex = acc->FindVertex(gids[i], memgraph::storage::View::OLD);
         ASSERT_TRUE(vertex);
         ASSERT_OK(vertex->RemoveLabel(label));
       }
-      ASSERT_OK(acc.Commit());
+      ASSERT_OK(acc->Commit());
     }
 
     bool status[kNumThreads];
@@ -207,7 +207,7 @@ TEST_F(StorageUniqueConstraints, ChangeLabels) {
     threads.reserve(kNumThreads);
     // NOLINTNEXTLINE(modernize-loop-convert)
     for (int i = 0; i < kNumThreads; ++i) {
-      threads.emplace_back(AddLabel, &storage, gids[i], label, &status[i]);
+      threads.emplace_back(AddLabel, storage.get(), gids[i], label, &status[i]);
     }
     int count_ok = 0;
     // NOLINTNEXTLINE(modernize-loop-convert)
@@ -223,36 +223,36 @@ TEST_F(StorageUniqueConstraints, ChangeLabels) {
   // should succeed.
 
   {
-    auto acc = storage.Access();
+    auto acc = storage->Access();
     // NOLINTNEXTLINE(modernize-loop-convert)
     for (int i = 0; i < kNumThreads; ++i) {
-      auto vertex = acc.FindVertex(gids[i], memgraph::storage::View::OLD);
+      auto vertex = acc->FindVertex(gids[i], memgraph::storage::View::OLD);
       ASSERT_TRUE(vertex);
       ASSERT_OK(vertex->SetProperty(prop1, PropertyValue(3 * i)));
       ASSERT_OK(vertex->SetProperty(prop2, PropertyValue(3 * i + 1)));
       ASSERT_OK(vertex->SetProperty(prop3, PropertyValue(3 * i + 2)));
     }
-    ASSERT_OK(acc.Commit());
+    ASSERT_OK(acc->Commit());
   }
 
   for (int iter = 0; iter < 20; ++iter) {
     // Clear labels.
     {
-      auto acc = storage.Access();
+      auto acc = storage->Access();
       // NOLINTNEXTLINE(modernize-loop-convert)
       for (int i = 0; i < kNumThreads; ++i) {
-        auto vertex = acc.FindVertex(gids[i], memgraph::storage::View::OLD);
+        auto vertex = acc->FindVertex(gids[i], memgraph::storage::View::OLD);
         ASSERT_TRUE(vertex);
         ASSERT_OK(vertex->RemoveLabel(label));
       }
-      ASSERT_OK(acc.Commit());
+      ASSERT_OK(acc->Commit());
     }
 
     bool status[kNumThreads];
     std::vector<std::thread> threads;
     threads.reserve(kNumThreads);
     for (int i = 0; i < kNumThreads; ++i) {
-      threads.emplace_back(AddLabel, &storage, gids[i], label, &status[i]);
+      threads.emplace_back(AddLabel, storage.get(), gids[i], label, &status[i]);
     }
     int count_ok = 0;
     for (int i = 0; i < kNumThreads; ++i) {
diff --git a/tests/e2e/CMakeLists.txt b/tests/e2e/CMakeLists.txt
index bb0341123..6f729c968 100644
--- a/tests/e2e/CMakeLists.txt
+++ b/tests/e2e/CMakeLists.txt
@@ -55,6 +55,7 @@ add_subdirectory(python_query_modules_reloading)
 add_subdirectory(analyze_graph)
 add_subdirectory(transaction_queue)
 add_subdirectory(mock_api)
+add_subdirectory(disk_storage)
 add_subdirectory(load_csv)
 add_subdirectory(init_file_flags)
 add_subdirectory(analytical_mode)
diff --git a/tests/e2e/disk_storage/CMakeLists.txt b/tests/e2e/disk_storage/CMakeLists.txt
new file mode 100644
index 000000000..680f980f6
--- /dev/null
+++ b/tests/e2e/disk_storage/CMakeLists.txt
@@ -0,0 +1,14 @@
+function(copy_disk_storage_e2e_python_files FILE_NAME)
+    copy_e2e_python_files(disk_storage ${FILE_NAME})
+endfunction()
+
+copy_disk_storage_e2e_python_files(common.py)
+copy_disk_storage_e2e_python_files(data_import.py)
+copy_disk_storage_e2e_python_files(update_storage_mode_db_not_empty.py)
+copy_disk_storage_e2e_python_files(update_storage_mode_disk_to_memory.py)
+copy_disk_storage_e2e_python_files(update_storage_mode_memory_to_disk.py)
+copy_disk_storage_e2e_python_files(free_memory_disabled.py)
+copy_disk_storage_e2e_python_files(replication_disabled.py)
+copy_disk_storage_e2e_python_files(snapshot_disabled.py)
+copy_disk_storage_e2e_python_files(lock_data_dir_disabled.py)
+copy_disk_storage_e2e_python_files(create_edge_from_indices.py)
diff --git a/tests/e2e/disk_storage/common.py b/tests/e2e/disk_storage/common.py
new file mode 100644
index 000000000..fb60257df
--- /dev/null
+++ b/tests/e2e/disk_storage/common.py
@@ -0,0 +1,27 @@
+# Copyright 2023 Memgraph Ltd.
+#
+# Use of this software is governed by the Business Source License
+# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+# License, and you may not use this file except in compliance with the Business Source License.
+#
+# As of the Change Date specified in that file, in accordance with
+# the Business Source License, use of this software will be governed
+# by the Apache License, Version 2.0, included in the file
+# licenses/APL.txt.
+
+import typing
+
+import mgclient
+import pytest
+
+
+def execute_and_fetch_all(cursor: mgclient.Cursor, query: str, params: dict = {}) -> typing.List[tuple]:
+    cursor.execute(query, params)
+    return cursor.fetchall()
+
+
+@pytest.fixture
+def connect(**kwargs) -> mgclient.Connection:
+    connection = mgclient.connect(host="localhost", port=7687, **kwargs)
+    connection.autocommit = True
+    return connection
diff --git a/tests/e2e/disk_storage/create_edge_from_indices.py b/tests/e2e/disk_storage/create_edge_from_indices.py
new file mode 100644
index 000000000..a1bb0a35c
--- /dev/null
+++ b/tests/e2e/disk_storage/create_edge_from_indices.py
@@ -0,0 +1,34 @@
+# Copyright 2023 Memgraph Ltd.
+#
+# Use of this software is governed by the Business Source License
+# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+# License, and you may not use this file except in compliance with the Business Source License.
+#
+# As of the Change Date specified in that file, in accordance with
+# the Business Source License, use of this software will be governed
+# by the Apache License, Version 2.0, included in the file
+# licenses/APL.txt.
+
+import sys
+import typing
+
+import pytest
+from common import connect, execute_and_fetch_all
+
+
+def test_creating_edges_by_loading_vertices_from_index(connect):
+    cursor = connect.cursor()
+    execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
+    execute_and_fetch_all(cursor, "CREATE (:User {id: 1, completion_percentage: 14, gender: 'man', age: 26})")
+    execute_and_fetch_all(cursor, "CREATE (:User {id: 2, completion_percentage: 15, gender: 'man', age: 30})")
+    execute_and_fetch_all(cursor, "CREATE INDEX ON :User(id)")
+    execute_and_fetch_all(cursor, "MATCH (n:User {id: 1}), (m:User {id: 2}) CREATE (n)-[e: Friend]->(m)")
+    # n and m will be in the index cache
+    # edge will be created and put it into the main memory edge cache
+    # we just iterate over vertices from main memory cache. -> this cache is empty
+    # iterating over edges is done by using out_edges of each vertex
+    assert len(execute_and_fetch_all(cursor, "MATCH (n:User {id: 1})-[e: Friend]->(m:User {id: 2}) RETURN e")) == 1
+
+
+if __name__ == "__main__":
+    sys.exit(pytest.main([__file__, "-rA"]))
diff --git a/tests/e2e/disk_storage/data_import.py b/tests/e2e/disk_storage/data_import.py
new file mode 100644
index 000000000..ddf5fc5d2
--- /dev/null
+++ b/tests/e2e/disk_storage/data_import.py
@@ -0,0 +1,45 @@
+# Copyright 2023 Memgraph Ltd.
+#
+# Use of this software is governed by the Business Source License
+# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+# License, and you may not use this file except in compliance with the Business Source License.
+#
+# As of the Change Date specified in that file, in accordance with
+# the Business Source License, use of this software will be governed
+# by the Apache License, Version 2.0, included in the file
+# licenses/APL.txt.
+
+import sys
+
+import pytest
+from common import connect, execute_and_fetch_all
+
+num_entries = 100000
+
+
+def test_disk_import_fail(connect):
+    cursor = connect.cursor()
+    execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
+    try:
+        execute_and_fetch_all(cursor, "FOREACH (i IN range(1, {num_entries}) | CREATE (n:DiskLabel {{id: i}}));")
+        assert False
+    except:
+        assert True
+
+
+def test_batched_disk_import_passes(connect):
+    step = int(num_entries / 5)
+    for i in range(1, num_entries, step):
+        cursor = connect.cursor()
+        execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
+        query = "FOREACH (i IN range({i}, {i} + {step}) | CREATE (n:DiskLabel {{id: {i}}}));".format(i=i, step=step)
+        try:
+            execute_and_fetch_all(cursor, query)
+        except:
+            assert False
+        cursor.close()
+    assert True
+
+
+if __name__ == "__main__":
+    sys.exit(pytest.main([__file__, "-rA"]))
diff --git a/tests/e2e/disk_storage/free_memory_disabled.py b/tests/e2e/disk_storage/free_memory_disabled.py
new file mode 100644
index 000000000..4023f4214
--- /dev/null
+++ b/tests/e2e/disk_storage/free_memory_disabled.py
@@ -0,0 +1,29 @@
+# Copyright 2023 Memgraph Ltd.
+#
+# Use of this software is governed by the Business Source License
+# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+# License, and you may not use this file except in compliance with the Business Source License.
+#
+# As of the Change Date specified in that file, in accordance with
+# the Business Source License, use of this software will be governed
+# by the Apache License, Version 2.0, included in the file
+# licenses/APL.txt.
+
+import sys
+
+import pytest
+from common import connect, execute_and_fetch_all
+
+
+def test_free_memory_is_disabled(connect):
+    cursor = connect.cursor()
+    execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
+    try:
+        execute_and_fetch_all(cursor, "FREE MEMORY")
+        assert False
+    except:
+        assert True
+
+
+if __name__ == "__main__":
+    sys.exit(pytest.main([__file__, "-rA"]))
diff --git a/tests/e2e/disk_storage/lock_data_dir_disabled.py b/tests/e2e/disk_storage/lock_data_dir_disabled.py
new file mode 100644
index 000000000..a20848c4f
--- /dev/null
+++ b/tests/e2e/disk_storage/lock_data_dir_disabled.py
@@ -0,0 +1,29 @@
+# Copyright 2023 Memgraph Ltd.
+#
+# Use of this software is governed by the Business Source License
+# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+# License, and you may not use this file except in compliance with the Business Source License.
+#
+# As of the Change Date specified in that file, in accordance with
+# the Business Source License, use of this software will be governed
+# by the Apache License, Version 2.0, included in the file
+# licenses/APL.txt.
+
+import sys
+
+import pytest
+from common import connect, execute_and_fetch_all
+
+
+def test_lock_data_dir_is_disabled(connect):
+    cursor = connect.cursor()
+    execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
+    try:
+        execute_and_fetch_all(cursor, "LOCK DATA DIRECTORY")
+        assert False
+    except:
+        assert True
+
+
+if __name__ == "__main__":
+    sys.exit(pytest.main([__file__, "-rA"]))
diff --git a/tests/e2e/disk_storage/replication_disabled.py b/tests/e2e/disk_storage/replication_disabled.py
new file mode 100644
index 000000000..1fd0237d7
--- /dev/null
+++ b/tests/e2e/disk_storage/replication_disabled.py
@@ -0,0 +1,29 @@
+# Copyright 2023 Memgraph Ltd.
+#
+# Use of this software is governed by the Business Source License
+# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+# License, and you may not use this file except in compliance with the Business Source License.
+#
+# As of the Change Date specified in that file, in accordance with
+# the Business Source License, use of this software will be governed
+# by the Apache License, Version 2.0, included in the file
+# licenses/APL.txt.
+
+import sys
+
+import pytest
+from common import connect, execute_and_fetch_all
+
+
+def test_replication_is_disabled(connect):
+    cursor = connect.cursor()
+    execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
+    try:
+        execute_and_fetch_all(cursor, "SET REPLICATION ROLE TO MAIN")
+        assert False
+    except:
+        assert True
+
+
+if __name__ == "__main__":
+    sys.exit(pytest.main([__file__, "-rA"]))
diff --git a/tests/e2e/disk_storage/snapshot_disabled.py b/tests/e2e/disk_storage/snapshot_disabled.py
new file mode 100644
index 000000000..575b9f381
--- /dev/null
+++ b/tests/e2e/disk_storage/snapshot_disabled.py
@@ -0,0 +1,29 @@
+# Copyright 2023 Memgraph Ltd.
+#
+# Use of this software is governed by the Business Source License
+# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+# License, and you may not use this file except in compliance with the Business Source License.
+#
+# As of the Change Date specified in that file, in accordance with
+# the Business Source License, use of this software will be governed
+# by the Apache License, Version 2.0, included in the file
+# licenses/APL.txt.
+
+import sys
+
+import pytest
+from common import connect, execute_and_fetch_all
+
+
+def test_snapshot_is_disabled(connect):
+    cursor = connect.cursor()
+    execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
+    try:
+        execute_and_fetch_all(cursor, "CREATE SNAPSHOT")
+        assert False
+    except:
+        assert True
+
+
+if __name__ == "__main__":
+    sys.exit(pytest.main([__file__, "-rA"]))
diff --git a/tests/e2e/disk_storage/update_storage_mode_db_not_empty.py b/tests/e2e/disk_storage/update_storage_mode_db_not_empty.py
new file mode 100644
index 000000000..ee61e2185
--- /dev/null
+++ b/tests/e2e/disk_storage/update_storage_mode_db_not_empty.py
@@ -0,0 +1,31 @@
+# Copyright 2023 Memgraph Ltd.
+#
+# Use of this software is governed by the Business Source License
+# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+# License, and you may not use this file except in compliance with the Business Source License.
+#
+# As of the Change Date specified in that file, in accordance with
+# the Business Source License, use of this software will be governed
+# by the Apache License, Version 2.0, included in the file
+# licenses/APL.txt.
+
+import sys
+
+import pytest
+from common import connect, execute_and_fetch_all
+
+
+def test_forbid_switching_from_memory_to_disk_when_database_is_not_empty(connect):
+    cursor = connect.cursor()
+    execute_and_fetch_all(cursor, "CREATE (:DiskLabel {id: 1})")
+    try:
+        execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
+        assert False
+    except:
+        execute_and_fetch_all(cursor, "MATCH (n) DETACH DELETE n")
+        execute_and_fetch_all(cursor, "FREE MEMORY")  # to enforce garbage collection
+        assert True
+
+
+if __name__ == "__main__":
+    sys.exit(pytest.main([__file__, "-rA"]))
diff --git a/tests/e2e/disk_storage/update_storage_mode_disk_to_memory.py b/tests/e2e/disk_storage/update_storage_mode_disk_to_memory.py
new file mode 100644
index 000000000..bf8bf55ba
--- /dev/null
+++ b/tests/e2e/disk_storage/update_storage_mode_disk_to_memory.py
@@ -0,0 +1,29 @@
+# Copyright 2023 Memgraph Ltd.
+#
+# Use of this software is governed by the Business Source License
+# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+# License, and you may not use this file except in compliance with the Business Source License.
+#
+# As of the Change Date specified in that file, in accordance with
+# the Business Source License, use of this software will be governed
+# by the Apache License, Version 2.0, included in the file
+# licenses/APL.txt.
+
+import sys
+
+import pytest
+from common import connect, execute_and_fetch_all
+
+
+def test_forbid_switching_from_disk_to_memory(connect):
+    cursor = connect.cursor()
+    execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
+    try:
+        execute_and_fetch_all(cursor, "STORAGE MODE IN_MEMORY_TRANSACTIONAL")
+        assert False
+    except:
+        assert True
+
+
+if __name__ == "__main__":
+    sys.exit(pytest.main([__file__, "-rA"]))
diff --git a/tests/e2e/disk_storage/update_storage_mode_memory_to_disk.py b/tests/e2e/disk_storage/update_storage_mode_memory_to_disk.py
new file mode 100644
index 000000000..2fb7fc6ce
--- /dev/null
+++ b/tests/e2e/disk_storage/update_storage_mode_memory_to_disk.py
@@ -0,0 +1,24 @@
+# Copyright 2023 Memgraph Ltd.
+#
+# Use of this software is governed by the Business Source License
+# included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+# License, and you may not use this file except in compliance with the Business Source License.
+#
+# As of the Change Date specified in that file, in accordance with
+# the Business Source License, use of this software will be governed
+# by the Apache License, Version 2.0, included in the file
+# licenses/APL.txt.
+
+import sys
+
+import pytest
+from common import connect, execute_and_fetch_all
+
+
+def test_allow_switching_from_memory_to_disk_when_database_is_empty(connect):
+    cursor = connect.cursor()
+    execute_and_fetch_all(cursor, "STORAGE MODE ON_DISK_TRANSACTIONAL")
+
+
+if __name__ == "__main__":
+    sys.exit(pytest.main([__file__, "-rA"]))
diff --git a/tests/e2e/disk_storage/workloads.yaml b/tests/e2e/disk_storage/workloads.yaml
new file mode 100644
index 000000000..4f449e77d
--- /dev/null
+++ b/tests/e2e/disk_storage/workloads.yaml
@@ -0,0 +1,53 @@
+disk_storage: &disk_storage
+  cluster:
+    main:
+      args: ["--bolt-port", "7687", "--log-level", "TRACE", "--memory-limit", "50"]
+      log_file: "disk_storage.log"
+      setup_queries: []
+      validation_queries: []
+
+workloads:
+  - name: "Test that loading vertices from indices and creating edge with them works."
+    binary: "tests/e2e/pytest_runner.sh"
+    args: ["disk_storage/create_edge_from_indices.py"]
+    <<: *disk_storage
+
+  - name: "Test that free memory query is disabled with on-disk storage."
+    binary: "tests/e2e/pytest_runner.sh"
+    args: ["disk_storage/free_memory_disabled.py"]
+    <<: *disk_storage
+
+  - name: "Test that replication queries are disabled with on-disk storage."
+    binary: "tests/e2e/pytest_runner.sh"
+    args: ["disk_storage/replication_disabled.py"]
+    <<: *disk_storage
+
+  - name: "Test that create snapshot queries are disabled with on-disk storage."
+    binary: "tests/e2e/pytest_runner.sh"
+    args: ["disk_storage/snapshot_disabled.py"]
+    <<: *disk_storage
+
+  - name: "Test that lock data directory query is disabled with on-disk storage."
+    binary: "tests/e2e/pytest_runner.sh"
+    args: ["disk_storage/lock_data_dir_disabled.py"]
+    <<: *disk_storage
+
+  - name: "Tests importing data on disk "
+    binary: "tests/e2e/pytest_runner.sh"
+    args: ["disk_storage/data_import.py"]
+    <<: *disk_storage
+
+  - name: "Tests when switching from in-memory storage to disk storage when the db isn't empty. "
+    binary: "tests/e2e/pytest_runner.sh"
+    args: ["disk_storage/update_storage_mode_db_not_empty.py"]
+    <<: *disk_storage
+
+  - name: "Tests when switching from disk storage to in-memory storage is forbidden. "
+    binary: "tests/e2e/pytest_runner.sh"
+    args: ["disk_storage/update_storage_mode_disk_to_memory.py"]
+    <<: *disk_storage
+
+  - name: "Tests when switching from in-memory to disk is allowed. "
+    binary: "tests/e2e/pytest_runner.sh"
+    args: ["disk_storage/update_storage_mode_memory_to_disk.py"]
+    <<: *disk_storage
diff --git a/tests/e2e/isolation_levels/isolation_levels.cpp b/tests/e2e/isolation_levels/isolation_levels.cpp
index 8b109fd18..4cec5b13d 100644
--- a/tests/e2e/isolation_levels/isolation_levels.cpp
+++ b/tests/e2e/isolation_levels/isolation_levels.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -40,6 +40,19 @@ auto GetVertexCount(std::unique_ptr<mg::Client> &client) {
   return row[0].ValueInt();
 }
 
+bool IsDiskStorageMode(std::unique_ptr<mg::Client> &client) {
+  MG_ASSERT(client->Execute("SHOW STORAGE INFO"));
+  auto maybe_rows = client->FetchAll();
+  MG_ASSERT(maybe_rows, "Failed to fetch storage info");
+
+  for (auto &row : *maybe_rows) {
+    if (row[0].ValueString() == "storage_mode") {
+      return row[1].ValueString() == "ON_DISK_TRANSACTIONAL";
+    }
+  }
+  return false;
+}
+
 void CleanDatabase() {
   auto client = GetClient();
   MG_ASSERT(client->Execute("MATCH (n) DETACH DELETE n;"));
@@ -146,15 +159,22 @@ inline constexpr std::array isolation_levels{std::pair{"SNAPSHOT ISOLATION", &Te
                                              std::pair{"READ COMMITTED", &TestReadCommitted},
                                              std::pair{"READ UNCOMMITTED", &TestReadUncommitted}};
 
-void TestGlobalIsolationLevel() {
+void TestGlobalIsolationLevel(bool isDiskStorage) {
   spdlog::info("\n\n----Test global isolation levels----\n");
   auto first_client = GetClient();
   auto second_client = GetClient();
 
   for (const auto &[isolation_level, verification_function] : isolation_levels) {
     spdlog::info("--------------------------");
+
+    if (isDiskStorage && strcmp(isolation_level, "SNAPSHOT ISOLATION") != 0) {
+      spdlog::info("Skipping for disk storage unsupported isolation level {}", isolation_level);
+      continue;
+    }
+
     spdlog::info("Setting global isolation level to {}", isolation_level);
     MG_ASSERT(first_client->Execute(fmt::format("SET GLOBAL TRANSACTION ISOLATION LEVEL {}", isolation_level)));
+
     first_client->DiscardAll();
 
     verification_function(first_client);
@@ -163,18 +183,26 @@ void TestGlobalIsolationLevel() {
   }
 }
 
-void TestSessionIsolationLevel() {
+void TestSessionIsolationLevel(bool isDiskStorage) {
   spdlog::info("\n\n----Test session isolation levels----\n");
 
   auto global_client = GetClient();
   auto session_client = GetClient();
   for (const auto &[global_isolation_level, global_verification_function] : isolation_levels) {
+    if (isDiskStorage && strcmp(global_isolation_level, "SNAPSHOT ISOLATION") != 0) {
+      spdlog::info("Skipping for disk storage unsupported global isolation level {}", global_isolation_level);
+      continue;
+    }
     spdlog::info("Setting global isolation level to {}", global_isolation_level);
     MG_ASSERT(global_client->Execute(fmt::format("SET GLOBAL TRANSACTION ISOLATION LEVEL {}", global_isolation_level)));
     global_client->DiscardAll();
 
     for (const auto &[session_isolation_level, session_verification_function] : isolation_levels) {
       spdlog::info("--------------------------");
+      if (isDiskStorage && strcmp(session_isolation_level, "SNAPSHOT ISOLATION") != 0) {
+        spdlog::info("Skipping for disk storage unsupported session isolation level {}", session_isolation_level);
+        continue;
+      }
       spdlog::info("Setting session isolation level to {}", session_isolation_level);
       MG_ASSERT(
           session_client->Execute(fmt::format("SET SESSION TRANSACTION ISOLATION LEVEL {}", session_isolation_level)));
@@ -190,17 +218,26 @@ void TestSessionIsolationLevel() {
 }
 
 // Priority of applying the isolation level from highest priority NEXT -> SESSION -> GLOBAL
-void TestNextIsolationLevel() {
+void TestNextIsolationLevel(bool isDiskStorage) {
   spdlog::info("\n\n----Test next isolation levels----\n");
 
   auto global_client = GetClient();
   auto session_client = GetClient();
   for (const auto &[global_isolation_level, global_verification_function] : isolation_levels) {
+    if (isDiskStorage && strcmp(global_isolation_level, "SNAPSHOT ISOLATION") != 0) {
+      spdlog::info("Skipping for disk storage unsupported global isolation level {}", global_isolation_level);
+      continue;
+    }
     spdlog::info("Setting global isolation level to {}", global_isolation_level);
+
     MG_ASSERT(global_client->Execute(fmt::format("SET GLOBAL TRANSACTION ISOLATION LEVEL {}", global_isolation_level)));
     global_client->DiscardAll();
 
     for (const auto &[session_isolation_level, session_verification_function] : isolation_levels) {
+      if (isDiskStorage && strcmp(session_isolation_level, "SNAPSHOT ISOLATION") != 0) {
+        spdlog::info("Skipping for disk storage unsupported session isolation level {}", session_isolation_level);
+        continue;
+      }
       spdlog::info("Setting session isolation level to {}", session_isolation_level);
       MG_ASSERT(
           session_client->Execute(fmt::format("SET SESSION TRANSACTION ISOLATION LEVEL {}", session_isolation_level)));
@@ -213,6 +250,11 @@ void TestNextIsolationLevel() {
         spdlog::info("Verifying client which is using session isolation level");
         session_verification_function(session_client);
 
+        if (isDiskStorage && strcmp(next_isolation_level, "SNAPSHOT ISOLATION") != 0) {
+          spdlog::info("Skipping for disk storage unsupported next transaction isolation level {}",
+                       next_isolation_level);
+          continue;
+        }
         spdlog::info("Setting isolation level of the next transaction to {}", next_isolation_level);
         MG_ASSERT(global_client->Execute(fmt::format("SET NEXT TRANSACTION ISOLATION LEVEL {}", next_isolation_level)));
         global_client->DiscardAll();
@@ -244,9 +286,13 @@ int main(int argc, char **argv) {
 
   mg::Client::Init();
 
-  TestGlobalIsolationLevel();
-  TestSessionIsolationLevel();
-  TestNextIsolationLevel();
+  auto client = GetClient();
+  bool isDiskStorage = IsDiskStorageMode(client);
+  client->DiscardAll();
+
+  TestGlobalIsolationLevel(isDiskStorage);
+  TestSessionIsolationLevel(isDiskStorage);
+  TestNextIsolationLevel(isDiskStorage);
 
   return 0;
 }
diff --git a/tests/e2e/isolation_levels/workloads.yaml b/tests/e2e/isolation_levels/workloads.yaml
index 5d793396e..cca4693ab 100644
--- a/tests/e2e/isolation_levels/workloads.yaml
+++ b/tests/e2e/isolation_levels/workloads.yaml
@@ -6,9 +6,21 @@ template_cluster: &template_cluster
       log_file: "isolation-levels-e2e.log"
       setup_queries: []
       validation_queries: []
+disk_cluster: &disk_cluster
+  cluster:
+    main:
+      args: ["--bolt-port", *bolt_port, "--log-level=TRACE"]
+      log_file: "isolation-levels-disk-e2e.log"
+      setup_queries: ["storage mode on_disk_transactional"]
+      validation_queries: []
 
 workloads:
   - name: "Isolation levels"
     binary: "tests/e2e/isolation_levels/memgraph__e2e__isolation_levels"
     args: ["--bolt-port", *bolt_port]
     <<: *template_cluster
+
+  - name: "Isolation levels for disk storage"
+    binary: "tests/e2e/isolation_levels/memgraph__e2e__isolation_levels"
+    args: ["--bolt-port", *bolt_port]
+    <<: *disk_cluster
diff --git a/tests/e2e/magic_functions/workloads.yaml b/tests/e2e/magic_functions/workloads.yaml
index 1f130099d..a2346b373 100644
--- a/tests/e2e/magic_functions/workloads.yaml
+++ b/tests/e2e/magic_functions/workloads.yaml
@@ -5,6 +5,13 @@ template_cluster: &template_cluster
       log_file: "magic-functions-e2e.log"
       setup_queries: []
       validation_queries: []
+disk_cluster: &disk_cluster
+  cluster:
+    main:
+      args: ["--bolt-port", "7687", "--log-level=TRACE"]
+      log_file: "magic-functions-e2e.log"
+      setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"]
+      validation_queries: []
 
 workloads:
   - name: "Magic functions runner"
@@ -12,3 +19,9 @@ workloads:
     proc: "tests/e2e/magic_functions/functions/"
     args: ["magic_functions/function_example.py"]
     <<: *template_cluster
+
+  - name: "Magic functions runner for disk storage"
+    binary: "tests/e2e/pytest_runner.sh"
+    proc: "tests/e2e/magic_functions/functions/"
+    args: ["magic_functions/function_example.py"]
+    <<: *disk_cluster
diff --git a/tests/e2e/memory/workloads.yaml b/tests/e2e/memory/workloads.yaml
index bf7ba373e..88573c761 100644
--- a/tests/e2e/memory/workloads.yaml
+++ b/tests/e2e/memory/workloads.yaml
@@ -6,6 +6,14 @@ template_cluster: &template_cluster
       log_file: "memory-e2e.log"
       setup_queries: []
       validation_queries: []
+disk_cluster: &disk_cluster
+  cluster:
+    main:
+      args: ["--bolt-port", *bolt_port, "--memory-limit=1000", "--storage-gc-cycle-sec=180", "--log-level=TRACE"]
+      log_file: "memory-e2e.log"
+      setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"]
+      validation_queries: []
+
 
 workloads:
   - name: "Memory control"
diff --git a/tests/e2e/mock_api/workloads.yaml b/tests/e2e/mock_api/workloads.yaml
index 165477479..f4b638a11 100644
--- a/tests/e2e/mock_api/workloads.yaml
+++ b/tests/e2e/mock_api/workloads.yaml
@@ -74,8 +74,91 @@ compare_mock: &compare_mock
         - "MATCH (u) SET u.permanent_id = u.__mg_id__;"
         - "MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;"
       validation_queries: []
+disk_cluster: &disk_cluster
+  cluster:
+    main:
+      args: ["--bolt-port", "7687", "--log-level=TRACE", "--also-log-to-stderr"]
+      log_file: "test-compare-mock-e2e.log"
+      setup_queries:
+        - "STORAGE MODE ON_DISK_TRANSACTIONAL;"
+        - "CREATE INDEX ON :__mg_vertex__(__mg_id__);"
+        - "CREATE (:__mg_vertex__:`Person` {__mg_id__: 0, `name`: 'Peter', `surname`: 'Yang'});"
+        - "CREATE (:__mg_vertex__:`Team` {__mg_id__: 1, `name`: 'Engineering'});"
+        - "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 2, `name`: 'Memgraph'});"
+        - "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 3, `name`: 'MAGE'});"
+        - "CREATE (:__mg_vertex__:`Repository` {__mg_id__: 4, `name`: 'GQLAlchemy'});"
+        - "CREATE (:__mg_vertex__:`Company`:`Startup` {__mg_id__: 5, `name`: 'Memgraph'});"
+        - "CREATE (:__mg_vertex__:`File` {__mg_id__: 6, `name`: 'welcome_to_engineering.txt'});"
+        - "CREATE (:__mg_vertex__:`Storage` {__mg_id__: 7, `name`: 'Google Drive'});"
+        - "CREATE (:__mg_vertex__:`Storage` {__mg_id__: 8, `name`: 'Notion'});"
+        - "CREATE (:__mg_vertex__:`File` {__mg_id__: 9, `name`: 'welcome_to_memgraph.txt'});"
+        - "CREATE (:__mg_vertex__:`Person` {__mg_id__: 10, `name`: 'Carl'});"
+        - "CREATE (:__mg_vertex__:`Folder` {__mg_id__: 11, `name`: 'engineering_folder'});"
+        - "CREATE (:__mg_vertex__:`Person` {__mg_id__: 12, `name`: 'Anna'});"
+        - "CREATE (:__mg_vertex__:`Folder` {__mg_id__: 13, `name`: 'operations_folder'});"
+        - "CREATE (:__mg_vertex__:`Team` {__mg_id__: 14, `name`: 'Operations'});"
+        - "CREATE (:__mg_vertex__:`File` {__mg_id__: 15, `name`: 'operations101.txt'});"
+        - "CREATE (:__mg_vertex__:`File` {__mg_id__: 16, `name`: 'expenses2022.csv'});"
+        - "CREATE (:__mg_vertex__:`File` {__mg_id__: 17, `name`: 'salaries2022.csv'});"
+        - "CREATE (:__mg_vertex__:`File` {__mg_id__: 18, `name`: 'engineering101.txt'});"
+        - "CREATE (:__mg_vertex__:`File` {__mg_id__: 19, `name`: 'working_with_github.txt'});"
+        - "CREATE (:__mg_vertex__:`File` {__mg_id__: 20, `name`: 'working_with_notion.txt'});"
+        - "CREATE (:__mg_vertex__:`Team` {__mg_id__: 21, `name`: 'Marketing'});"
+        - "CREATE (:__mg_vertex__:`Person` {__mg_id__: 22, `name`: 'Julie'});"
+        - "CREATE (:__mg_vertex__:`Account` {__mg_id__: 23, `name`: 'Facebook'});"
+        - "CREATE (:__mg_vertex__:`Account` {__mg_id__: 24, `name`: 'LinkedIn'});"
+        - "CREATE (:__mg_vertex__:`Account` {__mg_id__: 25, `name`: 'HackerNews'});"
+        - "CREATE (:__mg_vertex__:`File` {__mg_id__: 26, `name`: 'welcome_to_marketing.txt'});"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 1 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 0}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 1}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 2}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 0 AND v.__mg_id__ = 14 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 3}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 2 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 4}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 3 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 5}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 4 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 6}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 6 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 7}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 1 AND v.__mg_id__ = 11 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 8}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 1 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 9}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 21 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 10}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 14 CREATE (u)-[:`HAS_TEAM` {`permanent_id`: 11}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 12}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 6 AND v.__mg_id__ = 8 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 13}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 9 AND v.__mg_id__ = 12 CREATE (u)-[:`CREATED_BY` {`permanent_id`: 14}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 1 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 15}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 16}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 10 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 17}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 7 CREATE(u)-[:`IS_STORED_IN` {`permanent_id`: 18}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 18 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 19}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 19 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 20}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 11 AND v.__mg_id__ = 20 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 21}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 12 AND v.__mg_id__ = 14 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 22}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 15 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 23}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 16 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 24}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 17 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 25}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 13 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 26}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 14 AND v.__mg_id__ = 13 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 27}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 23 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 28}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 24 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 29}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 25 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 30}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 21 AND v.__mg_id__ = 26 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 31}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 21 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 32}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 5 CREATE (u)-[:`IS_PART_OF` {`permanent_id`: 33}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 22 AND v.__mg_id__ = 9 CREATE (u)-[:`HAS_ACCESS_TO` {`permanent_id`: 34}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 26 AND v.__mg_id__ = 7 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 35}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 26 AND v.__mg_id__ = 8 CREATE (u)-[:`IS_STORED_IN` {`permanent_id`: 36}]->(v);"
+        - "MATCH (u:__mg_vertex__), (v:__mg_vertex__) WHERE u.__mg_id__ = 5 AND v.__mg_id__ = 1 CREATE (u)-[:`HAS_TEAM_2` {`importance`: 'HIGH', `permanent_id`: 37}]->(v);"
+        - "DROP INDEX ON :__mg_vertex__(__mg_id__);"
+        - "MATCH (u) SET u.permanent_id = u.__mg_id__;"
+        - "MATCH (u) REMOVE u:__mg_vertex__, u.__mg_id__;"
+      validation_queries: []
 
 workloads:
+  - name: "test-compare-mock on disk" # should be the same as the python file
+    binary: "tests/e2e/pytest_runner.sh"
+    proc: "tests/e2e/mock_api/procedures/"
+    args: ["mock_api/test_compare_mock.py"]
+    <<: *disk_cluster
+
   - name: "test-compare-mock" # should be the same as the python file
     binary: "tests/e2e/pytest_runner.sh"
     proc: "tests/e2e/mock_api/procedures/"
diff --git a/tests/e2e/python_query_modules_reloading/workloads.yaml b/tests/e2e/python_query_modules_reloading/workloads.yaml
index 82f91644b..56cd65ff9 100644
--- a/tests/e2e/python_query_modules_reloading/workloads.yaml
+++ b/tests/e2e/python_query_modules_reloading/workloads.yaml
@@ -5,6 +5,14 @@ test_reload_query_module: &test_reload_query_module
       log_file: "py-query-modules-reloading-e2e.log"
       setup_queries: []
       validation_queries: []
+disk_test_reload_query_module: &disk_test_reload_query_module
+  cluster:
+    main:
+      args: ["--bolt-port", "7687", "--log-level=TRACE", "--also-log-to-stderr"]
+      log_file: "py-query-modules-reloading-e2e.log"
+      setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"]
+      validation_queries: []
+
 
 workloads:
   - name: "test-reload-query-module" # should be the same as the python file
@@ -12,3 +20,8 @@ workloads:
     proc: "tests/e2e/python_query_modules_reloading/procedures/"
     args: ["python_query_modules_reloading/test_reload_query_module.py"]
     <<: *test_reload_query_module
+  - name: "test-reload-query-module on disk" # should be the same as the python file
+    binary: "tests/e2e/pytest_runner.sh"
+    proc: "tests/e2e/python_query_modules_reloading/procedures/"
+    args: ["python_query_modules_reloading/test_reload_query_module.py"]
+    <<: *disk_test_reload_query_module
diff --git a/tests/e2e/run_e2e.sh b/tests/e2e/run_e2e.sh
new file mode 100644
index 000000000..55c376941
--- /dev/null
+++ b/tests/e2e/run_e2e.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+# TODO: andi as a side project
+python3 runner.py --workloads-root-directory disk_storage
diff --git a/tests/e2e/runner.py b/tests/e2e/runner.py
index 56e05733a..e3be4afa2 100755
--- a/tests/e2e/runner.py
+++ b/tests/e2e/runner.py
@@ -13,6 +13,7 @@ import atexit
 import logging
 import os
 import subprocess
+import time
 from argparse import ArgumentParser
 from pathlib import Path
 
@@ -30,6 +31,7 @@ def load_args():
     parser = ArgumentParser()
     parser.add_argument("--workloads-root-directory", required=True)
     parser.add_argument("--workload-name", default=None, required=False)
+    parser.add_argument("--debug", default=False, required=False)
     return parser.parse_args()
 
 
@@ -60,6 +62,11 @@ def run(args):
                 procdir = os.path.join(BUILD_DIR, workload["proc"])
             interactive_mg_runner.start_all(workload["cluster"], procdir)
 
+        if args.debug:
+            hosts = subprocess.check_output("pgrep memgraph", shell=True)
+            print(f"PID: {hosts}")
+            time.sleep(10)
+
         # Test.
         mg_test_binary = os.path.join(BUILD_DIR, workload["binary"])
         subprocess.run([mg_test_binary] + workload["args"], check=True, stderr=subprocess.STDOUT)
diff --git a/tests/e2e/temporal_types/workloads.yaml b/tests/e2e/temporal_types/workloads.yaml
index 359edb714..f4bccefae 100644
--- a/tests/e2e/temporal_types/workloads.yaml
+++ b/tests/e2e/temporal_types/workloads.yaml
@@ -6,9 +6,21 @@ template_cluster: &template_cluster
       log_file: "temporal-types-e2e.log"
       setup_queries: []
       validation_queries: []
+disk_template_cluster: &disk_template_cluster
+  cluster:
+    main:
+      args: ["--bolt_port", *bolt_port, "--log-level=TRACE"]
+      log_file: "temporal-types-e2e.log"
+      setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"]
+      validation_queries: []
+
 
 workloads:
   - name: "Temporal"
     binary: "tests/e2e/temporal_types/memgraph__e2e__temporal_roundtrip"
     args: ["--bolt_port", *bolt_port]
     <<: *template_cluster
+  - name: "Temporal on disk"
+    binary: "tests/e2e/temporal_types/memgraph__e2e__temporal_roundtrip"
+    args: ["--bolt_port", *bolt_port]
+    <<: *disk_template_cluster
diff --git a/tests/e2e/transaction_queue/workloads.yaml b/tests/e2e/transaction_queue/workloads.yaml
index b5f15facf..d73c2ad13 100644
--- a/tests/e2e/transaction_queue/workloads.yaml
+++ b/tests/e2e/transaction_queue/workloads.yaml
@@ -5,6 +5,14 @@ test_transaction_queue: &test_transaction_queue
       log_file: "transaction_queue.log"
       setup_queries: []
       validation_queries: []
+disk_test_transaction_queue: &disk_test_transaction_queue
+  cluster:
+    main:
+      args: ["--bolt-port", "7687", "--log-level=TRACE", "--also-log-to-stderr"]
+      log_file: "transaction_queue.log"
+      setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"]
+      validation_queries: []
+
 
 workloads:
   - name: "test-transaction-queue" # should be the same as the python file
@@ -12,3 +20,8 @@ workloads:
     proc: "tests/e2e/transaction_queue/procedures/"
     args: ["transaction_queue/test_transaction_queue.py"]
     <<: *test_transaction_queue
+  - name: "test-transaction-queue on disk" # should be the same as the python file
+    binary: "tests/e2e/pytest_runner.sh"
+    proc: "tests/e2e/transaction_queue/procedures/"
+    args: ["transaction_queue/test_transaction_queue.py"]
+    <<: *disk_test_transaction_queue
diff --git a/tests/e2e/triggers/workloads.yaml b/tests/e2e/triggers/workloads.yaml
index a80a8f2fa..7f6cd7743 100644
--- a/tests/e2e/triggers/workloads.yaml
+++ b/tests/e2e/triggers/workloads.yaml
@@ -13,6 +13,20 @@ storage_properties_edges_false: &storage_properties_edges_false
       log_file: "triggers-e2e.log"
       setup_queries: []
       validation_queries: []
+disk_template_cluster: &disk_template_cluster
+  cluster:
+    main:
+      args: ["--bolt-port", *bolt_port, "--log-level=TRACE", "--storage-properties-on-edges=True"]
+      log_file: "triggers-e2e.log"
+      setup_queries: []
+      validation_queries: []
+disk_storage_properties_edges_false: &disk_storage_properties_edges_false
+  cluster:
+    main:
+      args: ["--bolt-port", *bolt_port, "--log-level=TRACE", "--also-log-to-stderr", "--storage-properties-on-edges=False"]
+      log_file: "triggers-e2e.log"
+      setup_queries: []
+      validation_queries: []
 
 workloads:
   - name: "ON CREATE Triggers"
@@ -39,3 +53,28 @@ workloads:
     proc: "tests/e2e/triggers/procedures/"
     args: ["triggers/triggers_properties_false.py"]
     <<: *storage_properties_edges_false
+
+  - name: "ON CREATE Triggers on disk"
+    binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_create"
+    args: ["--bolt-port", *bolt_port]
+    proc: "tests/e2e/triggers/procedures/"
+    <<: *disk_template_cluster
+  - name: "ON UPDATE Triggers on disk"
+    binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_update"
+    args: ["--bolt-port", *bolt_port]
+    proc: "tests/e2e/triggers/procedures/"
+    <<: *disk_template_cluster
+  - name: "ON DELETE Triggers Storage Properties On Edges True On Disk"
+    binary: "tests/e2e/triggers/memgraph__e2e__triggers__on_delete"
+    args: ["--bolt-port", *bolt_port]
+    proc: "tests/e2e/triggers/procedures/"
+    <<: *disk_template_cluster
+  - name: "Triggers privilege check on disk"
+    binary: "tests/e2e/triggers/memgraph__e2e__triggers__privileges"
+    args: ["--bolt-port", *bolt_port]
+    <<: *disk_template_cluster
+  - name: "ON DELETE Triggers Storage Properties On Edges False On Disk" # should be the same as the python file
+    binary: "tests/e2e/pytest_runner.sh"
+    proc: "tests/e2e/triggers/procedures/"
+    args: ["triggers/triggers_properties_false.py"]
+    <<: *disk_storage_properties_edges_false
diff --git a/tests/e2e/write_procedures/workloads.yaml b/tests/e2e/write_procedures/workloads.yaml
index 667947920..be03d0cab 100644
--- a/tests/e2e/write_procedures/workloads.yaml
+++ b/tests/e2e/write_procedures/workloads.yaml
@@ -5,6 +5,13 @@ template_cluster: &template_cluster
       log_file: "write-procedures-e2e.log"
       setup_queries: []
       validation_queries: []
+disk_template_cluster: &disk_template_cluster
+  cluster:
+    main:
+      args: ["--bolt-port", "7687", "--log-level=TRACE"]
+      log_file: "write-procedures-e2e.log"
+      setup_queries: ["STORAGE MODE ON_DISK_TRANSACTIONAL"]
+      validation_queries: []
 
 workloads:
   - name: "Write procedures simple"
@@ -17,3 +24,13 @@ workloads:
     proc: "tests/e2e/write_procedures/procedures/"
     args: ["write_procedures/read_subgraph.py"]
     <<: *template_cluster
+  - name: "Write procedures simple on disk"
+    binary: "tests/e2e/pytest_runner.sh"
+    proc: "tests/e2e/write_procedures/procedures/"
+    args: ["write_procedures/simple_write.py"]
+    <<: *disk_template_cluster
+  - name: "Graph projection procedures on disk"
+    binary: "tests/e2e/pytest_runner.sh"
+    proc: "tests/e2e/write_procedures/procedures/"
+    args: ["write_procedures/read_subgraph.py"]
+    <<: *disk_template_cluster
diff --git a/tests/manual/interactive_planning.cpp b/tests/manual/interactive_planning.cpp
index a8b66ff26..cbcbe38b8 100644
--- a/tests/manual/interactive_planning.cpp
+++ b/tests/manual/interactive_planning.cpp
@@ -27,7 +27,6 @@
 #include "query/plan/planner.hpp"
 #include "query/plan/pretty_print.hpp"
 #include "query/typed_value.hpp"
-#include "storage/v2/indices.hpp"
 #include "storage/v2/property_value.hpp"
 #include "utils/string.hpp"
 
diff --git a/tests/manual/query_planner.cpp b/tests/manual/query_planner.cpp
index 67875734b..700b70a7a 100644
--- a/tests/manual/query_planner.cpp
+++ b/tests/manual/query_planner.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,16 +13,16 @@
 
 #include <gflags/gflags.h>
 
-#include "storage/v2/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 DECLARE_int32(min_log_level);
 
 int main(int argc, char *argv[]) {
   gflags::ParseCommandLineFlags(&argc, &argv, true);
   spdlog::set_level(spdlog::level::err);
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  std::unique_ptr<memgraph::storage::Storage> db(new memgraph::storage::InMemoryStorage());
+  auto storage_dba = db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   RunInteractivePlanning(&dba);
   return 0;
 }
diff --git a/tests/manual/single_query.cpp b/tests/manual/single_query.cpp
index 96407c41b..cb0bf6f80 100644
--- a/tests/manual/single_query.cpp
+++ b/tests/manual/single_query.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,8 +13,9 @@
 #include "license/license.hpp"
 #include "query/config.hpp"
 #include "query/interpreter.hpp"
+#include "storage/v2/config.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/isolation_level.hpp"
-#include "storage/v2/storage.hpp"
 #include "utils/on_scope_exit.hpp"
 
 int main(int argc, char *argv[]) {
@@ -26,15 +27,15 @@ int main(int argc, char *argv[]) {
     exit(1);
   }
 
-  memgraph::storage::Storage db;
   auto data_directory = std::filesystem::temp_directory_path() / "single_query_test";
   memgraph::utils::OnScopeExit([&data_directory] { std::filesystem::remove_all(data_directory); });
 
   memgraph::license::global_license_checker.EnableTesting();
-  memgraph::query::InterpreterContext interpreter_context{&db, memgraph::query::InterpreterConfig{}, data_directory};
+  memgraph::query::InterpreterContext interpreter_context{memgraph::storage::Config{},
+                                                          memgraph::query::InterpreterConfig{}, data_directory};
   memgraph::query::Interpreter interpreter{&interpreter_context};
 
-  ResultStreamFaker stream(&db);
+  ResultStreamFaker stream(interpreter_context.db.get());
   auto [header, _, qid] = interpreter.Prepare(argv[1], {}, nullptr);
   stream.Header(header);
   auto summary = interpreter.PullAll(&stream);
diff --git a/tests/property_based/random_graph.cpp b/tests/property_based/random_graph.cpp
index 909381b9c..5beb22930 100644
--- a/tests/property_based/random_graph.cpp
+++ b/tests/property_based/random_graph.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -9,6 +9,7 @@
 // by the Apache License, Version 2.0, included in the file
 // licenses/APL.txt.
 
+#include <memory>
 #include <unordered_map>
 #include <vector>
 
@@ -19,7 +20,9 @@
 #include <rapidcheck.h>
 #include <rapidcheck/gtest.h>
 
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/storage.hpp"
+#include "storage/v2/vertex_accessor.hpp"
 
 /**
  * It is possible to run test with custom seed with:
@@ -32,45 +35,45 @@ RC_GTEST_PROP(RandomGraph, RandomGraph, (std::vector<std::string> vertex_labels,
   int vertices_num = vertex_labels.size();
   int edges_num = edge_types.size();
 
-  memgraph::storage::Storage db;
+  std::unique_ptr<memgraph::storage::Storage> db{new memgraph::storage::InMemoryStorage()};
   std::vector<memgraph::storage::VertexAccessor> vertices;
   std::unordered_map<memgraph::storage::VertexAccessor, std::string> vertex_label_map;
   std::unordered_map<memgraph::storage::EdgeAccessor, std::string> edge_type_map;
 
-  auto dba = db.Access();
+  auto dba = db->Access();
 
   for (auto label : vertex_labels) {
-    auto vertex_accessor = dba.CreateVertex();
-    RC_ASSERT(vertex_accessor.AddLabel(dba.NameToLabel(label)).HasValue());
-    vertex_label_map.insert({vertex_accessor, label});
+    auto vertex_accessor = dba->CreateVertex();
+    RC_ASSERT(vertex_accessor.AddLabel(dba->NameToLabel(label)).HasValue());
+    vertex_label_map.emplace(vertex_accessor, label);
     vertices.push_back(vertex_accessor);
   }
 
   for (auto type : edge_types) {
     auto &from = vertices[*rc::gen::inRange(0, vertices_num)];
     auto &to = vertices[*rc::gen::inRange(0, vertices_num)];
-    auto maybe_edge_accessor = dba.CreateEdge(&from, &to, dba.NameToEdgeType(type));
+    auto maybe_edge_accessor = dba->CreateEdge(&from, &to, dba->NameToEdgeType(type));
     RC_ASSERT(maybe_edge_accessor.HasValue());
     edge_type_map.insert({*maybe_edge_accessor, type});
   }
 
-  dba.AdvanceCommand();
+  dba->AdvanceCommand();
 
   int edges_num_check = 0;
   int vertices_num_check = 0;
-  for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
+  for (auto vertex : dba->Vertices(memgraph::storage::View::OLD)) {
     auto label = vertex_label_map.at(vertex);
     auto maybe_labels = vertex.Labels(memgraph::storage::View::OLD);
     RC_ASSERT(maybe_labels.HasValue());
     const auto &labels = *maybe_labels;
     RC_ASSERT(labels.size() == 1);
-    RC_ASSERT(dba.LabelToName(labels[0]) == label);
+    RC_ASSERT(dba->LabelToName(labels[0]) == label);
     vertices_num_check++;
     auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
     RC_ASSERT(maybe_edges.HasValue());
     for (auto &edge : *maybe_edges) {
       const auto &type = edge_type_map.at(edge);
-      RC_ASSERT(dba.EdgeTypeToName(edge.EdgeType()) == type);
+      RC_ASSERT(dba->EdgeTypeToName(edge.EdgeType()) == type);
       edges_num_check++;
     }
   }
diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt
index 411c5571c..1c69d2606 100644
--- a/tests/unit/CMakeLists.txt
+++ b/tests/unit/CMakeLists.txt
@@ -303,11 +303,23 @@ target_link_libraries(${test_prefix}storage_v2_constraints mg-storage-v2)
 add_unit_test(storage_v2_decoder_encoder.cpp)
 target_link_libraries(${test_prefix}storage_v2_decoder_encoder mg-storage-v2)
 
-add_unit_test(storage_v2_durability.cpp)
-target_link_libraries(${test_prefix}storage_v2_durability mg-storage-v2)
+add_unit_test(storage_v2_durability_inmemory.cpp)
+target_link_libraries(${test_prefix}storage_v2_durability_inmemory mg-storage-v2)
 
-add_unit_test(storage_v2_edge.cpp)
-target_link_libraries(${test_prefix}storage_v2_edge mg-storage-v2)
+add_unit_test(storage_rocks.cpp)
+target_link_libraries(${test_prefix}storage_rocks mg-storage-v2)
+
+add_unit_test(storage_v2_disk.cpp)
+target_link_libraries(${test_prefix}storage_v2_disk mg-storage-v2)
+
+add_unit_test(clearing_old_disk_data.cpp)
+target_link_libraries(${test_prefix}clearing_old_disk_data mg-storage-v2)
+
+add_unit_test(storage_v2_edge_inmemory.cpp)
+target_link_libraries(${test_prefix}storage_v2_edge_inmemory mg-storage-v2)
+
+add_unit_test(storage_v2_edge_ondisk.cpp)
+target_link_libraries(${test_prefix}storage_v2_edge_ondisk mg-storage-v2)
 
 add_unit_test(storage_v2_gc.cpp)
 target_link_libraries(${test_prefix}storage_v2_gc mg-storage-v2)
diff --git a/tests/unit/auth_checker.cpp b/tests/unit/auth_checker.cpp
index 9028f0d3c..b048e9491 100644
--- a/tests/unit/auth_checker.cpp
+++ b/tests/unit/auth_checker.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -13,30 +13,38 @@
 #include <gtest/gtest.h>
 
 #include "auth/models.hpp"
+#include "disk_test_utils.hpp"
 #include "glue/auth_checker.hpp"
 
 #include "license/license.hpp"
 #include "query_plan_common.hpp"
+#include "storage/v2/config.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/view.hpp"
 
 #ifdef MG_ENTERPRISE
+template <typename StorageType>
 class FineGrainedAuthCheckerFixture : public testing::Test {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
+  const std::string testSuite = "auth_checker";
+
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
 
   // make a V-graph (v3)<-[r2]-(v1)-[r1]->(v2)
   memgraph::query::VertexAccessor v1{dba.InsertVertex()};
   memgraph::query::VertexAccessor v2{dba.InsertVertex()};
   memgraph::query::VertexAccessor v3{dba.InsertVertex()};
-  memgraph::storage::EdgeTypeId edge_type_one{db.NameToEdgeType("edge_type_1")};
-  memgraph::storage::EdgeTypeId edge_type_two{db.NameToEdgeType("edge_type_2")};
+  memgraph::storage::EdgeTypeId edge_type_one{db->NameToEdgeType("edge_type_1")};
+  memgraph::storage::EdgeTypeId edge_type_two{db->NameToEdgeType("edge_type_2")};
 
-  memgraph::query::EdgeAccessor r1{*dba.InsertEdge(&v1, &v2, edge_type_one)};
-  memgraph::query::EdgeAccessor r2{*dba.InsertEdge(&v1, &v3, edge_type_one)};
-  memgraph::query::EdgeAccessor r3{*dba.InsertEdge(&v1, &v2, edge_type_two)};
-  memgraph::query::EdgeAccessor r4{*dba.InsertEdge(&v1, &v3, edge_type_two)};
+  memgraph::query::EdgeAccessor r1{*dba.InsertEdge(&this->v1, &this->v2, edge_type_one)};
+  memgraph::query::EdgeAccessor r2{*dba.InsertEdge(&this->v1, &this->v3, edge_type_one)};
+  memgraph::query::EdgeAccessor r3{*dba.InsertEdge(&this->v1, &this->v2, edge_type_two)};
+  memgraph::query::EdgeAccessor r4{*dba.InsertEdge(&this->v1, &this->v3, edge_type_two)};
 
   void SetUp() override {
     memgraph::license::global_license_checker.EnableTesting();
@@ -45,167 +53,176 @@ class FineGrainedAuthCheckerFixture : public testing::Test {
     ASSERT_TRUE(v3.AddLabel(dba.NameToLabel("l3")).HasValue());
     dba.AdvanceCommand();
   }
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
 };
 
-TEST_F(FineGrainedAuthCheckerFixture, GrantedAllLabels) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(FineGrainedAuthCheckerFixture, StorageTypes);
+
+TYPED_TEST(FineGrainedAuthCheckerFixture, GrantedAllLabels) {
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
 
-  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
+  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &this->dba};
 
   ASSERT_TRUE(
-      auth_checker.Has(v1, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v1, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_TRUE(
-      auth_checker.Has(v1, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v1, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_TRUE(
-      auth_checker.Has(v2, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v2, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_TRUE(
-      auth_checker.Has(v2, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v2, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_TRUE(
-      auth_checker.Has(v3, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v3, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_TRUE(
-      auth_checker.Has(v3, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v3, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
 }
 
-TEST_F(FineGrainedAuthCheckerFixture, GrantedAllEdgeTypes) {
+TYPED_TEST(FineGrainedAuthCheckerFixture, GrantedAllEdgeTypes) {
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
-  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
+  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &this->dba};
 
-  ASSERT_TRUE(auth_checker.Has(r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
-  ASSERT_TRUE(auth_checker.Has(r2, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
-  ASSERT_TRUE(auth_checker.Has(r3, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
-  ASSERT_TRUE(auth_checker.Has(r4, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_TRUE(auth_checker.Has(this->r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_TRUE(auth_checker.Has(this->r2, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_TRUE(auth_checker.Has(this->r3, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_TRUE(auth_checker.Has(this->r4, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
 }
 
-TEST_F(FineGrainedAuthCheckerFixture, DeniedAllLabels) {
+TYPED_TEST(FineGrainedAuthCheckerFixture, DeniedAllLabels) {
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
-  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
+  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &this->dba};
 
   ASSERT_FALSE(
-      auth_checker.Has(v1, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v1, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_FALSE(
-      auth_checker.Has(v1, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v1, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_FALSE(
-      auth_checker.Has(v2, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v2, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_FALSE(
-      auth_checker.Has(v2, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v2, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_FALSE(
-      auth_checker.Has(v3, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v3, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_FALSE(
-      auth_checker.Has(v3, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v3, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
 }
 
-TEST_F(FineGrainedAuthCheckerFixture, DeniedAllEdgeTypes) {
+TYPED_TEST(FineGrainedAuthCheckerFixture, DeniedAllEdgeTypes) {
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
-  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
+  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &this->dba};
 
-  ASSERT_FALSE(auth_checker.Has(r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
-  ASSERT_FALSE(auth_checker.Has(r2, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
-  ASSERT_FALSE(auth_checker.Has(r3, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
-  ASSERT_FALSE(auth_checker.Has(r4, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_FALSE(auth_checker.Has(this->r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_FALSE(auth_checker.Has(this->r2, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_FALSE(auth_checker.Has(this->r3, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_FALSE(auth_checker.Has(this->r4, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
 }
 
-TEST_F(FineGrainedAuthCheckerFixture, GrantLabel) {
+TYPED_TEST(FineGrainedAuthCheckerFixture, GrantLabel) {
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("l1",
                                                                memgraph::auth::FineGrainedPermission::CREATE_DELETE);
-  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
+  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &this->dba};
 
   ASSERT_TRUE(
-      auth_checker.Has(v1, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v1, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_TRUE(
-      auth_checker.Has(v1, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v1, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
 }
 
-TEST_F(FineGrainedAuthCheckerFixture, DenyLabel) {
+TYPED_TEST(FineGrainedAuthCheckerFixture, DenyLabel) {
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("l3", memgraph::auth::FineGrainedPermission::NOTHING);
-  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
+  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &this->dba};
 
   ASSERT_FALSE(
-      auth_checker.Has(v3, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v3, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_FALSE(
-      auth_checker.Has(v3, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v3, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
 }
 
-TEST_F(FineGrainedAuthCheckerFixture, GrantAndDenySpecificLabels) {
+TYPED_TEST(FineGrainedAuthCheckerFixture, GrantAndDenySpecificLabels) {
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("l1",
                                                                memgraph::auth::FineGrainedPermission::CREATE_DELETE);
   user.fine_grained_access_handler().label_permissions().Grant("l2",
                                                                memgraph::auth::FineGrainedPermission::CREATE_DELETE);
   user.fine_grained_access_handler().label_permissions().Grant("l3", memgraph::auth::FineGrainedPermission::NOTHING);
-  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
+  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &this->dba};
 
   ASSERT_TRUE(
-      auth_checker.Has(v1, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v1, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_TRUE(
-      auth_checker.Has(v1, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v1, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_TRUE(
-      auth_checker.Has(v2, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v2, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_TRUE(
-      auth_checker.Has(v2, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v2, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_FALSE(
-      auth_checker.Has(v3, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v3, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_FALSE(
-      auth_checker.Has(v3, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v3, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
 }
 
-TEST_F(FineGrainedAuthCheckerFixture, MultipleVertexLabels) {
+TYPED_TEST(FineGrainedAuthCheckerFixture, MultipleVertexLabels) {
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("l1",
                                                                memgraph::auth::FineGrainedPermission::CREATE_DELETE);
   user.fine_grained_access_handler().label_permissions().Grant("l2",
                                                                memgraph::auth::FineGrainedPermission::CREATE_DELETE);
   user.fine_grained_access_handler().label_permissions().Grant("l3", memgraph::auth::FineGrainedPermission::NOTHING);
-  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-  ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("l3")).HasValue());
-  ASSERT_TRUE(v2.AddLabel(dba.NameToLabel("l1")).HasValue());
-  dba.AdvanceCommand();
+  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &this->dba};
+  ASSERT_TRUE(this->v1.AddLabel(this->dba.NameToLabel("l3")).HasValue());
+  ASSERT_TRUE(this->v2.AddLabel(this->dba.NameToLabel("l1")).HasValue());
+  this->dba.AdvanceCommand();
 
   ASSERT_FALSE(
-      auth_checker.Has(v1, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v1, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_FALSE(
-      auth_checker.Has(v1, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v1, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_TRUE(
-      auth_checker.Has(v2, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v2, memgraph::storage::View::NEW, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
   ASSERT_TRUE(
-      auth_checker.Has(v2, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+      auth_checker.Has(this->v2, memgraph::storage::View::OLD, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
 }
 
-TEST_F(FineGrainedAuthCheckerFixture, GrantEdgeType) {
+TYPED_TEST(FineGrainedAuthCheckerFixture, GrantEdgeType) {
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "edge_type_1", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
-  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
+  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &this->dba};
 
-  ASSERT_TRUE(auth_checker.Has(r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_TRUE(auth_checker.Has(this->r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
 }
 
-TEST_F(FineGrainedAuthCheckerFixture, DenyEdgeType) {
+TYPED_TEST(FineGrainedAuthCheckerFixture, DenyEdgeType) {
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type_1",
                                                                    memgraph::auth::FineGrainedPermission::NOTHING);
-  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
+  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &this->dba};
 
-  ASSERT_FALSE(auth_checker.Has(r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_FALSE(auth_checker.Has(this->r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
 }
 
-TEST_F(FineGrainedAuthCheckerFixture, GrantAndDenySpecificEdgeTypes) {
+TYPED_TEST(FineGrainedAuthCheckerFixture, GrantAndDenySpecificEdgeTypes) {
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "edge_type_1", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
   user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type_2",
                                                                    memgraph::auth::FineGrainedPermission::NOTHING);
-  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
+  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &this->dba};
 
-  ASSERT_TRUE(auth_checker.Has(r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
-  ASSERT_TRUE(auth_checker.Has(r2, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
-  ASSERT_FALSE(auth_checker.Has(r3, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
-  ASSERT_FALSE(auth_checker.Has(r4, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_TRUE(auth_checker.Has(this->r1, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_TRUE(auth_checker.Has(this->r2, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_FALSE(auth_checker.Has(this->r3, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
+  ASSERT_FALSE(auth_checker.Has(this->r4, memgraph::query::AuthQuery::FineGrainedPrivilege::READ));
 }
 #endif
diff --git a/tests/unit/bfs_common.hpp b/tests/unit/bfs_common.hpp
index 431ab9214..e11fd507d 100644
--- a/tests/unit/bfs_common.hpp
+++ b/tests/unit/bfs_common.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -200,22 +200,6 @@ enum class FineGrainedTestType {
   LABEL_3_DENIED
 };
 
-// Common interface for single-node and distributed Memgraph.
-class Database {
- public:
-  virtual memgraph::storage::Storage::Accessor Access() = 0;
-  virtual std::unique_ptr<memgraph::query::plan::LogicalOperator> MakeBfsOperator(
-      memgraph::query::Symbol source_sym, memgraph::query::Symbol sink_sym, memgraph::query::Symbol edge_sym,
-      memgraph::query::EdgeAtom::Direction direction, const std::vector<memgraph::storage::EdgeTypeId> &edge_types,
-      const std::shared_ptr<memgraph::query::plan::LogicalOperator> &input, bool existing_node,
-      memgraph::query::Expression *lower_bound, memgraph::query::Expression *upper_bound,
-      const memgraph::query::plan::ExpansionLambda &filter_lambda) = 0;
-  virtual std::pair<std::vector<memgraph::query::VertexAccessor>, std::vector<memgraph::query::EdgeAccessor>>
-  BuildGraph(memgraph::query::DbAccessor *dba, const std::vector<int> &vertex_locations,
-             const std::vector<std::tuple<int, int, std::string>> &edges) = 0;
-  virtual ~Database() {}
-};
-
 // Returns an operator that yields vertices given by their address. We will also
 // include memgraph::query::TypedValue() to account for the optional match case.
 std::unique_ptr<memgraph::query::plan::LogicalOperator> YieldVertices(
@@ -291,431 +275,453 @@ std::vector<std::vector<int>> CheckPathsAndExtractDistances(
   return distances;
 }
 
-void BfsTest(Database *db, int lower_bound, int upper_bound, memgraph::query::EdgeAtom::Direction direction,
-             std::vector<std::string> edge_types, bool known_sink, FilterLambdaType filter_lambda_type) {
-  auto storage_dba = db->Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  memgraph::query::AstStorage storage;
-  memgraph::query::ExecutionContext context{&dba};
-  memgraph::query::Symbol blocked_sym = context.symbol_table.CreateSymbol("blocked", true);
-  memgraph::query::Symbol source_sym = context.symbol_table.CreateSymbol("source", true);
-  memgraph::query::Symbol sink_sym = context.symbol_table.CreateSymbol("sink", true);
-  memgraph::query::Symbol edges_sym = context.symbol_table.CreateSymbol("edges", true);
-  memgraph::query::Symbol inner_node_sym = context.symbol_table.CreateSymbol("inner_node", true);
-  memgraph::query::Symbol inner_edge_sym = context.symbol_table.CreateSymbol("inner_edge", true);
-  memgraph::query::Identifier *blocked = IDENT("blocked")->MapTo(blocked_sym);
-  memgraph::query::Identifier *inner_node = IDENT("inner_node")->MapTo(inner_node_sym);
-  memgraph::query::Identifier *inner_edge = IDENT("inner_edge")->MapTo(inner_edge_sym);
+// Common interface for single-node and distributed Memgraph.
+class Database {
+ public:
+  virtual std::unique_ptr<memgraph::storage::Storage::Accessor> Access() = 0;
+  virtual std::unique_ptr<memgraph::query::plan::LogicalOperator> MakeBfsOperator(
+      memgraph::query::Symbol source_sym, memgraph::query::Symbol sink_sym, memgraph::query::Symbol edge_sym,
+      memgraph::query::EdgeAtom::Direction direction, const std::vector<memgraph::storage::EdgeTypeId> &edge_types,
+      const std::shared_ptr<memgraph::query::plan::LogicalOperator> &input, bool existing_node,
+      memgraph::query::Expression *lower_bound, memgraph::query::Expression *upper_bound,
+      const memgraph::query::plan::ExpansionLambda &filter_lambda) = 0;
+  virtual std::pair<std::vector<memgraph::query::VertexAccessor>, std::vector<memgraph::query::EdgeAccessor>>
+  BuildGraph(memgraph::query::DbAccessor *dba, const std::vector<int> &vertex_locations,
+             const std::vector<std::tuple<int, int, std::string>> &edges) = 0;
+  virtual ~Database() {}
 
-  std::vector<memgraph::query::VertexAccessor> vertices;
-  std::vector<memgraph::query::EdgeAccessor> edges;
+  void BfsTest(Database *db, int lower_bound, int upper_bound, memgraph::query::EdgeAtom::Direction direction,
+               std::vector<std::string> edge_types, bool known_sink, FilterLambdaType filter_lambda_type) {
+    auto storage_dba = db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
+    memgraph::query::ExecutionContext context{&dba};
+    memgraph::query::Symbol blocked_sym = context.symbol_table.CreateSymbol("blocked", true);
+    memgraph::query::Symbol source_sym = context.symbol_table.CreateSymbol("source", true);
+    memgraph::query::Symbol sink_sym = context.symbol_table.CreateSymbol("sink", true);
+    memgraph::query::Symbol edges_sym = context.symbol_table.CreateSymbol("edges", true);
+    memgraph::query::Symbol inner_node_sym = context.symbol_table.CreateSymbol("inner_node", true);
+    memgraph::query::Symbol inner_edge_sym = context.symbol_table.CreateSymbol("inner_edge", true);
+    memgraph::query::Identifier *blocked = IDENT("blocked")->MapTo(blocked_sym);
+    memgraph::query::Identifier *inner_node = IDENT("inner_node")->MapTo(inner_node_sym);
+    memgraph::query::Identifier *inner_edge = IDENT("inner_edge")->MapTo(inner_edge_sym);
 
-  std::tie(vertices, edges) = db->BuildGraph(&dba, kVertexLocations, kEdges);
+    std::vector<memgraph::query::VertexAccessor> vertices;
+    std::vector<memgraph::query::EdgeAccessor> edges;
 
-  dba.AdvanceCommand();
+    std::tie(vertices, edges) = db->BuildGraph(&dba, kVertexLocations, kEdges);
 
-  std::shared_ptr<memgraph::query::plan::LogicalOperator> input_op;
+    dba.AdvanceCommand();
 
-  memgraph::query::Expression *filter_expr;
+    std::shared_ptr<memgraph::query::plan::LogicalOperator> input_op;
 
-  // First build a filter lambda and an operator yielding blocked entities.
-  switch (filter_lambda_type) {
-    case FilterLambdaType::NONE:
-      // No filter lambda, nothing is ever blocked.
-      input_op = std::make_shared<Yield>(
-          nullptr, std::vector<memgraph::query::Symbol>{blocked_sym},
-          std::vector<std::vector<memgraph::query::TypedValue>>{{memgraph::query::TypedValue()}});
-      filter_expr = nullptr;
-      break;
-    case FilterLambdaType::USE_FRAME:
-      // We block each entity in the graph and run BFS.
-      input_op = YieldEntities(&dba, vertices, edges, blocked_sym, nullptr);
-      filter_expr = AND(NEQ(inner_node, blocked), NEQ(inner_edge, blocked));
-      break;
-    case FilterLambdaType::USE_FRAME_NULL:
-      // We block each entity in the graph and run BFS.
-      input_op = YieldEntities(&dba, vertices, edges, blocked_sym, nullptr);
-      filter_expr = IF(AND(NEQ(inner_node, blocked), NEQ(inner_edge, blocked)), LITERAL(true),
-                       LITERAL(memgraph::storage::PropertyValue()));
-      break;
-    case FilterLambdaType::USE_CTX:
-      // We only block vertex #5 and run BFS.
-      input_op = std::make_shared<Yield>(
-          nullptr, std::vector<memgraph::query::Symbol>{blocked_sym},
-          std::vector<std::vector<memgraph::query::TypedValue>>{{memgraph::query::TypedValue(vertices[5])}});
-      filter_expr = NEQ(PROPERTY_LOOKUP(inner_node, PROPERTY_PAIR("id")), PARAMETER_LOOKUP(0));
-      context.evaluation_context.parameters.Add(0, memgraph::storage::PropertyValue(5));
-      break;
-    case FilterLambdaType::ERROR:
-      // Evaluate to 42 for vertex #5 which is on worker 1.
-      filter_expr = IF(EQ(PROPERTY_LOOKUP(inner_node, PROPERTY_PAIR("id")), LITERAL(5)), LITERAL(42), LITERAL(true));
-  }
+    memgraph::query::Expression *filter_expr;
 
-  // We run BFS once from each vertex for each blocked entity.
-  input_op = YieldVertices(&dba, vertices, source_sym, input_op);
-
-  // If the sink is known, we run BFS for all posible combinations of source,
-  // sink and blocked entity.
-  if (known_sink) {
-    input_op = YieldVertices(&dba, vertices, sink_sym, input_op);
-  }
-
-  std::vector<memgraph::storage::EdgeTypeId> storage_edge_types;
-  for (const auto &t : edge_types) {
-    storage_edge_types.push_back(dba.NameToEdgeType(t));
-  }
-
-  input_op = db->MakeBfsOperator(source_sym, sink_sym, edges_sym, direction, storage_edge_types, input_op, known_sink,
-                                 lower_bound == -1 ? nullptr : LITERAL(lower_bound),
-                                 upper_bound == -1 ? nullptr : LITERAL(upper_bound),
-                                 memgraph::query::plan::ExpansionLambda{inner_edge_sym, inner_node_sym, filter_expr});
-
-  context.evaluation_context.properties = memgraph::query::NamesToProperties(storage.properties_, &dba);
-  context.evaluation_context.labels = memgraph::query::NamesToLabels(storage.labels_, &dba);
-  std::vector<std::vector<memgraph::query::TypedValue>> results;
-
-  // An exception should be thrown on one of the pulls.
-  if (filter_lambda_type == FilterLambdaType::ERROR) {
-    EXPECT_THROW(PullResults(input_op.get(), &context,
-                             std::vector<memgraph::query::Symbol>{source_sym, sink_sym, edges_sym, blocked_sym}),
-                 memgraph::query::QueryRuntimeException);
-    return;
-  }
-
-  results = PullResults(input_op.get(), &context,
-                        std::vector<memgraph::query::Symbol>{source_sym, sink_sym, edges_sym, blocked_sym});
-
-  // Group results based on blocked entity and compare them to results
-  // obtained by running Floyd-Warshall.
-  for (size_t i = 0; i < results.size();) {
-    int j = i;
-    auto blocked = results[j][3];
-    while (j < results.size() && memgraph::query::TypedValue::BoolEqual{}(results[j][3], blocked)) ++j;
-
-    SCOPED_TRACE(fmt::format("blocked entity = {}", ToString(blocked, dba)));
-
-    // When an edge is blocked, it is blocked in both directions so we remove
-    // it before modifying edge list to account for direction and edge types;
-    auto edges = kEdges;
-    if (blocked.IsEdge()) {
-      int from = GetProp(blocked.ValueEdge(), "from", &dba).ValueInt();
-      int to = GetProp(blocked.ValueEdge(), "to", &dba).ValueInt();
-      edges.erase(std::remove_if(edges.begin(), edges.end(),
-                                 [from, to](const auto &e) { return std::get<0>(e) == from && std::get<1>(e) == to; }),
-                  edges.end());
+    // First build a filter lambda and an operator yielding blocked entities.
+    switch (filter_lambda_type) {
+      case FilterLambdaType::NONE:
+        // No filter lambda, nothing is ever blocked.
+        input_op = std::make_shared<Yield>(
+            nullptr, std::vector<memgraph::query::Symbol>{blocked_sym},
+            std::vector<std::vector<memgraph::query::TypedValue>>{{memgraph::query::TypedValue()}});
+        filter_expr = nullptr;
+        break;
+      case FilterLambdaType::USE_FRAME:
+        // We block each entity in the graph and run BFS.
+        input_op = YieldEntities(&dba, vertices, edges, blocked_sym, nullptr);
+        filter_expr = AND(NEQ(inner_node, blocked), NEQ(inner_edge, blocked));
+        break;
+      case FilterLambdaType::USE_FRAME_NULL:
+        // We block each entity in the graph and run BFS.
+        input_op = YieldEntities(&dba, vertices, edges, blocked_sym, nullptr);
+        filter_expr = IF(AND(NEQ(inner_node, blocked), NEQ(inner_edge, blocked)), LITERAL(true),
+                         LITERAL(memgraph::storage::PropertyValue()));
+        break;
+      case FilterLambdaType::USE_CTX:
+        // We only block vertex #5 and run BFS.
+        input_op = std::make_shared<Yield>(
+            nullptr, std::vector<memgraph::query::Symbol>{blocked_sym},
+            std::vector<std::vector<memgraph::query::TypedValue>>{{memgraph::query::TypedValue(vertices[5])}});
+        filter_expr = NEQ(PROPERTY_LOOKUP(dba, inner_node, PROPERTY_PAIR(dba, "id")), PARAMETER_LOOKUP(0));
+        context.evaluation_context.parameters.Add(0, memgraph::storage::PropertyValue(5));
+        break;
+      case FilterLambdaType::ERROR:
+        // Evaluate to 42 for vertex #5 which is on worker 1.
+        filter_expr =
+            IF(EQ(PROPERTY_LOOKUP(dba, inner_node, PROPERTY_PAIR(dba, "id")), LITERAL(5)), LITERAL(42), LITERAL(true));
     }
 
-    // Now add edges in opposite direction if necessary.
-    auto edges_blocked = GetEdgeList(edges, direction, edge_types);
+    // We run BFS once from each vertex for each blocked entity.
+    input_op = YieldVertices(&dba, vertices, source_sym, input_op);
 
-    // When a vertex is blocked, we remove all edges that lead into it.
-    if (blocked.IsVertex()) {
-      int id = GetProp(blocked.ValueVertex(), "id", &dba).ValueInt();
-      edges_blocked.erase(
-          std::remove_if(edges_blocked.begin(), edges_blocked.end(), [id](const auto &e) { return e.second == id; }),
-          edges_blocked.end());
+    // If the sink is known, we run BFS for all posible combinations of source,
+    // sink and blocked entity.
+    if (known_sink) {
+      input_op = YieldVertices(&dba, vertices, sink_sym, input_op);
     }
 
-    auto correct_with_bounds = FloydWarshall(kVertexCount, edges_blocked);
+    std::vector<memgraph::storage::EdgeTypeId> storage_edge_types;
+    for (const auto &t : edge_types) {
+      storage_edge_types.push_back(dba.NameToEdgeType(t));
+    }
 
-    if (lower_bound == -1) lower_bound = 0;
-    if (upper_bound == -1) upper_bound = kVertexCount;
+    input_op = db->MakeBfsOperator(source_sym, sink_sym, edges_sym, direction, storage_edge_types, input_op, known_sink,
+                                   lower_bound == -1 ? nullptr : LITERAL(lower_bound),
+                                   upper_bound == -1 ? nullptr : LITERAL(upper_bound),
+                                   memgraph::query::plan::ExpansionLambda{inner_edge_sym, inner_node_sym, filter_expr});
 
-    // Remove paths whose length doesn't satisfy given length bounds.
-    for (int a = 0; a < kVertexCount; ++a) {
-      for (int b = 0; b < kVertexCount; ++b) {
-        if (a != b && (correct_with_bounds[a][b] < lower_bound || correct_with_bounds[a][b] > upper_bound))
-          correct_with_bounds[a][b] = -1;
+    context.evaluation_context.properties = memgraph::query::NamesToProperties(storage.properties_, &dba);
+    context.evaluation_context.labels = memgraph::query::NamesToLabels(storage.labels_, &dba);
+    std::vector<std::vector<memgraph::query::TypedValue>> results;
+
+    // An exception should be thrown on one of the pulls.
+    if (filter_lambda_type == FilterLambdaType::ERROR) {
+      EXPECT_THROW(PullResults(input_op.get(), &context,
+                               std::vector<memgraph::query::Symbol>{source_sym, sink_sym, edges_sym, blocked_sym}),
+                   memgraph::query::QueryRuntimeException);
+      return;
+    }
+
+    results = PullResults(input_op.get(), &context,
+                          std::vector<memgraph::query::Symbol>{source_sym, sink_sym, edges_sym, blocked_sym});
+
+    // Group results based on blocked entity and compare them to results
+    // obtained by running Floyd-Warshall.
+    for (size_t i = 0; i < results.size();) {
+      int j = i;
+      auto blocked = results[j][3];
+      while (j < results.size() && memgraph::query::TypedValue::BoolEqual{}(results[j][3], blocked)) ++j;
+
+      SCOPED_TRACE(fmt::format("blocked entity = {}", ToString(blocked, dba)));
+
+      // When an edge is blocked, it is blocked in both directions so we remove
+      // it before modifying edge list to account for direction and edge types;
+      auto edges = kEdges;
+      if (blocked.IsEdge()) {
+        int from = GetProp(blocked.ValueEdge(), "from", &dba).ValueInt();
+        int to = GetProp(blocked.ValueEdge(), "to", &dba).ValueInt();
+        edges.erase(
+            std::remove_if(edges.begin(), edges.end(),
+                           [from, to](const auto &e) { return std::get<0>(e) == from && std::get<1>(e) == to; }),
+            edges.end());
       }
+
+      // Now add edges in opposite direction if necessary.
+      auto edges_blocked = GetEdgeList(edges, direction, edge_types);
+
+      // When a vertex is blocked, we remove all edges that lead into it.
+      if (blocked.IsVertex()) {
+        int id = GetProp(blocked.ValueVertex(), "id", &dba).ValueInt();
+        edges_blocked.erase(
+            std::remove_if(edges_blocked.begin(), edges_blocked.end(), [id](const auto &e) { return e.second == id; }),
+            edges_blocked.end());
+      }
+
+      auto correct_with_bounds = FloydWarshall(kVertexCount, edges_blocked);
+
+      if (lower_bound == -1) lower_bound = 0;
+      if (upper_bound == -1) upper_bound = kVertexCount;
+
+      // Remove paths whose length doesn't satisfy given length bounds.
+      for (int a = 0; a < kVertexCount; ++a) {
+        for (int b = 0; b < kVertexCount; ++b) {
+          if (a != b && (correct_with_bounds[a][b] < lower_bound || correct_with_bounds[a][b] > upper_bound))
+            correct_with_bounds[a][b] = -1;
+        }
+      }
+
+      int num_results = 0;
+      for (int a = 0; a < kVertexCount; ++a)
+        for (int b = 0; b < kVertexCount; ++b)
+          if (a != b && correct_with_bounds[a][b] != -1) {
+            ++num_results;
+          }
+      // There should be exactly 1 successful pull for each existing path.
+      EXPECT_EQ(j - i, num_results);
+
+      auto distances = CheckPathsAndExtractDistances(
+          &dba, edges_blocked,
+          std::vector<std::vector<memgraph::query::TypedValue>>(results.begin() + i, results.begin() + j));
+
+      // The distances should also match.
+      EXPECT_EQ(distances, correct_with_bounds);
+
+      i = j;
     }
 
-    int num_results = 0;
-    for (int a = 0; a < kVertexCount; ++a)
-      for (int b = 0; b < kVertexCount; ++b)
-        if (a != b && correct_with_bounds[a][b] != -1) {
-          ++num_results;
-        }
-    // There should be exactly 1 successful pull for each existing path.
-    EXPECT_EQ(j - i, num_results);
-
-    auto distances = CheckPathsAndExtractDistances(
-        &dba, edges_blocked,
-        std::vector<std::vector<memgraph::query::TypedValue>>(results.begin() + i, results.begin() + j));
-
-    // The distances should also match.
-    EXPECT_EQ(distances, correct_with_bounds);
-
-    i = j;
+    dba.Abort();
   }
 
-  dba.Abort();
-}
-
 #ifdef MG_ENTERPRISE
-void BfsTestWithFineGrainedFiltering(Database *db, int lower_bound, int upper_bound,
-                                     memgraph::query::EdgeAtom::Direction direction,
-                                     std::vector<std::string> edge_types, bool known_sink,
-                                     FineGrainedTestType fine_grained_test_type) {
-  auto storage_dba = db->Access();
-  memgraph::query::DbAccessor db_accessor(&storage_dba);
-  memgraph::query::AstStorage storage;
-  memgraph::query::ExecutionContext context{&db_accessor};
-  memgraph::query::Symbol blocked_symbol = context.symbol_table.CreateSymbol("blocked", true);
-  memgraph::query::Symbol source_symbol = context.symbol_table.CreateSymbol("source", true);
-  memgraph::query::Symbol sink_symbol = context.symbol_table.CreateSymbol("sink", true);
-  memgraph::query::Symbol edges_symbol = context.symbol_table.CreateSymbol("edges", true);
-  memgraph::query::Symbol inner_node_symbol = context.symbol_table.CreateSymbol("inner_node", true);
-  memgraph::query::Symbol inner_edge_symbol = context.symbol_table.CreateSymbol("inner_edge", true);
+  void BfsTestWithFineGrainedFiltering(Database *db, int lower_bound, int upper_bound,
+                                       memgraph::query::EdgeAtom::Direction direction,
+                                       std::vector<std::string> edge_types, bool known_sink,
+                                       FineGrainedTestType fine_grained_test_type) {
+    auto storage_dba = db->Access();
+    memgraph::query::DbAccessor db_accessor(storage_dba.get());
+    memgraph::query::ExecutionContext context{&db_accessor};
+    memgraph::query::Symbol blocked_symbol = context.symbol_table.CreateSymbol("blocked", true);
+    memgraph::query::Symbol source_symbol = context.symbol_table.CreateSymbol("source", true);
+    memgraph::query::Symbol sink_symbol = context.symbol_table.CreateSymbol("sink", true);
+    memgraph::query::Symbol edges_symbol = context.symbol_table.CreateSymbol("edges", true);
+    memgraph::query::Symbol inner_node_symbol = context.symbol_table.CreateSymbol("inner_node", true);
+    memgraph::query::Symbol inner_edge_symbol = context.symbol_table.CreateSymbol("inner_edge", true);
 
-  std::vector<memgraph::query::VertexAccessor> vertices;
-  std::vector<memgraph::query::EdgeAccessor> edges;
+    std::vector<memgraph::query::VertexAccessor> vertices;
+    std::vector<memgraph::query::EdgeAccessor> edges;
 
-  std::tie(vertices, edges) = db->BuildGraph(&db_accessor, kVertexLocations, kEdges);
+    std::tie(vertices, edges) = db->BuildGraph(&db_accessor, kVertexLocations, kEdges);
 
-  db_accessor.AdvanceCommand();
+    db_accessor.AdvanceCommand();
 
-  std::shared_ptr<memgraph::query::plan::LogicalOperator> input_operator;
+    std::shared_ptr<memgraph::query::plan::LogicalOperator> input_operator;
 
-  memgraph::query::Expression *filter_expr = nullptr;
+    memgraph::query::Expression *filter_expr = nullptr;
 
-  input_operator =
-      std::make_shared<Yield>(nullptr, std::vector<memgraph::query::Symbol>{blocked_symbol},
-                              std::vector<std::vector<memgraph::query::TypedValue>>{{memgraph::query::TypedValue()}});
+    input_operator =
+        std::make_shared<Yield>(nullptr, std::vector<memgraph::query::Symbol>{blocked_symbol},
+                                std::vector<std::vector<memgraph::query::TypedValue>>{{memgraph::query::TypedValue()}});
 
-  memgraph::auth::User user{"test"};
-  std::vector<std::pair<int, int>> edges_in_result;
-  switch (fine_grained_test_type) {
-    case FineGrainedTestType::ALL_GRANTED:
-      user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().edge_type_permissions().Grant("*",
-                                                                       memgraph::auth::FineGrainedPermission::READ);
-      edges_in_result = GetEdgeList(kEdges, direction, {"a", "b"});
-      break;
-    case FineGrainedTestType::ALL_DENIED:
-      break;
-    case FineGrainedTestType::EDGE_TYPE_A_DENIED:
-      user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().edge_type_permissions().Grant("b",
-                                                                       memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().edge_type_permissions().Grant("a",
-                                                                       memgraph::auth::FineGrainedPermission::NOTHING);
+    memgraph::auth::User user{"test"};
+    std::vector<std::pair<int, int>> edges_in_result;
+    switch (fine_grained_test_type) {
+      case FineGrainedTestType::ALL_GRANTED:
+        user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().edge_type_permissions().Grant("*",
+                                                                         memgraph::auth::FineGrainedPermission::READ);
+        edges_in_result = GetEdgeList(kEdges, direction, {"a", "b"});
+        break;
+      case FineGrainedTestType::ALL_DENIED:
+        break;
+      case FineGrainedTestType::EDGE_TYPE_A_DENIED:
+        user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().edge_type_permissions().Grant("b",
+                                                                         memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().edge_type_permissions().Grant(
+            "a", memgraph::auth::FineGrainedPermission::NOTHING);
 
-      edges_in_result = GetEdgeList(kEdges, direction, {"b"});
-      break;
-    case FineGrainedTestType::EDGE_TYPE_B_DENIED:
-      user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().edge_type_permissions().Grant("a",
-                                                                       memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().edge_type_permissions().Grant("b",
-                                                                       memgraph::auth::FineGrainedPermission::NOTHING);
+        edges_in_result = GetEdgeList(kEdges, direction, {"b"});
+        break;
+      case FineGrainedTestType::EDGE_TYPE_B_DENIED:
+        user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().edge_type_permissions().Grant("a",
+                                                                         memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().edge_type_permissions().Grant(
+            "b", memgraph::auth::FineGrainedPermission::NOTHING);
 
-      edges_in_result = GetEdgeList(kEdges, direction, {"a"});
-      break;
-    case FineGrainedTestType::LABEL_0_DENIED:
-      user.fine_grained_access_handler().edge_type_permissions().Grant("*",
-                                                                       memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().label_permissions().Grant("3", memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().label_permissions().Grant("4", memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().label_permissions().Grant("0", memgraph::auth::FineGrainedPermission::NOTHING);
+        edges_in_result = GetEdgeList(kEdges, direction, {"a"});
+        break;
+      case FineGrainedTestType::LABEL_0_DENIED:
+        user.fine_grained_access_handler().edge_type_permissions().Grant("*",
+                                                                         memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().label_permissions().Grant("3", memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().label_permissions().Grant("4", memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().label_permissions().Grant("0",
+                                                                     memgraph::auth::FineGrainedPermission::NOTHING);
 
-      edges_in_result = GetEdgeList(kEdges, direction, {"a", "b"});
-      edges_in_result.erase(
-          std::remove_if(edges_in_result.begin(), edges_in_result.end(), [](const auto &e) { return e.second == 0; }),
-          edges_in_result.end());
-      break;
-    case FineGrainedTestType::LABEL_3_DENIED:
-      user.fine_grained_access_handler().edge_type_permissions().Grant("*",
-                                                                       memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().label_permissions().Grant("0", memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().label_permissions().Grant("4", memgraph::auth::FineGrainedPermission::READ);
-      user.fine_grained_access_handler().label_permissions().Grant("3", memgraph::auth::FineGrainedPermission::NOTHING);
+        edges_in_result = GetEdgeList(kEdges, direction, {"a", "b"});
+        edges_in_result.erase(
+            std::remove_if(edges_in_result.begin(), edges_in_result.end(), [](const auto &e) { return e.second == 0; }),
+            edges_in_result.end());
+        break;
+      case FineGrainedTestType::LABEL_3_DENIED:
+        user.fine_grained_access_handler().edge_type_permissions().Grant("*",
+                                                                         memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().label_permissions().Grant("0", memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().label_permissions().Grant("4", memgraph::auth::FineGrainedPermission::READ);
+        user.fine_grained_access_handler().label_permissions().Grant("3",
+                                                                     memgraph::auth::FineGrainedPermission::NOTHING);
 
-      edges_in_result = GetEdgeList(kEdges, direction, {"a", "b"});
-      edges_in_result.erase(
-          std::remove_if(edges_in_result.begin(), edges_in_result.end(), [](const auto &e) { return e.second == 3; }),
-          edges_in_result.end());
-      break;
+        edges_in_result = GetEdgeList(kEdges, direction, {"a", "b"});
+        edges_in_result.erase(
+            std::remove_if(edges_in_result.begin(), edges_in_result.end(), [](const auto &e) { return e.second == 3; }),
+            edges_in_result.end());
+        break;
+    }
+
+    memgraph::glue::FineGrainedAuthChecker auth_checker{user, &db_accessor};
+    context.auth_checker = std::make_unique<memgraph::glue::FineGrainedAuthChecker>(std::move(auth_checker));
+    // We run BFS once from each vertex for each blocked entity.
+    input_operator = YieldVertices(&db_accessor, vertices, source_symbol, input_operator);
+
+    // If the sink is known, we run BFS for all posible combinations of source,
+    // sink and blocked entity.
+    if (known_sink) {
+      input_operator = YieldVertices(&db_accessor, vertices, sink_symbol, input_operator);
+    }
+
+    std::vector<memgraph::storage::EdgeTypeId> storage_edge_types;
+    for (const auto &t : edge_types) {
+      storage_edge_types.push_back(db_accessor.NameToEdgeType(t));
+    }
+
+    input_operator = db->MakeBfsOperator(
+        source_symbol, sink_symbol, edges_symbol, direction, storage_edge_types, input_operator, known_sink,
+        lower_bound == -1 ? nullptr : LITERAL(lower_bound), upper_bound == -1 ? nullptr : LITERAL(upper_bound),
+        memgraph::query::plan::ExpansionLambda{inner_edge_symbol, inner_node_symbol, filter_expr});
+
+    context.evaluation_context.properties = memgraph::query::NamesToProperties(storage.properties_, &db_accessor);
+    context.evaluation_context.labels = memgraph::query::NamesToLabels(storage.labels_, &db_accessor);
+    std::vector<std::vector<memgraph::query::TypedValue>> results;
+
+    results =
+        PullResults(input_operator.get(), &context,
+                    std::vector<memgraph::query::Symbol>{source_symbol, sink_symbol, edges_symbol, blocked_symbol});
+
+    switch (fine_grained_test_type) {
+      case FineGrainedTestType::ALL_GRANTED:
+        switch (direction) {
+          case memgraph::query::EdgeAtom::Direction::IN:
+            CheckPathsAndExtractDistances(
+                &db_accessor, edges_in_result,
+                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            break;
+          case memgraph::query::EdgeAtom::Direction::OUT:
+            CheckPathsAndExtractDistances(
+                &db_accessor, edges_in_result,
+                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            break;
+          case memgraph::query::EdgeAtom::Direction::BOTH:
+            CheckPathsAndExtractDistances(
+                &db_accessor, edges_in_result,
+                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            break;
+        }
+        break;
+      case FineGrainedTestType::ALL_DENIED:
+        switch (direction) {
+          case memgraph::query::EdgeAtom::Direction::IN:
+            EXPECT_EQ(results.size(), 0);
+            break;
+          case memgraph::query::EdgeAtom::Direction::OUT:
+            EXPECT_EQ(results.size(), 0);
+            break;
+          case memgraph::query::EdgeAtom::Direction::BOTH:
+            EXPECT_EQ(results.size(), 0);
+            break;
+        }
+        break;
+      case FineGrainedTestType::EDGE_TYPE_A_DENIED:
+        switch (direction) {
+          case memgraph::query::EdgeAtom::Direction::IN:
+            CheckPathsAndExtractDistances(
+                &db_accessor, edges_in_result,
+                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            break;
+          case memgraph::query::EdgeAtom::Direction::OUT:
+            CheckPathsAndExtractDistances(
+                &db_accessor, edges_in_result,
+                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            break;
+          case memgraph::query::EdgeAtom::Direction::BOTH:
+            CheckPathsAndExtractDistances(
+                &db_accessor, edges_in_result,
+                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            break;
+        }
+        break;
+      case FineGrainedTestType::EDGE_TYPE_B_DENIED:
+        switch (direction) {
+          case memgraph::query::EdgeAtom::Direction::IN:
+            CheckPathsAndExtractDistances(
+                &db_accessor, edges_in_result,
+                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            break;
+          case memgraph::query::EdgeAtom::Direction::OUT:
+            CheckPathsAndExtractDistances(
+                &db_accessor, edges_in_result,
+                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            break;
+          case memgraph::query::EdgeAtom::Direction::BOTH:
+            CheckPathsAndExtractDistances(
+                &db_accessor, edges_in_result,
+                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            break;
+        }
+        break;
+      case FineGrainedTestType::LABEL_0_DENIED:
+        switch (direction) {
+          case memgraph::query::EdgeAtom::Direction::IN:
+            if (known_sink) {
+              CheckPathsAndExtractDistances(
+                  &db_accessor, edges_in_result,
+                  std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            } else {
+              CheckPathsAndExtractDistances(
+                  &db_accessor, edges_in_result,
+                  std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            }
+            break;
+          case memgraph::query::EdgeAtom::Direction::OUT:
+            if (known_sink) {
+              CheckPathsAndExtractDistances(
+                  &db_accessor, edges_in_result,
+                  std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            } else {
+              CheckPathsAndExtractDistances(
+                  &db_accessor, edges_in_result,
+                  std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            }
+            break;
+          case memgraph::query::EdgeAtom::Direction::BOTH:
+            if (known_sink) {
+              CheckPathsAndExtractDistances(
+                  &db_accessor, edges_in_result,
+                  std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            } else {
+              CheckPathsAndExtractDistances(
+                  &db_accessor, edges_in_result,
+                  std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            }
+            break;
+        }
+        break;
+      case FineGrainedTestType::LABEL_3_DENIED:
+        switch (direction) {
+          case memgraph::query::EdgeAtom::Direction::IN:
+            if (known_sink) {
+              CheckPathsAndExtractDistances(
+                  &db_accessor, edges_in_result,
+                  std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            } else {
+              CheckPathsAndExtractDistances(
+                  &db_accessor, edges_in_result,
+                  std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            }
+            break;
+          case memgraph::query::EdgeAtom::Direction::OUT:
+            if (known_sink) {
+              CheckPathsAndExtractDistances(
+                  &db_accessor, edges_in_result,
+                  std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            } else {
+              CheckPathsAndExtractDistances(
+                  &db_accessor, edges_in_result,
+                  std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            }
+            break;
+          case memgraph::query::EdgeAtom::Direction::BOTH:
+            if (known_sink) {
+              CheckPathsAndExtractDistances(
+                  &db_accessor, edges_in_result,
+                  std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            } else {
+              CheckPathsAndExtractDistances(
+                  &db_accessor, edges_in_result,
+                  std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
+            }
+            break;
+        }
+        break;
+    }
+
+    db_accessor.Abort();
   }
-
-  memgraph::glue::FineGrainedAuthChecker auth_checker{user, &db_accessor};
-  context.auth_checker = std::make_unique<memgraph::glue::FineGrainedAuthChecker>(std::move(auth_checker));
-  // We run BFS once from each vertex for each blocked entity.
-  input_operator = YieldVertices(&db_accessor, vertices, source_symbol, input_operator);
-
-  // If the sink is known, we run BFS for all posible combinations of source,
-  // sink and blocked entity.
-  if (known_sink) {
-    input_operator = YieldVertices(&db_accessor, vertices, sink_symbol, input_operator);
-  }
-
-  std::vector<memgraph::storage::EdgeTypeId> storage_edge_types;
-  for (const auto &t : edge_types) {
-    storage_edge_types.push_back(db_accessor.NameToEdgeType(t));
-  }
-
-  input_operator = db->MakeBfsOperator(
-      source_symbol, sink_symbol, edges_symbol, direction, storage_edge_types, input_operator, known_sink,
-      lower_bound == -1 ? nullptr : LITERAL(lower_bound), upper_bound == -1 ? nullptr : LITERAL(upper_bound),
-      memgraph::query::plan::ExpansionLambda{inner_edge_symbol, inner_node_symbol, filter_expr});
-
-  context.evaluation_context.properties = memgraph::query::NamesToProperties(storage.properties_, &db_accessor);
-  context.evaluation_context.labels = memgraph::query::NamesToLabels(storage.labels_, &db_accessor);
-  std::vector<std::vector<memgraph::query::TypedValue>> results;
-
-  results = PullResults(input_operator.get(), &context,
-                        std::vector<memgraph::query::Symbol>{source_symbol, sink_symbol, edges_symbol, blocked_symbol});
-
-  switch (fine_grained_test_type) {
-    case FineGrainedTestType::ALL_GRANTED:
-      switch (direction) {
-        case memgraph::query::EdgeAtom::Direction::IN:
-          CheckPathsAndExtractDistances(
-              &db_accessor, edges_in_result,
-              std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          break;
-        case memgraph::query::EdgeAtom::Direction::OUT:
-          CheckPathsAndExtractDistances(
-              &db_accessor, edges_in_result,
-              std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          break;
-        case memgraph::query::EdgeAtom::Direction::BOTH:
-          CheckPathsAndExtractDistances(
-              &db_accessor, edges_in_result,
-              std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          break;
-      }
-      break;
-    case FineGrainedTestType::ALL_DENIED:
-      switch (direction) {
-        case memgraph::query::EdgeAtom::Direction::IN:
-          EXPECT_EQ(results.size(), 0);
-          break;
-        case memgraph::query::EdgeAtom::Direction::OUT:
-          EXPECT_EQ(results.size(), 0);
-          break;
-        case memgraph::query::EdgeAtom::Direction::BOTH:
-          EXPECT_EQ(results.size(), 0);
-          break;
-      }
-      break;
-    case FineGrainedTestType::EDGE_TYPE_A_DENIED:
-      switch (direction) {
-        case memgraph::query::EdgeAtom::Direction::IN:
-          CheckPathsAndExtractDistances(
-              &db_accessor, edges_in_result,
-              std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          break;
-        case memgraph::query::EdgeAtom::Direction::OUT:
-          CheckPathsAndExtractDistances(
-              &db_accessor, edges_in_result,
-              std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          break;
-        case memgraph::query::EdgeAtom::Direction::BOTH:
-          CheckPathsAndExtractDistances(
-              &db_accessor, edges_in_result,
-              std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          break;
-      }
-      break;
-    case FineGrainedTestType::EDGE_TYPE_B_DENIED:
-      switch (direction) {
-        case memgraph::query::EdgeAtom::Direction::IN:
-          CheckPathsAndExtractDistances(
-              &db_accessor, edges_in_result,
-              std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          break;
-        case memgraph::query::EdgeAtom::Direction::OUT:
-          CheckPathsAndExtractDistances(
-              &db_accessor, edges_in_result,
-              std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          break;
-        case memgraph::query::EdgeAtom::Direction::BOTH:
-          CheckPathsAndExtractDistances(
-              &db_accessor, edges_in_result,
-              std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          break;
-      }
-      break;
-    case FineGrainedTestType::LABEL_0_DENIED:
-      switch (direction) {
-        case memgraph::query::EdgeAtom::Direction::IN:
-          if (known_sink) {
-            CheckPathsAndExtractDistances(
-                &db_accessor, edges_in_result,
-                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          } else {
-            CheckPathsAndExtractDistances(
-                &db_accessor, edges_in_result,
-                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          }
-          break;
-        case memgraph::query::EdgeAtom::Direction::OUT:
-          if (known_sink) {
-            CheckPathsAndExtractDistances(
-                &db_accessor, edges_in_result,
-                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          } else {
-            CheckPathsAndExtractDistances(
-                &db_accessor, edges_in_result,
-                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          }
-          break;
-        case memgraph::query::EdgeAtom::Direction::BOTH:
-          if (known_sink) {
-            CheckPathsAndExtractDistances(
-                &db_accessor, edges_in_result,
-                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          } else {
-            CheckPathsAndExtractDistances(
-                &db_accessor, edges_in_result,
-                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          }
-          break;
-      }
-      break;
-    case FineGrainedTestType::LABEL_3_DENIED:
-      switch (direction) {
-        case memgraph::query::EdgeAtom::Direction::IN:
-          if (known_sink) {
-            CheckPathsAndExtractDistances(
-                &db_accessor, edges_in_result,
-                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          } else {
-            CheckPathsAndExtractDistances(
-                &db_accessor, edges_in_result,
-                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          }
-          break;
-        case memgraph::query::EdgeAtom::Direction::OUT:
-          if (known_sink) {
-            CheckPathsAndExtractDistances(
-                &db_accessor, edges_in_result,
-                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          } else {
-            CheckPathsAndExtractDistances(
-                &db_accessor, edges_in_result,
-                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          }
-          break;
-        case memgraph::query::EdgeAtom::Direction::BOTH:
-          if (known_sink) {
-            CheckPathsAndExtractDistances(
-                &db_accessor, edges_in_result,
-                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          } else {
-            CheckPathsAndExtractDistances(
-                &db_accessor, edges_in_result,
-                std::vector<std::vector<memgraph::query::TypedValue>>(results.begin(), results.begin()));
-          }
-          break;
-      }
-      break;
-  }
-
-  db_accessor.Abort();
-}
 #endif
+
+ protected:
+  memgraph::query::AstStorage storage;
+};
diff --git a/tests/unit/bfs_fine_grained.cpp b/tests/unit/bfs_fine_grained.cpp
index e628a4c6b..235431804 100644
--- a/tests/unit/bfs_fine_grained.cpp
+++ b/tests/unit/bfs_fine_grained.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -18,16 +18,31 @@
 #include <gtest/internal/gtest-param-util-generated.h>
 
 #include "auth/models.hpp"
+#include "disk_test_utils.hpp"
 #include "license/license.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 using namespace memgraph::query;
 using namespace memgraph::query::plan;
 
+template <typename StorageType>
 class VertexDb : public Database {
  public:
-  VertexDb() : db_() {}
+  const std::string testSuite = "bfs_fine_grained";
 
-  memgraph::storage::Storage::Accessor Access() override { return db_.Access(); }
+  VertexDb() {
+    config_ = disk_test_utils::GenerateOnDiskConfig(testSuite);
+    db_ = std::make_unique<StorageType>(config_);
+  }
+
+  ~VertexDb() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+
+  std::unique_ptr<memgraph::storage::Storage::Accessor> Access() override { return db_->Access(); }
 
   std::unique_ptr<LogicalOperator> MakeBfsOperator(Symbol source_sym, Symbol sink_sym, Symbol edge_sym,
                                                    EdgeAtom::Direction direction,
@@ -71,25 +86,27 @@ class VertexDb : public Database {
   }
 
  protected:
-  memgraph::storage::Storage db_;
+  memgraph::storage::Config config_;
+  std::unique_ptr<memgraph::storage::Storage> db_;
 };
 
 #ifdef MG_ENTERPRISE
-class FineGrainedBfsTest
+class FineGrainedBfsTestInMemory
     : public ::testing::TestWithParam<
           std::tuple<int, int, EdgeAtom::Direction, std::vector<std::string>, bool, FineGrainedTestType>> {
  public:
+  using StorageType = memgraph::storage::InMemoryStorage;
   static void SetUpTestCase() {
     memgraph::license::global_license_checker.EnableTesting();
-    db_ = std::make_unique<VertexDb>();
+    db_ = std::make_unique<VertexDb<StorageType>>();
   }
   static void TearDownTestCase() { db_ = nullptr; }
 
  protected:
-  static std::unique_ptr<VertexDb> db_;
+  static std::unique_ptr<VertexDb<StorageType>> db_;
 };
 
-TEST_P(FineGrainedBfsTest, All) {
+TEST_P(FineGrainedBfsTestInMemory, All) {
   int lower_bound;
   int upper_bound;
   EdgeAtom::Direction direction;
@@ -99,13 +116,54 @@ TEST_P(FineGrainedBfsTest, All) {
 
   std::tie(lower_bound, upper_bound, direction, edge_types, known_sink, fine_grained_test_type) = GetParam();
 
-  BfsTestWithFineGrainedFiltering(db_.get(), lower_bound, upper_bound, direction, edge_types, known_sink,
-                                  fine_grained_test_type);
+  this->db_->BfsTestWithFineGrainedFiltering(db_.get(), lower_bound, upper_bound, direction, edge_types, known_sink,
+                                             fine_grained_test_type);
 }
 
-std::unique_ptr<VertexDb> FineGrainedBfsTest::db_{nullptr};
+std::unique_ptr<VertexDb<FineGrainedBfsTestInMemory::StorageType>> FineGrainedBfsTestInMemory::db_{nullptr};
+
 INSTANTIATE_TEST_CASE_P(
-    FineGrained, FineGrainedBfsTest,
+    FineGrained, FineGrainedBfsTestInMemory,
+    testing::Combine(testing::Values(3), testing::Values(-1),
+                     testing::Values(EdgeAtom::Direction::OUT, EdgeAtom::Direction::IN, EdgeAtom::Direction::BOTH),
+                     testing::Values(std::vector<std::string>{}), testing::Bool(),
+                     testing::Values(FineGrainedTestType::ALL_GRANTED, FineGrainedTestType::ALL_DENIED,
+                                     FineGrainedTestType::EDGE_TYPE_A_DENIED, FineGrainedTestType::EDGE_TYPE_B_DENIED,
+                                     FineGrainedTestType::LABEL_0_DENIED, FineGrainedTestType::LABEL_3_DENIED)));
+
+class FineGrainedBfsTestOnDisk
+    : public ::testing::TestWithParam<
+          std::tuple<int, int, EdgeAtom::Direction, std::vector<std::string>, bool, FineGrainedTestType>> {
+ public:
+  using StorageType = memgraph::storage::DiskStorage;
+  static void SetUpTestCase() {
+    memgraph::license::global_license_checker.EnableTesting();
+    db_ = std::make_unique<VertexDb<StorageType>>();
+  }
+  static void TearDownTestCase() { db_ = nullptr; }
+
+ protected:
+  static std::unique_ptr<VertexDb<StorageType>> db_;
+};
+
+TEST_P(FineGrainedBfsTestOnDisk, All) {
+  int lower_bound;
+  int upper_bound;
+  EdgeAtom::Direction direction;
+  std::vector<std::string> edge_types;
+  bool known_sink;
+  FineGrainedTestType fine_grained_test_type;
+
+  std::tie(lower_bound, upper_bound, direction, edge_types, known_sink, fine_grained_test_type) = GetParam();
+
+  this->db_->BfsTestWithFineGrainedFiltering(db_.get(), lower_bound, upper_bound, direction, edge_types, known_sink,
+                                             fine_grained_test_type);
+}
+
+std::unique_ptr<VertexDb<FineGrainedBfsTestOnDisk::StorageType>> FineGrainedBfsTestOnDisk::db_{nullptr};
+
+INSTANTIATE_TEST_CASE_P(
+    FineGrained, FineGrainedBfsTestOnDisk,
     testing::Combine(testing::Values(3), testing::Values(-1),
                      testing::Values(EdgeAtom::Direction::OUT, EdgeAtom::Direction::IN, EdgeAtom::Direction::BOTH),
                      testing::Values(std::vector<std::string>{}), testing::Bool(),
diff --git a/tests/unit/bfs_single_node.cpp b/tests/unit/bfs_single_node.cpp
index 93002eef5..37aae0491 100644
--- a/tests/unit/bfs_single_node.cpp
+++ b/tests/unit/bfs_single_node.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -11,14 +11,27 @@
 
 #include "bfs_common.hpp"
 
+#include "disk_test_utils.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
+
 using namespace memgraph::query;
 using namespace memgraph::query::plan;
 
+template <typename StorageType>
 class SingleNodeDb : public Database {
  public:
-  SingleNodeDb() : db_() {}
+  const std::string testSuite = "bfs_single_node";
 
-  memgraph::storage::Storage::Accessor Access() override { return db_.Access(); }
+  SingleNodeDb() : config_(disk_test_utils::GenerateOnDiskConfig(testSuite)), db_(new StorageType(config_)) {}
+
+  ~SingleNodeDb() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+
+  std::unique_ptr<memgraph::storage::Storage::Accessor> Access() override { return db_->Access(); }
 
   std::unique_ptr<LogicalOperator> MakeBfsOperator(Symbol source_sym, Symbol sink_sym, Symbol edge_sym,
                                                    EdgeAtom::Direction direction,
@@ -61,21 +74,23 @@ class SingleNodeDb : public Database {
   }
 
  protected:
-  memgraph::storage::Storage db_;
+  memgraph::storage::Config config_;
+  std::unique_ptr<memgraph::storage::Storage> db_;
 };
 
-class SingleNodeBfsTest
+class SingleNodeBfsTestInMemory
     : public ::testing::TestWithParam<
           std::tuple<int, int, EdgeAtom::Direction, std::vector<std::string>, bool, FilterLambdaType>> {
  public:
-  static void SetUpTestCase() { db_ = std::make_unique<SingleNodeDb>(); }
+  using StorageType = memgraph::storage::InMemoryStorage;
+  static void SetUpTestCase() { db_ = std::make_unique<SingleNodeDb<StorageType>>(); }
   static void TearDownTestCase() { db_ = nullptr; }
 
  protected:
-  static std::unique_ptr<SingleNodeDb> db_;
+  static std::unique_ptr<SingleNodeDb<StorageType>> db_;
 };
 
-TEST_P(SingleNodeBfsTest, All) {
+TEST_P(SingleNodeBfsTestInMemory, All) {
   int lower_bound;
   int upper_bound;
   EdgeAtom::Direction direction;
@@ -83,12 +98,12 @@ TEST_P(SingleNodeBfsTest, All) {
   bool known_sink;
   FilterLambdaType filter_lambda_type;
   std::tie(lower_bound, upper_bound, direction, edge_types, known_sink, filter_lambda_type) = GetParam();
-  BfsTest(db_.get(), lower_bound, upper_bound, direction, edge_types, known_sink, filter_lambda_type);
+  this->db_->BfsTest(db_.get(), lower_bound, upper_bound, direction, edge_types, known_sink, filter_lambda_type);
 }
 
-std::unique_ptr<SingleNodeDb> SingleNodeBfsTest::db_{nullptr};
+std::unique_ptr<SingleNodeDb<SingleNodeBfsTestInMemory::StorageType>> SingleNodeBfsTestInMemory::db_{nullptr};
 
-INSTANTIATE_TEST_CASE_P(DirectionAndExpansionDepth, SingleNodeBfsTest,
+INSTANTIATE_TEST_CASE_P(DirectionAndExpansionDepth, SingleNodeBfsTestInMemory,
                         testing::Combine(testing::Range(-1, kVertexCount), testing::Range(-1, kVertexCount),
                                          testing::Values(EdgeAtom::Direction::OUT, EdgeAtom::Direction::IN,
                                                          EdgeAtom::Direction::BOTH),
@@ -96,14 +111,63 @@ INSTANTIATE_TEST_CASE_P(DirectionAndExpansionDepth, SingleNodeBfsTest,
                                          testing::Values(FilterLambdaType::NONE)));
 
 INSTANTIATE_TEST_CASE_P(
-    EdgeType, SingleNodeBfsTest,
+    EdgeType, SingleNodeBfsTestInMemory,
     testing::Combine(testing::Values(-1), testing::Values(-1),
                      testing::Values(EdgeAtom::Direction::OUT, EdgeAtom::Direction::IN, EdgeAtom::Direction::BOTH),
                      testing::Values(std::vector<std::string>{}, std::vector<std::string>{"a"},
                                      std::vector<std::string>{"b"}, std::vector<std::string>{"a", "b"}),
                      testing::Bool(), testing::Values(FilterLambdaType::NONE)));
 
-INSTANTIATE_TEST_CASE_P(FilterLambda, SingleNodeBfsTest,
+INSTANTIATE_TEST_CASE_P(FilterLambda, SingleNodeBfsTestInMemory,
+                        testing::Combine(testing::Values(-1), testing::Values(-1),
+                                         testing::Values(EdgeAtom::Direction::OUT, EdgeAtom::Direction::IN,
+                                                         EdgeAtom::Direction::BOTH),
+                                         testing::Values(std::vector<std::string>{}), testing::Bool(),
+                                         testing::Values(FilterLambdaType::NONE, FilterLambdaType::USE_FRAME,
+                                                         FilterLambdaType::USE_FRAME_NULL, FilterLambdaType::USE_CTX,
+                                                         FilterLambdaType::ERROR)));
+
+class SingleNodeBfsTestOnDisk
+    : public ::testing::TestWithParam<
+          std::tuple<int, int, EdgeAtom::Direction, std::vector<std::string>, bool, FilterLambdaType>> {
+ public:
+  using StorageType = memgraph::storage::DiskStorage;
+  static void SetUpTestCase() { db_ = std::make_unique<SingleNodeDb<StorageType>>(); }
+  static void TearDownTestCase() { db_ = nullptr; }
+
+ protected:
+  static std::unique_ptr<SingleNodeDb<StorageType>> db_;
+};
+
+TEST_P(SingleNodeBfsTestOnDisk, All) {
+  int lower_bound;
+  int upper_bound;
+  EdgeAtom::Direction direction;
+  std::vector<std::string> edge_types;
+  bool known_sink;
+  FilterLambdaType filter_lambda_type;
+  std::tie(lower_bound, upper_bound, direction, edge_types, known_sink, filter_lambda_type) = GetParam();
+  this->db_->BfsTest(db_.get(), lower_bound, upper_bound, direction, edge_types, known_sink, filter_lambda_type);
+}
+
+std::unique_ptr<SingleNodeDb<SingleNodeBfsTestOnDisk::StorageType>> SingleNodeBfsTestOnDisk::db_{nullptr};
+
+INSTANTIATE_TEST_CASE_P(DirectionAndExpansionDepth, SingleNodeBfsTestOnDisk,
+                        testing::Combine(testing::Range(-1, kVertexCount), testing::Range(-1, kVertexCount),
+                                         testing::Values(EdgeAtom::Direction::OUT, EdgeAtom::Direction::IN,
+                                                         EdgeAtom::Direction::BOTH),
+                                         testing::Values(std::vector<std::string>{}), testing::Bool(),
+                                         testing::Values(FilterLambdaType::NONE)));
+
+INSTANTIATE_TEST_CASE_P(
+    EdgeType, SingleNodeBfsTestOnDisk,
+    testing::Combine(testing::Values(-1), testing::Values(-1),
+                     testing::Values(EdgeAtom::Direction::OUT, EdgeAtom::Direction::IN, EdgeAtom::Direction::BOTH),
+                     testing::Values(std::vector<std::string>{}, std::vector<std::string>{"a"},
+                                     std::vector<std::string>{"b"}, std::vector<std::string>{"a", "b"}),
+                     testing::Bool(), testing::Values(FilterLambdaType::NONE)));
+
+INSTANTIATE_TEST_CASE_P(FilterLambda, SingleNodeBfsTestOnDisk,
                         testing::Combine(testing::Values(-1), testing::Values(-1),
                                          testing::Values(EdgeAtom::Direction::OUT, EdgeAtom::Direction::IN,
                                                          EdgeAtom::Direction::BOTH),
diff --git a/tests/unit/bolt_encoder.cpp b/tests/unit/bolt_encoder.cpp
index 41b5c60cb..6ffb4b496 100644
--- a/tests/unit/bolt_encoder.cpp
+++ b/tests/unit/bolt_encoder.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -11,12 +11,16 @@
 
 #include <array>
 #include <bit>
+#include <memory>
 
 #include "bolt_common.hpp"
 #include "bolt_testdata.hpp"
 #include "communication/bolt/v1/codes.hpp"
 #include "communication/bolt/v1/encoder/encoder.hpp"
+#include "disk_test_utils.hpp"
 #include "glue/communication.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/storage.hpp"
 #include "utils/temporal.hpp"
 using memgraph::communication::bolt::Value;
@@ -174,40 +178,39 @@ TEST_F(BoltEncoder, Map) {
   CheckOutput(output, nullptr, 0);
 }
 
-TEST_F(BoltEncoder, VertexAndEdge) {
+void TestVertexAndEdgeWithDifferentStorages(std::unique_ptr<memgraph::storage::Storage> &&db) {
   output.clear();
 
   // create vertex
-  memgraph::storage::Storage db;
-  auto dba = db.Access();
-  auto va1 = dba.CreateVertex();
-  auto va2 = dba.CreateVertex();
-  auto l1 = dba.NameToLabel("label1");
-  auto l2 = dba.NameToLabel("label2");
+  auto dba = db->Access();
+  auto va1 = dba->CreateVertex();
+  auto va2 = dba->CreateVertex();
+  auto l1 = dba->NameToLabel("label1");
+  auto l2 = dba->NameToLabel("label2");
   ASSERT_TRUE(va1.AddLabel(l1).HasValue());
   ASSERT_TRUE(va1.AddLabel(l2).HasValue());
-  auto p1 = dba.NameToProperty("prop1");
-  auto p2 = dba.NameToProperty("prop2");
+  auto p1 = dba->NameToProperty("prop1");
+  auto p2 = dba->NameToProperty("prop2");
   memgraph::storage::PropertyValue pv1(12), pv2(200);
   ASSERT_TRUE(va1.SetProperty(p1, pv1).HasValue());
   ASSERT_TRUE(va1.SetProperty(p2, pv2).HasValue());
 
   // create edge
-  auto et = dba.NameToEdgeType("edgetype");
-  auto ea = dba.CreateEdge(&va1, &va2, et);
-  auto p3 = dba.NameToProperty("prop3");
-  auto p4 = dba.NameToProperty("prop4");
+  auto et = dba->NameToEdgeType("edgetype");
+  auto ea = dba->CreateEdge(&va1, &va2, et).GetValue();
+  auto p3 = dba->NameToProperty("prop3");
+  auto p4 = dba->NameToProperty("prop4");
   memgraph::storage::PropertyValue pv3(42), pv4(1234);
-  ASSERT_TRUE(ea->SetProperty(p3, pv3).HasValue());
-  ASSERT_TRUE(ea->SetProperty(p4, pv4).HasValue());
+  ASSERT_TRUE(ea.SetProperty(p3, pv3).HasValue());
+  ASSERT_TRUE(ea.SetProperty(p4, pv4).HasValue());
 
   // check everything
   std::vector<Value> vals;
-  vals.push_back(*memgraph::glue::ToBoltValue(memgraph::query::TypedValue(memgraph::query::VertexAccessor(va1)), db,
+  vals.push_back(*memgraph::glue::ToBoltValue(memgraph::query::TypedValue(memgraph::query::VertexAccessor(va1)), *db,
                                               memgraph::storage::View::NEW));
-  vals.push_back(*memgraph::glue::ToBoltValue(memgraph::query::TypedValue(memgraph::query::VertexAccessor(va2)), db,
+  vals.push_back(*memgraph::glue::ToBoltValue(memgraph::query::TypedValue(memgraph::query::VertexAccessor(va2)), *db,
                                               memgraph::storage::View::NEW));
-  vals.push_back(*memgraph::glue::ToBoltValue(memgraph::query::TypedValue(memgraph::query::EdgeAccessor(*ea)), db,
+  vals.push_back(*memgraph::glue::ToBoltValue(memgraph::query::TypedValue(memgraph::query::EdgeAccessor(ea)), *db,
                                               memgraph::storage::View::NEW));
   bolt_encoder.MessageRecord(vals);
 
@@ -219,12 +222,27 @@ TEST_F(BoltEncoder, VertexAndEdge) {
   CheckOutput(output, vertexedge_encoded + 6, 34, false);
   CheckInt(output, va2.Gid().AsInt());
   CheckOutput(output, vertexedge_encoded + 41, 4, false);
-  CheckInt(output, ea->Gid().AsInt());
+  CheckInt(output, ea.Gid().AsInt());
   CheckInt(output, va1.Gid().AsInt());
   CheckInt(output, va2.Gid().AsInt());
   CheckOutput(output, vertexedge_encoded + 48, 26);
 }
 
+TEST_F(BoltEncoder, VertexAndEdgeInMemoryStorage) {
+  std::unique_ptr<memgraph::storage::Storage> db{new memgraph::storage::InMemoryStorage()};
+  TestVertexAndEdgeWithDifferentStorages(std::move(db));
+}
+
+TEST_F(BoltEncoder, VertexAndEdgeOnDiskStorage) {
+  const std::string testSuite = "bolt_encoder";
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+
+  std::unique_ptr<memgraph::storage::Storage> db{new memgraph::storage::DiskStorage(config)};
+  TestVertexAndEdgeWithDifferentStorages(std::move(db));
+
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
 TEST_F(BoltEncoder, BoltV1ExampleMessages) {
   // this test checks example messages from: http://boltprotocol.org/v1/
 
diff --git a/tests/unit/clearing_old_disk_data.cpp b/tests/unit/clearing_old_disk_data.cpp
new file mode 100644
index 000000000..d27938f50
--- /dev/null
+++ b/tests/unit/clearing_old_disk_data.cpp
@@ -0,0 +1,181 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include <gtest/gtest.h>
+#include <rocksdb/options.h>
+#include <limits>
+
+#include "disk_test_utils.hpp"
+#include "spdlog/spdlog.h"
+#include "storage/v2/disk/rocksdb_storage.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/property_value.hpp"
+#include "storage/v2/view.hpp"
+
+class ClearingOldDiskDataTest : public ::testing::Test {
+ public:
+  const std::string testSuite = "clearing_old_disk_data";
+  std::unique_ptr<memgraph::storage::DiskStorage> disk_storage =
+      std::make_unique<memgraph::storage::DiskStorage>(disk_test_utils::GenerateOnDiskConfig(testSuite));
+
+  void TearDown() override { disk_test_utils::RemoveRocksDbDirs(testSuite); }
+};
+
+TEST_F(ClearingOldDiskDataTest, TestNumOfEntriesWithVertexTimestampUpdate) {
+  auto *tx_db = disk_storage->GetRocksDBStorage()->db_;
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 0);
+
+  auto acc1 = disk_storage->Access(std::nullopt);
+  auto vertex1 = acc1->CreateVertex();
+  auto label1 = acc1->NameToLabel("DiskLabel");
+  auto property1 = acc1->NameToProperty("DiskProperty");
+  ASSERT_TRUE(vertex1.AddLabel(label1).HasValue());
+  ASSERT_TRUE(vertex1.SetProperty(property1, memgraph::storage::PropertyValue(10)).HasValue());
+  ASSERT_FALSE(acc1->Commit().HasError());
+
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+
+  auto acc2 = disk_storage->Access(std::nullopt);
+  auto vertex2 = acc2->FindVertex(vertex1.Gid(), memgraph::storage::View::NEW).value();
+  /// This is the same property as in the first transaction, we just want to test
+  /// the number of entries inside RocksDB when the timestamp changes
+  auto property2 = acc2->NameToProperty("DiskProperty");
+  ASSERT_TRUE(vertex2.SetProperty(property2, memgraph::storage::PropertyValue(10)).HasValue());
+  ASSERT_FALSE(acc2->Commit().HasError());
+
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+}
+
+TEST_F(ClearingOldDiskDataTest, TestNumOfEntriesWithVertexValueUpdate) {
+  auto *tx_db = disk_storage->GetRocksDBStorage()->db_;
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 0);
+
+  auto acc1 = disk_storage->Access(std::nullopt);
+  auto vertex1 = acc1->CreateVertex();
+  auto label1 = acc1->NameToLabel("DiskLabel");
+  auto property1 = acc1->NameToProperty("DiskProperty");
+  ASSERT_TRUE(vertex1.AddLabel(label1).HasValue());
+  ASSERT_TRUE(vertex1.SetProperty(property1, memgraph::storage::PropertyValue(10)).HasValue());
+  ASSERT_FALSE(acc1->Commit().HasError());
+
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+
+  auto acc2 = disk_storage->Access(std::nullopt);
+  auto vertex2 = acc2->FindVertex(vertex1.Gid(), memgraph::storage::View::NEW).value();
+  /// This is the same property as in the first transaction, we just want to test
+  /// the number of entries inside RocksDB when the timestamp changes
+  auto property2 = acc2->NameToProperty("DiskProperty");
+  ASSERT_TRUE(vertex2.SetProperty(property2, memgraph::storage::PropertyValue(15)).HasValue());
+  ASSERT_FALSE(acc2->Commit().HasError());
+
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+}
+
+TEST_F(ClearingOldDiskDataTest, TestNumOfEntriesWithVertexKeyUpdate) {
+  auto *tx_db = disk_storage->GetRocksDBStorage()->db_;
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 0);
+
+  auto acc1 = disk_storage->Access(std::nullopt);
+  auto vertex1 = acc1->CreateVertex();
+  auto label1 = acc1->NameToLabel("DiskLabel");
+  auto property1 = acc1->NameToProperty("DiskProperty");
+  ASSERT_TRUE(vertex1.AddLabel(label1).HasValue());
+  ASSERT_TRUE(vertex1.SetProperty(property1, memgraph::storage::PropertyValue(10)).HasValue());
+  ASSERT_FALSE(acc1->Commit().HasError());
+
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+
+  auto acc2 = disk_storage->Access(std::nullopt);
+  auto vertex2 = acc2->FindVertex(vertex1.Gid(), memgraph::storage::View::NEW).value();
+  auto label2 = acc2->NameToLabel("DiskLabel2");
+  ASSERT_TRUE(vertex2.AddLabel(label2).HasValue());
+  ASSERT_FALSE(acc2->Commit().HasError());
+
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+}
+
+TEST_F(ClearingOldDiskDataTest, TestNumOfEntriesWithEdgeTimestampUpdate) {
+  auto *tx_db = disk_storage->GetRocksDBStorage()->db_;
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 0);
+
+  auto acc1 = disk_storage->Access(std::nullopt);
+
+  auto label1 = acc1->NameToLabel("DiskLabel");
+  auto property1 = acc1->NameToProperty("DiskProperty");
+  auto edge_type = acc1->NameToEdgeType("test");
+
+  auto from = acc1->CreateVertex();
+  auto to = acc1->CreateVertex();
+  auto edge = acc1->CreateEdge(&from, &to, edge_type);
+  MG_ASSERT(edge.HasValue());
+
+  ASSERT_TRUE(from.AddLabel(label1).HasValue());
+  ASSERT_TRUE(to.AddLabel(label1).HasValue());
+  ASSERT_TRUE(from.SetProperty(property1, memgraph::storage::PropertyValue(10)).HasValue());
+  ASSERT_TRUE(to.SetProperty(property1, memgraph::storage::PropertyValue(10)).HasValue());
+  ASSERT_TRUE(edge->SetProperty(property1, memgraph::storage::PropertyValue(10)).HasValue());
+  ASSERT_FALSE(acc1->Commit().HasError());
+
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 3);
+
+  auto acc2 = disk_storage->Access(std::nullopt);
+  auto from_vertex = acc2->FindVertex(from.Gid(), memgraph::storage::View::NEW).value();
+
+  acc2->PrefetchOutEdges(from_vertex);
+  auto ret = from_vertex.OutEdges(memgraph::storage::View::NEW);
+  auto fetched_edge = ret.GetValue()[0];
+
+  /// This is the same property as in the first transaction, we just want to test
+  /// the number of entries inside RocksDB when the timestamp changes
+  auto property2 = acc2->NameToProperty("DiskProperty");
+  ASSERT_TRUE(fetched_edge.SetProperty(property2, memgraph::storage::PropertyValue(10)).HasValue());
+  ASSERT_FALSE(acc2->Commit().HasError());
+
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 3);
+}
+
+TEST_F(ClearingOldDiskDataTest, TestNumOfEntriesWithEdgeValueUpdate) {
+  auto *tx_db = disk_storage->GetRocksDBStorage()->db_;
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 0);
+
+  auto acc1 = disk_storage->Access(std::nullopt);
+
+  auto label1 = acc1->NameToLabel("DiskLabel");
+  auto property1 = acc1->NameToProperty("DiskProperty");
+  auto edge_type = acc1->NameToEdgeType("test");
+
+  auto from = acc1->CreateVertex();
+  auto to = acc1->CreateVertex();
+  auto edge = acc1->CreateEdge(&from, &to, edge_type);
+  MG_ASSERT(edge.HasValue());
+
+  ASSERT_TRUE(from.AddLabel(label1).HasValue());
+  ASSERT_TRUE(to.AddLabel(label1).HasValue());
+  ASSERT_TRUE(from.SetProperty(property1, memgraph::storage::PropertyValue(10)).HasValue());
+  ASSERT_TRUE(to.SetProperty(property1, memgraph::storage::PropertyValue(10)).HasValue());
+  ASSERT_TRUE(edge->SetProperty(property1, memgraph::storage::PropertyValue(10)).HasValue());
+  ASSERT_FALSE(acc1->Commit().HasError());
+
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 3);
+
+  auto acc2 = disk_storage->Access(std::nullopt);
+  auto from_vertex = acc2->FindVertex(from.Gid(), memgraph::storage::View::NEW).value();
+
+  acc2->PrefetchOutEdges(from_vertex);
+  auto ret = from_vertex.OutEdges(memgraph::storage::View::NEW);
+  auto fetched_edge = ret.GetValue()[0];
+
+  auto property2 = acc2->NameToProperty("DiskProperty");
+  ASSERT_TRUE(fetched_edge.SetProperty(property2, memgraph::storage::PropertyValue(15)).HasValue());
+  ASSERT_FALSE(acc2->Commit().HasError());
+
+  ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 3);
+}
diff --git a/tests/unit/cpp_api.cpp b/tests/unit/cpp_api.cpp
index f2e47f03e..95c96426a 100644
--- a/tests/unit/cpp_api.cpp
+++ b/tests/unit/cpp_api.cpp
@@ -16,13 +16,23 @@
 #include <gflags/gflags.h>
 #include <gtest/gtest.h>
 
+#include "disk_test_utils.hpp"
 #include "mgp.hpp"
 #include "query/procedure/mg_procedure_impl.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/view.hpp"
 
+template <typename StorageType>
 struct CppApiTestFixture : public ::testing::Test {
  protected:
-  virtual void SetUp() { mgp::memory = &memory; }
+  virtual void SetUp() override { mgp::memory = &memory; }
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
 
   mgp_graph CreateGraph(const memgraph::storage::View view = memgraph::storage::View::NEW) {
     // the execution context can be null as it shouldn't be used in these tests
@@ -30,22 +40,28 @@ struct CppApiTestFixture : public ::testing::Test {
   }
 
   memgraph::query::DbAccessor &CreateDbAccessor(const memgraph::storage::IsolationLevel isolationLevel) {
-    accessors_.push_back(storage.Access(isolationLevel));
-    db_accessors_.emplace_back(&accessors_.back());
+    accessors_.push_back(storage->Access(isolationLevel));
+    db_accessors_.emplace_back(accessors_.back().get());
     return db_accessors_.back();
   }
 
-  memgraph::storage::Storage storage;
+  const std::string testSuite = "cpp_api";
+
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> storage{new StorageType(config)};
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
 
  private:
-  std::list<memgraph::storage::Storage::Accessor> accessors_;
+  std::list<std::unique_ptr<memgraph::storage::Storage::Accessor>> accessors_;
   std::list<memgraph::query::DbAccessor> db_accessors_;
   std::unique_ptr<memgraph::query::ExecutionContext> ctx_ = std::make_unique<memgraph::query::ExecutionContext>();
 };
 
-TEST_F(CppApiTestFixture, TestGraph) {
-  mgp_graph raw_graph = CreateGraph();
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(CppApiTestFixture, StorageTypes);
+
+TYPED_TEST(CppApiTestFixture, TestGraph) {
+  mgp_graph raw_graph = this->CreateGraph();
   auto graph = mgp::Graph(&raw_graph);
 
   auto node_1 = graph.CreateNode();
@@ -85,7 +101,7 @@ TEST_F(CppApiTestFixture, TestGraph) {
   ASSERT_EQ(n_rels, 3);
 }
 
-TEST_F(CppApiTestFixture, TestId) {
+TYPED_TEST(CppApiTestFixture, TestId) {
   int64_t int_1 = 8;
   uint64_t int_2 = 8;
   int64_t int_3 = 7;
@@ -116,7 +132,7 @@ TEST_F(CppApiTestFixture, TestId) {
   ASSERT_NE(id_2, id_4);
 }
 
-TEST_F(CppApiTestFixture, TestList) {
+TYPED_TEST(CppApiTestFixture, TestList) {
   auto list_1 = mgp::List();
 
   ASSERT_EQ(list_1.Size(), 0);
@@ -154,7 +170,7 @@ TEST_F(CppApiTestFixture, TestList) {
   auto value_y = mgp::Value(mgp::List());
 }
 
-TEST_F(CppApiTestFixture, TestMap) {
+TYPED_TEST(CppApiTestFixture, TestMap) {
   auto map_1 = mgp::Map();
 
   std::map<std::string_view, mgp::Value> map_1a;
@@ -196,8 +212,8 @@ TEST_F(CppApiTestFixture, TestMap) {
   auto value_z = value_x;
 }
 
-TEST_F(CppApiTestFixture, TestNode) {
-  mgp_graph raw_graph = CreateGraph();
+TYPED_TEST(CppApiTestFixture, TestNode) {
+  mgp_graph raw_graph = this->CreateGraph();
   auto graph = mgp::Graph(&raw_graph);
 
   auto node_1 = graph.CreateNode();
@@ -243,8 +259,8 @@ TEST_F(CppApiTestFixture, TestNode) {
   auto value_y = mgp::Value(graph.CreateNode());
 }
 
-TEST_F(CppApiTestFixture, TestNodeWithNeighbors) {
-  mgp_graph raw_graph = CreateGraph();
+TYPED_TEST(CppApiTestFixture, TestNodeWithNeighbors) {
+  mgp_graph raw_graph = this->CreateGraph();
   auto graph = mgp::Graph(&raw_graph);
 
   auto node_1 = graph.CreateNode();
@@ -270,8 +286,8 @@ TEST_F(CppApiTestFixture, TestNodeWithNeighbors) {
   ASSERT_EQ(count_in_relationships, 2);
 }
 
-TEST_F(CppApiTestFixture, TestRelationship) {
-  mgp_graph raw_graph = CreateGraph();
+TYPED_TEST(CppApiTestFixture, TestRelationship) {
+  mgp_graph raw_graph = this->CreateGraph();
   auto graph = mgp::Graph(&raw_graph);
 
   auto node_1 = graph.CreateNode();
@@ -297,8 +313,8 @@ TEST_F(CppApiTestFixture, TestRelationship) {
   auto value_y = mgp::Value(graph.CreateRelationship(node_2, node_1, "edge_type"));
 }
 
-TEST_F(CppApiTestFixture, TestPath) {
-  mgp_graph raw_graph = CreateGraph();
+TYPED_TEST(CppApiTestFixture, TestPath) {
+  mgp_graph raw_graph = this->CreateGraph();
   auto graph = mgp::Graph(&raw_graph);
 
   auto node_1 = graph.CreateNode();
@@ -330,7 +346,7 @@ TEST_F(CppApiTestFixture, TestPath) {
   auto value_y = mgp::Value(mgp::Path(node_0));
 }
 
-TEST_F(CppApiTestFixture, TestDate) {
+TYPED_TEST(CppApiTestFixture, TestDate) {
   auto date_1 = mgp::Date("2022-04-09");
   auto date_2 = mgp::Date(2022, 4, 9);
 
@@ -357,7 +373,7 @@ TEST_F(CppApiTestFixture, TestDate) {
   auto value_y = mgp::Value(mgp::Date("2022-04-09"));
 }
 
-TEST_F(CppApiTestFixture, TestLocalTime) {
+TYPED_TEST(CppApiTestFixture, TestLocalTime) {
   auto lt_1 = mgp::LocalTime("09:15:00");
   auto lt_2 = mgp::LocalTime(9, 15, 0, 0, 0);
   auto lt_3 = mgp::LocalTime::Now();
@@ -385,7 +401,7 @@ TEST_F(CppApiTestFixture, TestLocalTime) {
   auto value_y = mgp::Value(mgp::LocalTime("09:15:00"));
 }
 
-TEST_F(CppApiTestFixture, TestLocalDateTime) {
+TYPED_TEST(CppApiTestFixture, TestLocalDateTime) {
   auto ldt_1 = mgp::LocalDateTime("2021-10-05T14:15:00");
   auto ldt_2 = mgp::LocalDateTime(2021, 10, 5, 14, 15, 0, 0, 0);
 
@@ -418,7 +434,7 @@ TEST_F(CppApiTestFixture, TestLocalDateTime) {
   auto value_y = mgp::Value(mgp::LocalDateTime("2021-10-05T14:15:00"));
 }
 
-TEST_F(CppApiTestFixture, TestDuration) {
+TYPED_TEST(CppApiTestFixture, TestDuration) {
   auto duration_1 = mgp::Duration("PT2M2.33S");
   auto duration_2 = mgp::Duration(1465355);
   auto duration_3 = mgp::Duration(5, 14, 15, 0, 0, 0);
@@ -440,8 +456,8 @@ TEST_F(CppApiTestFixture, TestDuration) {
   auto value_y = mgp::Value(mgp::Duration("PT2M2.33S"));
 }
 
-TEST_F(CppApiTestFixture, TestNodeProperties) {
-  mgp_graph raw_graph = CreateGraph(memgraph::storage::View::NEW);
+TYPED_TEST(CppApiTestFixture, TestNodeProperties) {
+  mgp_graph raw_graph = this->CreateGraph(memgraph::storage::View::NEW);
   auto graph = mgp::Graph(&raw_graph);
 
   auto node_1 = graph.CreateNode();
diff --git a/tests/unit/disk_test_utils.hpp b/tests/unit/disk_test_utils.hpp
new file mode 100644
index 000000000..259e78909
--- /dev/null
+++ b/tests/unit/disk_test_utils.hpp
@@ -0,0 +1,49 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#pragma once
+
+#include <rocksdb/utilities/transaction_db.h>
+#include <filesystem>
+#include "storage/v2/config.hpp"
+#include "storage/v2/disk/storage.hpp"
+
+namespace disk_test_utils {
+
+memgraph::storage::Config GenerateOnDiskConfig(const std::string &testName) {
+  return {.disk = {.main_storage_directory = "rocksdb_" + testName + "_db",
+                   .label_index_directory = "rocksdb_" + testName + "_label_index",
+                   .label_property_index_directory = "rocksdb_" + testName + "_label_property_index",
+                   .unique_constraints_directory = "rocksdb_" + testName + "_unique_constraints",
+                   .name_id_mapper_directory = "rocksdb_" + testName + "_name_id_mapper",
+                   .id_name_mapper_directory = "rocksdb_" + testName + "_id_name_mapper",
+                   .durability_directory = "rocksdb_" + testName + "_durability",
+                   .wal_directory = "rocksdb_" + testName + "_wal"}};
+}
+
+void RemoveRocksDbDirs(const std::string &testName) {
+  std::filesystem::remove_all("rocksdb_" + testName + "_db");
+  std::filesystem::remove_all("rocksdb_" + testName + "_label_index");
+  std::filesystem::remove_all("rocksdb_" + testName + "_label_property_index");
+  std::filesystem::remove_all("rocksdb_" + testName + "_unique_constraints");
+  std::filesystem::remove_all("rocksdb_" + testName + "_name_id_mapper");
+  std::filesystem::remove_all("rocksdb_" + testName + "_id_name_mapper");
+  std::filesystem::remove_all("rocksdb_" + testName + "_durability");
+  std::filesystem::remove_all("rocksdb_" + testName + "_wal");
+}
+
+uint64_t GetRealNumberOfEntriesInRocksDB(rocksdb::TransactionDB *disk_storage) {
+  uint64_t num_keys = 0;
+  disk_storage->GetAggregatedIntProperty("rocksdb.estimate-num-keys", &num_keys);
+  return num_keys;
+}
+
+}  // namespace disk_test_utils
diff --git a/tests/unit/interpreter.cpp b/tests/unit/interpreter.cpp
index f2ce07479..ad01586dd 100644
--- a/tests/unit/interpreter.cpp
+++ b/tests/unit/interpreter.cpp
@@ -16,6 +16,7 @@
 #include "communication/bolt/v1/value.hpp"
 #include "communication/result_stream_faker.hpp"
 #include "csv/parsing.hpp"
+#include "disk_test_utils.hpp"
 #include "glue/communication.hpp"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -27,6 +28,7 @@
 #include "query/stream.hpp"
 #include "query/typed_value.hpp"
 #include "query_common.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/isolation_level.hpp"
 #include "storage/v2/property_value.hpp"
 #include "utils/logging.hpp"
@@ -46,11 +48,26 @@ auto ToEdgeList(const memgraph::communication::bolt::Value &v) {
 // TODO: This is not a unit test, but tests/integration dir is chaotic at the
 // moment. After tests refactoring is done, move/rename this.
 
+template <typename StorageType>
 class InterpreterTest : public ::testing::Test {
  public:
-  memgraph::storage::Storage db_;
-  std::filesystem::path data_directory{std::filesystem::temp_directory_path() / "MG_tests_unit_interpreter"};
-  memgraph::query::InterpreterContext interpreter_context{&db_, {}, data_directory};
+  const std::string testSuite = "interpreter";
+  const std::string testSuiteCsv = "interpreter_csv";
+
+  InterpreterTest()
+      : data_directory(std::filesystem::temp_directory_path() / "MG_tests_unit_interpreter"),
+        interpreter_context(std::make_unique<StorageType>(disk_test_utils::GenerateOnDiskConfig(testSuite)),
+                            {.execution_timeout_sec = 600}, data_directory) {}
+
+  std::filesystem::path data_directory;
+  memgraph::query::InterpreterContext interpreter_context;
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+      disk_test_utils::RemoveRocksDbDirs(testSuiteCsv);
+    }
+  }
 
   InterpreterFaker default_interpreter{&interpreter_context};
 
@@ -67,23 +84,26 @@ class InterpreterTest : public ::testing::Test {
   }
 };
 
-TEST_F(InterpreterTest, MultiplePulls) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(InterpreterTest, StorageTypes);
+
+TYPED_TEST(InterpreterTest, MultiplePulls) {
   {
-    auto [stream, qid] = Prepare("UNWIND [1,2,3,4,5] as n RETURN n");
+    auto [stream, qid] = this->Prepare("UNWIND [1,2,3,4,5] as n RETURN n");
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     EXPECT_EQ(stream.GetHeader()[0], "n");
-    Pull(&stream, 1);
+    this->Pull(&stream, 1);
     ASSERT_EQ(stream.GetSummary().count("has_more"), 1);
     ASSERT_TRUE(stream.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream.GetResults()[0].size(), 1U);
     ASSERT_EQ(stream.GetResults()[0][0].ValueInt(), 1);
-    Pull(&stream, 2);
+    this->Pull(&stream, 2);
     ASSERT_EQ(stream.GetSummary().count("has_more"), 1);
     ASSERT_TRUE(stream.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream.GetResults().size(), 3U);
     ASSERT_EQ(stream.GetResults()[1][0].ValueInt(), 2);
     ASSERT_EQ(stream.GetResults()[2][0].ValueInt(), 3);
-    Pull(&stream);
+    this->Pull(&stream);
     ASSERT_EQ(stream.GetSummary().count("has_more"), 1);
     ASSERT_FALSE(stream.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream.GetResults().size(), 5U);
@@ -94,9 +114,9 @@ TEST_F(InterpreterTest, MultiplePulls) {
 
 // Run query with different ast twice to see if query executes correctly when
 // ast is read from cache.
-TEST_F(InterpreterTest, AstCache) {
+TYPED_TEST(InterpreterTest, AstCache) {
   {
-    auto stream = Interpret("RETURN 2 + 3");
+    auto stream = this->Interpret("RETURN 2 + 3");
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     EXPECT_EQ(stream.GetHeader()[0], "2 + 3");
     ASSERT_EQ(stream.GetResults().size(), 1U);
@@ -105,42 +125,42 @@ TEST_F(InterpreterTest, AstCache) {
   }
   {
     // Cached ast, different literals.
-    auto stream = Interpret("RETURN 5 + 4");
+    auto stream = this->Interpret("RETURN 5 + 4");
     ASSERT_EQ(stream.GetResults().size(), 1U);
     ASSERT_EQ(stream.GetResults()[0].size(), 1U);
     ASSERT_EQ(stream.GetResults()[0][0].ValueInt(), 9);
   }
   {
     // Different ast (because of different types).
-    auto stream = Interpret("RETURN 5.5 + 4");
+    auto stream = this->Interpret("RETURN 5.5 + 4");
     ASSERT_EQ(stream.GetResults().size(), 1U);
     ASSERT_EQ(stream.GetResults()[0].size(), 1U);
     ASSERT_EQ(stream.GetResults()[0][0].ValueDouble(), 9.5);
   }
   {
     // Cached ast, same literals.
-    auto stream = Interpret("RETURN 2 + 3");
+    auto stream = this->Interpret("RETURN 2 + 3");
     ASSERT_EQ(stream.GetResults().size(), 1U);
     ASSERT_EQ(stream.GetResults()[0].size(), 1U);
     ASSERT_EQ(stream.GetResults()[0][0].ValueInt(), 5);
   }
   {
     // Cached ast, different literals.
-    auto stream = Interpret("RETURN 10.5 + 1");
+    auto stream = this->Interpret("RETURN 10.5 + 1");
     ASSERT_EQ(stream.GetResults().size(), 1U);
     ASSERT_EQ(stream.GetResults()[0].size(), 1U);
     ASSERT_EQ(stream.GetResults()[0][0].ValueDouble(), 11.5);
   }
   {
     // Cached ast, same literals, different whitespaces.
-    auto stream = Interpret("RETURN  10.5 + 1");
+    auto stream = this->Interpret("RETURN  10.5 + 1");
     ASSERT_EQ(stream.GetResults().size(), 1U);
     ASSERT_EQ(stream.GetResults()[0].size(), 1U);
     ASSERT_EQ(stream.GetResults()[0][0].ValueDouble(), 11.5);
   }
   {
     // Cached ast, same literals, different named header.
-    auto stream = Interpret("RETURN  10.5+1");
+    auto stream = this->Interpret("RETURN  10.5+1");
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     EXPECT_EQ(stream.GetHeader()[0], "10.5+1");
     ASSERT_EQ(stream.GetResults().size(), 1U);
@@ -150,10 +170,10 @@ TEST_F(InterpreterTest, AstCache) {
 }
 
 // Run query with same ast multiple times with different parameters.
-TEST_F(InterpreterTest, Parameters) {
+TYPED_TEST(InterpreterTest, Parameters) {
   {
-    auto stream = Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::PropertyValue(10)},
-                                                   {"a b", memgraph::storage::PropertyValue(15)}});
+    auto stream = this->Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::PropertyValue(10)},
+                                                         {"a b", memgraph::storage::PropertyValue(15)}});
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     EXPECT_EQ(stream.GetHeader()[0], "$2 + $`a b`");
     ASSERT_EQ(stream.GetResults().size(), 1U);
@@ -162,9 +182,9 @@ TEST_F(InterpreterTest, Parameters) {
   }
   {
     // Not needed parameter.
-    auto stream = Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::PropertyValue(10)},
-                                                   {"a b", memgraph::storage::PropertyValue(15)},
-                                                   {"c", memgraph::storage::PropertyValue(10)}});
+    auto stream = this->Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::PropertyValue(10)},
+                                                         {"a b", memgraph::storage::PropertyValue(15)},
+                                                         {"c", memgraph::storage::PropertyValue(10)}});
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     EXPECT_EQ(stream.GetHeader()[0], "$2 + $`a b`");
     ASSERT_EQ(stream.GetResults().size(), 1U);
@@ -173,18 +193,18 @@ TEST_F(InterpreterTest, Parameters) {
   }
   {
     // Cached ast, different parameters.
-    auto stream = Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::PropertyValue("da")},
-                                                   {"a b", memgraph::storage::PropertyValue("ne")}});
+    auto stream = this->Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::PropertyValue("da")},
+                                                         {"a b", memgraph::storage::PropertyValue("ne")}});
     ASSERT_EQ(stream.GetResults().size(), 1U);
     ASSERT_EQ(stream.GetResults()[0].size(), 1U);
     ASSERT_EQ(stream.GetResults()[0][0].ValueString(), "dane");
   }
   {
     // Non-primitive literal.
-    auto stream =
-        Interpret("RETURN $2", {{"2", memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
-                                          memgraph::storage::PropertyValue(5), memgraph::storage::PropertyValue(2),
-                                          memgraph::storage::PropertyValue(3)})}});
+    auto stream = this->Interpret("RETURN $2",
+                                  {{"2", memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
+                                             memgraph::storage::PropertyValue(5), memgraph::storage::PropertyValue(2),
+                                             memgraph::storage::PropertyValue(3)})}});
     ASSERT_EQ(stream.GetResults().size(), 1U);
     ASSERT_EQ(stream.GetResults()[0].size(), 1U);
     auto result = memgraph::query::test_common::ToIntList(memgraph::glue::ToTypedValue(stream.GetResults()[0][0]));
@@ -192,21 +212,22 @@ TEST_F(InterpreterTest, Parameters) {
   }
   {
     // Cached ast, unprovided parameter.
-    ASSERT_THROW(Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::PropertyValue("da")},
-                                                  {"ab", memgraph::storage::PropertyValue("ne")}}),
+    ASSERT_THROW(this->Interpret("RETURN $2 + $`a b`", {{"2", memgraph::storage::PropertyValue("da")},
+                                                        {"ab", memgraph::storage::PropertyValue("ne")}}),
                  memgraph::query::UnprovidedParameterError);
   }
 }
 
 // Run CREATE/MATCH/MERGE queries with property map
-TEST_F(InterpreterTest, ParametersAsPropertyMap) {
+TYPED_TEST(InterpreterTest, ParametersAsPropertyMap) {
   {
     std::map<std::string, memgraph::storage::PropertyValue> property_map{};
     property_map["name"] = memgraph::storage::PropertyValue("name1");
     property_map["age"] = memgraph::storage::PropertyValue(25);
-    auto stream = Interpret("CREATE (n $prop) RETURN n", {
-                                                             {"prop", memgraph::storage::PropertyValue(property_map)},
-                                                         });
+    auto stream =
+        this->Interpret("CREATE (n $prop) RETURN n", {
+                                                         {"prop", memgraph::storage::PropertyValue(property_map)},
+                                                     });
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     ASSERT_EQ(stream.GetHeader()[0], "n");
     ASSERT_EQ(stream.GetResults().size(), 1U);
@@ -219,11 +240,11 @@ TEST_F(InterpreterTest, ParametersAsPropertyMap) {
     std::map<std::string, memgraph::storage::PropertyValue> property_map{};
     property_map["name"] = memgraph::storage::PropertyValue("name1");
     property_map["age"] = memgraph::storage::PropertyValue(25);
-    Interpret("CREATE (:Person)");
-    auto stream = Interpret("MATCH (m: Person) CREATE (n $prop) RETURN n",
-                            {
-                                {"prop", memgraph::storage::PropertyValue(property_map)},
-                            });
+    this->Interpret("CREATE (:Person)");
+    auto stream = this->Interpret("MATCH (m: Person) CREATE (n $prop) RETURN n",
+                                  {
+                                      {"prop", memgraph::storage::PropertyValue(property_map)},
+                                  });
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     ASSERT_EQ(stream.GetHeader()[0], "n");
     ASSERT_EQ(stream.GetResults().size(), 1U);
@@ -236,10 +257,10 @@ TEST_F(InterpreterTest, ParametersAsPropertyMap) {
     std::map<std::string, memgraph::storage::PropertyValue> property_map{};
     property_map["name"] = memgraph::storage::PropertyValue("name1");
     property_map["weight"] = memgraph::storage::PropertyValue(121);
-    auto stream =
-        Interpret("CREATE ()-[r:TO $prop]->() RETURN r", {
-                                                             {"prop", memgraph::storage::PropertyValue(property_map)},
-                                                         });
+    auto stream = this->Interpret("CREATE ()-[r:TO $prop]->() RETURN r",
+                                  {
+                                      {"prop", memgraph::storage::PropertyValue(property_map)},
+                                  });
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     ASSERT_EQ(stream.GetHeader()[0], "r");
     ASSERT_EQ(stream.GetResults().size(), 1U);
@@ -252,42 +273,52 @@ TEST_F(InterpreterTest, ParametersAsPropertyMap) {
     std::map<std::string, memgraph::storage::PropertyValue> property_map{};
     property_map["name"] = memgraph::storage::PropertyValue("name1");
     property_map["age"] = memgraph::storage::PropertyValue(15);
-    ASSERT_THROW(Interpret("MATCH (n $prop) RETURN n",
-                           {
-                               {"prop", memgraph::storage::PropertyValue(property_map)},
-                           }),
+    ASSERT_THROW(this->Interpret("MATCH (n $prop) RETURN n",
+                                 {
+                                     {"prop", memgraph::storage::PropertyValue(property_map)},
+                                 }),
                  memgraph::query::SemanticException);
   }
   {
     std::map<std::string, memgraph::storage::PropertyValue> property_map{};
     property_map["name"] = memgraph::storage::PropertyValue("name1");
     property_map["age"] = memgraph::storage::PropertyValue(15);
-    ASSERT_THROW(Interpret("MERGE (n $prop) RETURN n",
-                           {
-                               {"prop", memgraph::storage::PropertyValue(property_map)},
-                           }),
+    ASSERT_THROW(this->Interpret("MERGE (n $prop) RETURN n",
+                                 {
+                                     {"prop", memgraph::storage::PropertyValue(property_map)},
+                                 }),
                  memgraph::query::SemanticException);
   }
 }
 
 // Test bfs end to end.
-TEST_F(InterpreterTest, Bfs) {
+TYPED_TEST(InterpreterTest, Bfs) {
   srand(0);
-  const auto kNumLevels = 10;
-  const auto kNumNodesPerLevel = 100;
-  const auto kNumEdgesPerNode = 100;
-  const auto kNumUnreachableNodes = 1000;
-  const auto kNumUnreachableEdges = 100000;
+  auto kNumLevels = 10;
+  auto kNumNodesPerLevel = 100;
+  auto kNumEdgesPerNode = 100;
+  auto kNumUnreachableNodes = 1000;
+  auto kNumUnreachableEdges = 100000;
+  auto kResCoeff = 5;
   const auto kReachable = "reachable";
   const auto kId = "id";
 
+  if (std::is_same<TypeParam, memgraph::storage::DiskStorage>::value) {
+    kNumLevels = 5;
+    kNumNodesPerLevel = 20;
+    kNumEdgesPerNode = 20;
+    kNumUnreachableNodes = 200;
+    kNumUnreachableEdges = 20000;
+    kResCoeff = 4;
+  }
+
   std::vector<std::vector<memgraph::query::VertexAccessor>> levels(kNumLevels);
   int id = 0;
 
   // Set up.
   {
-    auto storage_dba = db_.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->interpreter_context.db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto add_node = [&](int level, bool reachable) {
       auto node = dba.InsertVertex();
       MG_ASSERT(node.SetProperty(dba.NameToProperty(kId), memgraph::storage::PropertyValue(id++)).HasValue());
@@ -341,7 +372,7 @@ TEST_F(InterpreterTest, Bfs) {
     ASSERT_FALSE(dba.Commit().HasError());
   }
 
-  auto stream = Interpret(
+  auto stream = this->Interpret(
       "MATCH (n {id: 0})-[r *bfs..5 (e, n | n.reachable and "
       "e.reachable)]->(m) RETURN n, r, m");
 
@@ -349,9 +380,8 @@ TEST_F(InterpreterTest, Bfs) {
   EXPECT_EQ(stream.GetHeader()[0], "n");
   EXPECT_EQ(stream.GetHeader()[1], "r");
   EXPECT_EQ(stream.GetHeader()[2], "m");
-  ASSERT_EQ(stream.GetResults().size(), 5 * kNumNodesPerLevel);
+  ASSERT_EQ(stream.GetResults().size(), kResCoeff * kNumNodesPerLevel);
 
-  auto dba = db_.Access();
   int expected_level = 1;
   int remaining_nodes_in_level = kNumNodesPerLevel;
   std::unordered_set<int64_t> matched_ids;
@@ -386,24 +416,23 @@ TEST_F(InterpreterTest, Bfs) {
 }
 
 // Test shortest path end to end.
-TEST_F(InterpreterTest, ShortestPath) {
+TYPED_TEST(InterpreterTest, ShortestPath) {
   const auto test_shortest_path = [this](const bool use_duration) {
     const auto get_weight = [use_duration](const auto value) {
       return fmt::format(fmt::runtime(use_duration ? "DURATION('PT{}S')" : "{}"), value);
     };
 
-    Interpret(
+    this->Interpret(
         fmt::format("CREATE (n:A {{x: 1}}), (m:B {{x: 2}}), (l:C {{x: 1}}), (n)-[:r1 {{w: {} "
                     "}}]->(m)-[:r2 {{w: {}}}]->(l), (n)-[:r3 {{w: {}}}]->(l)",
                     get_weight(1), get_weight(2), get_weight(4)));
 
-    auto stream = Interpret("MATCH (n)-[e *wshortest 5 (e, n | e.w) ]->(m) return e");
+    auto stream = this->Interpret("MATCH (n)-[e *wshortest 5 (e, n | e.w) ]->(m) return e");
 
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     EXPECT_EQ(stream.GetHeader()[0], "e");
     ASSERT_EQ(stream.GetResults().size(), 3U);
 
-    auto dba = db_.Access();
     std::vector<std::vector<std::string>> expected_results{{"r1"}, {"r2"}, {"r1", "r2"}};
 
     for (const auto &result : stream.GetResults()) {
@@ -427,7 +456,7 @@ TEST_F(InterpreterTest, ShortestPath) {
       EXPECT_TRUE(any_match);
     }
 
-    Interpret("MATCH (n) DETACH DELETE n");
+    this->Interpret("MATCH (n) DETACH DELETE n");
   };
 
   static constexpr bool kUseNumeric{false};
@@ -442,22 +471,21 @@ TEST_F(InterpreterTest, ShortestPath) {
   }
 }
 
-TEST_F(InterpreterTest, AllShortestById) {
-  auto stream_init = Interpret(
+TYPED_TEST(InterpreterTest, AllShortestById) {
+  auto stream_init = this->Interpret(
       "CREATE (n:A {x: 1}), (m:B {x: 2}), (l:C {x: 3}), (k:D {x: 4}), (n)-[:r1 {w: 1 "
       "}]->(m)-[:r2 {w: 2}]->(l), (n)-[:r3 {w: 4}]->(l), (k)-[:r4 {w: 3}]->(l) return id(n), id(l)");
 
   auto id_n = stream_init.GetResults().front()[0].ValueInt();
   auto id_l = stream_init.GetResults().front()[1].ValueInt();
 
-  auto stream = Interpret(
+  auto stream = this->Interpret(
       fmt::format("MATCH (n)-[e *allshortest 5 (e, n | e.w) ]->(l) WHERE id(n)={} AND id(l)={} return e", id_n, id_l));
 
   ASSERT_EQ(stream.GetHeader().size(), 1U);
   EXPECT_EQ(stream.GetHeader()[0], "e");
   ASSERT_EQ(stream.GetResults().size(), 1U);
 
-  auto dba = db_.Access();
   std::vector<std::string> expected_result = {"r1", "r2"};
 
   const auto &result = stream.GetResults()[0];
@@ -472,70 +500,71 @@ TEST_F(InterpreterTest, AllShortestById) {
 
   EXPECT_TRUE(expected_result == datum);
 
-  Interpret("MATCH (n) DETACH DELETE n");
+  this->Interpret("MATCH (n) DETACH DELETE n");
 }
 
-TEST_F(InterpreterTest, CreateLabelIndexInMulticommandTransaction) {
-  Interpret("BEGIN");
-  ASSERT_THROW(Interpret("CREATE INDEX ON :X"), memgraph::query::IndexInMulticommandTxException);
-  Interpret("ROLLBACK");
+TYPED_TEST(InterpreterTest, CreateLabelIndexInMulticommandTransaction) {
+  this->Interpret("BEGIN");
+  ASSERT_THROW(this->Interpret("CREATE INDEX ON :X"), memgraph::query::IndexInMulticommandTxException);
+  this->Interpret("ROLLBACK");
 }
 
-TEST_F(InterpreterTest, CreateLabelPropertyIndexInMulticommandTransaction) {
-  Interpret("BEGIN");
-  ASSERT_THROW(Interpret("CREATE INDEX ON :X(y)"), memgraph::query::IndexInMulticommandTxException);
-  Interpret("ROLLBACK");
+TYPED_TEST(InterpreterTest, CreateLabelPropertyIndexInMulticommandTransaction) {
+  this->Interpret("BEGIN");
+  ASSERT_THROW(this->Interpret("CREATE INDEX ON :X(y)"), memgraph::query::IndexInMulticommandTxException);
+  this->Interpret("ROLLBACK");
 }
 
-TEST_F(InterpreterTest, CreateExistenceConstraintInMulticommandTransaction) {
-  Interpret("BEGIN");
-  ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT EXISTS (n.a)"),
+TYPED_TEST(InterpreterTest, CreateExistenceConstraintInMulticommandTransaction) {
+  this->Interpret("BEGIN");
+  ASSERT_THROW(this->Interpret("CREATE CONSTRAINT ON (n:A) ASSERT EXISTS (n.a)"),
                memgraph::query::ConstraintInMulticommandTxException);
-  Interpret("ROLLBACK");
+  this->Interpret("ROLLBACK");
 }
 
-TEST_F(InterpreterTest, CreateUniqueConstraintInMulticommandTransaction) {
-  Interpret("BEGIN");
-  ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE"),
+TYPED_TEST(InterpreterTest, CreateUniqueConstraintInMulticommandTransaction) {
+  this->Interpret("BEGIN");
+  ASSERT_THROW(this->Interpret("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE"),
                memgraph::query::ConstraintInMulticommandTxException);
-  Interpret("ROLLBACK");
+  this->Interpret("ROLLBACK");
 }
 
-TEST_F(InterpreterTest, ShowIndexInfoInMulticommandTransaction) {
-  Interpret("BEGIN");
-  ASSERT_THROW(Interpret("SHOW INDEX INFO"), memgraph::query::InfoInMulticommandTxException);
-  Interpret("ROLLBACK");
+TYPED_TEST(InterpreterTest, ShowIndexInfoInMulticommandTransaction) {
+  this->Interpret("BEGIN");
+  ASSERT_THROW(this->Interpret("SHOW INDEX INFO"), memgraph::query::InfoInMulticommandTxException);
+  this->Interpret("ROLLBACK");
 }
 
-TEST_F(InterpreterTest, ShowConstraintInfoInMulticommandTransaction) {
-  Interpret("BEGIN");
-  ASSERT_THROW(Interpret("SHOW CONSTRAINT INFO"), memgraph::query::InfoInMulticommandTxException);
-  Interpret("ROLLBACK");
+TYPED_TEST(InterpreterTest, ShowConstraintInfoInMulticommandTransaction) {
+  this->Interpret("BEGIN");
+  ASSERT_THROW(this->Interpret("SHOW CONSTRAINT INFO"), memgraph::query::InfoInMulticommandTxException);
+  this->Interpret("ROLLBACK");
 }
 
-TEST_F(InterpreterTest, ShowStorageInfoInMulticommandTransaction) {
-  Interpret("BEGIN");
-  ASSERT_THROW(Interpret("SHOW STORAGE INFO"), memgraph::query::InfoInMulticommandTxException);
-  Interpret("ROLLBACK");
+TYPED_TEST(InterpreterTest, ShowStorageInfoInMulticommandTransaction) {
+  this->Interpret("BEGIN");
+  ASSERT_THROW(this->Interpret("SHOW STORAGE INFO"), memgraph::query::InfoInMulticommandTxException);
+  this->Interpret("ROLLBACK");
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(InterpreterTest, ExistenceConstraintTest) {
-  Interpret("CREATE CONSTRAINT ON (n:A) ASSERT EXISTS (n.a);");
-  Interpret("CREATE (:A{a:1})");
-  Interpret("CREATE (:A{a:2})");
-  ASSERT_THROW(Interpret("CREATE (:A)"), memgraph::query::QueryException);
-  Interpret("MATCH (n:A{a:2}) SET n.a=3");
-  Interpret("CREATE (:A{a:2})");
-  Interpret("MATCH (n:A{a:2}) DETACH DELETE n");
-  Interpret("CREATE (n:A{a:2})");
-  ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT EXISTS (n.b);"), memgraph::query::QueryRuntimeException);
+TYPED_TEST(InterpreterTest, ExistenceConstraintTest) {
+  this->Interpret("CREATE CONSTRAINT ON (n:A) ASSERT EXISTS (n.a);");
+  this->Interpret("CREATE (:A{a:1})");
+  this->Interpret("CREATE (:A{a:2})");
+  ASSERT_THROW(this->Interpret("CREATE (:A)"), memgraph::query::QueryException);
+  this->Interpret("MATCH (n:A{a:2}) SET n.a=3");
+  this->Interpret("CREATE (:A{a:2})");
+  this->Interpret("MATCH (n:A{a:2}) DETACH DELETE n");
+  this->Interpret("CREATE (n:A{a:2})");
+  ASSERT_THROW(this->Interpret("CREATE CONSTRAINT ON (n:A) ASSERT EXISTS (n.b);"),
+               memgraph::query::QueryRuntimeException);
 }
 
-TEST_F(InterpreterTest, UniqueConstraintTest) {
+TYPED_TEST(InterpreterTest, UniqueConstraintTest) {
   // Empty property list should result with syntax exception.
-  ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT IS UNIQUE;"), memgraph::query::SyntaxException);
-  ASSERT_THROW(Interpret("DROP CONSTRAINT ON (n:A) ASSERT IS UNIQUE;"), memgraph::query::SyntaxException);
+  ASSERT_THROW(this->Interpret("CREATE CONSTRAINT ON (n:A) ASSERT IS UNIQUE;"), memgraph::query::SyntaxException);
+  ASSERT_THROW(this->Interpret("DROP CONSTRAINT ON (n:A) ASSERT IS UNIQUE;"), memgraph::query::SyntaxException);
 
   // Too large list of properties should also result with syntax exception.
   {
@@ -548,35 +577,36 @@ TEST_F(InterpreterTest, UniqueConstraintTest) {
     stream << " IS UNIQUE;";
     std::string create_query = "CREATE CONSTRAINT" + stream.str();
     std::string drop_query = "DROP CONSTRAINT" + stream.str();
-    ASSERT_THROW(Interpret(create_query), memgraph::query::SyntaxException);
-    ASSERT_THROW(Interpret(drop_query), memgraph::query::SyntaxException);
+    ASSERT_THROW(this->Interpret(create_query), memgraph::query::SyntaxException);
+    ASSERT_THROW(this->Interpret(drop_query), memgraph::query::SyntaxException);
   }
 
   // Providing property list with duplicates results with syntax exception.
-  ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b, n.a IS UNIQUE;"),
+  ASSERT_THROW(this->Interpret("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b, n.a IS UNIQUE;"),
+               memgraph::query::SyntaxException);
+  ASSERT_THROW(this->Interpret("DROP CONSTRAINT ON (n:A) ASSERT n.a, n.b, n.a IS UNIQUE;"),
                memgraph::query::SyntaxException);
-  ASSERT_THROW(Interpret("DROP CONSTRAINT ON (n:A) ASSERT n.a, n.b, n.a IS UNIQUE;"), memgraph::query::SyntaxException);
 
   // Commit of vertex should fail if a constraint is violated.
-  Interpret("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE;");
-  Interpret("CREATE (:A{a:1, b:2})");
-  Interpret("CREATE (:A{a:1, b:3})");
-  ASSERT_THROW(Interpret("CREATE (:A{a:1, b:2})"), memgraph::query::QueryException);
+  this->Interpret("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE;");
+  this->Interpret("CREATE (:A{a:1, b:2})");
+  this->Interpret("CREATE (:A{a:1, b:3})");
+  ASSERT_THROW(this->Interpret("CREATE (:A{a:1, b:2})"), memgraph::query::QueryException);
 
   // Attempt to create a constraint should fail if it's violated.
-  Interpret("CREATE (:A{a:1, c:2})");
-  Interpret("CREATE (:A{a:1, c:2})");
-  ASSERT_THROW(Interpret("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.c IS UNIQUE;"),
+  this->Interpret("CREATE (:A{a:1, c:2})");
+  this->Interpret("CREATE (:A{a:1, c:2})");
+  ASSERT_THROW(this->Interpret("CREATE CONSTRAINT ON (n:A) ASSERT n.a, n.c IS UNIQUE;"),
                memgraph::query::QueryRuntimeException);
 
-  Interpret("MATCH (n:A{a:2, b:2}) SET n.a=1");
-  Interpret("CREATE (:A{a:2})");
-  Interpret("MATCH (n:A{a:2}) DETACH DELETE n");
-  Interpret("CREATE (n:A{a:2})");
+  this->Interpret("MATCH (n:A{a:2, b:2}) SET n.a=1");
+  this->Interpret("CREATE (:A{a:2})");
+  this->Interpret("MATCH (n:A{a:2}) DETACH DELETE n");
+  this->Interpret("CREATE (n:A{a:2})");
 
   // Show constraint info.
   {
-    auto stream = Interpret("SHOW CONSTRAINT INFO");
+    auto stream = this->Interpret("SHOW CONSTRAINT INFO");
     ASSERT_EQ(stream.GetHeader().size(), 3U);
     const auto &header = stream.GetHeader();
     ASSERT_EQ(header[0], "constraint type");
@@ -594,15 +624,15 @@ TEST_F(InterpreterTest, UniqueConstraintTest) {
   }
 
   // Drop constraint.
-  Interpret("DROP CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE;");
+  this->Interpret("DROP CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE;");
   // Removing the same constraint twice should not throw any exception.
-  Interpret("DROP CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE;");
+  this->Interpret("DROP CONSTRAINT ON (n:A) ASSERT n.a, n.b IS UNIQUE;");
 }
 
-TEST_F(InterpreterTest, ExplainQuery) {
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 0U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 0U);
-  auto stream = Interpret("EXPLAIN MATCH (n) RETURN *;");
+TYPED_TEST(InterpreterTest, ExplainQuery) {
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 0U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 0U);
+  auto stream = this->Interpret("EXPLAIN MATCH (n) RETURN *;");
   ASSERT_EQ(stream.GetHeader().size(), 1U);
   EXPECT_EQ(stream.GetHeader().front(), "QUERY PLAN");
   std::vector<std::string> expected_rows{" * Produce {n}", " * ScanAll (n)", " * Once"};
@@ -614,53 +644,53 @@ TEST_F(InterpreterTest, ExplainQuery) {
     ++expected_it;
   }
   // We should have a plan cache for MATCH ...
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
   // We should have AST cache for EXPLAIN ... and for inner MATCH ...
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
-  Interpret("MATCH (n) RETURN *;");
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
+  this->Interpret("MATCH (n) RETURN *;");
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
 }
 
-TEST_F(InterpreterTest, ExplainQueryMultiplePulls) {
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 0U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 0U);
-  auto [stream, qid] = Prepare("EXPLAIN MATCH (n) RETURN *;");
+TYPED_TEST(InterpreterTest, ExplainQueryMultiplePulls) {
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 0U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 0U);
+  auto [stream, qid] = this->Prepare("EXPLAIN MATCH (n) RETURN *;");
   ASSERT_EQ(stream.GetHeader().size(), 1U);
   EXPECT_EQ(stream.GetHeader().front(), "QUERY PLAN");
   std::vector<std::string> expected_rows{" * Produce {n}", " * ScanAll (n)", " * Once"};
-  Pull(&stream, 1);
+  this->Pull(&stream, 1);
   ASSERT_EQ(stream.GetResults().size(), 1);
   auto expected_it = expected_rows.begin();
   ASSERT_EQ(stream.GetResults()[0].size(), 1U);
   EXPECT_EQ(stream.GetResults()[0].front().ValueString(), *expected_it);
   ++expected_it;
 
-  Pull(&stream, 1);
+  this->Pull(&stream, 1);
   ASSERT_EQ(stream.GetResults().size(), 2);
   ASSERT_EQ(stream.GetResults()[1].size(), 1U);
   EXPECT_EQ(stream.GetResults()[1].front().ValueString(), *expected_it);
   ++expected_it;
 
-  Pull(&stream);
+  this->Pull(&stream);
   ASSERT_EQ(stream.GetResults().size(), 3);
   ASSERT_EQ(stream.GetResults()[2].size(), 1U);
   EXPECT_EQ(stream.GetResults()[2].front().ValueString(), *expected_it);
   // We should have a plan cache for MATCH ...
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
   // We should have AST cache for EXPLAIN ... and for inner MATCH ...
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
-  Interpret("MATCH (n) RETURN *;");
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
+  this->Interpret("MATCH (n) RETURN *;");
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
 }
 
-TEST_F(InterpreterTest, ExplainQueryInMulticommandTransaction) {
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 0U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 0U);
-  Interpret("BEGIN");
-  auto stream = Interpret("EXPLAIN MATCH (n) RETURN *;");
-  Interpret("COMMIT");
+TYPED_TEST(InterpreterTest, ExplainQueryInMulticommandTransaction) {
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 0U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 0U);
+  this->Interpret("BEGIN");
+  auto stream = this->Interpret("EXPLAIN MATCH (n) RETURN *;");
+  this->Interpret("COMMIT");
   ASSERT_EQ(stream.GetHeader().size(), 1U);
   EXPECT_EQ(stream.GetHeader().front(), "QUERY PLAN");
   std::vector<std::string> expected_rows{" * Produce {n}", " * ScanAll (n)", " * Once"};
@@ -672,19 +702,19 @@ TEST_F(InterpreterTest, ExplainQueryInMulticommandTransaction) {
     ++expected_it;
   }
   // We should have a plan cache for MATCH ...
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
   // We should have AST cache for EXPLAIN ... and for inner MATCH ...
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
-  Interpret("MATCH (n) RETURN *;");
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
+  this->Interpret("MATCH (n) RETURN *;");
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
 }
 
-TEST_F(InterpreterTest, ExplainQueryWithParams) {
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 0U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 0U);
+TYPED_TEST(InterpreterTest, ExplainQueryWithParams) {
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 0U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 0U);
   auto stream =
-      Interpret("EXPLAIN MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue(42)}});
+      this->Interpret("EXPLAIN MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue(42)}});
   ASSERT_EQ(stream.GetHeader().size(), 1U);
   EXPECT_EQ(stream.GetHeader().front(), "QUERY PLAN");
   std::vector<std::string> expected_rows{" * Produce {n}", " * Filter", " * ScanAll (n)", " * Once"};
@@ -696,18 +726,18 @@ TEST_F(InterpreterTest, ExplainQueryWithParams) {
     ++expected_it;
   }
   // We should have a plan cache for MATCH ...
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
   // We should have AST cache for EXPLAIN ... and for inner MATCH ...
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
-  Interpret("MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue("something else")}});
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
+  this->Interpret("MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue("something else")}});
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
 }
 
-TEST_F(InterpreterTest, ProfileQuery) {
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 0U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 0U);
-  auto stream = Interpret("PROFILE MATCH (n) RETURN *;");
+TYPED_TEST(InterpreterTest, ProfileQuery) {
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 0U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 0U);
+  auto stream = this->Interpret("PROFILE MATCH (n) RETURN *;");
   std::vector<std::string> expected_header{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"};
   EXPECT_EQ(stream.GetHeader(), expected_header);
   std::vector<std::string> expected_rows{"* Produce", "* ScanAll", "* Once"};
@@ -719,61 +749,61 @@ TEST_F(InterpreterTest, ProfileQuery) {
     ++expected_it;
   }
   // We should have a plan cache for MATCH ...
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
   // We should have AST cache for PROFILE ... and for inner MATCH ...
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
-  Interpret("MATCH (n) RETURN *;");
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
+  this->Interpret("MATCH (n) RETURN *;");
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
 }
 
-TEST_F(InterpreterTest, ProfileQueryMultiplePulls) {
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 0U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 0U);
-  auto [stream, qid] = Prepare("PROFILE MATCH (n) RETURN *;");
+TYPED_TEST(InterpreterTest, ProfileQueryMultiplePulls) {
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 0U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 0U);
+  auto [stream, qid] = this->Prepare("PROFILE MATCH (n) RETURN *;");
   std::vector<std::string> expected_header{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"};
   EXPECT_EQ(stream.GetHeader(), expected_header);
 
   std::vector<std::string> expected_rows{"* Produce", "* ScanAll", "* Once"};
   auto expected_it = expected_rows.begin();
 
-  Pull(&stream, 1);
+  this->Pull(&stream, 1);
   ASSERT_EQ(stream.GetResults().size(), 1U);
   ASSERT_EQ(stream.GetResults()[0].size(), 4U);
   ASSERT_EQ(stream.GetResults()[0][0].ValueString(), *expected_it);
   ++expected_it;
 
-  Pull(&stream, 1);
+  this->Pull(&stream, 1);
   ASSERT_EQ(stream.GetResults().size(), 2U);
   ASSERT_EQ(stream.GetResults()[1].size(), 4U);
   ASSERT_EQ(stream.GetResults()[1][0].ValueString(), *expected_it);
   ++expected_it;
 
-  Pull(&stream);
+  this->Pull(&stream);
   ASSERT_EQ(stream.GetResults().size(), 3U);
   ASSERT_EQ(stream.GetResults()[2].size(), 4U);
   ASSERT_EQ(stream.GetResults()[2][0].ValueString(), *expected_it);
 
   // We should have a plan cache for MATCH ...
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
   // We should have AST cache for PROFILE ... and for inner MATCH ...
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
-  Interpret("MATCH (n) RETURN *;");
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
+  this->Interpret("MATCH (n) RETURN *;");
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
 }
 
-TEST_F(InterpreterTest, ProfileQueryInMulticommandTransaction) {
-  Interpret("BEGIN");
-  ASSERT_THROW(Interpret("PROFILE MATCH (n) RETURN *;"), memgraph::query::ProfileInMulticommandTxException);
-  Interpret("ROLLBACK");
+TYPED_TEST(InterpreterTest, ProfileQueryInMulticommandTransaction) {
+  this->Interpret("BEGIN");
+  ASSERT_THROW(this->Interpret("PROFILE MATCH (n) RETURN *;"), memgraph::query::ProfileInMulticommandTxException);
+  this->Interpret("ROLLBACK");
 }
 
-TEST_F(InterpreterTest, ProfileQueryWithParams) {
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 0U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 0U);
+TYPED_TEST(InterpreterTest, ProfileQueryWithParams) {
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 0U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 0U);
   auto stream =
-      Interpret("PROFILE MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue(42)}});
+      this->Interpret("PROFILE MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue(42)}});
   std::vector<std::string> expected_header{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"};
   EXPECT_EQ(stream.GetHeader(), expected_header);
   std::vector<std::string> expected_rows{"* Produce", "* Filter", "* ScanAll", "* Once"};
@@ -785,18 +815,18 @@ TEST_F(InterpreterTest, ProfileQueryWithParams) {
     ++expected_it;
   }
   // We should have a plan cache for MATCH ...
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
   // We should have AST cache for PROFILE ... and for inner MATCH ...
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
-  Interpret("MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue("something else")}});
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
+  this->Interpret("MATCH (n) WHERE n.id = $id RETURN *;", {{"id", memgraph::storage::PropertyValue("something else")}});
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
 }
 
-TEST_F(InterpreterTest, ProfileQueryWithLiterals) {
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 0U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 0U);
-  auto stream = Interpret("PROFILE UNWIND range(1, 1000) AS x CREATE (:Node {id: x});", {});
+TYPED_TEST(InterpreterTest, ProfileQueryWithLiterals) {
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 0U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 0U);
+  auto stream = this->Interpret("PROFILE UNWIND range(1, 1000) AS x CREATE (:Node {id: x});", {});
   std::vector<std::string> expected_header{"OPERATOR", "ACTUAL HITS", "RELATIVE TIME", "ABSOLUTE TIME"};
   EXPECT_EQ(stream.GetHeader(), expected_header);
   std::vector<std::string> expected_rows{"* EmptyResult", "* CreateNode", "* Unwind", "* Once"};
@@ -808,25 +838,25 @@ TEST_F(InterpreterTest, ProfileQueryWithLiterals) {
     ++expected_it;
   }
   // We should have a plan cache for UNWIND ...
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
   // We should have AST cache for PROFILE ... and for inner UNWIND ...
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
-  Interpret("UNWIND range(42, 4242) AS x CREATE (:Node {id: x});", {});
-  EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
-  EXPECT_EQ(interpreter_context.ast_cache.size(), 2U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
+  this->Interpret("UNWIND range(42, 4242) AS x CREATE (:Node {id: x});", {});
+  EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
+  EXPECT_EQ(this->interpreter_context.ast_cache.size(), 2U);
 }
 
-TEST_F(InterpreterTest, Transactions) {
-  auto &interpreter = default_interpreter.interpreter;
+TYPED_TEST(InterpreterTest, Transactions) {
+  auto &interpreter = this->default_interpreter.interpreter;
   {
     ASSERT_THROW(interpreter.CommitTransaction(), memgraph::query::ExplicitTransactionUsageException);
     ASSERT_THROW(interpreter.RollbackTransaction(), memgraph::query::ExplicitTransactionUsageException);
     interpreter.BeginTransaction();
     ASSERT_THROW(interpreter.BeginTransaction(), memgraph::query::ExplicitTransactionUsageException);
-    auto [stream, qid] = Prepare("RETURN 2");
+    auto [stream, qid] = this->Prepare("RETURN 2");
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     EXPECT_EQ(stream.GetHeader()[0], "2");
-    Pull(&stream, 1);
+    this->Pull(&stream, 1);
     ASSERT_EQ(stream.GetSummary().count("has_more"), 1);
     ASSERT_FALSE(stream.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream.GetResults()[0].size(), 1U);
@@ -835,10 +865,10 @@ TEST_F(InterpreterTest, Transactions) {
   }
   {
     interpreter.BeginTransaction();
-    auto [stream, qid] = Prepare("RETURN 2");
+    auto [stream, qid] = this->Prepare("RETURN 2");
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     EXPECT_EQ(stream.GetHeader()[0], "2");
-    Pull(&stream, 1);
+    this->Pull(&stream, 1);
     ASSERT_EQ(stream.GetSummary().count("has_more"), 1);
     ASSERT_FALSE(stream.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream.GetResults()[0].size(), 1U);
@@ -847,40 +877,40 @@ TEST_F(InterpreterTest, Transactions) {
   }
 }
 
-TEST_F(InterpreterTest, Qid) {
-  auto &interpreter = default_interpreter.interpreter;
+TYPED_TEST(InterpreterTest, Qid) {
+  auto &interpreter = this->default_interpreter.interpreter;
   {
     interpreter.BeginTransaction();
-    auto [stream, qid] = Prepare("RETURN 2");
+    auto [stream, qid] = this->Prepare("RETURN 2");
     ASSERT_TRUE(qid);
-    ASSERT_THROW(Pull(&stream, {}, *qid + 1), memgraph::query::InvalidArgumentsException);
+    ASSERT_THROW(this->Pull(&stream, {}, *qid + 1), memgraph::query::InvalidArgumentsException);
     interpreter.RollbackTransaction();
   }
   {
     interpreter.BeginTransaction();
-    auto [stream1, qid1] = Prepare("UNWIND(range(1,3)) as n RETURN n");
+    auto [stream1, qid1] = this->Prepare("UNWIND(range(1,3)) as n RETURN n");
     ASSERT_TRUE(qid1);
     ASSERT_EQ(stream1.GetHeader().size(), 1U);
     EXPECT_EQ(stream1.GetHeader()[0], "n");
 
-    auto [stream2, qid2] = Prepare("UNWIND(range(4,6)) as n RETURN n");
+    auto [stream2, qid2] = this->Prepare("UNWIND(range(4,6)) as n RETURN n");
     ASSERT_TRUE(qid2);
     ASSERT_EQ(stream2.GetHeader().size(), 1U);
     EXPECT_EQ(stream2.GetHeader()[0], "n");
 
-    Pull(&stream1, 1, qid1);
+    this->Pull(&stream1, 1, qid1);
     ASSERT_EQ(stream1.GetSummary().count("has_more"), 1);
     ASSERT_TRUE(stream1.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream1.GetResults().size(), 1U);
     ASSERT_EQ(stream1.GetResults()[0].size(), 1U);
     ASSERT_EQ(stream1.GetResults()[0][0].ValueInt(), 1);
 
-    auto [stream3, qid3] = Prepare("UNWIND(range(7,9)) as n RETURN n");
+    auto [stream3, qid3] = this->Prepare("UNWIND(range(7,9)) as n RETURN n");
     ASSERT_TRUE(qid3);
     ASSERT_EQ(stream3.GetHeader().size(), 1U);
     EXPECT_EQ(stream3.GetHeader()[0], "n");
 
-    Pull(&stream2, {}, qid2);
+    this->Pull(&stream2, {}, qid2);
     ASSERT_EQ(stream2.GetSummary().count("has_more"), 1);
     ASSERT_FALSE(stream2.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream2.GetResults().size(), 3U);
@@ -889,14 +919,14 @@ TEST_F(InterpreterTest, Qid) {
     ASSERT_EQ(stream2.GetResults()[1][0].ValueInt(), 5);
     ASSERT_EQ(stream2.GetResults()[2][0].ValueInt(), 6);
 
-    Pull(&stream3, 1, qid3);
+    this->Pull(&stream3, 1, qid3);
     ASSERT_EQ(stream3.GetSummary().count("has_more"), 1);
     ASSERT_TRUE(stream3.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream3.GetResults().size(), 1U);
     ASSERT_EQ(stream3.GetResults()[0].size(), 1U);
     ASSERT_EQ(stream3.GetResults()[0][0].ValueInt(), 7);
 
-    Pull(&stream1, {}, qid1);
+    this->Pull(&stream1, {}, qid1);
     ASSERT_EQ(stream1.GetSummary().count("has_more"), 1);
     ASSERT_FALSE(stream1.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream1.GetResults().size(), 3U);
@@ -904,7 +934,7 @@ TEST_F(InterpreterTest, Qid) {
     ASSERT_EQ(stream1.GetResults()[1][0].ValueInt(), 2);
     ASSERT_EQ(stream1.GetResults()[2][0].ValueInt(), 3);
 
-    Pull(&stream3);
+    this->Pull(&stream3);
     ASSERT_EQ(stream3.GetSummary().count("has_more"), 1);
     ASSERT_FALSE(stream3.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream3.GetResults().size(), 3U);
@@ -975,7 +1005,7 @@ std::string CreateRow(const std::vector<std::string> &columns, const std::string
 }
 }  // namespace
 
-TEST_F(InterpreterTest, LoadCsvClause) {
+TYPED_TEST(InterpreterTest, LoadCsvClause) {
   auto dir_manager = TmpDirManager("csv_directory");
   const auto csv_path = dir_manager.Path() / "file.csv";
   auto writer = FileWriter(csv_path);
@@ -999,17 +1029,17 @@ TEST_F(InterpreterTest, LoadCsvClause) {
   {
     const std::string query = fmt::format(R"(LOAD CSV FROM "{}" WITH HEADER IGNORE BAD DELIMITER "{}" AS x RETURN x.A)",
                                           csv_path.string(), delimiter);
-    auto [stream, qid] = Prepare(query);
+    auto [stream, qid] = this->Prepare(query);
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     EXPECT_EQ(stream.GetHeader()[0], "x.A");
 
-    Pull(&stream, 1);
+    this->Pull(&stream, 1);
     ASSERT_EQ(stream.GetSummary().count("has_more"), 1);
     ASSERT_TRUE(stream.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream.GetResults().size(), 1U);
     ASSERT_EQ(stream.GetResults()[0][0].ValueString(), "a");
 
-    Pull(&stream, 1);
+    this->Pull(&stream, 1);
     ASSERT_EQ(stream.GetSummary().count("has_more"), 1);
     ASSERT_FALSE(stream.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream.GetResults().size(), 2U);
@@ -1019,11 +1049,11 @@ TEST_F(InterpreterTest, LoadCsvClause) {
   {
     const std::string query = fmt::format(R"(LOAD CSV FROM "{}" WITH HEADER IGNORE BAD DELIMITER "{}" AS x RETURN x.C)",
                                           csv_path.string(), delimiter);
-    auto [stream, qid] = Prepare(query);
+    auto [stream, qid] = this->Prepare(query);
     ASSERT_EQ(stream.GetHeader().size(), 1U);
     EXPECT_EQ(stream.GetHeader()[0], "x.C");
 
-    Pull(&stream);
+    this->Pull(&stream);
     ASSERT_EQ(stream.GetSummary().count("has_more"), 1);
     ASSERT_FALSE(stream.GetSummary().at("has_more").ValueBool());
     ASSERT_EQ(stream.GetResults().size(), 2U);
@@ -1032,26 +1062,26 @@ TEST_F(InterpreterTest, LoadCsvClause) {
   }
 }
 
-TEST_F(InterpreterTest, CacheableQueries) {
+TYPED_TEST(InterpreterTest, CacheableQueries) {
   // This should be cached
   {
     SCOPED_TRACE("Cacheable query");
-    Interpret("RETURN 1");
-    EXPECT_EQ(interpreter_context.ast_cache.size(), 1U);
-    EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
+    this->Interpret("RETURN 1");
+    EXPECT_EQ(this->interpreter_context.ast_cache.size(), 1U);
+    EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
   }
 
   {
     SCOPED_TRACE("Uncacheable query");
     // Queries which are calling procedure should not be cached because the
     // result signature could be changed
-    Interpret("CALL mg.load_all()");
-    EXPECT_EQ(interpreter_context.ast_cache.size(), 1U);
-    EXPECT_EQ(interpreter_context.plan_cache.size(), 1U);
+    this->Interpret("CALL mg.load_all()");
+    EXPECT_EQ(this->interpreter_context.ast_cache.size(), 1U);
+    EXPECT_EQ(this->interpreter_context.plan_cache.size(), 1U);
   }
 }
 
-TEST_F(InterpreterTest, AllowLoadCsvConfig) {
+TYPED_TEST(InterpreterTest, AllowLoadCsvConfig) {
   const auto check_load_csv_queries = [&](const bool allow_load_csv) {
     TmpDirManager directory_manager{"allow_load_csv"};
     const auto csv_path = directory_manager.Path() / "file.csv";
@@ -1066,7 +1096,9 @@ TEST_F(InterpreterTest, AllowLoadCsvConfig) {
         "row"};
 
     memgraph::query::InterpreterContext csv_interpreter_context{
-        &db_, {.query = {.allow_load_csv = allow_load_csv}}, directory_manager.Path()};
+        std::make_unique<TypeParam>(disk_test_utils::GenerateOnDiskConfig(this->testSuiteCsv)),
+        {.query = {.allow_load_csv = allow_load_csv}},
+        directory_manager.Path()};
     InterpreterFaker interpreter_faker{&csv_interpreter_context};
     for (const auto &query : queries) {
       if (allow_load_csv) {
@@ -1093,18 +1125,18 @@ void AssertAllValuesAreZero(const std::map<std::string, memgraph::communication:
   }
 }
 
-TEST_F(InterpreterTest, ExecutionStatsIsValid) {
+TYPED_TEST(InterpreterTest, ExecutionStatsIsValid) {
   {
-    auto [stream, qid] = Prepare("MATCH (n) DELETE n;");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("MATCH (n) DELETE n;");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("stats"), 0);
   }
   {
     std::array stats_keys{"nodes-created",  "nodes-deleted", "relationships-created", "relationships-deleted",
                           "properties-set", "labels-added",  "labels-removed"};
-    auto [stream, qid] = Prepare("CREATE ();");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("CREATE ();");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("stats"), 1);
     ASSERT_TRUE(stream.GetSummary().at("stats").IsMap());
@@ -1115,26 +1147,26 @@ TEST_F(InterpreterTest, ExecutionStatsIsValid) {
   }
 }
 
-TEST_F(InterpreterTest, ExecutionStatsValues) {
+TYPED_TEST(InterpreterTest, ExecutionStatsValues) {
   {
-    auto [stream, qid] = Prepare("CREATE (),(),(),();");
+    auto [stream, qid] = this->Prepare("CREATE (),(),(),();");
 
-    Pull(&stream);
+    this->Pull(&stream);
     auto stats = stream.GetSummary().at("stats").ValueMap();
     ASSERT_EQ(stats["nodes-created"].ValueInt(), 4);
     AssertAllValuesAreZero(stats, {"nodes-created"});
   }
   {
-    auto [stream, qid] = Prepare("MATCH (n) DELETE n;");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("MATCH (n) DELETE n;");
+    this->Pull(&stream);
 
     auto stats = stream.GetSummary().at("stats").ValueMap();
     ASSERT_EQ(stats["nodes-deleted"].ValueInt(), 4);
     AssertAllValuesAreZero(stats, {"nodes-deleted"});
   }
   {
-    auto [stream, qid] = Prepare("CREATE (n)-[:TO]->(m), (n)-[:TO]->(m), (n)-[:TO]->(m);");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("CREATE (n)-[:TO]->(m), (n)-[:TO]->(m), (n)-[:TO]->(m);");
+    this->Pull(&stream);
 
     auto stats = stream.GetSummary().at("stats").ValueMap();
     ASSERT_EQ(stats["nodes-created"].ValueInt(), 2);
@@ -1142,8 +1174,8 @@ TEST_F(InterpreterTest, ExecutionStatsValues) {
     AssertAllValuesAreZero(stats, {"nodes-created", "relationships-created"});
   }
   {
-    auto [stream, qid] = Prepare("MATCH (n) DETACH DELETE n;");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("MATCH (n) DETACH DELETE n;");
+    this->Pull(&stream);
 
     auto stats = stream.GetSummary().at("stats").ValueMap();
     ASSERT_EQ(stats["nodes-deleted"].ValueInt(), 2);
@@ -1151,8 +1183,8 @@ TEST_F(InterpreterTest, ExecutionStatsValues) {
     AssertAllValuesAreZero(stats, {"nodes-deleted", "relationships-deleted"});
   }
   {
-    auto [stream, qid] = Prepare("CREATE (:L1:L2:L3), (:L1), (:L1), (:L2);");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("CREATE (:L1:L2:L3), (:L1), (:L1), (:L2);");
+    this->Pull(&stream);
 
     auto stats = stream.GetSummary().at("stats").ValueMap();
     ASSERT_EQ(stats["nodes-created"].ValueInt(), 4);
@@ -1160,8 +1192,8 @@ TEST_F(InterpreterTest, ExecutionStatsValues) {
     AssertAllValuesAreZero(stats, {"nodes-created", "labels-added"});
   }
   {
-    auto [stream, qid] = Prepare("MATCH (n:L1) SET n.name='test';");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("MATCH (n:L1) SET n.name='test';");
+    this->Pull(&stream);
 
     auto stats = stream.GetSummary().at("stats").ValueMap();
     ASSERT_EQ(stats["properties-set"].ValueInt(), 3);
@@ -1169,16 +1201,16 @@ TEST_F(InterpreterTest, ExecutionStatsValues) {
   }
 }
 
-TEST_F(InterpreterTest, NotificationsValidStructure) {
+TYPED_TEST(InterpreterTest, NotificationsValidStructure) {
   {
-    auto [stream, qid] = Prepare("MATCH (n) DELETE n;");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("MATCH (n) DELETE n;");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 0);
   }
   {
-    auto [stream, qid] = Prepare("CREATE INDEX ON :Person(id);");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("CREATE INDEX ON :Person(id);");
+    this->Pull(&stream);
 
     // Assert notifications list
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
@@ -1200,10 +1232,10 @@ TEST_F(InterpreterTest, NotificationsValidStructure) {
   }
 }
 
-TEST_F(InterpreterTest, IndexInfoNotifications) {
+TYPED_TEST(InterpreterTest, IndexInfoNotifications) {
   {
-    auto [stream, qid] = Prepare("CREATE INDEX ON :Person;");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("CREATE INDEX ON :Person;");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1215,8 +1247,8 @@ TEST_F(InterpreterTest, IndexInfoNotifications) {
     ASSERT_EQ(notification["description"].ValueString(), "");
   }
   {
-    auto [stream, qid] = Prepare("CREATE INDEX ON :Person(id);");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("CREATE INDEX ON :Person(id);");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1228,8 +1260,8 @@ TEST_F(InterpreterTest, IndexInfoNotifications) {
     ASSERT_EQ(notification["description"].ValueString(), "");
   }
   {
-    auto [stream, qid] = Prepare("CREATE INDEX ON :Person(id);");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("CREATE INDEX ON :Person(id);");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1241,8 +1273,8 @@ TEST_F(InterpreterTest, IndexInfoNotifications) {
     ASSERT_EQ(notification["description"].ValueString(), "");
   }
   {
-    auto [stream, qid] = Prepare("DROP INDEX ON :Person(id);");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("DROP INDEX ON :Person(id);");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1254,8 +1286,8 @@ TEST_F(InterpreterTest, IndexInfoNotifications) {
     ASSERT_EQ(notification["description"].ValueString(), "");
   }
   {
-    auto [stream, qid] = Prepare("DROP INDEX ON :Person(id);");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("DROP INDEX ON :Person(id);");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1268,10 +1300,10 @@ TEST_F(InterpreterTest, IndexInfoNotifications) {
   }
 }
 
-TEST_F(InterpreterTest, ConstraintUniqueInfoNotifications) {
+TYPED_TEST(InterpreterTest, ConstraintUniqueInfoNotifications) {
   {
-    auto [stream, qid] = Prepare("CREATE CONSTRAINT ON (n:Person) ASSERT n.email, n.id IS UNIQUE;");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("CREATE CONSTRAINT ON (n:Person) ASSERT n.email, n.id IS UNIQUE;");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1284,8 +1316,8 @@ TEST_F(InterpreterTest, ConstraintUniqueInfoNotifications) {
     ASSERT_EQ(notification["description"].ValueString(), "");
   }
   {
-    auto [stream, qid] = Prepare("CREATE CONSTRAINT ON (n:Person) ASSERT n.email, n.id IS UNIQUE;");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("CREATE CONSTRAINT ON (n:Person) ASSERT n.email, n.id IS UNIQUE;");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1298,8 +1330,8 @@ TEST_F(InterpreterTest, ConstraintUniqueInfoNotifications) {
     ASSERT_EQ(notification["description"].ValueString(), "");
   }
   {
-    auto [stream, qid] = Prepare("DROP CONSTRAINT ON (n:Person) ASSERT n.email, n.id IS UNIQUE;");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("DROP CONSTRAINT ON (n:Person) ASSERT n.email, n.id IS UNIQUE;");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1312,8 +1344,8 @@ TEST_F(InterpreterTest, ConstraintUniqueInfoNotifications) {
     ASSERT_EQ(notification["description"].ValueString(), "");
   }
   {
-    auto [stream, qid] = Prepare("DROP CONSTRAINT ON (n:Person) ASSERT n.email, n.id IS UNIQUE;");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("DROP CONSTRAINT ON (n:Person) ASSERT n.email, n.id IS UNIQUE;");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1327,10 +1359,10 @@ TEST_F(InterpreterTest, ConstraintUniqueInfoNotifications) {
   }
 }
 
-TEST_F(InterpreterTest, ConstraintExistsInfoNotifications) {
+TYPED_TEST(InterpreterTest, ConstraintExistsInfoNotifications) {
   {
-    auto [stream, qid] = Prepare("CREATE CONSTRAINT ON (n:L1) ASSERT EXISTS (n.name);");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("CREATE CONSTRAINT ON (n:L1) ASSERT EXISTS (n.name);");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1342,8 +1374,8 @@ TEST_F(InterpreterTest, ConstraintExistsInfoNotifications) {
     ASSERT_EQ(notification["description"].ValueString(), "");
   }
   {
-    auto [stream, qid] = Prepare("CREATE CONSTRAINT ON (n:L1) ASSERT EXISTS (n.name);");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("CREATE CONSTRAINT ON (n:L1) ASSERT EXISTS (n.name);");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1355,8 +1387,8 @@ TEST_F(InterpreterTest, ConstraintExistsInfoNotifications) {
     ASSERT_EQ(notification["description"].ValueString(), "");
   }
   {
-    auto [stream, qid] = Prepare("DROP CONSTRAINT ON (n:L1) ASSERT EXISTS (n.name);");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("DROP CONSTRAINT ON (n:L1) ASSERT EXISTS (n.name);");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1368,8 +1400,8 @@ TEST_F(InterpreterTest, ConstraintExistsInfoNotifications) {
     ASSERT_EQ(notification["description"].ValueString(), "");
   }
   {
-    auto [stream, qid] = Prepare("DROP CONSTRAINT ON (n:L1) ASSERT EXISTS (n.name);");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("DROP CONSTRAINT ON (n:L1) ASSERT EXISTS (n.name);");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1382,12 +1414,12 @@ TEST_F(InterpreterTest, ConstraintExistsInfoNotifications) {
   }
 }
 
-TEST_F(InterpreterTest, TriggerInfoNotifications) {
+TYPED_TEST(InterpreterTest, TriggerInfoNotifications) {
   {
-    auto [stream, qid] = Prepare(
+    auto [stream, qid] = this->Prepare(
         "CREATE TRIGGER bestTriggerEver ON  CREATE AFTER COMMIT EXECUTE "
         "CREATE ();");
-    Pull(&stream);
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1399,8 +1431,8 @@ TEST_F(InterpreterTest, TriggerInfoNotifications) {
     ASSERT_EQ(notification["description"].ValueString(), "");
   }
   {
-    auto [stream, qid] = Prepare("DROP TRIGGER bestTriggerEver;");
-    Pull(&stream);
+    auto [stream, qid] = this->Prepare("DROP TRIGGER bestTriggerEver;");
+    this->Pull(&stream);
 
     ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
     auto notifications = stream.GetSummary().at("notifications").ValueList();
@@ -1413,7 +1445,7 @@ TEST_F(InterpreterTest, TriggerInfoNotifications) {
   }
 }
 
-TEST_F(InterpreterTest, LoadCsvClauseNotification) {
+TYPED_TEST(InterpreterTest, LoadCsvClauseNotification) {
   auto dir_manager = TmpDirManager("csv_directory");
   const auto csv_path = dir_manager.Path() / "file.csv";
   auto writer = FileWriter(csv_path);
@@ -1430,8 +1462,8 @@ TEST_F(InterpreterTest, LoadCsvClauseNotification) {
 
   const std::string query = fmt::format(R"(LOAD CSV FROM "{}" WITH HEADER IGNORE BAD DELIMITER "{}" AS x RETURN x;)",
                                         csv_path.string(), delimiter);
-  auto [stream, qid] = Prepare(query);
-  Pull(&stream);
+  auto [stream, qid] = this->Prepare(query);
+  this->Pull(&stream);
 
   ASSERT_EQ(stream.GetSummary().count("notifications"), 1);
   auto notifications = stream.GetSummary().at("notifications").ValueList();
diff --git a/tests/unit/interpreter_faker.hpp b/tests/unit/interpreter_faker.hpp
index 5b14a543e..9f6ee5c76 100644
--- a/tests/unit/interpreter_faker.hpp
+++ b/tests/unit/interpreter_faker.hpp
@@ -20,7 +20,7 @@ struct InterpreterFaker {
   }
 
   auto Prepare(const std::string &query, const std::map<std::string, memgraph::storage::PropertyValue> &params = {}) {
-    ResultStreamFaker stream(interpreter_context->db);
+    ResultStreamFaker stream(interpreter_context->db.get());
     const auto [header, _, qid] = interpreter.Prepare(query, params, nullptr);
     stream.Header(header);
     return std::make_pair(std::move(stream), qid);
diff --git a/tests/unit/plan_pretty_print.cpp b/tests/unit/plan_pretty_print.cpp
index da24db9a2..94b7342f9 100644
--- a/tests/unit/plan_pretty_print.cpp
+++ b/tests/unit/plan_pretty_print.cpp
@@ -11,11 +11,14 @@
 
 #include <gtest/gtest.h>
 
+#include "disk_test_utils.hpp"
 #include "query/frontend/semantic/symbol_table.hpp"
 #include "query/plan/operator.hpp"
 #include "query/plan/pretty_print.hpp"
 
 #include "query_common.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 using namespace memgraph::query;
 using namespace memgraph::query::plan;
@@ -32,39 +35,54 @@ void PrintTo(const json &json, std::ostream *os) { *os << std::endl << json.dump
 
 using namespace nlohmann;
 
+template <typename StorageType>
 class PrintToJsonTest : public ::testing::Test {
  protected:
-  PrintToJsonTest() : db(), dba(db.Access()) {}
+  const std::string testSuite = "plan_pretty_print";
+
+  PrintToJsonTest()
+      : config(disk_test_utils::GenerateOnDiskConfig(testSuite)),
+        db(new StorageType(config)),
+        dba_storage(db->Access()),
+        dba(dba_storage.get()) {}
+
+  ~PrintToJsonTest() {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
 
   AstStorage storage;
   SymbolTable symbol_table;
 
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor dba;
+  memgraph::storage::Config config;
+  std::unique_ptr<memgraph::storage::Storage> db;
+  std::unique_ptr<memgraph::storage::Storage::Accessor> dba_storage;
+  memgraph::query::DbAccessor dba;
 
   Symbol GetSymbol(std::string name) { return symbol_table.CreateSymbol(name, true); }
 
-  void Check(LogicalOperator *root, std::string expected) {
-    memgraph::query::DbAccessor query_dba(&dba);
-    EXPECT_EQ(PlanToJson(query_dba, root), json::parse(expected));
-  }
+  void Check(LogicalOperator *root, std::string expected) { EXPECT_EQ(PlanToJson(dba, root), json::parse(expected)); }
 };
 
-TEST_F(PrintToJsonTest, Once) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(PrintToJsonTest, StorageTypes);
+
+TYPED_TEST(PrintToJsonTest, Once) {
   std::shared_ptr<LogicalOperator> last_op;
   last_op = std::make_shared<Once>();
 
-  Check(last_op.get(), R"(
+  this->Check(last_op.get(), R"(
         {
           "name" : "Once"
         })");
 }
 
-TEST_F(PrintToJsonTest, ScanAll) {
+TYPED_TEST(PrintToJsonTest, ScanAll) {
   std::shared_ptr<LogicalOperator> last_op;
-  last_op = std::make_shared<ScanAll>(nullptr, GetSymbol("node"));
+  last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node"));
 
-  Check(last_op.get(), R"(
+  this->Check(last_op.get(), R"(
         {
           "name" : "ScanAll",
           "output_symbol" : "node",
@@ -72,11 +90,11 @@ TEST_F(PrintToJsonTest, ScanAll) {
         })");
 }
 
-TEST_F(PrintToJsonTest, ScanAllByLabel) {
+TYPED_TEST(PrintToJsonTest, ScanAllByLabel) {
   std::shared_ptr<LogicalOperator> last_op;
-  last_op = std::make_shared<ScanAllByLabel>(nullptr, GetSymbol("node"), dba.NameToLabel("Label"));
+  last_op = std::make_shared<ScanAllByLabel>(nullptr, this->GetSymbol("node"), this->dba.NameToLabel("Label"));
 
-  Check(last_op.get(), R"(
+  this->Check(last_op.get(), R"(
         {
           "name" : "ScanAllByLabel",
           "label" : "Label",
@@ -85,15 +103,15 @@ TEST_F(PrintToJsonTest, ScanAllByLabel) {
         })");
 }
 
-TEST_F(PrintToJsonTest, ScanAllByLabelPropertyRange) {
+TYPED_TEST(PrintToJsonTest, ScanAllByLabelPropertyRange) {
   {
     std::shared_ptr<LogicalOperator> last_op;
     last_op = std::make_shared<ScanAllByLabelPropertyRange>(
-        nullptr, GetSymbol("node"), dba.NameToLabel("Label"), dba.NameToProperty("prop"), "prop",
+        nullptr, this->GetSymbol("node"), this->dba.NameToLabel("Label"), this->dba.NameToProperty("prop"), "prop",
         memgraph::utils::MakeBoundInclusive<Expression *>(LITERAL(1)),
         memgraph::utils::MakeBoundExclusive<Expression *>(LITERAL(20)));
 
-    Check(last_op.get(), R"(
+    this->Check(last_op.get(), R"(
         {
           "name" : "ScanAllByLabelPropertyRange",
           "label" : "Label",
@@ -113,10 +131,10 @@ TEST_F(PrintToJsonTest, ScanAllByLabelPropertyRange) {
   {
     std::shared_ptr<LogicalOperator> last_op;
     last_op = std::make_shared<ScanAllByLabelPropertyRange>(
-        nullptr, GetSymbol("node"), dba.NameToLabel("Label"), dba.NameToProperty("prop"), "prop", std::nullopt,
-        memgraph::utils::MakeBoundExclusive<Expression *>(LITERAL(20)));
+        nullptr, this->GetSymbol("node"), this->dba.NameToLabel("Label"), this->dba.NameToProperty("prop"), "prop",
+        std::nullopt, memgraph::utils::MakeBoundExclusive<Expression *>(LITERAL(20)));
 
-    Check(last_op.get(), R"(
+    this->Check(last_op.get(), R"(
         {
           "name" : "ScanAllByLabelPropertyRange",
           "label" : "Label",
@@ -133,10 +151,10 @@ TEST_F(PrintToJsonTest, ScanAllByLabelPropertyRange) {
   {
     std::shared_ptr<LogicalOperator> last_op;
     last_op = std::make_shared<ScanAllByLabelPropertyRange>(
-        nullptr, GetSymbol("node"), dba.NameToLabel("Label"), dba.NameToProperty("prop"), "prop",
+        nullptr, this->GetSymbol("node"), this->dba.NameToLabel("Label"), this->dba.NameToProperty("prop"), "prop",
         memgraph::utils::MakeBoundInclusive<Expression *>(LITERAL(1)), std::nullopt);
 
-    Check(last_op.get(), R"(
+    this->Check(last_op.get(), R"(
         {
           "name" : "ScanAllByLabelPropertyRange",
           "label" : "Label",
@@ -152,13 +170,13 @@ TEST_F(PrintToJsonTest, ScanAllByLabelPropertyRange) {
   }
 }
 
-TEST_F(PrintToJsonTest, ScanAllByLabelPropertyValue) {
+TYPED_TEST(PrintToJsonTest, ScanAllByLabelPropertyValue) {
   std::shared_ptr<LogicalOperator> last_op;
-  last_op =
-      std::make_shared<ScanAllByLabelPropertyValue>(nullptr, GetSymbol("node"), dba.NameToLabel("Label"),
-                                                    dba.NameToProperty("prop"), "prop", ADD(LITERAL(21), LITERAL(21)));
+  last_op = std::make_shared<ScanAllByLabelPropertyValue>(
+      nullptr, this->GetSymbol("node"), this->dba.NameToLabel("Label"), this->dba.NameToProperty("prop"), "prop",
+      ADD(LITERAL(21), LITERAL(21)));
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
         {
           "name" : "ScanAllByLabelPropertyValue",
           "label" : "Label",
@@ -169,15 +187,15 @@ TEST_F(PrintToJsonTest, ScanAllByLabelPropertyValue) {
         })sep");
 }
 
-TEST_F(PrintToJsonTest, CreateNode) {
+TYPED_TEST(PrintToJsonTest, CreateNode) {
   std::shared_ptr<LogicalOperator> last_op;
-  last_op = std::make_shared<CreateNode>(nullptr,
-                                         NodeCreationInfo{GetSymbol("node"),
-                                                          {dba.NameToLabel("Label1"), dba.NameToLabel("Label2")},
-                                                          {{dba.NameToProperty("prop1"), LITERAL(5)},
-                                                           {dba.NameToProperty("prop2"), LITERAL("some cool stuff")}}});
+  last_op = std::make_shared<CreateNode>(
+      nullptr, NodeCreationInfo{this->GetSymbol("node"),
+                                {this->dba.NameToLabel("Label1"), this->dba.NameToLabel("Label2")},
+                                {{this->dba.NameToProperty("prop1"), LITERAL(5)},
+                                 {this->dba.NameToProperty("prop2"), LITERAL("some cool stuff")}}});
 
-  Check(last_op.get(), R"(
+  this->Check(last_op.get(), R"(
           {
             "name" : "CreateNode",
             "node_info" : {
@@ -192,21 +210,21 @@ TEST_F(PrintToJsonTest, CreateNode) {
           })");
 }
 
-TEST_F(PrintToJsonTest, CreateExpand) {
-  Symbol node1_sym = GetSymbol("node1");
-  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, GetSymbol("node1"));
+TYPED_TEST(PrintToJsonTest, CreateExpand) {
+  Symbol node1_sym = this->GetSymbol("node1");
+  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node1"));
   last_op = std::make_shared<CreateExpand>(
-      NodeCreationInfo{
-          GetSymbol("node2"),
-          {dba.NameToLabel("Label1"), dba.NameToLabel("Label2")},
-          {{dba.NameToProperty("prop1"), LITERAL(5)}, {dba.NameToProperty("prop2"), LITERAL("some cool stuff")}}},
-      EdgeCreationInfo{GetSymbol("edge"),
-                       {{dba.NameToProperty("weight"), LITERAL(5.32)}},
-                       dba.NameToEdgeType("edge_type"),
+      NodeCreationInfo{this->GetSymbol("node2"),
+                       {this->dba.NameToLabel("Label1"), this->dba.NameToLabel("Label2")},
+                       {{this->dba.NameToProperty("prop1"), LITERAL(5)},
+                        {this->dba.NameToProperty("prop2"), LITERAL("some cool stuff")}}},
+      EdgeCreationInfo{this->GetSymbol("edge"),
+                       {{this->dba.NameToProperty("weight"), LITERAL(5.32)}},
+                       this->dba.NameToEdgeType("edge_type"),
                        EdgeAtom::Direction::OUT},
       last_op, node1_sym, false);
 
-  Check(last_op.get(), R"(
+  this->Check(last_op.get(), R"(
           {
             "name" : "CreateExpand",
             "node_info" : {
@@ -235,15 +253,16 @@ TEST_F(PrintToJsonTest, CreateExpand) {
           })");
 }
 
-TEST_F(PrintToJsonTest, Expand) {
-  auto node1_sym = GetSymbol("node1");
+TYPED_TEST(PrintToJsonTest, Expand) {
+  auto node1_sym = this->GetSymbol("node1");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node1_sym);
-  last_op = std::make_shared<Expand>(
-      last_op, node1_sym, GetSymbol("node2"), GetSymbol("edge"), EdgeAtom::Direction::BOTH,
-      std::vector<memgraph::storage::EdgeTypeId>{dba.NameToEdgeType("EdgeType1"), dba.NameToEdgeType("EdgeType2")},
-      false, memgraph::storage::View::OLD);
+  last_op = std::make_shared<Expand>(last_op, node1_sym, this->GetSymbol("node2"), this->GetSymbol("edge"),
+                                     EdgeAtom::Direction::BOTH,
+                                     std::vector<memgraph::storage::EdgeTypeId>{this->dba.NameToEdgeType("EdgeType1"),
+                                                                                this->dba.NameToEdgeType("EdgeType2")},
+                                     false, memgraph::storage::View::OLD);
 
-  Check(last_op.get(), R"(
+  this->Check(last_op.get(), R"(
           {
             "name" : "Expand",
             "input_symbol" : "node1",
@@ -260,19 +279,20 @@ TEST_F(PrintToJsonTest, Expand) {
           })");
 }
 
-TEST_F(PrintToJsonTest, ExpandVariable) {
-  auto node1_sym = GetSymbol("node1");
+TYPED_TEST(PrintToJsonTest, ExpandVariable) {
+  auto node1_sym = this->GetSymbol("node1");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node1_sym);
   last_op = std::make_shared<ExpandVariable>(
-      last_op, node1_sym, GetSymbol("node2"), GetSymbol("edge"), EdgeAtom::Type::BREADTH_FIRST,
+      last_op, node1_sym, this->GetSymbol("node2"), this->GetSymbol("edge"), EdgeAtom::Type::BREADTH_FIRST,
       EdgeAtom::Direction::OUT,
-      std::vector<memgraph::storage::EdgeTypeId>{dba.NameToEdgeType("EdgeType1"), dba.NameToEdgeType("EdgeType2")},
+      std::vector<memgraph::storage::EdgeTypeId>{this->dba.NameToEdgeType("EdgeType1"),
+                                                 this->dba.NameToEdgeType("EdgeType2")},
       false, LITERAL(2), LITERAL(5), false,
-      ExpansionLambda{GetSymbol("inner_node"), GetSymbol("inner_edge"),
-                      PROPERTY_LOOKUP("inner_node", dba.NameToProperty("unblocked"))},
+      ExpansionLambda{this->GetSymbol("inner_node"), this->GetSymbol("inner_edge"),
+                      PROPERTY_LOOKUP(this->dba, "inner_node", this->dba.NameToProperty("unblocked"))},
       std::nullopt, std::nullopt);
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "ExpandVariable",
             "input_symbol" : "node1",
@@ -294,19 +314,21 @@ TEST_F(PrintToJsonTest, ExpandVariable) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, ExpandVariableWsp) {
-  auto node1_sym = GetSymbol("node1");
+TYPED_TEST(PrintToJsonTest, ExpandVariableWsp) {
+  auto node1_sym = this->GetSymbol("node1");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node1_sym);
   last_op = std::make_shared<ExpandVariable>(
-      last_op, node1_sym, GetSymbol("node2"), GetSymbol("edge"), EdgeAtom::Type::WEIGHTED_SHORTEST_PATH,
+      last_op, node1_sym, this->GetSymbol("node2"), this->GetSymbol("edge"), EdgeAtom::Type::WEIGHTED_SHORTEST_PATH,
       EdgeAtom::Direction::OUT,
-      std::vector<memgraph::storage::EdgeTypeId>{dba.NameToEdgeType("EdgeType1"), dba.NameToEdgeType("EdgeType2")},
-      false, LITERAL(2), LITERAL(5), false, ExpansionLambda{GetSymbol("inner_node"), GetSymbol("inner_edge"), nullptr},
-      ExpansionLambda{GetSymbol("inner_node"), GetSymbol("inner_edge"),
-                      PROPERTY_LOOKUP("inner_edge", dba.NameToProperty("weight"))},
-      GetSymbol("total"));
+      std::vector<memgraph::storage::EdgeTypeId>{this->dba.NameToEdgeType("EdgeType1"),
+                                                 this->dba.NameToEdgeType("EdgeType2")},
+      false, LITERAL(2), LITERAL(5), false,
+      ExpansionLambda{this->GetSymbol("inner_node"), this->GetSymbol("inner_edge"), nullptr},
+      ExpansionLambda{this->GetSymbol("inner_node"), this->GetSymbol("inner_edge"),
+                      PROPERTY_LOOKUP(this->dba, "inner_edge", this->dba.NameToProperty("weight"))},
+      this->GetSymbol("total"));
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "ExpandVariable",
             "input_symbol" : "node1",
@@ -330,12 +352,12 @@ TEST_F(PrintToJsonTest, ExpandVariableWsp) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, ConstructNamedPath) {
-  auto node1_sym = GetSymbol("node1");
-  auto edge1_sym = GetSymbol("edge1");
-  auto node2_sym = GetSymbol("node2");
-  auto edge2_sym = GetSymbol("edge2");
-  auto node3_sym = GetSymbol("node3");
+TYPED_TEST(PrintToJsonTest, ConstructNamedPath) {
+  auto node1_sym = this->GetSymbol("node1");
+  auto edge1_sym = this->GetSymbol("edge1");
+  auto node2_sym = this->GetSymbol("node2");
+  auto edge2_sym = this->GetSymbol("edge2");
+  auto node3_sym = this->GetSymbol("node3");
 
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node1_sym);
   last_op = std::make_shared<Expand>(last_op, node1_sym, node2_sym, edge1_sym, EdgeAtom::Direction::OUT,
@@ -343,9 +365,9 @@ TEST_F(PrintToJsonTest, ConstructNamedPath) {
   last_op = std::make_shared<Expand>(last_op, node2_sym, node3_sym, edge2_sym, EdgeAtom::Direction::OUT,
                                      std::vector<memgraph::storage::EdgeTypeId>{}, false, memgraph::storage::View::OLD);
   last_op = std::make_shared<ConstructNamedPath>(
-      last_op, GetSymbol("path"), std::vector<Symbol>{node1_sym, edge1_sym, node2_sym, edge2_sym, node3_sym});
+      last_op, this->GetSymbol("path"), std::vector<Symbol>{node1_sym, edge1_sym, node2_sym, edge2_sym, node3_sym});
 
-  Check(last_op.get(), R"(
+  this->Check(last_op.get(), R"(
           {
             "name" : "ConstructNamedPath",
             "path_symbol" : "path",
@@ -376,12 +398,13 @@ TEST_F(PrintToJsonTest, ConstructNamedPath) {
           })");
 }
 
-TEST_F(PrintToJsonTest, Filter) {
-  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, GetSymbol("node1"));
-  last_op = std::make_shared<Filter>(last_op, std::vector<std::shared_ptr<LogicalOperator>>{},
-                                     EQ(PROPERTY_LOOKUP("node1", dba.NameToProperty("prop")), LITERAL(5)));
+TYPED_TEST(PrintToJsonTest, Filter) {
+  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node1"));
+  last_op =
+      std::make_shared<Filter>(last_op, std::vector<std::shared_ptr<LogicalOperator>>{},
+                               EQ(PROPERTY_LOOKUP(this->dba, "node1", this->dba.NameToProperty("prop")), LITERAL(5)));
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Filter",
             "expression" : "(== (PropertyLookup (Identifier \"node1\") \"prop\") 5)",
@@ -393,11 +416,11 @@ TEST_F(PrintToJsonTest, Filter) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Produce) {
+TYPED_TEST(PrintToJsonTest, Produce) {
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<Produce>(
       nullptr, std::vector<NamedExpression *>{NEXPR("pet", LITERAL(5)), NEXPR("string", LITERAL("string"))});
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Produce",
             "named_expressions" : [
@@ -414,15 +437,15 @@ TEST_F(PrintToJsonTest, Produce) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Delete) {
-  auto node_sym = GetSymbol("node1");
+TYPED_TEST(PrintToJsonTest, Delete) {
+  auto node_sym = this->GetSymbol("node1");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
-  last_op =
-      std::make_shared<Expand>(last_op, node_sym, GetSymbol("node2"), GetSymbol("edge"), EdgeAtom::Direction::BOTH,
-                               std::vector<memgraph::storage::EdgeTypeId>{}, false, memgraph::storage::View::OLD);
+  last_op = std::make_shared<Expand>(last_op, node_sym, this->GetSymbol("node2"), this->GetSymbol("edge"),
+                                     EdgeAtom::Direction::BOTH, std::vector<memgraph::storage::EdgeTypeId>{}, false,
+                                     memgraph::storage::View::OLD);
   last_op = std::make_shared<plan::Delete>(last_op, std::vector<Expression *>{IDENT("node2")}, true);
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Delete",
             "expressions" : [ "(Identifier \"node2\")" ],
@@ -444,14 +467,14 @@ TEST_F(PrintToJsonTest, Delete) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, SetProperty) {
-  memgraph::storage::PropertyId prop = dba.NameToProperty("prop");
+TYPED_TEST(PrintToJsonTest, SetProperty) {
+  memgraph::storage::PropertyId prop = this->dba.NameToProperty("prop");
 
-  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, GetSymbol("node"));
-  last_op = std::make_shared<plan::SetProperty>(last_op, prop, PROPERTY_LOOKUP("node", prop),
-                                                ADD(PROPERTY_LOOKUP("node", prop), LITERAL(1)));
+  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node"));
+  last_op = std::make_shared<plan::SetProperty>(last_op, prop, PROPERTY_LOOKUP(this->dba, "node", prop),
+                                                ADD(PROPERTY_LOOKUP(this->dba, "node", prop), LITERAL(1)));
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "SetProperty",
             "property" : "prop",
@@ -465,15 +488,15 @@ TEST_F(PrintToJsonTest, SetProperty) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, SetProperties) {
-  auto node_sym = GetSymbol("node");
+TYPED_TEST(PrintToJsonTest, SetProperties) {
+  auto node_sym = this->GetSymbol("node");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
-  last_op = std::make_shared<plan::SetProperties>(
-      last_op, node_sym,
-      MAP({{storage.GetPropertyIx("prop1"), LITERAL(1)}, {storage.GetPropertyIx("prop2"), LITERAL("propko")}}),
-      plan::SetProperties::Op::REPLACE);
+  last_op = std::make_shared<plan::SetProperties>(last_op, node_sym,
+                                                  MAP({{this->storage.GetPropertyIx("prop1"), LITERAL(1)},
+                                                       {this->storage.GetPropertyIx("prop2"), LITERAL("propko")}}),
+                                                  plan::SetProperties::Op::REPLACE);
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "SetProperties",
             "input_symbol" : "node",
@@ -487,13 +510,14 @@ TEST_F(PrintToJsonTest, SetProperties) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, SetLabels) {
-  auto node_sym = GetSymbol("node");
+TYPED_TEST(PrintToJsonTest, SetLabels) {
+  auto node_sym = this->GetSymbol("node");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
   last_op = std::make_shared<plan::SetLabels>(
-      last_op, node_sym, std::vector<memgraph::storage::LabelId>{dba.NameToLabel("label1"), dba.NameToLabel("label2")});
+      last_op, node_sym,
+      std::vector<memgraph::storage::LabelId>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
 
-  Check(last_op.get(), R"(
+  this->Check(last_op.get(), R"(
           {
             "name" : "SetLabels",
             "input_symbol" : "node",
@@ -506,13 +530,13 @@ TEST_F(PrintToJsonTest, SetLabels) {
           })");
 }
 
-TEST_F(PrintToJsonTest, RemoveProperty) {
-  auto node_sym = GetSymbol("node");
+TYPED_TEST(PrintToJsonTest, RemoveProperty) {
+  auto node_sym = this->GetSymbol("node");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
-  last_op = std::make_shared<plan::RemoveProperty>(last_op, dba.NameToProperty("prop"),
-                                                   PROPERTY_LOOKUP("node", dba.NameToProperty("prop")));
+  last_op = std::make_shared<plan::RemoveProperty>(
+      last_op, this->dba.NameToProperty("prop"), PROPERTY_LOOKUP(this->dba, "node", this->dba.NameToProperty("prop")));
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "RemoveProperty",
             "lhs" : "(PropertyLookup (Identifier \"node\") \"prop\")",
@@ -525,13 +549,14 @@ TEST_F(PrintToJsonTest, RemoveProperty) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, RemoveLabels) {
-  auto node_sym = GetSymbol("node");
+TYPED_TEST(PrintToJsonTest, RemoveLabels) {
+  auto node_sym = this->GetSymbol("node");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
   last_op = std::make_shared<plan::RemoveLabels>(
-      last_op, node_sym, std::vector<memgraph::storage::LabelId>{dba.NameToLabel("label1"), dba.NameToLabel("label2")});
+      last_op, node_sym,
+      std::vector<memgraph::storage::LabelId>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
 
-  Check(last_op.get(), R"(
+  this->Check(last_op.get(), R"(
           {
             "name" : "RemoveLabels",
             "input_symbol" : "node",
@@ -544,14 +569,14 @@ TEST_F(PrintToJsonTest, RemoveLabels) {
           })");
 }
 
-TEST_F(PrintToJsonTest, EdgeUniquenessFilter) {
-  auto node1_sym = GetSymbol("node1");
-  auto node2_sym = GetSymbol("node2");
-  auto node3_sym = GetSymbol("node3");
-  auto node4_sym = GetSymbol("node4");
+TYPED_TEST(PrintToJsonTest, EdgeUniquenessFilter) {
+  auto node1_sym = this->GetSymbol("node1");
+  auto node2_sym = this->GetSymbol("node2");
+  auto node3_sym = this->GetSymbol("node3");
+  auto node4_sym = this->GetSymbol("node4");
 
-  auto edge1_sym = GetSymbol("edge1");
-  auto edge2_sym = GetSymbol("edge2");
+  auto edge1_sym = this->GetSymbol("edge1");
+  auto edge2_sym = this->GetSymbol("edge2");
 
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node1_sym);
   last_op = std::make_shared<Expand>(last_op, node1_sym, node2_sym, edge1_sym, EdgeAtom::Direction::IN,
@@ -561,7 +586,7 @@ TEST_F(PrintToJsonTest, EdgeUniquenessFilter) {
                                      std::vector<memgraph::storage::EdgeTypeId>{}, false, memgraph::storage::View::OLD);
   last_op = std::make_shared<EdgeUniquenessFilter>(last_op, edge2_sym, std::vector<Symbol>{edge1_sym});
 
-  Check(last_op.get(), R"(
+  this->Check(last_op.get(), R"(
           {
             "name" : "EdgeUniquenessFilter",
             "expand_symbol" : "edge2",
@@ -596,15 +621,15 @@ TEST_F(PrintToJsonTest, EdgeUniquenessFilter) {
           })");
 }
 
-TEST_F(PrintToJsonTest, Accumulate) {
-  memgraph::storage::PropertyId prop = dba.NameToProperty("prop");
-  auto node_sym = GetSymbol("node");
+TYPED_TEST(PrintToJsonTest, Accumulate) {
+  memgraph::storage::PropertyId prop = this->dba.NameToProperty("prop");
+  auto node_sym = this->GetSymbol("node");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
-  last_op = std::make_shared<plan::SetProperty>(last_op, prop, PROPERTY_LOOKUP("node", prop),
-                                                ADD(PROPERTY_LOOKUP("node", prop), LITERAL(1)));
+  last_op = std::make_shared<plan::SetProperty>(last_op, prop, PROPERTY_LOOKUP(this->dba, "node", prop),
+                                                ADD(PROPERTY_LOOKUP(this->dba, "node", prop), LITERAL(1)));
   last_op = std::make_shared<plan::Accumulate>(last_op, std::vector<Symbol>{node_sym}, true);
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Accumulate",
             "symbols" : ["node"],
@@ -623,71 +648,72 @@ TEST_F(PrintToJsonTest, Accumulate) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Aggregate) {
-  memgraph::storage::PropertyId value = dba.NameToProperty("value");
-  memgraph::storage::PropertyId color = dba.NameToProperty("color");
-  memgraph::storage::PropertyId type = dba.NameToProperty("type");
-  auto node_sym = GetSymbol("node");
-  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
-  last_op = std::make_shared<plan::Aggregate>(
-      last_op,
-      std::vector<Aggregate::Element>{{PROPERTY_LOOKUP("node", value), nullptr, Aggregation::Op::SUM, GetSymbol("sum")},
-                                      {PROPERTY_LOOKUP("node", value), PROPERTY_LOOKUP("node", color),
-                                       Aggregation::Op::COLLECT_MAP, GetSymbol("map")},
-                                      {nullptr, nullptr, Aggregation::Op::COUNT, GetSymbol("count")}},
-      std::vector<Expression *>{PROPERTY_LOOKUP("node", type)}, std::vector<Symbol>{node_sym});
-
-  Check(last_op.get(), R"sep(
-          {
-            "name" : "Aggregate",
-            "aggregations" : [
-              {
-                "value" : "(PropertyLookup (Identifier \"node\") \"value\")",
-                "op" : "sum",
-                "output_symbol" : "sum",
-                "distinct" : false
-              },
-              {
-                "value" : "(PropertyLookup (Identifier \"node\") \"value\")",
-                "key" : "(PropertyLookup (Identifier \"node\") \"color\")",
-                "op" : "collect",
-                "output_symbol" : "map",
-                "distinct" : false
-              },
-              {
-                "op": "count",
-                "output_symbol": "count",
-                "distinct" : false
-              }
-            ],
-            "group_by" : [
-              "(PropertyLookup (Identifier \"node\") \"type\")"
-            ],
-            "remember" : ["node"],
-            "input" : {
-              "name" : "ScanAll",
-              "output_symbol" : "node",
-              "input" : { "name" : "Once" }
-            }
-          })sep");
-}
-
-TEST_F(PrintToJsonTest, AggregateWithDistinct) {
-  memgraph::storage::PropertyId value = dba.NameToProperty("value");
-  memgraph::storage::PropertyId color = dba.NameToProperty("color");
-  memgraph::storage::PropertyId type = dba.NameToProperty("type");
-  auto node_sym = GetSymbol("node");
+TYPED_TEST(PrintToJsonTest, Aggregate) {
+  memgraph::storage::PropertyId value = this->dba.NameToProperty("value");
+  memgraph::storage::PropertyId color = this->dba.NameToProperty("color");
+  memgraph::storage::PropertyId type = this->dba.NameToProperty("type");
+  auto node_sym = this->GetSymbol("node");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
   last_op = std::make_shared<plan::Aggregate>(
       last_op,
       std::vector<Aggregate::Element>{
-          {PROPERTY_LOOKUP("node", value), nullptr, Aggregation::Op::SUM, GetSymbol("sum"), true},
-          {PROPERTY_LOOKUP("node", value), PROPERTY_LOOKUP("node", color), Aggregation::Op::COLLECT_MAP,
-           GetSymbol("map"), true},
-          {nullptr, nullptr, Aggregation::Op::COUNT, GetSymbol("count"), true}},
-      std::vector<Expression *>{PROPERTY_LOOKUP("node", type)}, std::vector<Symbol>{node_sym});
+          {PROPERTY_LOOKUP(this->dba, "node", value), nullptr, Aggregation::Op::SUM, this->GetSymbol("sum")},
+          {PROPERTY_LOOKUP(this->dba, "node", value), PROPERTY_LOOKUP(this->dba, "node", color),
+           Aggregation::Op::COLLECT_MAP, this->GetSymbol("map")},
+          {nullptr, nullptr, Aggregation::Op::COUNT, this->GetSymbol("count")}},
+      std::vector<Expression *>{PROPERTY_LOOKUP(this->dba, "node", type)}, std::vector<Symbol>{node_sym});
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
+          {
+            "name" : "Aggregate",
+            "aggregations" : [
+              {
+                "value" : "(PropertyLookup (Identifier \"node\") \"value\")",
+                "op" : "sum",
+                "output_symbol" : "sum",
+                "distinct" : false
+              },
+              {
+                "value" : "(PropertyLookup (Identifier \"node\") \"value\")",
+                "key" : "(PropertyLookup (Identifier \"node\") \"color\")",
+                "op" : "collect",
+                "output_symbol" : "map",
+                "distinct" : false
+              },
+              {
+                "op": "count",
+                "output_symbol": "count",
+                "distinct" : false
+              }
+            ],
+            "group_by" : [
+              "(PropertyLookup (Identifier \"node\") \"type\")"
+            ],
+            "remember" : ["node"],
+            "input" : {
+              "name" : "ScanAll",
+              "output_symbol" : "node",
+              "input" : { "name" : "Once" }
+            }
+          })sep");
+}
+
+TYPED_TEST(PrintToJsonTest, AggregateWithDistinct) {
+  memgraph::storage::PropertyId value = this->dba.NameToProperty("value");
+  memgraph::storage::PropertyId color = this->dba.NameToProperty("color");
+  memgraph::storage::PropertyId type = this->dba.NameToProperty("type");
+  auto node_sym = this->GetSymbol("node");
+  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
+  last_op = std::make_shared<plan::Aggregate>(
+      last_op,
+      std::vector<Aggregate::Element>{
+          {PROPERTY_LOOKUP(this->dba, "node", value), nullptr, Aggregation::Op::SUM, this->GetSymbol("sum"), true},
+          {PROPERTY_LOOKUP(this->dba, "node", value), PROPERTY_LOOKUP(this->dba, "node", color),
+           Aggregation::Op::COLLECT_MAP, this->GetSymbol("map"), true},
+          {nullptr, nullptr, Aggregation::Op::COUNT, this->GetSymbol("count"), true}},
+      std::vector<Expression *>{PROPERTY_LOOKUP(this->dba, "node", type)}, std::vector<Symbol>{node_sym});
+
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Aggregate",
             "aggregations" : [
@@ -722,11 +748,11 @@ TEST_F(PrintToJsonTest, AggregateWithDistinct) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Skip) {
-  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, GetSymbol("node"));
+TYPED_TEST(PrintToJsonTest, Skip) {
+  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node"));
   last_op = std::make_shared<Skip>(last_op, LITERAL(42));
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Skip",
             "expression" : "42",
@@ -738,11 +764,11 @@ TEST_F(PrintToJsonTest, Skip) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Limit) {
-  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, GetSymbol("node"));
+TYPED_TEST(PrintToJsonTest, Limit) {
+  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node"));
   last_op = std::make_shared<Limit>(last_op, LITERAL(42));
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Limit",
             "expression" : "42",
@@ -754,17 +780,18 @@ TEST_F(PrintToJsonTest, Limit) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, OrderBy) {
-  Symbol node_sym = GetSymbol("node");
-  memgraph::storage::PropertyId value = dba.NameToProperty("value");
-  memgraph::storage::PropertyId color = dba.NameToProperty("color");
+TYPED_TEST(PrintToJsonTest, OrderBy) {
+  Symbol node_sym = this->GetSymbol("node");
+  memgraph::storage::PropertyId value = this->dba.NameToProperty("value");
+  memgraph::storage::PropertyId color = this->dba.NameToProperty("color");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
-  last_op = std::make_shared<OrderBy>(last_op,
-                                      std::vector<SortItem>{{Ordering::ASC, PROPERTY_LOOKUP("node", value)},
-                                                            {Ordering::DESC, PROPERTY_LOOKUP("node", color)}},
-                                      std::vector<Symbol>{node_sym});
+  last_op =
+      std::make_shared<OrderBy>(last_op,
+                                std::vector<SortItem>{{Ordering::ASC, PROPERTY_LOOKUP(this->dba, "node", value)},
+                                                      {Ordering::DESC, PROPERTY_LOOKUP(this->dba, "node", color)}},
+                                std::vector<Symbol>{node_sym});
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "OrderBy",
             "order_by" : [
@@ -786,9 +813,9 @@ TEST_F(PrintToJsonTest, OrderBy) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Merge) {
-  Symbol node_sym = GetSymbol("node");
-  memgraph::storage::LabelId label = dba.NameToLabel("label");
+TYPED_TEST(PrintToJsonTest, Merge) {
+  Symbol node_sym = this->GetSymbol("node");
+  memgraph::storage::LabelId label = this->dba.NameToLabel("label");
 
   std::shared_ptr<LogicalOperator> match = std::make_shared<ScanAllByLabel>(nullptr, node_sym, label);
 
@@ -797,7 +824,7 @@ TEST_F(PrintToJsonTest, Merge) {
 
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<plan::Merge>(nullptr, match, create);
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Merge",
             "input" : { "name" : "Once" },
@@ -819,10 +846,10 @@ TEST_F(PrintToJsonTest, Merge) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Optional) {
-  Symbol node1_sym = GetSymbol("node1");
-  Symbol node2_sym = GetSymbol("node2");
-  Symbol edge_sym = GetSymbol("edge");
+TYPED_TEST(PrintToJsonTest, Optional) {
+  Symbol node1_sym = this->GetSymbol("node1");
+  Symbol node2_sym = this->GetSymbol("node2");
+  Symbol edge_sym = this->GetSymbol("edge");
 
   std::shared_ptr<LogicalOperator> input = std::make_shared<ScanAll>(nullptr, node1_sym);
 
@@ -833,7 +860,7 @@ TEST_F(PrintToJsonTest, Optional) {
   std::shared_ptr<LogicalOperator> last_op =
       std::make_shared<Optional>(input, expand, std::vector<Symbol>{node2_sym, edge_sym});
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Optional",
             "input" : {
@@ -855,11 +882,11 @@ TEST_F(PrintToJsonTest, Optional) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Unwind) {
+TYPED_TEST(PrintToJsonTest, Unwind) {
   std::shared_ptr<LogicalOperator> last_op =
-      std::make_shared<plan::Unwind>(nullptr, LIST(LITERAL(1), LITERAL(2), LITERAL(3)), GetSymbol("x"));
+      std::make_shared<plan::Unwind>(nullptr, LIST(LITERAL(1), LITERAL(2), LITERAL(3)), this->GetSymbol("x"));
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Unwind",
             "output_symbol" : "x",
@@ -868,13 +895,13 @@ TEST_F(PrintToJsonTest, Unwind) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Distinct) {
-  Symbol x = GetSymbol("x");
+TYPED_TEST(PrintToJsonTest, Distinct) {
+  Symbol x = this->GetSymbol("x");
   std::shared_ptr<LogicalOperator> last_op =
       std::make_shared<plan::Unwind>(nullptr, LIST(LITERAL(2), LITERAL(3), LITERAL(2)), x);
   last_op = std::make_shared<Distinct>(last_op, std::vector<Symbol>{x});
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Distinct",
             "value_symbols" : ["x"],
@@ -887,18 +914,18 @@ TEST_F(PrintToJsonTest, Distinct) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Union) {
-  Symbol x = GetSymbol("x");
+TYPED_TEST(PrintToJsonTest, Union) {
+  Symbol x = this->GetSymbol("x");
   std::shared_ptr<LogicalOperator> lhs =
       std::make_shared<plan::Unwind>(nullptr, LIST(LITERAL(2), LITERAL(3), LITERAL(2)), x);
 
-  Symbol node = GetSymbol("x");
+  Symbol node = this->GetSymbol("x");
   std::shared_ptr<LogicalOperator> rhs = std::make_shared<ScanAll>(nullptr, node);
 
-  std::shared_ptr<LogicalOperator> last_op = std::make_shared<Union>(lhs, rhs, std::vector<Symbol>{GetSymbol("x")},
-                                                                     std::vector<Symbol>{x}, std::vector<Symbol>{node});
+  std::shared_ptr<LogicalOperator> last_op = std::make_shared<Union>(
+      lhs, rhs, std::vector<Symbol>{this->GetSymbol("x")}, std::vector<Symbol>{x}, std::vector<Symbol>{node});
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Union",
             "union_symbols" : ["x"],
@@ -918,18 +945,18 @@ TEST_F(PrintToJsonTest, Union) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Cartesian) {
-  Symbol x = GetSymbol("x");
+TYPED_TEST(PrintToJsonTest, Cartesian) {
+  Symbol x = this->GetSymbol("x");
   std::shared_ptr<LogicalOperator> lhs =
       std::make_shared<plan::Unwind>(nullptr, LIST(LITERAL(2), LITERAL(3), LITERAL(2)), x);
 
-  Symbol node = GetSymbol("node");
+  Symbol node = this->GetSymbol("node");
   std::shared_ptr<LogicalOperator> rhs = std::make_shared<ScanAll>(nullptr, node);
 
   std::shared_ptr<LogicalOperator> last_op =
       std::make_shared<Cartesian>(lhs, std::vector<Symbol>{x}, rhs, std::vector<Symbol>{node});
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "name" : "Cartesian",
             "left_symbols" : ["x"],
@@ -948,14 +975,14 @@ TEST_F(PrintToJsonTest, Cartesian) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, CallProcedure) {
+TYPED_TEST(PrintToJsonTest, CallProcedure) {
   memgraph::query::plan::CallProcedure call_op;
   call_op.input_ = std::make_shared<Once>();
   call_op.procedure_name_ = "mg.reload";
   call_op.arguments_ = {LITERAL("example")};
   call_op.result_fields_ = {"name", "signature"};
-  call_op.result_symbols_ = {GetSymbol("name_alias"), GetSymbol("signature_alias")};
-  Check(&call_op, R"sep(
+  call_op.result_symbols_ = {this->GetSymbol("name_alias"), this->GetSymbol("signature_alias")};
+  this->Check(&call_op, R"sep(
           {
             "arguments" : ["\"example\""],
             "input" : { "name" : "Once" },
@@ -966,14 +993,14 @@ TEST_F(PrintToJsonTest, CallProcedure) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Foreach) {
-  Symbol x = GetSymbol("x");
-  std::shared_ptr<LogicalOperator> create =
-      std::make_shared<CreateNode>(nullptr, NodeCreationInfo{GetSymbol("node"), {dba.NameToLabel("Label1")}, {}});
+TYPED_TEST(PrintToJsonTest, Foreach) {
+  Symbol x = this->GetSymbol("x");
+  std::shared_ptr<LogicalOperator> create = std::make_shared<CreateNode>(
+      nullptr, NodeCreationInfo{this->GetSymbol("node"), {this->dba.NameToLabel("Label1")}, {}});
   std::shared_ptr<LogicalOperator> foreach =
       std::make_shared<plan::Foreach>(nullptr, std::move(create), LIST(LITERAL(1)), x);
 
-  Check(foreach.get(), R"sep(
+  this->Check(foreach.get(), R"sep(
           {
            "expression": "(ListLiteral [1])",
            "input": {
@@ -997,15 +1024,16 @@ TEST_F(PrintToJsonTest, Foreach) {
           })sep");
 }
 
-TEST_F(PrintToJsonTest, Exists) {
-  Symbol x = GetSymbol("x");
-  Symbol e = GetSymbol("edge");
-  Symbol n = GetSymbol("node");
-  Symbol output = GetSymbol("output_symbol");
+TYPED_TEST(PrintToJsonTest, Exists) {
+  Symbol x = this->GetSymbol("x");
+  Symbol e = this->GetSymbol("edge");
+  Symbol n = this->GetSymbol("node");
+  Symbol output = this->GetSymbol("output_symbol");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, x);
-  std::shared_ptr<LogicalOperator> expand = std::make_shared<Expand>(
-      nullptr, x, n, e, memgraph::query::EdgeAtom::Direction::BOTH,
-      std::vector<memgraph::storage::EdgeTypeId>{dba.NameToEdgeType("EdgeType1")}, false, memgraph::storage::View::OLD);
+  std::shared_ptr<LogicalOperator> expand =
+      std::make_shared<Expand>(nullptr, x, n, e, memgraph::query::EdgeAtom::Direction::BOTH,
+                               std::vector<memgraph::storage::EdgeTypeId>{this->dba.NameToEdgeType("EdgeType1")}, false,
+                               memgraph::storage::View::OLD);
   std::shared_ptr<LogicalOperator> limit = std::make_shared<Limit>(expand, LITERAL(1));
   std::shared_ptr<LogicalOperator> evaluate_pattern_filter = std::make_shared<EvaluatePatternFilter>(limit, output);
   last_op = std::make_shared<Filter>(
@@ -1013,7 +1041,7 @@ TEST_F(PrintToJsonTest, Exists) {
       EXISTS(PATTERN(NODE("x"), EDGE("edge", memgraph::query::EdgeAtom::Direction::BOTH, {}, false),
                      NODE("node", std::nullopt, false))));
 
-  Check(last_op.get(), R"sep(
+  this->Check(last_op.get(), R"sep(
           {
             "expression": "(Exists expression)",
             "input": {
diff --git a/tests/unit/query_common.hpp b/tests/unit/query_common.hpp
index 444053e01..a01d21f7c 100644
--- a/tests/unit/query_common.hpp
+++ b/tests/unit/query_common.hpp
@@ -509,52 +509,55 @@ auto GetForeach(AstStorage &storage, NamedExpression *named_expr, const std::vec
 ///   AstStorage storage;
 ///   auto query = QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
 ///                      RETURN(NEXPR("new_name"), IDENT("m")));
-#define NODE(...) memgraph::query::test_common::GetNode(storage, __VA_ARGS__)
-#define EDGE(...) memgraph::query::test_common::GetEdge(storage, __VA_ARGS__)
-#define EDGE_VARIABLE(...) memgraph::query::test_common::GetEdgeVariable(storage, __VA_ARGS__)
-#define PATTERN(...) memgraph::query::test_common::GetPattern(storage, {__VA_ARGS__})
-#define NAMED_PATTERN(name, ...) memgraph::query::test_common::GetPattern(storage, name, {__VA_ARGS__})
-#define OPTIONAL_MATCH(...) \
-  memgraph::query::test_common::GetWithPatterns(storage.Create<memgraph::query::Match>(true), {__VA_ARGS__})
+#define NODE(...) memgraph::query::test_common::GetNode(this->storage, __VA_ARGS__)
+#define EDGE(...) memgraph::query::test_common::GetEdge(this->storage, __VA_ARGS__)
+#define EDGE_VARIABLE(...) memgraph::query::test_common::GetEdgeVariable(this->storage, __VA_ARGS__)
+#define PATTERN(...) memgraph::query::test_common::GetPattern(this->storage, {__VA_ARGS__})
+#define NAMED_PATTERN(name, ...) memgraph::query::test_common::GetPattern(this->storage, name, {__VA_ARGS__})
+#define OPTIONAL_MATCH(...)                                                                                  \
+  memgraph::query::test_common::GetWithPatterns(this->storage.template Create<memgraph::query::Match>(true), \
+                                                {__VA_ARGS__})
 #define MATCH(...) \
-  memgraph::query::test_common::GetWithPatterns(storage.Create<memgraph::query::Match>(), {__VA_ARGS__})
-#define WHERE(expr) storage.Create<memgraph::query::Where>((expr))
+  memgraph::query::test_common::GetWithPatterns(this->storage.template Create<memgraph::query::Match>(), {__VA_ARGS__})
+#define WHERE(expr) this->storage.template Create<memgraph::query::Where>((expr))
 #define CREATE(...) \
-  memgraph::query::test_common::GetWithPatterns(storage.Create<memgraph::query::Create>(), {__VA_ARGS__})
-#define IDENT(...) storage.Create<memgraph::query::Identifier>(__VA_ARGS__)
-#define LITERAL(val) storage.Create<memgraph::query::PrimitiveLiteral>((val))
-#define LIST(...) storage.Create<memgraph::query::ListLiteral>(std::vector<memgraph::query::Expression *>{__VA_ARGS__})
-#define MAP(...)                               \
-  storage.Create<memgraph::query::MapLiteral>( \
+  memgraph::query::test_common::GetWithPatterns(this->storage.template Create<memgraph::query::Create>(), {__VA_ARGS__})
+#define IDENT(...) this->storage.template Create<memgraph::query::Identifier>(__VA_ARGS__)
+#define LITERAL(val) this->storage.template Create<memgraph::query::PrimitiveLiteral>((val))
+#define LIST(...) \
+  this->storage.template Create<memgraph::query::ListLiteral>(std::vector<memgraph::query::Expression *>{__VA_ARGS__})
+#define MAP(...)                                              \
+  this->storage.template Create<memgraph::query::MapLiteral>( \
       std::unordered_map<memgraph::query::PropertyIx, memgraph::query::Expression *>{__VA_ARGS__})
-#define MAP_PROJECTION(map_variable, elements)           \
-  storage.Create<memgraph::query::MapProjectionLiteral>( \
-      (memgraph::query::Expression *){map_variable},     \
+#define PROPERTY_PAIR(dba, property_name) std::make_pair(property_name, dba.NameToProperty(property_name))
+#define PROPERTY_LOOKUP(dba, ...) memgraph::query::test_common::GetPropertyLookup(this->storage, dba, __VA_ARGS__)
+#define PARAMETER_LOOKUP(token_position) \
+  this->storage.template Create<memgraph::query::ParameterLookup>((token_position))
+#define NEXPR(name, expr) this->storage.template Create<memgraph::query::NamedExpression>((name), (expr))
+#define MAP_PROJECTION(map_variable, elements)                          \
+  this->storage.template Create<memgraph::query::MapProjectionLiteral>( \
+      (memgraph::query::Expression *){map_variable},                    \
       std::unordered_map<memgraph::query::PropertyIx, memgraph::query::Expression *>{elements})
-#define PROPERTY_PAIR(property_name) std::make_pair(property_name, dba.NameToProperty(property_name))
-#define PROPERTY_LOOKUP(...) memgraph::query::test_common::GetPropertyLookup(storage, dba, __VA_ARGS__)
-#define ALL_PROPERTIES_LOOKUP(expr) memgraph::query::test_common::GetAllPropertiesLookup(storage, expr)
-#define PARAMETER_LOOKUP(token_position) storage.Create<memgraph::query::ParameterLookup>((token_position))
-#define NEXPR(name, expr) storage.Create<memgraph::query::NamedExpression>((name), (expr))
+#define ALL_PROPERTIES_LOOKUP(expr) memgraph::query::test_common::GetAllPropertiesLookup(this->storage, expr)
 // AS is alternative to NEXPR which does not initialize NamedExpression with
 // Expression. It should be used with RETURN or WITH. For example:
 // RETURN(IDENT("n"), AS("n")) vs. RETURN(NEXPR("n", IDENT("n"))).
-#define AS(name) storage.Create<memgraph::query::NamedExpression>((name))
-#define RETURN(...) memgraph::query::test_common::GetReturn(storage, false, __VA_ARGS__)
-#define WITH(...) memgraph::query::test_common::GetWith(storage, false, __VA_ARGS__)
-#define RETURN_DISTINCT(...) memgraph::query::test_common::GetReturn(storage, true, __VA_ARGS__)
-#define WITH_DISTINCT(...) memgraph::query::test_common::GetWith(storage, true, __VA_ARGS__)
-#define UNWIND(...) memgraph::query::test_common::GetUnwind(storage, __VA_ARGS__)
+#define AS(name) this->storage.template Create<memgraph::query::NamedExpression>((name))
+#define RETURN(...) memgraph::query::test_common::GetReturn(this->storage, false, __VA_ARGS__)
+#define WITH(...) memgraph::query::test_common::GetWith(this->storage, false, __VA_ARGS__)
+#define RETURN_DISTINCT(...) memgraph::query::test_common::GetReturn(this->storage, true, __VA_ARGS__)
+#define WITH_DISTINCT(...) memgraph::query::test_common::GetWith(this->storage, true, __VA_ARGS__)
+#define UNWIND(...) memgraph::query::test_common::GetUnwind(this->storage, __VA_ARGS__)
 #define ORDER_BY(...) memgraph::query::test_common::GetOrderBy(__VA_ARGS__)
 #define SKIP(expr) \
   memgraph::query::test_common::Skip { (expr) }
 #define LIMIT(expr) \
   memgraph::query::test_common::Limit { (expr) }
-#define DELETE(...) memgraph::query::test_common::GetDelete(storage, {__VA_ARGS__})
+#define DELETE(...) memgraph::query::test_common::GetDelete(this->storage, {__VA_ARGS__})
 #define DETACH_DELETE(...) memgraph::query::test_common::GetDelete(storage, {__VA_ARGS__}, true)
-#define SET(...) memgraph::query::test_common::GetSet(storage, __VA_ARGS__)
-#define REMOVE(...) memgraph::query::test_common::GetRemove(storage, __VA_ARGS__)
-#define MERGE(...) memgraph::query::test_common::GetMerge(storage, __VA_ARGS__)
+#define SET(...) memgraph::query::test_common::GetSet(this->storage, __VA_ARGS__)
+#define REMOVE(...) memgraph::query::test_common::GetRemove(this->storage, __VA_ARGS__)
+#define MERGE(...) memgraph::query::test_common::GetMerge(this->storage, __VA_ARGS__)
 #define ON_MATCH(...)                                      \
   memgraph::query::test_common::OnMatch {                  \
     std::vector<memgraph::query::Clause *> { __VA_ARGS__ } \
@@ -566,62 +569,72 @@ auto GetForeach(AstStorage &storage, NamedExpression *named_expr, const std::vec
 #define CREATE_INDEX_ON(label, property)                                                            \
   storage.Create<memgraph::query::IndexQuery>(memgraph::query::IndexQuery::Action::CREATE, (label), \
                                               std::vector<memgraph::query::PropertyIx>{(property)})
-#define QUERY(...) memgraph::query::test_common::GetQuery(storage, __VA_ARGS__)
-#define SINGLE_QUERY(...) memgraph::query::test_common::GetSingleQuery(storage.Create<SingleQuery>(), __VA_ARGS__)
-#define UNION(...) memgraph::query::test_common::GetCypherUnion(storage.Create<CypherUnion>(true), __VA_ARGS__)
-#define UNION_ALL(...) memgraph::query::test_common::GetCypherUnion(storage.Create<CypherUnion>(false), __VA_ARGS__)
-#define FOREACH(...) memgraph::query::test_common::GetForeach(storage, __VA_ARGS__)
+#define QUERY(...) memgraph::query::test_common::GetQuery(this->storage, __VA_ARGS__)
+#define SINGLE_QUERY(...) \
+  memgraph::query::test_common::GetSingleQuery(this->storage.template Create<SingleQuery>(), __VA_ARGS__)
+#define UNION(...) \
+  memgraph::query::test_common::GetCypherUnion(this->storage.template Create<CypherUnion>(true), __VA_ARGS__)
+#define UNION_ALL(...) \
+  memgraph::query::test_common::GetCypherUnion(this->storage.template Create<CypherUnion>(false), __VA_ARGS__)
+#define FOREACH(...) memgraph::query::test_common::GetForeach(this->storage, __VA_ARGS__)
 // Various operators
-#define NOT(expr) storage.Create<memgraph::query::NotOperator>((expr))
-#define UPLUS(expr) storage.Create<memgraph::query::UnaryPlusOperator>((expr))
-#define UMINUS(expr) storage.Create<memgraph::query::UnaryMinusOperator>((expr))
-#define IS_NULL(expr) storage.Create<memgraph::query::IsNullOperator>((expr))
-#define ADD(expr1, expr2) storage.Create<memgraph::query::AdditionOperator>((expr1), (expr2))
-#define LESS(expr1, expr2) storage.Create<memgraph::query::LessOperator>((expr1), (expr2))
-#define LESS_EQ(expr1, expr2) storage.Create<memgraph::query::LessEqualOperator>((expr1), (expr2))
-#define GREATER(expr1, expr2) storage.Create<memgraph::query::GreaterOperator>((expr1), (expr2))
-#define GREATER_EQ(expr1, expr2) storage.Create<memgraph::query::GreaterEqualOperator>((expr1), (expr2))
-#define SUM(expr, distinct) \
-  storage.Create<memgraph::query::Aggregation>((expr), nullptr, memgraph::query::Aggregation::Op::SUM, (distinct))
-#define COUNT(expr, distinct) \
-  storage.Create<memgraph::query::Aggregation>((expr), nullptr, memgraph::query::Aggregation::Op::COUNT, (distinct))
+#define NOT(expr) this->storage.template Create<memgraph::query::NotOperator>((expr))
+#define UPLUS(expr) this->storage.template Create<memgraph::query::UnaryPlusOperator>((expr))
+#define UMINUS(expr) this->storage.template Create<memgraph::query::UnaryMinusOperator>((expr))
+#define IS_NULL(expr) this->storage.template Create<memgraph::query::IsNullOperator>((expr))
+#define ADD(expr1, expr2) this->storage.template Create<memgraph::query::AdditionOperator>((expr1), (expr2))
+#define LESS(expr1, expr2) this->storage.template Create<memgraph::query::LessOperator>((expr1), (expr2))
+#define LESS_EQ(expr1, expr2) this->storage.template Create<memgraph::query::LessEqualOperator>((expr1), (expr2))
+#define GREATER(expr1, expr2) this->storage.template Create<memgraph::query::GreaterOperator>((expr1), (expr2))
+#define GREATER_EQ(expr1, expr2) this->storage.template Create<memgraph::query::GreaterEqualOperator>((expr1), (expr2))
+#define SUM(expr, distinct)                                                                                           \
+  this->storage.template Create<memgraph::query::Aggregation>((expr), nullptr, memgraph::query::Aggregation::Op::SUM, \
+                                                              (distinct))
+#define COUNT(expr, distinct)                                                  \
+  this->storage.template Create<memgraph::query::Aggregation>((expr), nullptr, \
+                                                              memgraph::query::Aggregation::Op::COUNT, (distinct))
 #define AVG(expr, distinct) \
   storage.Create<memgraph::query::Aggregation>((expr), nullptr, memgraph::query::Aggregation::Op::AVG, (distinct))
 #define COLLECT_LIST(expr, distinct)                                                                            \
   storage.Create<memgraph::query::Aggregation>((expr), nullptr, memgraph::query::Aggregation::Op::COLLECT_LIST, \
                                                (distinct))
-#define EQ(expr1, expr2) storage.Create<memgraph::query::EqualOperator>((expr1), (expr2))
-#define NEQ(expr1, expr2) storage.Create<memgraph::query::NotEqualOperator>((expr1), (expr2))
-#define AND(expr1, expr2) storage.Create<memgraph::query::AndOperator>((expr1), (expr2))
-#define OR(expr1, expr2) storage.Create<memgraph::query::OrOperator>((expr1), (expr2))
-#define IN_LIST(expr1, expr2) storage.Create<memgraph::query::InListOperator>((expr1), (expr2))
+#define EQ(expr1, expr2) this->storage.template Create<memgraph::query::EqualOperator>((expr1), (expr2))
+#define NEQ(expr1, expr2) this->storage.template Create<memgraph::query::NotEqualOperator>((expr1), (expr2))
+#define AND(expr1, expr2) this->storage.template Create<memgraph::query::AndOperator>((expr1), (expr2))
+#define OR(expr1, expr2) this->storage.template Create<memgraph::query::OrOperator>((expr1), (expr2))
+#define IN_LIST(expr1, expr2) this->storage.template Create<memgraph::query::InListOperator>((expr1), (expr2))
 #define IF(cond, then, else) storage.Create<memgraph::query::IfOperator>((cond), (then), (else))
 // Function call
-#define FN(function_name, ...)                                                           \
-  storage.Create<memgraph::query::Function>(memgraph::utils::ToUpperCase(function_name), \
-                                            std::vector<memgraph::query::Expression *>{__VA_ARGS__})
+#define FN(function_name, ...)                                                                          \
+  this->storage.template Create<memgraph::query::Function>(memgraph::utils::ToUpperCase(function_name), \
+                                                           std::vector<memgraph::query::Expression *>{__VA_ARGS__})
 // List slicing
 #define SLICE(list, lower_bound, upper_bound) \
-  storage.Create<memgraph::query::ListSlicingOperator>(list, lower_bound, upper_bound)
+  this->storage.template Create<memgraph::query::ListSlicingOperator>(list, lower_bound, upper_bound)
 // all(variable IN list WHERE predicate)
-#define ALL(variable, list, where) \
-  storage.Create<memgraph::query::All>(storage.Create<memgraph::query::Identifier>(variable), list, where)
-#define SINGLE(variable, list, where) \
-  storage.Create<memgraph::query::Single>(storage.Create<memgraph::query::Identifier>(variable), list, where)
+#define ALL(variable, list, where)                     \
+  this->storage.template Create<memgraph::query::All>( \
+      this->storage.template Create<memgraph::query::Identifier>(variable), list, where)
+#define SINGLE(variable, list, where)                     \
+  this->storage.template Create<memgraph::query::Single>( \
+      this->storage.template Create<memgraph::query::Identifier>(variable), list, where)
 #define ANY(variable, list, where) \
   storage.Create<memgraph::query::Any>(storage.Create<memgraph::query::Identifier>(variable), list, where)
 #define NONE(variable, list, where) \
   storage.Create<memgraph::query::None>(storage.Create<memgraph::query::Identifier>(variable), list, where)
-#define REDUCE(accumulator, initializer, variable, list, expr)                                                   \
-  storage.Create<memgraph::query::Reduce>(storage.Create<memgraph::query::Identifier>(accumulator), initializer, \
-                                          storage.Create<memgraph::query::Identifier>(variable), list, expr)
-#define COALESCE(...) storage.Create<memgraph::query::Coalesce>(std::vector<memgraph::query::Expression *>{__VA_ARGS__})
-#define EXTRACT(variable, list, expr) \
-  storage.Create<memgraph::query::Extract>(storage.Create<memgraph::query::Identifier>(variable), list, expr)
-#define EXISTS(pattern) storage.Create<memgraph::query::Exists>(pattern)
+#define REDUCE(accumulator, initializer, variable, list, expr)                              \
+  this->storage.template Create<memgraph::query::Reduce>(                                   \
+      this->storage.template Create<memgraph::query::Identifier>(accumulator), initializer, \
+      this->storage.template Create<memgraph::query::Identifier>(variable), list, expr)
+#define COALESCE(...) \
+  this->storage.template Create<memgraph::query::Coalesce>(std::vector<memgraph::query::Expression *>{__VA_ARGS__})
+#define EXTRACT(variable, list, expr)                      \
+  this->storage.template Create<memgraph::query::Extract>( \
+      this->storage.template Create<memgraph::query::Identifier>(variable), list, expr)
+#define EXISTS(pattern) this->storage.template Create<memgraph::query::Exists>(pattern)
 #define AUTH_QUERY(action, user, role, user_or_role, password, privileges, labels, edgeTypes)                  \
   storage.Create<memgraph::query::AuthQuery>((action), (user), (role), (user_or_role), password, (privileges), \
                                              (labels), (edgeTypes))
 #define DROP_USER(usernames) storage.Create<memgraph::query::DropUser>((usernames))
 #define CALL_PROCEDURE(...) memgraph::query::test_common::GetCallProcedure(storage, __VA_ARGS__)
-#define CALL_SUBQUERY(...) memgraph::query::test_common::GetCallSubquery(storage, __VA_ARGS__)
+#define CALL_SUBQUERY(...) memgraph::query::test_common::GetCallSubquery(this->storage, __VA_ARGS__)
diff --git a/tests/unit/query_cost_estimator.cpp b/tests/unit/query_cost_estimator.cpp
index d89031b82..f089e6cba 100644
--- a/tests/unit/query_cost_estimator.cpp
+++ b/tests/unit/query_cost_estimator.cpp
@@ -17,6 +17,7 @@
 #include "query/frontend/semantic/symbol_table.hpp"
 #include "query/plan/cost_estimator.hpp"
 #include "query/plan/operator.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/storage.hpp"
 
 using namespace memgraph::query;
@@ -33,11 +34,11 @@ using MiscParam = CostEstimator<memgraph::query::DbAccessor>::MiscParam;
  * estimation testing. */
 class QueryCostEstimator : public ::testing::Test {
  protected:
-  memgraph::storage::Storage db;
-  std::optional<memgraph::storage::Storage::Accessor> storage_dba;
+  std::unique_ptr<memgraph::storage::Storage> db = std::make_unique<memgraph::storage::InMemoryStorage>();
+  std::optional<std::unique_ptr<memgraph::storage::Storage::Accessor>> storage_dba;
   std::optional<memgraph::query::DbAccessor> dba;
-  memgraph::storage::LabelId label = db.NameToLabel("label");
-  memgraph::storage::PropertyId property = db.NameToProperty("property");
+  memgraph::storage::LabelId label = db->NameToLabel("label");
+  memgraph::storage::PropertyId property = db->NameToProperty("property");
 
   // we incrementally build the logical operator plan
   // start it off with Once
@@ -49,10 +50,10 @@ class QueryCostEstimator : public ::testing::Test {
   int symbol_count = 0;
 
   void SetUp() {
-    ASSERT_FALSE(db.CreateIndex(label).HasError());
-    ASSERT_FALSE(db.CreateIndex(label, property).HasError());
-    storage_dba.emplace(db.Access());
-    dba.emplace(&*storage_dba);
+    ASSERT_FALSE(db->CreateIndex(label).HasError());
+    ASSERT_FALSE(db->CreateIndex(label, property).HasError());
+    storage_dba.emplace(db->Access());
+    dba.emplace(storage_dba->get());
   }
 
   Symbol NextSymbol() { return symbol_table_.CreateSymbol("Symbol" + std::to_string(symbol_count++), true); }
diff --git a/tests/unit/query_dump.cpp b/tests/unit/query_dump.cpp
index c3781834c..6c992c38f 100644
--- a/tests/unit/query_dump.cpp
+++ b/tests/unit/query_dump.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -17,10 +17,14 @@
 #include <vector>
 
 #include "communication/result_stream_faker.hpp"
+#include "disk_test_utils.hpp"
 #include "query/config.hpp"
 #include "query/dump.hpp"
 #include "query/interpreter.hpp"
 #include "query/typed_value.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/edge_accessor.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/storage.hpp"
 #include "storage/v2/temporal.hpp"
 #include "utils/temporal.hpp"
@@ -130,18 +134,18 @@ DatabaseState GetState(memgraph::storage::Storage *db) {
   std::map<memgraph::storage::Gid, int64_t> gid_mapping;
   std::set<DatabaseState::Vertex> vertices;
   auto dba = db->Access();
-  for (const auto &vertex : dba.Vertices(memgraph::storage::View::NEW)) {
+  for (const auto &vertex : dba->Vertices(memgraph::storage::View::NEW)) {
     std::set<std::string> labels;
     auto maybe_labels = vertex.Labels(memgraph::storage::View::NEW);
     MG_ASSERT(maybe_labels.HasValue());
     for (const auto &label : *maybe_labels) {
-      labels.insert(dba.LabelToName(label));
+      labels.insert(dba->LabelToName(label));
     }
     std::map<std::string, memgraph::storage::PropertyValue> props;
     auto maybe_properties = vertex.Properties(memgraph::storage::View::NEW);
     MG_ASSERT(maybe_properties.HasValue());
     for (const auto &kv : *maybe_properties) {
-      props.emplace(dba.PropertyToName(kv.first), kv.second);
+      props.emplace(dba->PropertyToName(kv.first), kv.second);
     }
     MG_ASSERT(props.count(kPropertyId) == 1);
     const auto id = props[kPropertyId].ValueInt();
@@ -151,16 +155,16 @@ DatabaseState GetState(memgraph::storage::Storage *db) {
 
   // Capture all edges
   std::set<DatabaseState::Edge> edges;
-  for (const auto &vertex : dba.Vertices(memgraph::storage::View::NEW)) {
+  for (const auto &vertex : dba->Vertices(memgraph::storage::View::NEW)) {
     auto maybe_edges = vertex.OutEdges(memgraph::storage::View::NEW);
     MG_ASSERT(maybe_edges.HasValue());
     for (const auto &edge : *maybe_edges) {
-      const auto &edge_type_name = dba.EdgeTypeToName(edge.EdgeType());
+      const auto &edge_type_name = dba->EdgeTypeToName(edge.EdgeType());
       std::map<std::string, memgraph::storage::PropertyValue> props;
       auto maybe_properties = edge.Properties(memgraph::storage::View::NEW);
       MG_ASSERT(maybe_properties.HasValue());
       for (const auto &kv : *maybe_properties) {
-        props.emplace(dba.PropertyToName(kv.first), kv.second);
+        props.emplace(dba->PropertyToName(kv.first), kv.second);
       }
       const auto from = gid_mapping[edge.FromVertex().Gid()];
       const auto to = gid_mapping[edge.ToVertex().Gid()];
@@ -172,12 +176,12 @@ DatabaseState GetState(memgraph::storage::Storage *db) {
   std::set<DatabaseState::LabelItem> label_indices;
   std::set<DatabaseState::LabelPropertyItem> label_property_indices;
   {
-    auto info = dba.ListAllIndices();
+    auto info = dba->ListAllIndices();
     for (const auto &item : info.label) {
-      label_indices.insert({dba.LabelToName(item)});
+      label_indices.insert({dba->LabelToName(item)});
     }
     for (const auto &item : info.label_property) {
-      label_property_indices.insert({dba.LabelToName(item.first), dba.PropertyToName(item.second)});
+      label_property_indices.insert({dba->LabelToName(item.first), dba->PropertyToName(item.second)});
     }
   }
 
@@ -185,27 +189,25 @@ DatabaseState GetState(memgraph::storage::Storage *db) {
   std::set<DatabaseState::LabelPropertyItem> existence_constraints;
   std::set<DatabaseState::LabelPropertiesItem> unique_constraints;
   {
-    auto info = dba.ListAllConstraints();
+    auto info = dba->ListAllConstraints();
     for (const auto &item : info.existence) {
-      existence_constraints.insert({dba.LabelToName(item.first), dba.PropertyToName(item.second)});
+      existence_constraints.insert({dba->LabelToName(item.first), dba->PropertyToName(item.second)});
     }
     for (const auto &item : info.unique) {
       std::set<std::string> properties;
       for (const auto &property : item.second) {
-        properties.insert(dba.PropertyToName(property));
+        properties.insert(dba->PropertyToName(property));
       }
-      unique_constraints.insert({dba.LabelToName(item.first), std::move(properties)});
+      unique_constraints.insert({dba->LabelToName(item.first), std::move(properties)});
     }
   }
 
   return {vertices, edges, label_indices, label_property_indices, existence_constraints, unique_constraints};
 }
 
-auto Execute(memgraph::storage::Storage *db, const std::string &query) {
-  auto data_directory = std::filesystem::temp_directory_path() / "MG_tests_unit_query_dump";
-  memgraph::query::InterpreterContext context(db, memgraph::query::InterpreterConfig{}, data_directory);
-  memgraph::query::Interpreter interpreter(&context);
-  ResultStreamFaker stream(db);
+auto Execute(memgraph::query::InterpreterContext *context, const std::string &query) {
+  memgraph::query::Interpreter interpreter(context);
+  ResultStreamFaker stream(context->db.get());
 
   auto [header, _, qid] = interpreter.Prepare(query, {}, nullptr);
   stream.Header(header);
@@ -243,14 +245,16 @@ memgraph::storage::EdgeAccessor CreateEdge(memgraph::storage::Storage::Accessor
   MG_ASSERT(dba);
   auto edge = dba->CreateEdge(from, to, dba->NameToEdgeType(edge_type_name));
   MG_ASSERT(edge.HasValue());
+  auto edgeAcc = std::move(edge.GetValue());
   for (const auto &kv : props) {
-    MG_ASSERT(edge->SetProperty(dba->NameToProperty(kv.first), kv.second).HasValue());
+    MG_ASSERT(edgeAcc.SetProperty(dba->NameToProperty(kv.first), kv.second).HasValue());
   }
   if (add_property_id) {
-    MG_ASSERT(edge->SetProperty(dba->NameToProperty(kPropertyId), memgraph::storage::PropertyValue(edge->Gid().AsInt()))
-                  .HasValue());
+    MG_ASSERT(
+        edgeAcc.SetProperty(dba->NameToProperty(kPropertyId), memgraph::storage::PropertyValue(edgeAcc.Gid().AsInt()))
+            .HasValue());
   }
-  return *edge;
+  return edgeAcc;
 }
 
 template <class... TArgs>
@@ -266,34 +270,51 @@ void VerifyQueries(const std::vector<std::vector<memgraph::communication::bolt::
   ASSERT_EQ(got, expected);
 }
 
+template <typename StorageType>
+class DumpTest : public ::testing::Test {
+ public:
+  const std::string testSuite = "query_dump";
+  std::filesystem::path data_directory{std::filesystem::temp_directory_path() / "MG_tests_unit_query_dump_class"};
+  memgraph::query::InterpreterContext context{
+      std::make_unique<StorageType>(disk_test_utils::GenerateOnDiskConfig(testSuite)),
+      memgraph::query::InterpreterConfig{}, data_directory};
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+};
+
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(DumpTest, StorageTypes);
+
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, EmptyGraph) {
-  memgraph::storage::Storage db;
-  ResultStreamFaker stream(&db);
+TYPED_TEST(DumpTest, EmptyGraph) {
+  ResultStreamFaker stream(this->context.db.get());
   memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
   {
-    auto acc = db.Access();
-    memgraph::query::DbAccessor dba(&acc);
+    auto acc = this->context.db->Access();
+    memgraph::query::DbAccessor dba(acc.get());
     memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
   }
   ASSERT_EQ(stream.GetResults().size(), 0);
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, SingleVertex) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, SingleVertex) {
   {
-    auto dba = db.Access();
-    CreateVertex(&dba, {}, {}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->context.db->Access();
+    CreateVertex(dba.get(), {}, {}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     VerifyQueries(stream.GetResults(), kCreateInternalIndex, "CREATE (:__mg_vertex__ {__mg_id__: 0});",
@@ -302,20 +323,19 @@ TEST(DumpTest, SingleVertex) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, VertexWithSingleLabel) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, VertexWithSingleLabel) {
   {
-    auto dba = db.Access();
-    CreateVertex(&dba, {"Label1"}, {}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->context.db->Access();
+    CreateVertex(dba.get(), {"Label1"}, {}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     VerifyQueries(stream.GetResults(), kCreateInternalIndex, "CREATE (:__mg_vertex__:`Label1` {__mg_id__: 0});",
@@ -324,20 +344,19 @@ TEST(DumpTest, VertexWithSingleLabel) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, VertexWithMultipleLabels) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, VertexWithMultipleLabels) {
   {
-    auto dba = db.Access();
-    CreateVertex(&dba, {"Label1", "Label 2"}, {}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->context.db->Access();
+    CreateVertex(dba.get(), {"Label1", "Label 2"}, {}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     VerifyQueries(stream.GetResults(), kCreateInternalIndex,
@@ -347,20 +366,19 @@ TEST(DumpTest, VertexWithMultipleLabels) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, VertexWithSingleProperty) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, VertexWithSingleProperty) {
   {
-    auto dba = db.Access();
-    CreateVertex(&dba, {}, {{"prop", memgraph::storage::PropertyValue(42)}}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->context.db->Access();
+    CreateVertex(dba.get(), {}, {{"prop", memgraph::storage::PropertyValue(42)}}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     VerifyQueries(stream.GetResults(), kCreateInternalIndex, "CREATE (:__mg_vertex__ {__mg_id__: 0, `prop`: 42});",
@@ -369,22 +387,21 @@ TEST(DumpTest, VertexWithSingleProperty) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, MultipleVertices) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, MultipleVertices) {
   {
-    auto dba = db.Access();
-    CreateVertex(&dba, {}, {}, false);
-    CreateVertex(&dba, {}, {}, false);
-    CreateVertex(&dba, {}, {}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->context.db->Access();
+    CreateVertex(dba.get(), {}, {}, false);
+    CreateVertex(dba.get(), {}, {}, false);
+    CreateVertex(dba.get(), {}, {}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     VerifyQueries(stream.GetResults(), kCreateInternalIndex, "CREATE (:__mg_vertex__ {__mg_id__: 0});",
@@ -393,10 +410,9 @@ TEST(DumpTest, MultipleVertices) {
   }
 }
 
-TEST(DumpTest, PropertyValue) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, PropertyValue) {
   {
-    auto dba = db.Access();
+    auto dba = this->context.db->Access();
     auto null_value = memgraph::storage::PropertyValue();
     auto int_value = memgraph::storage::PropertyValue(13);
     auto bool_value = memgraph::storage::PropertyValue(true);
@@ -414,16 +430,16 @@ TEST(DumpTest, PropertyValue) {
     auto dur = memgraph::storage::PropertyValue(memgraph::storage::TemporalData(
         memgraph::storage::TemporalType::Duration, memgraph::utils::Duration({3, 4, 5, 6, 10, 11}).microseconds));
     auto list_value = memgraph::storage::PropertyValue({map_value, null_value, double_value, dt, lt, ldt, dur});
-    CreateVertex(&dba, {}, {{"p1", list_value}, {"p2", str_value}}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    CreateVertex(dba.get(), {}, {{"p1", list_value}, {"p2", str_value}}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     VerifyQueries(stream.GetResults(), kCreateInternalIndex,
@@ -436,22 +452,21 @@ TEST(DumpTest, PropertyValue) {
   }
 }
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, SingleEdge) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, SingleEdge) {
   {
-    auto dba = db.Access();
-    auto u = CreateVertex(&dba, {}, {}, false);
-    auto v = CreateVertex(&dba, {}, {}, false);
-    CreateEdge(&dba, &u, &v, "EdgeType", {}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->context.db->Access();
+    auto u = CreateVertex(dba.get(), {}, {}, false);
+    auto v = CreateVertex(dba.get(), {}, {}, false);
+    CreateEdge(dba.get(), &u, &v, "EdgeType", {}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     VerifyQueries(stream.GetResults(), kCreateInternalIndex, "CREATE (:__mg_vertex__ {__mg_id__: 0});",
@@ -463,25 +478,24 @@ TEST(DumpTest, SingleEdge) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, MultipleEdges) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, MultipleEdges) {
   {
-    auto dba = db.Access();
-    auto u = CreateVertex(&dba, {}, {}, false);
-    auto v = CreateVertex(&dba, {}, {}, false);
-    auto w = CreateVertex(&dba, {}, {}, false);
-    CreateEdge(&dba, &u, &v, "EdgeType", {}, false);
-    CreateEdge(&dba, &v, &u, "EdgeType 2", {}, false);
-    CreateEdge(&dba, &v, &w, "EdgeType `!\"", {}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->context.db->Access();
+    auto u = CreateVertex(dba.get(), {}, {}, false);
+    auto v = CreateVertex(dba.get(), {}, {}, false);
+    auto w = CreateVertex(dba.get(), {}, {}, false);
+    CreateEdge(dba.get(), &u, &v, "EdgeType", {}, false);
+    CreateEdge(dba.get(), &v, &u, "EdgeType 2", {}, false);
+    CreateEdge(dba.get(), &v, &w, "EdgeType `!\"", {}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     VerifyQueries(stream.GetResults(), kCreateInternalIndex, "CREATE (:__mg_vertex__ {__mg_id__: 0});",
@@ -497,22 +511,21 @@ TEST(DumpTest, MultipleEdges) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, EdgeWithProperties) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, EdgeWithProperties) {
   {
-    auto dba = db.Access();
-    auto u = CreateVertex(&dba, {}, {}, false);
-    auto v = CreateVertex(&dba, {}, {}, false);
-    CreateEdge(&dba, &u, &v, "EdgeType", {{"prop", memgraph::storage::PropertyValue(13)}}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->context.db->Access();
+    auto u = CreateVertex(dba.get(), {}, {}, false);
+    auto v = CreateVertex(dba.get(), {}, {}, false);
+    CreateEdge(dba.get(), &u, &v, "EdgeType", {{"prop", memgraph::storage::PropertyValue(13)}}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     VerifyQueries(stream.GetResults(), kCreateInternalIndex, "CREATE (:__mg_vertex__ {__mg_id__: 0});",
@@ -524,22 +537,25 @@ TEST(DumpTest, EdgeWithProperties) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, IndicesKeys) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, IndicesKeys) {
   {
-    auto dba = db.Access();
-    CreateVertex(&dba, {"Label1", "Label 2"}, {{"p", memgraph::storage::PropertyValue(1)}}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->context.db->Access();
+    CreateVertex(dba.get(), {"Label1", "Label 2"}, {{"p", memgraph::storage::PropertyValue(1)}}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
-  ASSERT_FALSE(db.CreateIndex(db.NameToLabel("Label1"), db.NameToProperty("prop")).HasError());
-  ASSERT_FALSE(db.CreateIndex(db.NameToLabel("Label 2"), db.NameToProperty("prop `")).HasError());
+  ASSERT_FALSE(
+      this->context.db->CreateIndex(this->context.db->NameToLabel("Label1"), this->context.db->NameToProperty("prop"))
+          .HasError());
+  ASSERT_FALSE(this->context.db
+                   ->CreateIndex(this->context.db->NameToLabel("Label 2"), this->context.db->NameToProperty("prop `"))
+                   .HasError());
 
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     VerifyQueries(stream.GetResults(), "CREATE INDEX ON :`Label1`(`prop`);", "CREATE INDEX ON :`Label 2`(`prop ```);",
@@ -549,24 +565,24 @@ TEST(DumpTest, IndicesKeys) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, ExistenceConstraints) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, ExistenceConstraints) {
   {
-    auto dba = db.Access();
-    CreateVertex(&dba, {"L`abel 1"}, {{"prop", memgraph::storage::PropertyValue(1)}}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->context.db->Access();
+    CreateVertex(dba.get(), {"L`abel 1"}, {{"prop", memgraph::storage::PropertyValue(1)}}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
   {
-    auto res = db.CreateExistenceConstraint(db.NameToLabel("L`abel 1"), db.NameToProperty("prop"));
+    auto res = this->context.db->CreateExistenceConstraint(this->context.db->NameToLabel("L`abel 1"),
+                                                           this->context.db->NameToProperty("prop"), {});
     ASSERT_FALSE(res.HasError());
   }
 
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     VerifyQueries(stream.GetResults(), "CREATE CONSTRAINT ON (u:`L``abel 1`) ASSERT EXISTS (u.`prop`);",
@@ -575,31 +591,31 @@ TEST(DumpTest, ExistenceConstraints) {
   }
 }
 
-TEST(DumpTest, UniqueConstraints) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, UniqueConstraints) {
   {
-    auto dba = db.Access();
-    CreateVertex(&dba, {"Label"},
+    auto dba = this->context.db->Access();
+    CreateVertex(dba.get(), {"Label"},
                  {{"prop", memgraph::storage::PropertyValue(1)}, {"prop2", memgraph::storage::PropertyValue(2)}},
                  false);
-    CreateVertex(&dba, {"Label"},
+    CreateVertex(dba.get(), {"Label"},
                  {{"prop", memgraph::storage::PropertyValue(2)}, {"prop2", memgraph::storage::PropertyValue(2)}},
                  false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    ASSERT_FALSE(dba->Commit().HasError());
   }
   {
-    auto res =
-        db.CreateUniqueConstraint(db.NameToLabel("Label"), {db.NameToProperty("prop"), db.NameToProperty("prop2")});
+    auto res = this->context.db->CreateUniqueConstraint(
+        this->context.db->NameToLabel("Label"),
+        {this->context.db->NameToProperty("prop"), this->context.db->NameToProperty("prop2")}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), memgraph::storage::UniqueConstraints::CreationStatus::SUCCESS);
   }
 
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     VerifyQueries(stream.GetResults(),
@@ -615,25 +631,29 @@ TEST(DumpTest, UniqueConstraints) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, CheckStateVertexWithMultipleProperties) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, CheckStateVertexWithMultipleProperties) {
   {
-    auto dba = db.Access();
+    auto dba = this->context.db->Access();
     std::map<std::string, memgraph::storage::PropertyValue> prop1 = {
         {"nested1", memgraph::storage::PropertyValue(1337)}, {"nested2", memgraph::storage::PropertyValue(3.14)}};
+
     CreateVertex(
-        &dba, {"Label1", "Label2"},
+        dba.get(), {"Label1", "Label2"},
         {{"prop1", memgraph::storage::PropertyValue(prop1)}, {"prop2", memgraph::storage::PropertyValue("$'\t'")}});
-    ASSERT_FALSE(dba.Commit().HasError());
+
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
-  memgraph::storage::Storage db_dump;
+  auto data_directory = std::filesystem::temp_directory_path() / "MG_tests_unit_query_dump";
+  memgraph::query::InterpreterContext interpreter_context(std::make_unique<TypeParam>(),
+                                                          memgraph::query::InterpreterConfig{}, data_directory);
+
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     const auto &results = stream.GetResults();
@@ -641,104 +661,111 @@ TEST(DumpTest, CheckStateVertexWithMultipleProperties) {
     for (const auto &item : results) {
       ASSERT_EQ(item.size(), 1);
       ASSERT_TRUE(item[0].IsString());
-      Execute(&db_dump, item[0].ValueString());
+      Execute(&interpreter_context, item[0].ValueString());
     }
   }
-  ASSERT_EQ(GetState(&db), GetState(&db_dump));
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, CheckStateSimpleGraph) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, CheckStateSimpleGraph) {
   {
-    auto dba = db.Access();
-    auto u = CreateVertex(&dba, {"Person"}, {{"name", memgraph::storage::PropertyValue("Ivan")}});
-    auto v = CreateVertex(&dba, {"Person"}, {{"name", memgraph::storage::PropertyValue("Josko")}});
+    auto dba = this->context.db->Access();
+    auto u = CreateVertex(dba.get(), {"Person"}, {{"name", memgraph::storage::PropertyValue("Ivan")}});
+    auto v = CreateVertex(dba.get(), {"Person"}, {{"name", memgraph::storage::PropertyValue("Josko")}});
     auto w = CreateVertex(
-        &dba, {"Person"},
+        dba.get(), {"Person"},
         {{"name", memgraph::storage::PropertyValue("Bosko")}, {"id", memgraph::storage::PropertyValue(0)}});
     auto z =
-        CreateVertex(&dba, {"Person"},
+        CreateVertex(dba.get(), {"Person"},
                      {{"name", memgraph::storage::PropertyValue("Buha")}, {"id", memgraph::storage::PropertyValue(1)}});
-    CreateEdge(&dba, &u, &v, "Knows", {});
-    CreateEdge(&dba, &v, &w, "Knows", {{"how_long", memgraph::storage::PropertyValue(5)}});
-    CreateEdge(&dba, &w, &u, "Knows", {{"how", memgraph::storage::PropertyValue("distant past")}});
-    CreateEdge(&dba, &v, &u, "Knows", {});
-    CreateEdge(&dba, &v, &u, "Likes", {});
-    CreateEdge(&dba, &z, &u, "Knows", {});
-    CreateEdge(&dba, &w, &z, "Knows", {{"how", memgraph::storage::PropertyValue("school")}});
-    CreateEdge(&dba, &w, &z, "Likes", {{"how", memgraph::storage::PropertyValue("very much")}});
-    CreateEdge(&dba, &w, &z, "Date",
+    CreateEdge(dba.get(), &u, &v, "Knows", {});
+    CreateEdge(dba.get(), &v, &w, "Knows", {{"how_long", memgraph::storage::PropertyValue(5)}});
+    CreateEdge(dba.get(), &w, &u, "Knows", {{"how", memgraph::storage::PropertyValue("distant past")}});
+    CreateEdge(dba.get(), &v, &u, "Knows", {});
+    CreateEdge(dba.get(), &v, &u, "Likes", {});
+    CreateEdge(dba.get(), &z, &u, "Knows", {});
+    CreateEdge(dba.get(), &w, &z, "Knows", {{"how", memgraph::storage::PropertyValue("school")}});
+    CreateEdge(dba.get(), &w, &z, "Likes", {{"how", memgraph::storage::PropertyValue("1234567890")}});
+    CreateEdge(dba.get(), &w, &z, "Date",
                {{"time", memgraph::storage::PropertyValue(memgraph::storage::TemporalData(
                              memgraph::storage::TemporalType::Date,
                              memgraph::utils::Date({1994, 12, 7}).MicrosecondsSinceEpoch()))}});
-    CreateEdge(&dba, &w, &z, "LocalTime",
+    CreateEdge(dba.get(), &w, &z, "LocalTime",
                {{"time", memgraph::storage::PropertyValue(memgraph::storage::TemporalData(
                              memgraph::storage::TemporalType::LocalTime,
                              memgraph::utils::LocalTime({14, 10, 44, 99, 99}).MicrosecondsSinceEpoch()))}});
     CreateEdge(
-        &dba, &w, &z, "LocalDateTime",
+        dba.get(), &w, &z, "LocalDateTime",
         {{"time", memgraph::storage::PropertyValue(memgraph::storage::TemporalData(
                       memgraph::storage::TemporalType::LocalDateTime,
                       memgraph::utils::LocalDateTime({1994, 12, 7}, {14, 10, 44, 99, 99}).MicrosecondsSinceEpoch()))}});
-    CreateEdge(&dba, &w, &z, "Duration",
+    CreateEdge(dba.get(), &w, &z, "Duration",
                {{"time", memgraph::storage::PropertyValue(memgraph::storage::TemporalData(
                              memgraph::storage::TemporalType::Duration,
                              memgraph::utils::Duration({3, 4, 5, 6, 10, 11}).microseconds))}});
-    CreateEdge(&dba, &w, &z, "NegativeDuration",
+    CreateEdge(dba.get(), &w, &z, "NegativeDuration",
                {{"time", memgraph::storage::PropertyValue(memgraph::storage::TemporalData(
                              memgraph::storage::TemporalType::Duration,
                              memgraph::utils::Duration({-3, -4, -5, -6, -10, -11}).microseconds))}});
-    ASSERT_FALSE(dba.Commit().HasError());
+    ASSERT_FALSE(dba->Commit().HasError());
   }
   {
-    auto ret = db.CreateExistenceConstraint(db.NameToLabel("Person"), db.NameToProperty("name"));
+    auto ret = this->context.db->CreateExistenceConstraint(this->context.db->NameToLabel("Person"),
+                                                           this->context.db->NameToProperty("name"), {});
     ASSERT_FALSE(ret.HasError());
   }
   {
-    auto ret = db.CreateUniqueConstraint(db.NameToLabel("Person"), {db.NameToProperty("name")});
+    auto ret = this->context.db->CreateUniqueConstraint(this->context.db->NameToLabel("Person"),
+                                                        {this->context.db->NameToProperty("name")}, {});
     ASSERT_TRUE(ret.HasValue());
     ASSERT_EQ(ret.GetValue(), memgraph::storage::UniqueConstraints::CreationStatus::SUCCESS);
   }
-  ASSERT_FALSE(db.CreateIndex(db.NameToLabel("Person"), db.NameToProperty("id")).HasError());
-  ASSERT_FALSE(db.CreateIndex(db.NameToLabel("Person"), db.NameToProperty("unexisting_property")).HasError());
+  ASSERT_FALSE(
+      this->context.db->CreateIndex(this->context.db->NameToLabel("Person"), this->context.db->NameToProperty("id"))
+          .HasError());
+  ASSERT_FALSE(this->context.db
+                   ->CreateIndex(this->context.db->NameToLabel("Person"),
+                                 this->context.db->NameToProperty("unexisting_property"))
+                   .HasError());
 
-  const auto &db_initial_state = GetState(&db);
-  memgraph::storage::Storage db_dump;
+  const auto &db_initial_state = GetState(this->context.db.get());
+  auto data_directory = std::filesystem::temp_directory_path() / "MG_tests_unit_query_dump";
+  memgraph::query::InterpreterContext interpreter_context(std::make_unique<TypeParam>(),
+                                                          memgraph::query::InterpreterConfig{}, data_directory);
   {
-    ResultStreamFaker stream(&db);
+    ResultStreamFaker stream(this->context.db.get());
     memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
     {
-      auto acc = db.Access();
-      memgraph::query::DbAccessor dba(&acc);
+      auto acc = this->context.db->Access();
+      memgraph::query::DbAccessor dba(acc.get());
       memgraph::query::DumpDatabaseToCypherQueries(&dba, &query_stream);
     }
     const auto &results = stream.GetResults();
     // Indices and constraints are 4 queries and there must be at least one more
     // query for the data.
     ASSERT_GE(results.size(), 5);
+    int i = 0;
     for (const auto &item : results) {
       ASSERT_EQ(item.size(), 1);
       ASSERT_TRUE(item[0].IsString());
-      Execute(&db_dump, item[0].ValueString());
+      spdlog::debug("Query: {}", item[0].ValueString());
+      Execute(&interpreter_context, item[0].ValueString());
+      ++i;
     }
   }
-  ASSERT_EQ(GetState(&db), GetState(&db_dump));
-  // Make sure that dump function doesn't make changes on the database.
-  ASSERT_EQ(GetState(&db), db_initial_state);
+  ASSERT_EQ(GetState(this->context.db.get()), db_initial_state);
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, ExecuteDumpDatabase) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, ExecuteDumpDatabase) {
   {
-    auto dba = db.Access();
-    CreateVertex(&dba, {}, {}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->context.db->Access();
+    CreateVertex(dba.get(), {}, {}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
   {
-    auto stream = Execute(&db, "DUMP DATABASE");
+    auto stream = Execute(&this->context, "DUMP DATABASE");
     const auto &header = stream.GetHeader();
     const auto &results = stream.GetResults();
     ASSERT_EQ(header.size(), 1U);
@@ -757,11 +784,11 @@ TEST(DumpTest, ExecuteDumpDatabase) {
 
 class StatefulInterpreter {
  public:
-  explicit StatefulInterpreter(memgraph::storage::Storage *db)
-      : db_(db), context_(db_, memgraph::query::InterpreterConfig{}, data_directory_), interpreter_(&context_) {}
+  explicit StatefulInterpreter(memgraph::query::InterpreterContext *context)
+      : context_(context), interpreter_(context_) {}
 
   auto Execute(const std::string &query) {
-    ResultStreamFaker stream(db_);
+    ResultStreamFaker stream(context_->db.get());
 
     auto [header, _, qid] = interpreter_.Prepare(query, {}, nullptr);
     stream.Header(header);
@@ -774,8 +801,7 @@ class StatefulInterpreter {
  private:
   static const std::filesystem::path data_directory_;
 
-  memgraph::storage::Storage *db_;
-  memgraph::query::InterpreterContext context_;
+  memgraph::query::InterpreterContext *context_;
   memgraph::query::Interpreter interpreter_;
 };
 
@@ -783,9 +809,8 @@ const std::filesystem::path StatefulInterpreter::data_directory_{std::filesystem
                                                                  "MG_tests_unit_query_dump_stateful"};
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, ExecuteDumpDatabaseInMulticommandTransaction) {
-  memgraph::storage::Storage db;
-  StatefulInterpreter interpreter(&db);
+TYPED_TEST(DumpTest, ExecuteDumpDatabaseInMulticommandTransaction) {
+  StatefulInterpreter interpreter(&this->context);
 
   // Begin the transaction before the vertex is created.
   interpreter.Execute("BEGIN");
@@ -802,9 +827,9 @@ TEST(DumpTest, ExecuteDumpDatabaseInMulticommandTransaction) {
 
   // Create the vertex.
   {
-    auto dba = db.Access();
-    CreateVertex(&dba, {}, {}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->context.db->Access();
+    CreateVertex(dba.get(), {}, {}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
   // Verify that nothing is dumped.
@@ -846,67 +871,74 @@ TEST(DumpTest, ExecuteDumpDatabaseInMulticommandTransaction) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(DumpTest, MultiplePartialPulls) {
-  memgraph::storage::Storage db;
+TYPED_TEST(DumpTest, MultiplePartialPulls) {
   {
     // Create indices
-    ASSERT_FALSE(db.CreateIndex(db.NameToLabel("PERSON"), db.NameToProperty("name")).HasError());
-    ASSERT_FALSE(db.CreateIndex(db.NameToLabel("PERSON"), db.NameToProperty("surname")).HasError());
+    ASSERT_FALSE(
+        this->context.db->CreateIndex(this->context.db->NameToLabel("PERSON"), this->context.db->NameToProperty("name"))
+            .HasError());
+    ASSERT_FALSE(this->context.db
+                     ->CreateIndex(this->context.db->NameToLabel("PERSON"), this->context.db->NameToProperty("surname"))
+                     .HasError());
 
     // Create existence constraints
     {
-      auto res = db.CreateExistenceConstraint(db.NameToLabel("PERSON"), db.NameToProperty("name"));
+      auto res = this->context.db->CreateExistenceConstraint(this->context.db->NameToLabel("PERSON"),
+                                                             this->context.db->NameToProperty("name"), {});
       ASSERT_FALSE(res.HasError());
     }
     {
-      auto res = db.CreateExistenceConstraint(db.NameToLabel("PERSON"), db.NameToProperty("surname"));
+      auto res = this->context.db->CreateExistenceConstraint(this->context.db->NameToLabel("PERSON"),
+                                                             this->context.db->NameToProperty("surname"), {});
       ASSERT_FALSE(res.HasError());
     }
 
     // Create unique constraints
     {
-      auto res = db.CreateUniqueConstraint(db.NameToLabel("PERSON"), {db.NameToProperty("name")});
+      auto res = this->context.db->CreateUniqueConstraint(this->context.db->NameToLabel("PERSON"),
+                                                          {this->context.db->NameToProperty("name")}, {});
       ASSERT_TRUE(res.HasValue());
       ASSERT_EQ(res.GetValue(), memgraph::storage::UniqueConstraints::CreationStatus::SUCCESS);
     }
     {
-      auto res = db.CreateUniqueConstraint(db.NameToLabel("PERSON"), {db.NameToProperty("surname")});
+      auto res = this->context.db->CreateUniqueConstraint(this->context.db->NameToLabel("PERSON"),
+                                                          {this->context.db->NameToProperty("surname")}, {});
       ASSERT_TRUE(res.HasValue());
       ASSERT_EQ(res.GetValue(), memgraph::storage::UniqueConstraints::CreationStatus::SUCCESS);
     }
 
-    auto dba = db.Access();
-    auto p1 = CreateVertex(&dba, {"PERSON"},
+    auto dba = this->context.db->Access();
+    auto p1 = CreateVertex(dba.get(), {"PERSON"},
                            {{"name", memgraph::storage::PropertyValue("Person1")},
                             {"surname", memgraph::storage::PropertyValue("Unique1")}},
                            false);
-    auto p2 = CreateVertex(&dba, {"PERSON"},
+    auto p2 = CreateVertex(dba.get(), {"PERSON"},
                            {{"name", memgraph::storage::PropertyValue("Person2")},
                             {"surname", memgraph::storage::PropertyValue("Unique2")}},
                            false);
-    auto p3 = CreateVertex(&dba, {"PERSON"},
+    auto p3 = CreateVertex(dba.get(), {"PERSON"},
                            {{"name", memgraph::storage::PropertyValue("Person3")},
                             {"surname", memgraph::storage::PropertyValue("Unique3")}},
                            false);
-    auto p4 = CreateVertex(&dba, {"PERSON"},
+    auto p4 = CreateVertex(dba.get(), {"PERSON"},
                            {{"name", memgraph::storage::PropertyValue("Person4")},
                             {"surname", memgraph::storage::PropertyValue("Unique4")}},
                            false);
-    auto p5 = CreateVertex(&dba, {"PERSON"},
+    auto p5 = CreateVertex(dba.get(), {"PERSON"},
                            {{"name", memgraph::storage::PropertyValue("Person5")},
                             {"surname", memgraph::storage::PropertyValue("Unique5")}},
                            false);
-    CreateEdge(&dba, &p1, &p2, "REL", {}, false);
-    CreateEdge(&dba, &p1, &p3, "REL", {}, false);
-    CreateEdge(&dba, &p4, &p5, "REL", {}, false);
-    CreateEdge(&dba, &p2, &p5, "REL", {}, false);
-    ASSERT_FALSE(dba.Commit().HasError());
+    CreateEdge(dba.get(), &p1, &p2, "REL", {}, false);
+    CreateEdge(dba.get(), &p1, &p3, "REL", {}, false);
+    CreateEdge(dba.get(), &p4, &p5, "REL", {}, false);
+    CreateEdge(dba.get(), &p2, &p5, "REL", {}, false);
+    ASSERT_FALSE(dba->Commit().HasError());
   }
 
-  ResultStreamFaker stream(&db);
+  ResultStreamFaker stream(this->context.db.get());
   memgraph::query::AnyStream query_stream(&stream, memgraph::utils::NewDeleteResource());
-  auto acc = db.Access();
-  memgraph::query::DbAccessor dba(&acc);
+  auto acc = this->context.db->Access();
+  memgraph::query::DbAccessor dba(acc.get());
 
   memgraph::query::PullPlanDump pullPlan{&dba};
 
diff --git a/tests/unit/query_expression_evaluator.cpp b/tests/unit/query_expression_evaluator.cpp
index 6f51facdf..86ac5d624 100644
--- a/tests/unit/query_expression_evaluator.cpp
+++ b/tests/unit/query_expression_evaluator.cpp
@@ -19,7 +19,9 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "disk_test_utils.hpp"
 #include "query/context.hpp"
+#include "query/db_accessor.hpp"
 #include "query/frontend/ast/ast.hpp"
 #include "query/frontend/opencypher/parser.hpp"
 #include "query/interpret/awesome_memgraph_functions.hpp"
@@ -27,6 +29,8 @@
 #include "query/interpret/frame.hpp"
 #include "query/path.hpp"
 #include "query/typed_value.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/storage.hpp"
 #include "utils/exceptions.hpp"
 #include "utils/string.hpp"
@@ -41,11 +45,15 @@ using testing::UnorderedElementsAre;
 
 namespace {
 
+template <typename StorageType>
 class ExpressionEvaluatorTest : public ::testing::Test {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
+  const std::string testSuite = "expression_evaluator";
+
+  memgraph::storage::Config config;
+  std::unique_ptr<memgraph::storage::Storage> db;
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba;
+  memgraph::query::DbAccessor dba;
 
   AstStorage storage;
   memgraph::utils::MonotonicBufferResource mem{1024};
@@ -55,8 +63,20 @@ class ExpressionEvaluatorTest : public ::testing::Test {
   Frame frame{128};
   ExpressionEvaluator eval{&frame, symbol_table, ctx, &dba, memgraph::storage::View::OLD};
 
+  ExpressionEvaluatorTest()
+      : config(disk_test_utils::GenerateOnDiskConfig(testSuite)),
+        db(new StorageType(config)),
+        storage_dba(db->Access()),
+        dba(storage_dba.get()) {}
+
+  ~ExpressionEvaluatorTest() {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+
   Identifier *CreateIdentifierWithValue(std::string name, const TypedValue &value) {
-    auto id = storage.Create<Identifier>(name, true);
+    auto id = storage.template Create<Identifier>(name, true);
     auto symbol = symbol_table.CreateSymbol(name, true);
     id->MapTo(symbol);
     frame[symbol] = value;
@@ -74,479 +94,539 @@ class ExpressionEvaluatorTest : public ::testing::Test {
   }
 };
 
-TEST_F(ExpressionEvaluatorTest, OrOperator) {
-  auto *op =
-      storage.Create<OrOperator>(storage.Create<PrimitiveLiteral>(true), storage.Create<PrimitiveLiteral>(false));
-  auto val1 = Eval(op);
+// using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+using StorageTypes = ::testing::Types<memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(ExpressionEvaluatorTest, StorageTypes);
+
+TYPED_TEST(ExpressionEvaluatorTest, OrOperator) {
+  auto *op = this->storage.template Create<OrOperator>(this->storage.template Create<PrimitiveLiteral>(true),
+                                                       this->storage.template Create<PrimitiveLiteral>(false));
+  auto val1 = this->Eval(op);
   ASSERT_EQ(val1.ValueBool(), true);
-  op = storage.Create<OrOperator>(storage.Create<PrimitiveLiteral>(true), storage.Create<PrimitiveLiteral>(true));
-  auto val2 = Eval(op);
+  op = this->storage.template Create<OrOperator>(this->storage.template Create<PrimitiveLiteral>(true),
+                                                 this->storage.template Create<PrimitiveLiteral>(true));
+  auto val2 = this->Eval(op);
   ASSERT_EQ(val2.ValueBool(), true);
 }
 
-TEST_F(ExpressionEvaluatorTest, XorOperator) {
-  auto *op =
-      storage.Create<XorOperator>(storage.Create<PrimitiveLiteral>(true), storage.Create<PrimitiveLiteral>(false));
-  auto val1 = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, XorOperator) {
+  auto *op = this->storage.template Create<XorOperator>(this->storage.template Create<PrimitiveLiteral>(true),
+                                                        this->storage.template Create<PrimitiveLiteral>(false));
+  auto val1 = this->Eval(op);
   ASSERT_EQ(val1.ValueBool(), true);
-  op = storage.Create<XorOperator>(storage.Create<PrimitiveLiteral>(true), storage.Create<PrimitiveLiteral>(true));
-  auto val2 = Eval(op);
+  op = this->storage.template Create<XorOperator>(this->storage.template Create<PrimitiveLiteral>(true),
+                                                  this->storage.template Create<PrimitiveLiteral>(true));
+  auto val2 = this->Eval(op);
   ASSERT_EQ(val2.ValueBool(), false);
 }
 
-TEST_F(ExpressionEvaluatorTest, AndOperator) {
-  auto *op =
-      storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(true), storage.Create<PrimitiveLiteral>(true));
-  auto val1 = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, AndOperator) {
+  auto *op = this->storage.template Create<AndOperator>(this->storage.template Create<PrimitiveLiteral>(true),
+                                                        this->storage.template Create<PrimitiveLiteral>(true));
+  auto val1 = this->Eval(op);
   ASSERT_EQ(val1.ValueBool(), true);
-  op = storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(false), storage.Create<PrimitiveLiteral>(true));
-  auto val2 = Eval(op);
+  op = this->storage.template Create<AndOperator>(this->storage.template Create<PrimitiveLiteral>(false),
+                                                  this->storage.template Create<PrimitiveLiteral>(true));
+  auto val2 = this->Eval(op);
   ASSERT_EQ(val2.ValueBool(), false);
 }
 
-TEST_F(ExpressionEvaluatorTest, AndOperatorShortCircuit) {
+TYPED_TEST(ExpressionEvaluatorTest, AndOperatorShortCircuit) {
   {
-    auto *op =
-        storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(false), storage.Create<PrimitiveLiteral>(5));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<AndOperator>(this->storage.template Create<PrimitiveLiteral>(false),
+                                                          this->storage.template Create<PrimitiveLiteral>(5));
+    auto value = this->Eval(op);
     EXPECT_EQ(value.ValueBool(), false);
   }
   {
-    auto *op =
-        storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(5), storage.Create<PrimitiveLiteral>(false));
+    auto *op = this->storage.template Create<AndOperator>(this->storage.template Create<PrimitiveLiteral>(5),
+                                                          this->storage.template Create<PrimitiveLiteral>(false));
     // We are evaluating left to right, so we don't short circuit here and
     // raise due to `5`. This differs from neo4j, where they evaluate both
     // sides and return `false` without checking for type of the first
     // expression.
-    EXPECT_THROW(Eval(op), QueryRuntimeException);
+    EXPECT_THROW(this->Eval(op), QueryRuntimeException);
   }
 }
 
-TEST_F(ExpressionEvaluatorTest, AndOperatorNull) {
+TYPED_TEST(ExpressionEvaluatorTest, AndOperatorNull) {
   {
     // Null doesn't short circuit
-    auto *op = storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
-                                           storage.Create<PrimitiveLiteral>(5));
-    EXPECT_THROW(Eval(op), QueryRuntimeException);
+    auto *op = this->storage.template Create<AndOperator>(
+        this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
+        this->storage.template Create<PrimitiveLiteral>(5));
+    EXPECT_THROW(this->Eval(op), QueryRuntimeException);
   }
   {
-    auto *op = storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
-                                           storage.Create<PrimitiveLiteral>(true));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<AndOperator>(
+        this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
+        this->storage.template Create<PrimitiveLiteral>(true));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
   {
-    auto *op = storage.Create<AndOperator>(storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
-                                           storage.Create<PrimitiveLiteral>(false));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<AndOperator>(
+        this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
+        this->storage.template Create<PrimitiveLiteral>(false));
+    auto value = this->Eval(op);
     ASSERT_TRUE(value.IsBool());
     EXPECT_EQ(value.ValueBool(), false);
   }
 }
 
-TEST_F(ExpressionEvaluatorTest, AdditionOperator) {
-  auto *op = storage.Create<AdditionOperator>(storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3));
-  auto value = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, AdditionOperator) {
+  auto *op = this->storage.template Create<AdditionOperator>(this->storage.template Create<PrimitiveLiteral>(2),
+                                                             this->storage.template Create<PrimitiveLiteral>(3));
+  auto value = this->Eval(op);
   ASSERT_EQ(value.ValueInt(), 5);
 }
 
-TEST_F(ExpressionEvaluatorTest, SubtractionOperator) {
-  auto *op =
-      storage.Create<SubtractionOperator>(storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3));
-  auto value = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, SubtractionOperator) {
+  auto *op = this->storage.template Create<SubtractionOperator>(this->storage.template Create<PrimitiveLiteral>(2),
+                                                                this->storage.template Create<PrimitiveLiteral>(3));
+  auto value = this->Eval(op);
   ASSERT_EQ(value.ValueInt(), -1);
 }
 
-TEST_F(ExpressionEvaluatorTest, MultiplicationOperator) {
-  auto *op =
-      storage.Create<MultiplicationOperator>(storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3));
-  auto value = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, MultiplicationOperator) {
+  auto *op = this->storage.template Create<MultiplicationOperator>(this->storage.template Create<PrimitiveLiteral>(2),
+                                                                   this->storage.template Create<PrimitiveLiteral>(3));
+  auto value = this->Eval(op);
   ASSERT_EQ(value.ValueInt(), 6);
 }
 
-TEST_F(ExpressionEvaluatorTest, DivisionOperator) {
-  auto *op =
-      storage.Create<DivisionOperator>(storage.Create<PrimitiveLiteral>(50), storage.Create<PrimitiveLiteral>(10));
-  auto value = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, DivisionOperator) {
+  auto *op = this->storage.template Create<DivisionOperator>(this->storage.template Create<PrimitiveLiteral>(50),
+                                                             this->storage.template Create<PrimitiveLiteral>(10));
+  auto value = this->Eval(op);
   ASSERT_EQ(value.ValueInt(), 5);
 }
 
-TEST_F(ExpressionEvaluatorTest, ModOperator) {
-  auto *op = storage.Create<ModOperator>(storage.Create<PrimitiveLiteral>(65), storage.Create<PrimitiveLiteral>(10));
-  auto value = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, ModOperator) {
+  auto *op = this->storage.template Create<ModOperator>(this->storage.template Create<PrimitiveLiteral>(65),
+                                                        this->storage.template Create<PrimitiveLiteral>(10));
+  auto value = this->Eval(op);
   ASSERT_EQ(value.ValueInt(), 5);
 }
 
-TEST_F(ExpressionEvaluatorTest, EqualOperator) {
-  auto *op = storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(10), storage.Create<PrimitiveLiteral>(15));
-  auto val1 = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, EqualOperator) {
+  auto *op = this->storage.template Create<EqualOperator>(this->storage.template Create<PrimitiveLiteral>(10),
+                                                          this->storage.template Create<PrimitiveLiteral>(15));
+  auto val1 = this->Eval(op);
   ASSERT_EQ(val1.ValueBool(), false);
-  op = storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(15), storage.Create<PrimitiveLiteral>(15));
-  auto val2 = Eval(op);
+  op = this->storage.template Create<EqualOperator>(this->storage.template Create<PrimitiveLiteral>(15),
+                                                    this->storage.template Create<PrimitiveLiteral>(15));
+  auto val2 = this->Eval(op);
   ASSERT_EQ(val2.ValueBool(), true);
-  op = storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(20), storage.Create<PrimitiveLiteral>(15));
-  auto val3 = Eval(op);
+  op = this->storage.template Create<EqualOperator>(this->storage.template Create<PrimitiveLiteral>(20),
+                                                    this->storage.template Create<PrimitiveLiteral>(15));
+  auto val3 = this->Eval(op);
   ASSERT_EQ(val3.ValueBool(), false);
 }
 
-TEST_F(ExpressionEvaluatorTest, NotEqualOperator) {
-  auto *op =
-      storage.Create<NotEqualOperator>(storage.Create<PrimitiveLiteral>(10), storage.Create<PrimitiveLiteral>(15));
-  auto val1 = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, NotEqualOperator) {
+  auto *op = this->storage.template Create<NotEqualOperator>(this->storage.template Create<PrimitiveLiteral>(10),
+                                                             this->storage.template Create<PrimitiveLiteral>(15));
+  auto val1 = this->Eval(op);
   ASSERT_EQ(val1.ValueBool(), true);
-  op = storage.Create<NotEqualOperator>(storage.Create<PrimitiveLiteral>(15), storage.Create<PrimitiveLiteral>(15));
-  auto val2 = Eval(op);
+  op = this->storage.template Create<NotEqualOperator>(this->storage.template Create<PrimitiveLiteral>(15),
+                                                       this->storage.template Create<PrimitiveLiteral>(15));
+  auto val2 = this->Eval(op);
   ASSERT_EQ(val2.ValueBool(), false);
-  op = storage.Create<NotEqualOperator>(storage.Create<PrimitiveLiteral>(20), storage.Create<PrimitiveLiteral>(15));
-  auto val3 = Eval(op);
+  op = this->storage.template Create<NotEqualOperator>(this->storage.template Create<PrimitiveLiteral>(20),
+                                                       this->storage.template Create<PrimitiveLiteral>(15));
+  auto val3 = this->Eval(op);
   ASSERT_EQ(val3.ValueBool(), true);
 }
 
-TEST_F(ExpressionEvaluatorTest, LessOperator) {
-  auto *op = storage.Create<LessOperator>(storage.Create<PrimitiveLiteral>(10), storage.Create<PrimitiveLiteral>(15));
-  auto val1 = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, LessOperator) {
+  auto *op = this->storage.template Create<LessOperator>(this->storage.template Create<PrimitiveLiteral>(10),
+                                                         this->storage.template Create<PrimitiveLiteral>(15));
+  auto val1 = this->Eval(op);
   ASSERT_EQ(val1.ValueBool(), true);
-  op = storage.Create<LessOperator>(storage.Create<PrimitiveLiteral>(15), storage.Create<PrimitiveLiteral>(15));
-  auto val2 = Eval(op);
+  op = this->storage.template Create<LessOperator>(this->storage.template Create<PrimitiveLiteral>(15),
+                                                   this->storage.template Create<PrimitiveLiteral>(15));
+  auto val2 = this->Eval(op);
   ASSERT_EQ(val2.ValueBool(), false);
-  op = storage.Create<LessOperator>(storage.Create<PrimitiveLiteral>(20), storage.Create<PrimitiveLiteral>(15));
-  auto val3 = Eval(op);
+  op = this->storage.template Create<LessOperator>(this->storage.template Create<PrimitiveLiteral>(20),
+                                                   this->storage.template Create<PrimitiveLiteral>(15));
+  auto val3 = this->Eval(op);
   ASSERT_EQ(val3.ValueBool(), false);
 }
 
-TEST_F(ExpressionEvaluatorTest, GreaterOperator) {
-  auto *op =
-      storage.Create<GreaterOperator>(storage.Create<PrimitiveLiteral>(10), storage.Create<PrimitiveLiteral>(15));
-  auto val1 = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, GreaterOperator) {
+  auto *op = this->storage.template Create<GreaterOperator>(this->storage.template Create<PrimitiveLiteral>(10),
+                                                            this->storage.template Create<PrimitiveLiteral>(15));
+  auto val1 = this->Eval(op);
   ASSERT_EQ(val1.ValueBool(), false);
-  op = storage.Create<GreaterOperator>(storage.Create<PrimitiveLiteral>(15), storage.Create<PrimitiveLiteral>(15));
-  auto val2 = Eval(op);
+  op = this->storage.template Create<GreaterOperator>(this->storage.template Create<PrimitiveLiteral>(15),
+                                                      this->storage.template Create<PrimitiveLiteral>(15));
+  auto val2 = this->Eval(op);
   ASSERT_EQ(val2.ValueBool(), false);
-  op = storage.Create<GreaterOperator>(storage.Create<PrimitiveLiteral>(20), storage.Create<PrimitiveLiteral>(15));
-  auto val3 = Eval(op);
+  op = this->storage.template Create<GreaterOperator>(this->storage.template Create<PrimitiveLiteral>(20),
+                                                      this->storage.template Create<PrimitiveLiteral>(15));
+  auto val3 = this->Eval(op);
   ASSERT_EQ(val3.ValueBool(), true);
 }
 
-TEST_F(ExpressionEvaluatorTest, LessEqualOperator) {
-  auto *op =
-      storage.Create<LessEqualOperator>(storage.Create<PrimitiveLiteral>(10), storage.Create<PrimitiveLiteral>(15));
-  auto val1 = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, LessEqualOperator) {
+  auto *op = this->storage.template Create<LessEqualOperator>(this->storage.template Create<PrimitiveLiteral>(10),
+                                                              this->storage.template Create<PrimitiveLiteral>(15));
+  auto val1 = this->Eval(op);
   ASSERT_EQ(val1.ValueBool(), true);
-  op = storage.Create<LessEqualOperator>(storage.Create<PrimitiveLiteral>(15), storage.Create<PrimitiveLiteral>(15));
-  auto val2 = Eval(op);
+  op = this->storage.template Create<LessEqualOperator>(this->storage.template Create<PrimitiveLiteral>(15),
+                                                        this->storage.template Create<PrimitiveLiteral>(15));
+  auto val2 = this->Eval(op);
   ASSERT_EQ(val2.ValueBool(), true);
-  op = storage.Create<LessEqualOperator>(storage.Create<PrimitiveLiteral>(20), storage.Create<PrimitiveLiteral>(15));
-  auto val3 = Eval(op);
+  op = this->storage.template Create<LessEqualOperator>(this->storage.template Create<PrimitiveLiteral>(20),
+                                                        this->storage.template Create<PrimitiveLiteral>(15));
+  auto val3 = this->Eval(op);
   ASSERT_EQ(val3.ValueBool(), false);
 }
 
-TEST_F(ExpressionEvaluatorTest, GreaterEqualOperator) {
-  auto *op =
-      storage.Create<GreaterEqualOperator>(storage.Create<PrimitiveLiteral>(10), storage.Create<PrimitiveLiteral>(15));
-  auto val1 = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, GreaterEqualOperator) {
+  auto *op = this->storage.template Create<GreaterEqualOperator>(this->storage.template Create<PrimitiveLiteral>(10),
+                                                                 this->storage.template Create<PrimitiveLiteral>(15));
+  auto val1 = this->Eval(op);
   ASSERT_EQ(val1.ValueBool(), false);
-  op = storage.Create<GreaterEqualOperator>(storage.Create<PrimitiveLiteral>(15), storage.Create<PrimitiveLiteral>(15));
-  auto val2 = Eval(op);
+  op = this->storage.template Create<GreaterEqualOperator>(this->storage.template Create<PrimitiveLiteral>(15),
+                                                           this->storage.template Create<PrimitiveLiteral>(15));
+  auto val2 = this->Eval(op);
   ASSERT_EQ(val2.ValueBool(), true);
-  op = storage.Create<GreaterEqualOperator>(storage.Create<PrimitiveLiteral>(20), storage.Create<PrimitiveLiteral>(15));
-  auto val3 = Eval(op);
+  op = this->storage.template Create<GreaterEqualOperator>(this->storage.template Create<PrimitiveLiteral>(20),
+                                                           this->storage.template Create<PrimitiveLiteral>(15));
+  auto val3 = this->Eval(op);
   ASSERT_EQ(val3.ValueBool(), true);
 }
 
-TEST_F(ExpressionEvaluatorTest, InListOperator) {
-  auto *list_literal = storage.Create<ListLiteral>(std::vector<Expression *>{
-      storage.Create<PrimitiveLiteral>(1), storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>("a")});
+TYPED_TEST(ExpressionEvaluatorTest, InListOperator) {
+  auto *list_literal = this->storage.template Create<ListLiteral>(std::vector<Expression *>{
+      this->storage.template Create<PrimitiveLiteral>(1), this->storage.template Create<PrimitiveLiteral>(2),
+      this->storage.template Create<PrimitiveLiteral>("a")});
   {
     // Element exists in list.
-    auto *op = storage.Create<InListOperator>(storage.Create<PrimitiveLiteral>(2), list_literal);
-    auto value = Eval(op);
+    auto *op =
+        this->storage.template Create<InListOperator>(this->storage.template Create<PrimitiveLiteral>(2), list_literal);
+    auto value = this->Eval(op);
     EXPECT_EQ(value.ValueBool(), true);
   }
   {
     // Element doesn't exist in list.
-    auto *op = storage.Create<InListOperator>(storage.Create<PrimitiveLiteral>("x"), list_literal);
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<InListOperator>(this->storage.template Create<PrimitiveLiteral>("x"),
+                                                             list_literal);
+    auto value = this->Eval(op);
     EXPECT_EQ(value.ValueBool(), false);
   }
   {
-    auto *list_literal = storage.Create<ListLiteral>(
-        std::vector<Expression *>{storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
-                                  storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>("a")});
+    auto *list_literal = this->storage.template Create<ListLiteral>(std::vector<Expression *>{
+        this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
+        this->storage.template Create<PrimitiveLiteral>(2), this->storage.template Create<PrimitiveLiteral>("a")});
     // Element doesn't exist in list with null element.
-    auto *op = storage.Create<InListOperator>(storage.Create<PrimitiveLiteral>("x"), list_literal);
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<InListOperator>(this->storage.template Create<PrimitiveLiteral>("x"),
+                                                             list_literal);
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
   {
     // Null list.
-    auto *op = storage.Create<InListOperator>(storage.Create<PrimitiveLiteral>("x"),
-                                              storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<InListOperator>(
+        this->storage.template Create<PrimitiveLiteral>("x"),
+        this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
   {
     // Null literal.
-    auto *op = storage.Create<InListOperator>(storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
-                                              list_literal);
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<InListOperator>(
+        this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()), list_literal);
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
   {
     // Null literal, empty list.
-    auto *op = storage.Create<InListOperator>(storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
-                                              storage.Create<ListLiteral>(std::vector<Expression *>()));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<InListOperator>(
+        this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
+        this->storage.template Create<ListLiteral>(std::vector<Expression *>()));
+    auto value = this->Eval(op);
     EXPECT_FALSE(value.ValueBool());
   }
 }
 
-TEST_F(ExpressionEvaluatorTest, ListIndexing) {
-  auto *list_literal = storage.Create<ListLiteral>(
-      std::vector<Expression *>{storage.Create<PrimitiveLiteral>(1), storage.Create<PrimitiveLiteral>(2),
-                                storage.Create<PrimitiveLiteral>(3), storage.Create<PrimitiveLiteral>(4)});
+TYPED_TEST(ExpressionEvaluatorTest, ListIndexing) {
+  auto *list_literal = this->storage.template Create<ListLiteral>(std::vector<Expression *>{
+      this->storage.template Create<PrimitiveLiteral>(1), this->storage.template Create<PrimitiveLiteral>(2),
+      this->storage.template Create<PrimitiveLiteral>(3), this->storage.template Create<PrimitiveLiteral>(4)});
   {
     // Legal indexing.
-    auto *op = storage.Create<SubscriptOperator>(list_literal, storage.Create<PrimitiveLiteral>(2));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(list_literal,
+                                                                this->storage.template Create<PrimitiveLiteral>(2));
+    auto value = this->Eval(op);
     EXPECT_EQ(value.ValueInt(), 3);
   }
   {
     // Out of bounds indexing.
-    auto *op = storage.Create<SubscriptOperator>(list_literal, storage.Create<PrimitiveLiteral>(4));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(list_literal,
+                                                                this->storage.template Create<PrimitiveLiteral>(4));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
   {
     // Out of bounds indexing with negative bound.
-    auto *op = storage.Create<SubscriptOperator>(list_literal, storage.Create<PrimitiveLiteral>(-100));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(list_literal,
+                                                                this->storage.template Create<PrimitiveLiteral>(-100));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
   {
     // Legal indexing with negative index.
-    auto *op = storage.Create<SubscriptOperator>(list_literal, storage.Create<PrimitiveLiteral>(-2));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(list_literal,
+                                                                this->storage.template Create<PrimitiveLiteral>(-2));
+    auto value = this->Eval(op);
     EXPECT_EQ(value.ValueInt(), 3);
   }
   {
     // Indexing with one operator being null.
-    auto *op = storage.Create<SubscriptOperator>(storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
-                                                 storage.Create<PrimitiveLiteral>(-2));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(
+        this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
+        this->storage.template Create<PrimitiveLiteral>(-2));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
   {
     // Indexing with incompatible type.
-    auto *op = storage.Create<SubscriptOperator>(list_literal, storage.Create<PrimitiveLiteral>("bla"));
-    EXPECT_THROW(Eval(op), QueryRuntimeException);
+    auto *op = this->storage.template Create<SubscriptOperator>(list_literal,
+                                                                this->storage.template Create<PrimitiveLiteral>("bla"));
+    EXPECT_THROW(this->Eval(op), QueryRuntimeException);
   }
 }
 
-TEST_F(ExpressionEvaluatorTest, MapIndexing) {
-  auto *map_literal = storage.Create<MapLiteral>(
-      std::unordered_map<PropertyIx, Expression *>{{storage.GetPropertyIx("a"), storage.Create<PrimitiveLiteral>(1)},
-                                                   {storage.GetPropertyIx("b"), storage.Create<PrimitiveLiteral>(2)},
-                                                   {storage.GetPropertyIx("c"), storage.Create<PrimitiveLiteral>(3)}});
+TYPED_TEST(ExpressionEvaluatorTest, MapIndexing) {
+  auto *map_literal = this->storage.template Create<MapLiteral>(std::unordered_map<PropertyIx, Expression *>{
+      {this->storage.GetPropertyIx("a"), this->storage.template Create<PrimitiveLiteral>(1)},
+      {this->storage.GetPropertyIx("b"), this->storage.template Create<PrimitiveLiteral>(2)},
+      {this->storage.GetPropertyIx("c"), this->storage.template Create<PrimitiveLiteral>(3)}});
   {
     // Legal indexing.
-    auto *op = storage.Create<SubscriptOperator>(map_literal, storage.Create<PrimitiveLiteral>("b"));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(map_literal,
+                                                                this->storage.template Create<PrimitiveLiteral>("b"));
+    auto value = this->Eval(op);
     EXPECT_EQ(value.ValueInt(), 2);
   }
   {
     // Legal indexing, non-existing key.
-    auto *op = storage.Create<SubscriptOperator>(map_literal, storage.Create<PrimitiveLiteral>("z"));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(map_literal,
+                                                                this->storage.template Create<PrimitiveLiteral>("z"));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
   {
     // Wrong key type.
-    auto *op = storage.Create<SubscriptOperator>(map_literal, storage.Create<PrimitiveLiteral>(42));
-    EXPECT_THROW(Eval(op), QueryRuntimeException);
+    auto *op = this->storage.template Create<SubscriptOperator>(map_literal,
+                                                                this->storage.template Create<PrimitiveLiteral>(42));
+    EXPECT_THROW(this->Eval(op), QueryRuntimeException);
   }
   {
     // Indexing with Null.
-    auto *op = storage.Create<SubscriptOperator>(map_literal,
-                                                 storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(
+        map_literal, this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
 }
 
-TEST_F(ExpressionEvaluatorTest, MapProjectionIndexing) {
-  auto *map_variable = storage.Create<MapLiteral>(
-      std::unordered_map<PropertyIx, Expression *>{{storage.GetPropertyIx("x"), storage.Create<PrimitiveLiteral>(0)}});
-  auto *map_projection_literal = storage.Create<MapProjectionLiteral>(
-      map_variable,
-      std::unordered_map<PropertyIx, Expression *>{
-          {storage.GetPropertyIx("a"), storage.Create<PrimitiveLiteral>(1)},
-          {storage.GetPropertyIx("y"), storage.Create<PropertyLookup>(map_variable, storage.GetPropertyIx("y"))}});
+TYPED_TEST(ExpressionEvaluatorTest, MapProjectionIndexing) {
+  auto *map_variable = this->storage.template Create<MapLiteral>(std::unordered_map<PropertyIx, Expression *>{
+      {this->storage.GetPropertyIx("x"), this->storage.template Create<PrimitiveLiteral>(0)}});
+  auto *map_projection_literal = this->storage.template Create<MapProjectionLiteral>(
+      map_variable, std::unordered_map<PropertyIx, Expression *>{
+                        {this->storage.GetPropertyIx("a"), this->storage.template Create<PrimitiveLiteral>(1)},
+                        {this->storage.GetPropertyIx("y"), this->storage.template Create<PropertyLookup>(
+                                                               map_variable, this->storage.GetPropertyIx("y"))}});
 
   {
     // Legal indexing.
-    auto *op = storage.Create<SubscriptOperator>(map_projection_literal, storage.Create<PrimitiveLiteral>("a"));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(map_projection_literal,
+                                                                this->storage.template Create<PrimitiveLiteral>("a"));
+    auto value = this->Eval(op);
     EXPECT_EQ(value.ValueInt(), 1);
   }
   {
     // Legal indexing; property created by PropertyLookup of a non-existent map variable key
-    auto *op = storage.Create<SubscriptOperator>(map_projection_literal, storage.Create<PrimitiveLiteral>("y"));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(map_projection_literal,
+                                                                this->storage.template Create<PrimitiveLiteral>("y"));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
   {
     // Legal indexing, non-existing property.
-    auto *op = storage.Create<SubscriptOperator>(map_projection_literal, storage.Create<PrimitiveLiteral>("z"));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(map_projection_literal,
+                                                                this->storage.template Create<PrimitiveLiteral>("z"));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
   {
     // Wrong key type.
-    auto *op = storage.Create<SubscriptOperator>(map_projection_literal, storage.Create<PrimitiveLiteral>(42));
-    EXPECT_THROW(Eval(op), QueryRuntimeException);
+    auto *op = this->storage.template Create<SubscriptOperator>(map_projection_literal,
+                                                                this->storage.template Create<PrimitiveLiteral>(42));
+    EXPECT_THROW(this->Eval(op), QueryRuntimeException);
   }
   {
     // Indexing with Null.
-    auto *op = storage.Create<SubscriptOperator>(map_projection_literal,
-                                                 storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(
+        map_projection_literal, this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
 }
 
-TEST_F(ExpressionEvaluatorTest, MapProjectionAllPropertiesLookupBefore) {
+TYPED_TEST(ExpressionEvaluatorTest, MapProjectionAllPropertiesLookupBefore) {
   // AllPropertiesLookup (.*) may contain properties whose names also occur in MapProjectionLiteral
   // The ones in MapProjectionLiteral are explicitly given and thus take precedence over those in AllPropertiesLookup
   // Test case: AllPropertiesLookup comes before the identically-named properties
 
-  auto *map_variable = storage.Create<MapLiteral>(
-      std::unordered_map<PropertyIx, Expression *>{{storage.GetPropertyIx("x"), storage.Create<PrimitiveLiteral>(0)}});
-  auto *map_projection_literal = storage.Create<MapProjectionLiteral>(
-      map_variable, std::unordered_map<PropertyIx, Expression *>{
-                        {storage.GetPropertyIx("*"), storage.Create<AllPropertiesLookup>(map_variable)},
-                        {storage.GetPropertyIx("x"), storage.Create<PrimitiveLiteral>(1)}});
+  auto *map_variable = this->storage.template Create<MapLiteral>(std::unordered_map<PropertyIx, Expression *>{
+      {this->storage.GetPropertyIx("x"), this->storage.template Create<PrimitiveLiteral>(0)}});
+  auto *map_projection_literal = this->storage.template Create<MapProjectionLiteral>(
+      map_variable,
+      std::unordered_map<PropertyIx, Expression *>{
+          {this->storage.GetPropertyIx("*"), this->storage.template Create<AllPropertiesLookup>(map_variable)},
+          {this->storage.GetPropertyIx("x"), this->storage.template Create<PrimitiveLiteral>(1)}});
 
-  auto *op = storage.Create<SubscriptOperator>(map_projection_literal, storage.Create<PrimitiveLiteral>("x"));
-  auto value = Eval(op);
+  auto *op = this->storage.template Create<SubscriptOperator>(map_projection_literal,
+                                                              this->storage.template Create<PrimitiveLiteral>("x"));
+  auto value = this->Eval(op);
   EXPECT_EQ(value.ValueInt(), 1);
 }
 
-TEST_F(ExpressionEvaluatorTest, MapProjectionAllPropertiesLookupAfter) {
+TYPED_TEST(ExpressionEvaluatorTest, MapProjectionAllPropertiesLookupAfter) {
   // AllPropertiesLookup (.*) may contain properties whose names also occur in MapProjectionLiteral
   // The ones in MapProjectionLiteral are explicitly given and thus take precedence over those in AllPropertiesLookup
   // Test case: AllPropertiesLookup comes after the identically-named properties
 
-  auto *map_variable = storage.Create<MapLiteral>(
-      std::unordered_map<PropertyIx, Expression *>{{storage.GetPropertyIx("x"), storage.Create<PrimitiveLiteral>(0)}});
-  auto *map_projection_literal = storage.Create<MapProjectionLiteral>(
-      map_variable, std::unordered_map<PropertyIx, Expression *>{
-                        {storage.GetPropertyIx("x"), storage.Create<PrimitiveLiteral>(1)},
-                        {storage.GetPropertyIx("*"), storage.Create<AllPropertiesLookup>(map_variable)}});
+  auto *map_variable = this->storage.template Create<MapLiteral>(std::unordered_map<PropertyIx, Expression *>{
+      {this->storage.GetPropertyIx("x"), this->storage.template Create<PrimitiveLiteral>(0)}});
+  auto *map_projection_literal = this->storage.template Create<MapProjectionLiteral>(
+      map_variable,
+      std::unordered_map<PropertyIx, Expression *>{
+          {this->storage.GetPropertyIx("x"), this->storage.template Create<PrimitiveLiteral>(1)},
+          {this->storage.GetPropertyIx("*"), this->storage.template Create<AllPropertiesLookup>(map_variable)}});
 
-  auto *op = storage.Create<SubscriptOperator>(map_projection_literal, storage.Create<PrimitiveLiteral>("x"));
-  auto value = Eval(op);
+  auto *op = this->storage.template Create<SubscriptOperator>(map_projection_literal,
+                                                              this->storage.template Create<PrimitiveLiteral>("x"));
+  auto value = this->Eval(op);
   EXPECT_EQ(value.ValueInt(), 1);
 }
 
-TEST_F(ExpressionEvaluatorTest, VertexAndEdgeIndexing) {
-  auto edge_type = dba.NameToEdgeType("edge_type");
-  auto prop = dba.NameToProperty("prop");
-  auto v1 = dba.InsertVertex();
-  auto e11 = dba.InsertEdge(&v1, &v1, edge_type);
+TYPED_TEST(ExpressionEvaluatorTest, VertexAndEdgeIndexing) {
+  auto edge_type = this->dba.NameToEdgeType("edge_type");
+  auto prop = this->dba.NameToProperty("prop");
+  auto v1 = this->dba.InsertVertex();
+  auto e11 = this->dba.InsertEdge(&v1, &v1, edge_type);
   ASSERT_TRUE(e11.HasValue());
   ASSERT_TRUE(v1.SetProperty(prop, memgraph::storage::PropertyValue(42)).HasValue());
   ASSERT_TRUE(e11->SetProperty(prop, memgraph::storage::PropertyValue(43)).HasValue());
-  dba.AdvanceCommand();
+  this->dba.AdvanceCommand();
 
-  auto *vertex_id = CreateIdentifierWithValue("v1", TypedValue(v1));
-  auto *edge_id = CreateIdentifierWithValue("e11", TypedValue(*e11));
+  auto *vertex_id = this->CreateIdentifierWithValue("v1", TypedValue(v1));
+  auto *edge_id = this->CreateIdentifierWithValue("e11", TypedValue(*e11));
   {
     // Legal indexing.
-    auto *op1 = storage.Create<SubscriptOperator>(vertex_id, storage.Create<PrimitiveLiteral>("prop"));
-    auto value1 = Eval(op1);
+    auto *op1 = this->storage.template Create<SubscriptOperator>(
+        vertex_id, this->storage.template Create<PrimitiveLiteral>("prop"));
+    auto value1 = this->Eval(op1);
     EXPECT_EQ(value1.ValueInt(), 42);
 
-    auto *op2 = storage.Create<SubscriptOperator>(edge_id, storage.Create<PrimitiveLiteral>("prop"));
-    auto value2 = Eval(op2);
+    auto *op2 = this->storage.template Create<SubscriptOperator>(
+        edge_id, this->storage.template Create<PrimitiveLiteral>("prop"));
+    auto value2 = this->Eval(op2);
     EXPECT_EQ(value2.ValueInt(), 43);
   }
   {
     // Legal indexing, non-existing key.
-    auto *op1 = storage.Create<SubscriptOperator>(vertex_id, storage.Create<PrimitiveLiteral>("blah"));
-    auto value1 = Eval(op1);
+    auto *op1 = this->storage.template Create<SubscriptOperator>(
+        vertex_id, this->storage.template Create<PrimitiveLiteral>("blah"));
+    auto value1 = this->Eval(op1);
     EXPECT_TRUE(value1.IsNull());
 
-    auto *op2 = storage.Create<SubscriptOperator>(edge_id, storage.Create<PrimitiveLiteral>("blah"));
-    auto value2 = Eval(op2);
+    auto *op2 = this->storage.template Create<SubscriptOperator>(
+        edge_id, this->storage.template Create<PrimitiveLiteral>("blah"));
+    auto value2 = this->Eval(op2);
     EXPECT_TRUE(value2.IsNull());
   }
   {
     // Wrong key type.
-    auto *op1 = storage.Create<SubscriptOperator>(vertex_id, storage.Create<PrimitiveLiteral>(1));
-    EXPECT_THROW(Eval(op1), QueryRuntimeException);
+    auto *op1 =
+        this->storage.template Create<SubscriptOperator>(vertex_id, this->storage.template Create<PrimitiveLiteral>(1));
+    EXPECT_THROW(this->Eval(op1), QueryRuntimeException);
 
-    auto *op2 = storage.Create<SubscriptOperator>(edge_id, storage.Create<PrimitiveLiteral>(1));
-    EXPECT_THROW(Eval(op2), QueryRuntimeException);
+    auto *op2 =
+        this->storage.template Create<SubscriptOperator>(edge_id, this->storage.template Create<PrimitiveLiteral>(1));
+    EXPECT_THROW(this->Eval(op2), QueryRuntimeException);
   }
   {
     // Indexing with Null.
-    auto *op1 = storage.Create<SubscriptOperator>(vertex_id,
-                                                  storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
-    auto value1 = Eval(op1);
+    auto *op1 = this->storage.template Create<SubscriptOperator>(
+        vertex_id, this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
+    auto value1 = this->Eval(op1);
     EXPECT_TRUE(value1.IsNull());
 
-    auto *op2 = storage.Create<SubscriptOperator>(edge_id,
-                                                  storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
-    auto value2 = Eval(op2);
+    auto *op2 = this->storage.template Create<SubscriptOperator>(
+        edge_id, this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
+    auto value2 = this->Eval(op2);
     EXPECT_TRUE(value2.IsNull());
   }
 }
 
-TEST_F(ExpressionEvaluatorTest, TypedValueListIndexing) {
-  auto list_vector = memgraph::utils::pmr::vector<TypedValue>(ctx.memory);
+TYPED_TEST(ExpressionEvaluatorTest, TypedValueListIndexing) {
+  auto list_vector = memgraph::utils::pmr::vector<TypedValue>(this->ctx.memory);
   list_vector.emplace_back("string1");
   list_vector.emplace_back(TypedValue("string2"));
 
-  auto *identifier = storage.Create<Identifier>("n");
-  auto node_symbol = symbol_table.CreateSymbol("n", true);
+  auto *identifier = this->storage.template Create<Identifier>("n");
+  auto node_symbol = this->symbol_table.CreateSymbol("n", true);
   identifier->MapTo(node_symbol);
-  frame[node_symbol] = TypedValue(list_vector, ctx.memory);
+  this->frame[node_symbol] = TypedValue(list_vector, this->ctx.memory);
 
   {
     // Legal indexing.
-    auto *op = storage.Create<SubscriptOperator>(identifier, storage.Create<PrimitiveLiteral>(0));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(identifier,
+                                                                this->storage.template Create<PrimitiveLiteral>(0));
+    auto value = this->Eval(op);
     EXPECT_EQ(value.ValueString(), "string1");
   }
   {
     // Out of bounds indexing
-    auto *op = storage.Create<SubscriptOperator>(identifier, storage.Create<PrimitiveLiteral>(3));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(identifier,
+                                                                this->storage.template Create<PrimitiveLiteral>(3));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
   {
     // Out of bounds indexing with negative bound.
-    auto *op = storage.Create<SubscriptOperator>(identifier, storage.Create<PrimitiveLiteral>(-100));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(identifier,
+                                                                this->storage.template Create<PrimitiveLiteral>(-100));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
   {
     // Legal indexing with negative index.
-    auto *op = storage.Create<SubscriptOperator>(identifier, storage.Create<PrimitiveLiteral>(-2));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<SubscriptOperator>(identifier,
+                                                                this->storage.template Create<PrimitiveLiteral>(-2));
+    auto value = this->Eval(op);
     EXPECT_EQ(value.ValueString(), "string1");
   }
   {
     // Indexing with incompatible type.
-    auto *op = storage.Create<SubscriptOperator>(identifier, storage.Create<PrimitiveLiteral>("bla"));
-    EXPECT_THROW(Eval(op), QueryRuntimeException);
+    auto *op = this->storage.template Create<SubscriptOperator>(identifier,
+                                                                this->storage.template Create<PrimitiveLiteral>("bla"));
+    EXPECT_THROW(this->Eval(op), QueryRuntimeException);
   }
 }
 
-TEST_F(ExpressionEvaluatorTest, ListSlicingOperator) {
-  auto *list_literal = storage.Create<ListLiteral>(
-      std::vector<Expression *>{storage.Create<PrimitiveLiteral>(1), storage.Create<PrimitiveLiteral>(2),
-                                storage.Create<PrimitiveLiteral>(3), storage.Create<PrimitiveLiteral>(4)});
+TYPED_TEST(ExpressionEvaluatorTest, ListSlicingOperator) {
+  auto *list_literal = this->storage.template Create<ListLiteral>(std::vector<Expression *>{
+      this->storage.template Create<PrimitiveLiteral>(1), this->storage.template Create<PrimitiveLiteral>(2),
+      this->storage.template Create<PrimitiveLiteral>(3), this->storage.template Create<PrimitiveLiteral>(4)});
 
   auto extract_ints = [](TypedValue list) {
     std::vector<int64_t> int_list;
@@ -557,175 +637,185 @@ TEST_F(ExpressionEvaluatorTest, ListSlicingOperator) {
   };
   {
     // Legal slicing with both bounds defined.
-    auto *op = storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(2),
-                                                   storage.Create<PrimitiveLiteral>(4));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<ListSlicingOperator>(list_literal,
+                                                                  this->storage.template Create<PrimitiveLiteral>(2),
+                                                                  this->storage.template Create<PrimitiveLiteral>(4));
+    auto value = this->Eval(op);
     EXPECT_THAT(extract_ints(value), ElementsAre(3, 4));
   }
   {
     // Legal slicing with negative bound.
-    auto *op = storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(2),
-                                                   storage.Create<PrimitiveLiteral>(-1));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<ListSlicingOperator>(list_literal,
+                                                                  this->storage.template Create<PrimitiveLiteral>(2),
+                                                                  this->storage.template Create<PrimitiveLiteral>(-1));
+    auto value = this->Eval(op);
     EXPECT_THAT(extract_ints(value), ElementsAre(3));
   }
   {
     // Lower bound larger than upper bound.
-    auto *op = storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(2),
-                                                   storage.Create<PrimitiveLiteral>(-4));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<ListSlicingOperator>(list_literal,
+                                                                  this->storage.template Create<PrimitiveLiteral>(2),
+                                                                  this->storage.template Create<PrimitiveLiteral>(-4));
+    auto value = this->Eval(op);
     EXPECT_THAT(extract_ints(value), ElementsAre());
   }
   {
     // Bounds ouf or range.
-    auto *op = storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(-100),
-                                                   storage.Create<PrimitiveLiteral>(10));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<ListSlicingOperator>(list_literal,
+                                                                  this->storage.template Create<PrimitiveLiteral>(-100),
+                                                                  this->storage.template Create<PrimitiveLiteral>(10));
+    auto value = this->Eval(op);
     EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3, 4));
   }
   {
     // Lower bound undefined.
-    auto *op = storage.Create<ListSlicingOperator>(list_literal, nullptr, storage.Create<PrimitiveLiteral>(3));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<ListSlicingOperator>(list_literal, nullptr,
+                                                                  this->storage.template Create<PrimitiveLiteral>(3));
+    auto value = this->Eval(op);
     EXPECT_THAT(extract_ints(value), ElementsAre(1, 2, 3));
   }
   {
     // Upper bound undefined.
-    auto *op = storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(-2), nullptr);
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<ListSlicingOperator>(
+        list_literal, this->storage.template Create<PrimitiveLiteral>(-2), nullptr);
+    auto value = this->Eval(op);
     EXPECT_THAT(extract_ints(value), ElementsAre(3, 4));
   }
   {
     // Bound of illegal type and null value bound.
-    auto *op = storage.Create<ListSlicingOperator>(list_literal,
-                                                   storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
-                                                   storage.Create<PrimitiveLiteral>("mirko"));
-    EXPECT_THROW(Eval(op), QueryRuntimeException);
+    auto *op = this->storage.template Create<ListSlicingOperator>(
+        list_literal, this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
+        this->storage.template Create<PrimitiveLiteral>("mirko"));
+    EXPECT_THROW(this->Eval(op), QueryRuntimeException);
   }
   {
     // List of illegal type.
-    auto *op = storage.Create<ListSlicingOperator>(storage.Create<PrimitiveLiteral>("a"),
-                                                   storage.Create<PrimitiveLiteral>(-2), nullptr);
-    EXPECT_THROW(Eval(op), QueryRuntimeException);
+    auto *op = this->storage.template Create<ListSlicingOperator>(this->storage.template Create<PrimitiveLiteral>("a"),
+                                                                  this->storage.template Create<PrimitiveLiteral>(-2),
+                                                                  nullptr);
+    EXPECT_THROW(this->Eval(op), QueryRuntimeException);
   }
   {
     // Null value list with undefined upper bound.
-    auto *op = storage.Create<ListSlicingOperator>(storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
-                                                   storage.Create<PrimitiveLiteral>(-2), nullptr);
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<ListSlicingOperator>(
+        this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()),
+        this->storage.template Create<PrimitiveLiteral>(-2), nullptr);
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
     ;
   }
   {
     // Null value index.
-    auto *op =
-        storage.Create<ListSlicingOperator>(list_literal, storage.Create<PrimitiveLiteral>(-2),
-                                            storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<ListSlicingOperator>(
+        list_literal, this->storage.template Create<PrimitiveLiteral>(-2),
+        this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
     ;
   }
 }
 
-TEST_F(ExpressionEvaluatorTest, IfOperator) {
-  auto *then_expression = storage.Create<PrimitiveLiteral>(10);
-  auto *else_expression = storage.Create<PrimitiveLiteral>(20);
+TYPED_TEST(ExpressionEvaluatorTest, IfOperator) {
+  auto *then_expression = this->storage.template Create<PrimitiveLiteral>(10);
+  auto *else_expression = this->storage.template Create<PrimitiveLiteral>(20);
   {
-    auto *condition_true =
-        storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(2));
-    auto *op = storage.Create<IfOperator>(condition_true, then_expression, else_expression);
-    auto value = Eval(op);
+    auto *condition_true = this->storage.template Create<EqualOperator>(
+        this->storage.template Create<PrimitiveLiteral>(2), this->storage.template Create<PrimitiveLiteral>(2));
+    auto *op = this->storage.template Create<IfOperator>(condition_true, then_expression, else_expression);
+    auto value = this->Eval(op);
     ASSERT_EQ(value.ValueInt(), 10);
   }
   {
-    auto *condition_false =
-        storage.Create<EqualOperator>(storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3));
-    auto *op = storage.Create<IfOperator>(condition_false, then_expression, else_expression);
-    auto value = Eval(op);
+    auto *condition_false = this->storage.template Create<EqualOperator>(
+        this->storage.template Create<PrimitiveLiteral>(2), this->storage.template Create<PrimitiveLiteral>(3));
+    auto *op = this->storage.template Create<IfOperator>(condition_false, then_expression, else_expression);
+    auto value = this->Eval(op);
     ASSERT_EQ(value.ValueInt(), 20);
   }
   {
-    auto *condition_exception =
-        storage.Create<AdditionOperator>(storage.Create<PrimitiveLiteral>(2), storage.Create<PrimitiveLiteral>(3));
-    auto *op = storage.Create<IfOperator>(condition_exception, then_expression, else_expression);
-    ASSERT_THROW(Eval(op), QueryRuntimeException);
+    auto *condition_exception = this->storage.template Create<AdditionOperator>(
+        this->storage.template Create<PrimitiveLiteral>(2), this->storage.template Create<PrimitiveLiteral>(3));
+    auto *op = this->storage.template Create<IfOperator>(condition_exception, then_expression, else_expression);
+    ASSERT_THROW(this->Eval(op), QueryRuntimeException);
   }
 }
 
-TEST_F(ExpressionEvaluatorTest, NotOperator) {
-  auto *op = storage.Create<NotOperator>(storage.Create<PrimitiveLiteral>(false));
-  auto value = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, NotOperator) {
+  auto *op = this->storage.template Create<NotOperator>(this->storage.template Create<PrimitiveLiteral>(false));
+  auto value = this->Eval(op);
   ASSERT_EQ(value.ValueBool(), true);
 }
 
-TEST_F(ExpressionEvaluatorTest, UnaryPlusOperator) {
-  auto *op = storage.Create<UnaryPlusOperator>(storage.Create<PrimitiveLiteral>(5));
-  auto value = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, UnaryPlusOperator) {
+  auto *op = this->storage.template Create<UnaryPlusOperator>(this->storage.template Create<PrimitiveLiteral>(5));
+  auto value = this->Eval(op);
   ASSERT_EQ(value.ValueInt(), 5);
 }
 
-TEST_F(ExpressionEvaluatorTest, UnaryMinusOperator) {
-  auto *op = storage.Create<UnaryMinusOperator>(storage.Create<PrimitiveLiteral>(5));
-  auto value = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, UnaryMinusOperator) {
+  auto *op = this->storage.template Create<UnaryMinusOperator>(this->storage.template Create<PrimitiveLiteral>(5));
+  auto value = this->Eval(op);
   ASSERT_EQ(value.ValueInt(), -5);
 }
 
-TEST_F(ExpressionEvaluatorTest, IsNullOperator) {
-  auto *op = storage.Create<IsNullOperator>(storage.Create<PrimitiveLiteral>(1));
-  auto val1 = Eval(op);
+TYPED_TEST(ExpressionEvaluatorTest, IsNullOperator) {
+  auto *op = this->storage.template Create<IsNullOperator>(this->storage.template Create<PrimitiveLiteral>(1));
+  auto val1 = this->Eval(op);
   ASSERT_EQ(val1.ValueBool(), false);
-  op = storage.Create<IsNullOperator>(storage.Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
-  auto val2 = Eval(op);
+  op = this->storage.template Create<IsNullOperator>(
+      this->storage.template Create<PrimitiveLiteral>(memgraph::storage::PropertyValue()));
+  auto val2 = this->Eval(op);
   ASSERT_EQ(val2.ValueBool(), true);
 }
 
-TEST_F(ExpressionEvaluatorTest, LabelsTest) {
-  auto v1 = dba.InsertVertex();
-  ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("ANIMAL")).HasValue());
-  ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("DOG")).HasValue());
-  ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("NICE_DOG")).HasValue());
-  dba.AdvanceCommand();
-  auto *identifier = storage.Create<Identifier>("n");
-  auto node_symbol = symbol_table.CreateSymbol("n", true);
+TYPED_TEST(ExpressionEvaluatorTest, LabelsTest) {
+  auto v1 = this->dba.InsertVertex();
+  ASSERT_TRUE(v1.AddLabel(this->dba.NameToLabel("ANIMAL")).HasValue());
+  ASSERT_TRUE(v1.AddLabel(this->dba.NameToLabel("DOG")).HasValue());
+  ASSERT_TRUE(v1.AddLabel(this->dba.NameToLabel("NICE_DOG")).HasValue());
+  this->dba.AdvanceCommand();
+  auto *identifier = this->storage.template Create<Identifier>("n");
+  auto node_symbol = this->symbol_table.CreateSymbol("n", true);
   identifier->MapTo(node_symbol);
-  frame[node_symbol] = TypedValue(v1);
+  this->frame[node_symbol] = TypedValue(v1);
   {
-    auto *op = storage.Create<LabelsTest>(
-        identifier, std::vector<LabelIx>{storage.GetLabelIx("DOG"), storage.GetLabelIx("ANIMAL")});
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<LabelsTest>(
+        identifier, std::vector<LabelIx>{this->storage.GetLabelIx("DOG"), this->storage.GetLabelIx("ANIMAL")});
+    auto value = this->Eval(op);
     EXPECT_EQ(value.ValueBool(), true);
   }
   {
-    auto *op = storage.Create<LabelsTest>(
-        identifier,
-        std::vector<LabelIx>{storage.GetLabelIx("DOG"), storage.GetLabelIx("BAD_DOG"), storage.GetLabelIx("ANIMAL")});
-    auto value = Eval(op);
+    auto *op = this->storage.template Create<LabelsTest>(
+        identifier, std::vector<LabelIx>{this->storage.GetLabelIx("DOG"), this->storage.GetLabelIx("BAD_DOG"),
+                                         this->storage.GetLabelIx("ANIMAL")});
+    auto value = this->Eval(op);
     EXPECT_EQ(value.ValueBool(), false);
   }
   {
-    frame[node_symbol] = TypedValue();
-    auto *op = storage.Create<LabelsTest>(
-        identifier,
-        std::vector<LabelIx>{storage.GetLabelIx("DOG"), storage.GetLabelIx("BAD_DOG"), storage.GetLabelIx("ANIMAL")});
-    auto value = Eval(op);
+    this->frame[node_symbol] = TypedValue();
+    auto *op = this->storage.template Create<LabelsTest>(
+        identifier, std::vector<LabelIx>{this->storage.GetLabelIx("DOG"), this->storage.GetLabelIx("BAD_DOG"),
+                                         this->storage.GetLabelIx("ANIMAL")});
+    auto value = this->Eval(op);
     EXPECT_TRUE(value.IsNull());
   }
 }
 
-TEST_F(ExpressionEvaluatorTest, Aggregation) {
-  auto aggr = storage.Create<Aggregation>(storage.Create<PrimitiveLiteral>(42), nullptr, Aggregation::Op::COUNT, false);
-  auto aggr_sym = symbol_table.CreateSymbol("aggr", true);
+TYPED_TEST(ExpressionEvaluatorTest, Aggregation) {
+  auto aggr = this->storage.template Create<Aggregation>(this->storage.template Create<PrimitiveLiteral>(42), nullptr,
+                                                         Aggregation::Op::COUNT, false);
+  auto aggr_sym = this->symbol_table.CreateSymbol("aggr", true);
   aggr->MapTo(aggr_sym);
-  frame[aggr_sym] = TypedValue(1);
-  auto value = Eval(aggr);
+  this->frame[aggr_sym] = TypedValue(1);
+  auto value = this->Eval(aggr);
   EXPECT_EQ(value.ValueInt(), 1);
 }
 
-TEST_F(ExpressionEvaluatorTest, ListLiteral) {
-  auto *list_literal = storage.Create<ListLiteral>(std::vector<Expression *>{storage.Create<PrimitiveLiteral>(1),
-                                                                             storage.Create<PrimitiveLiteral>("bla"),
-                                                                             storage.Create<PrimitiveLiteral>(true)});
-  TypedValue result = Eval(list_literal);
+TYPED_TEST(ExpressionEvaluatorTest, ListLiteral) {
+  auto *list_literal = this->storage.template Create<ListLiteral>(std::vector<Expression *>{
+      this->storage.template Create<PrimitiveLiteral>(1), this->storage.template Create<PrimitiveLiteral>("bla"),
+      this->storage.template Create<PrimitiveLiteral>(true)});
+  TypedValue result = this->Eval(list_literal);
   ASSERT_TRUE(result.IsList());
   auto &result_elems = result.ValueList();
   ASSERT_EQ(3, result_elems.size());
@@ -737,289 +827,289 @@ TEST_F(ExpressionEvaluatorTest, ListLiteral) {
   ;
 }
 
-TEST_F(ExpressionEvaluatorTest, ParameterLookup) {
-  ctx.parameters.Add(0, memgraph::storage::PropertyValue(42));
-  auto *param_lookup = storage.Create<ParameterLookup>(0);
-  auto value = Eval(param_lookup);
+TYPED_TEST(ExpressionEvaluatorTest, ParameterLookup) {
+  this->ctx.parameters.Add(0, memgraph::storage::PropertyValue(42));
+  auto *param_lookup = this->storage.template Create<ParameterLookup>(0);
+  auto value = this->Eval(param_lookup);
   ASSERT_TRUE(value.IsInt());
   EXPECT_EQ(value.ValueInt(), 42);
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionAll1) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionAll1) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *all = ALL("x", LIST(LITERAL(1), LITERAL(1)), WHERE(EQ(ident_x, LITERAL(1))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   all->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(all);
+  auto value = this->Eval(all);
   ASSERT_TRUE(value.IsBool());
   EXPECT_TRUE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionAll2) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionAll2) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *all = ALL("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   all->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(all);
+  auto value = this->Eval(all);
   ASSERT_TRUE(value.IsBool());
   EXPECT_FALSE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionAllNullList) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionAllNullList) {
   AstStorage storage;
   auto *all = ALL("x", LITERAL(memgraph::storage::PropertyValue()), WHERE(LITERAL(true)));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   all->identifier_->MapTo(x_sym);
-  auto value = Eval(all);
+  auto value = this->Eval(all);
   EXPECT_TRUE(value.IsNull());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionAllNullElementInList1) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionAllNullElementInList1) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *all = ALL("x", LIST(LITERAL(1), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(1))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   all->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(all);
+  auto value = this->Eval(all);
   ASSERT_TRUE(value.IsBool());
   EXPECT_FALSE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionAllNullElementInList2) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionAllNullElementInList2) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *all = ALL("x", LIST(LITERAL(2), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(1))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   all->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(all);
+  auto value = this->Eval(all);
   ASSERT_TRUE(value.IsBool());
   EXPECT_FALSE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionAllWhereWrongType) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionAllWhereWrongType) {
   AstStorage storage;
   auto *all = ALL("x", LIST(LITERAL(1)), WHERE(LITERAL(2)));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   all->identifier_->MapTo(x_sym);
-  EXPECT_THROW(Eval(all), QueryRuntimeException);
+  EXPECT_THROW(this->Eval(all), QueryRuntimeException);
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionSingle1) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionSingle1) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *single = SINGLE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   single->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(single);
+  auto value = this->Eval(single);
   ASSERT_TRUE(value.IsBool());
   EXPECT_TRUE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionSingle2) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionSingle2) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *single = SINGLE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(GREATER(ident_x, LITERAL(0))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   single->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(single);
+  auto value = this->Eval(single);
   ASSERT_TRUE(value.IsBool());
   EXPECT_FALSE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionSingleNullList) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionSingleNullList) {
   AstStorage storage;
   auto *single = SINGLE("x", LITERAL(memgraph::storage::PropertyValue()), WHERE(LITERAL(true)));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   single->identifier_->MapTo(x_sym);
-  auto value = Eval(single);
+  auto value = this->Eval(single);
   EXPECT_TRUE(value.IsNull());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionSingleNullElementInList1) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionSingleNullElementInList1) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *single =
       SINGLE("x", LIST(LITERAL(1), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(1))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   single->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(single);
+  auto value = this->Eval(single);
   ASSERT_TRUE(value.IsBool());
   EXPECT_TRUE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionSingleNullElementInList2) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionSingleNullElementInList2) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *single =
       SINGLE("x", LIST(LITERAL(2), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(1))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   single->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(single);
+  auto value = this->Eval(single);
   ASSERT_TRUE(value.IsBool());
   EXPECT_FALSE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionAny1) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionAny1) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *any = ANY("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   any->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(any);
+  auto value = this->Eval(any);
   ASSERT_TRUE(value.IsBool());
   EXPECT_TRUE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionAny2) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionAny2) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *any = ANY("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(0))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   any->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(any);
+  auto value = this->Eval(any);
   ASSERT_TRUE(value.IsBool());
   EXPECT_FALSE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionAnyNullList) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionAnyNullList) {
   AstStorage storage;
   auto *any = ANY("x", LITERAL(memgraph::storage::PropertyValue()), WHERE(LITERAL(true)));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   any->identifier_->MapTo(x_sym);
-  auto value = Eval(any);
+  auto value = this->Eval(any);
   EXPECT_TRUE(value.IsNull());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionAnyNullElementInList1) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionAnyNullElementInList1) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *any = ANY("x", LIST(LITERAL(0), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(0))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   any->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(any);
+  auto value = this->Eval(any);
   EXPECT_TRUE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionAnyNullElementInList2) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionAnyNullElementInList2) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *any = ANY("x", LIST(LITERAL(1), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(0))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   any->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(any);
+  auto value = this->Eval(any);
   EXPECT_FALSE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionAnyWhereWrongType) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionAnyWhereWrongType) {
   AstStorage storage;
   auto *any = ANY("x", LIST(LITERAL(1)), WHERE(LITERAL(2)));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   any->identifier_->MapTo(x_sym);
-  EXPECT_THROW(Eval(any), QueryRuntimeException);
+  EXPECT_THROW(this->Eval(any), QueryRuntimeException);
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionNone1) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionNone1) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *none = NONE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(0))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   none->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(none);
+  auto value = this->Eval(none);
   ASSERT_TRUE(value.IsBool());
   EXPECT_TRUE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionNone2) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionNone2) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *none = NONE("x", LIST(LITERAL(1), LITERAL(2)), WHERE(EQ(ident_x, LITERAL(1))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   none->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(none);
+  auto value = this->Eval(none);
   ASSERT_TRUE(value.IsBool());
   EXPECT_FALSE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionNoneNullList) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionNoneNullList) {
   AstStorage storage;
   auto *none = NONE("x", LITERAL(memgraph::storage::PropertyValue()), WHERE(LITERAL(true)));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   none->identifier_->MapTo(x_sym);
-  auto value = Eval(none);
+  auto value = this->Eval(none);
   EXPECT_TRUE(value.IsNull());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionNoneNullElementInList1) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionNoneNullElementInList1) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *any = NONE("x", LIST(LITERAL(1), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(0))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   any->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(any);
+  auto value = this->Eval(any);
   EXPECT_TRUE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionNoneNullElementInList2) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionNoneNullElementInList2) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *none = NONE("x", LIST(LITERAL(0), LITERAL(memgraph::storage::PropertyValue())), WHERE(EQ(ident_x, LITERAL(0))));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   none->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(none);
+  auto value = this->Eval(none);
   EXPECT_FALSE(value.ValueBool());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionNoneWhereWrongType) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionNoneWhereWrongType) {
   AstStorage storage;
   auto *none = NONE("x", LIST(LITERAL(1)), WHERE(LITERAL(2)));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   none->identifier_->MapTo(x_sym);
-  EXPECT_THROW(Eval(none), QueryRuntimeException);
+  EXPECT_THROW(this->Eval(none), QueryRuntimeException);
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionReduce) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionReduce) {
   AstStorage storage;
   auto *ident_sum = IDENT("sum");
   auto *ident_x = IDENT("x");
   auto *reduce = REDUCE("sum", LITERAL(0), "x", LIST(LITERAL(1), LITERAL(2)), ADD(ident_sum, ident_x));
-  const auto sum_sym = symbol_table.CreateSymbol("sum", true);
+  const auto sum_sym = this->symbol_table.CreateSymbol("sum", true);
   reduce->accumulator_->MapTo(sum_sym);
   ident_sum->MapTo(sum_sym);
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   reduce->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(reduce);
+  auto value = this->Eval(reduce);
   ASSERT_TRUE(value.IsInt());
   EXPECT_EQ(value.ValueInt(), 3);
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionExtract) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionExtract) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *extract =
       EXTRACT("x", LIST(LITERAL(1), LITERAL(2), LITERAL(memgraph::storage::PropertyValue())), ADD(ident_x, LITERAL(1)));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   extract->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(extract);
+  auto value = this->Eval(extract);
   EXPECT_TRUE(value.IsList());
   ;
   auto result = value.ValueList();
@@ -1028,344 +1118,359 @@ TEST_F(ExpressionEvaluatorTest, FunctionExtract) {
   EXPECT_TRUE(result[2].IsNull());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionExtractNull) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionExtractNull) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *extract = EXTRACT("x", LITERAL(memgraph::storage::PropertyValue()), ADD(ident_x, LITERAL(1)));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   extract->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  auto value = Eval(extract);
+  auto value = this->Eval(extract);
   EXPECT_TRUE(value.IsNull());
 }
 
-TEST_F(ExpressionEvaluatorTest, FunctionExtractExceptions) {
+TYPED_TEST(ExpressionEvaluatorTest, FunctionExtractExceptions) {
   AstStorage storage;
   auto *ident_x = IDENT("x");
   auto *extract = EXTRACT("x", LITERAL("bla"), ADD(ident_x, LITERAL(1)));
-  const auto x_sym = symbol_table.CreateSymbol("x", true);
+  const auto x_sym = this->symbol_table.CreateSymbol("x", true);
   extract->identifier_->MapTo(x_sym);
   ident_x->MapTo(x_sym);
-  EXPECT_THROW(Eval(extract), QueryRuntimeException);
+  EXPECT_THROW(this->Eval(extract), QueryRuntimeException);
 }
 
-TEST_F(ExpressionEvaluatorTest, Coalesce) {
+TYPED_TEST(ExpressionEvaluatorTest, Coalesce) {
   // coalesce()
-  EXPECT_THROW(Eval(COALESCE()), QueryRuntimeException);
+  EXPECT_THROW(this->Eval(COALESCE()), QueryRuntimeException);
 
   // coalesce(null, null)
-  EXPECT_TRUE(Eval(COALESCE(LITERAL(TypedValue()), LITERAL(TypedValue()))).IsNull());
+  EXPECT_TRUE(this->Eval(COALESCE(LITERAL(TypedValue()), LITERAL(TypedValue()))).IsNull());
 
   // coalesce(null, 2, 3)
-  EXPECT_EQ(Eval(COALESCE(LITERAL(TypedValue()), LITERAL(2), LITERAL(3))).ValueInt(), 2);
+  EXPECT_EQ(this->Eval(COALESCE(LITERAL(TypedValue()), LITERAL(2), LITERAL(3))).ValueInt(), 2);
 
   // coalesce(null, 2, assert(false), 3)
-  EXPECT_EQ(Eval(COALESCE(LITERAL(TypedValue()), LITERAL(2), FN("ASSERT", LITERAL(false)), LITERAL(3))).ValueInt(), 2);
+  EXPECT_EQ(
+      this->Eval(COALESCE(LITERAL(TypedValue()), LITERAL(2), FN("ASSERT", LITERAL(false)), LITERAL(3))).ValueInt(), 2);
 
   // (null, assert(false))
-  EXPECT_THROW(Eval(COALESCE(LITERAL(TypedValue()), FN("ASSERT", LITERAL(false)))), QueryRuntimeException);
+  EXPECT_THROW(this->Eval(COALESCE(LITERAL(TypedValue()), FN("ASSERT", LITERAL(false)))), QueryRuntimeException);
 
   // coalesce([null, null])
-  EXPECT_FALSE(Eval(COALESCE(LITERAL(TypedValue(std::vector<TypedValue>{TypedValue(), TypedValue()})))).IsNull());
+  EXPECT_FALSE(this->Eval(COALESCE(LITERAL(TypedValue(std::vector<TypedValue>{TypedValue(), TypedValue()})))).IsNull());
 }
 
-TEST_F(ExpressionEvaluatorTest, RegexMatchInvalidArguments) {
-  EXPECT_TRUE(Eval(storage.Create<RegexMatch>(LITERAL(TypedValue()), LITERAL("regex"))).IsNull());
-  EXPECT_TRUE(Eval(storage.Create<RegexMatch>(LITERAL(3), LITERAL("regex"))).IsNull());
-  EXPECT_TRUE(Eval(storage.Create<RegexMatch>(LIST(LITERAL("string")), LITERAL("regex"))).IsNull());
-  EXPECT_TRUE(Eval(storage.Create<RegexMatch>(LITERAL("string"), LITERAL(TypedValue()))).IsNull());
-  EXPECT_THROW(Eval(storage.Create<RegexMatch>(LITERAL("string"), LITERAL(42))), QueryRuntimeException);
-  EXPECT_THROW(Eval(storage.Create<RegexMatch>(LITERAL("string"), LIST(LITERAL("regex")))), QueryRuntimeException);
+TYPED_TEST(ExpressionEvaluatorTest, RegexMatchInvalidArguments) {
+  EXPECT_TRUE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL(TypedValue()), LITERAL("regex"))).IsNull());
+  EXPECT_TRUE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL(3), LITERAL("regex"))).IsNull());
+  EXPECT_TRUE(
+      this->Eval(this->storage.template Create<RegexMatch>(LIST(LITERAL("string")), LITERAL("regex"))).IsNull());
+  EXPECT_TRUE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("string"), LITERAL(TypedValue()))).IsNull());
+  EXPECT_THROW(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("string"), LITERAL(42))),
+               QueryRuntimeException);
+  EXPECT_THROW(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("string"), LIST(LITERAL("regex")))),
+               QueryRuntimeException);
 }
 
-TEST_F(ExpressionEvaluatorTest, RegexMatchInvalidRegex) {
-  EXPECT_THROW(Eval(storage.Create<RegexMatch>(LITERAL("text"), LITERAL("*ext"))), QueryRuntimeException);
-  EXPECT_THROW(Eval(storage.Create<RegexMatch>(LITERAL("text"), LITERAL("[ext"))), QueryRuntimeException);
+TYPED_TEST(ExpressionEvaluatorTest, RegexMatchInvalidRegex) {
+  EXPECT_THROW(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("text"), LITERAL("*ext"))),
+               QueryRuntimeException);
+  EXPECT_THROW(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("text"), LITERAL("[ext"))),
+               QueryRuntimeException);
 }
 
-TEST_F(ExpressionEvaluatorTest, RegexMatch) {
-  EXPECT_FALSE(Eval(storage.Create<RegexMatch>(LITERAL("text"), LITERAL(".*ex"))).ValueBool());
-  EXPECT_TRUE(Eval(storage.Create<RegexMatch>(LITERAL("text"), LITERAL(".*ext"))).ValueBool());
-  EXPECT_FALSE(Eval(storage.Create<RegexMatch>(LITERAL("text"), LITERAL("[ext]"))).ValueBool());
-  EXPECT_TRUE(Eval(storage.Create<RegexMatch>(LITERAL("text"), LITERAL(".+[ext]"))).ValueBool());
+TYPED_TEST(ExpressionEvaluatorTest, RegexMatch) {
+  EXPECT_FALSE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("text"), LITERAL(".*ex"))).ValueBool());
+  EXPECT_TRUE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("text"), LITERAL(".*ext"))).ValueBool());
+  EXPECT_FALSE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("text"), LITERAL("[ext]"))).ValueBool());
+  EXPECT_TRUE(this->Eval(this->storage.template Create<RegexMatch>(LITERAL("text"), LITERAL(".+[ext]"))).ValueBool());
 }
 
-class ExpressionEvaluatorPropertyLookup : public ExpressionEvaluatorTest {
+template <typename StorageType>
+class ExpressionEvaluatorPropertyLookup : public ExpressionEvaluatorTest<StorageType> {
  protected:
-  std::pair<std::string, memgraph::storage::PropertyId> prop_age = std::make_pair("age", dba.NameToProperty("age"));
+  std::pair<std::string, memgraph::storage::PropertyId> prop_age =
+      std::make_pair("age", this->dba.NameToProperty("age"));
   std::pair<std::string, memgraph::storage::PropertyId> prop_height =
-      std::make_pair("height", dba.NameToProperty("height"));
-  Identifier *identifier = storage.Create<Identifier>("element");
-  Symbol symbol = symbol_table.CreateSymbol("element", true);
+      std::make_pair("height", this->dba.NameToProperty("height"));
+  Identifier *identifier = this->storage.template Create<Identifier>("element");
+  Symbol symbol = this->symbol_table.CreateSymbol("element", true);
 
   void SetUp() { identifier->MapTo(symbol); }
 
   auto Value(std::pair<std::string, memgraph::storage::PropertyId> property) {
-    auto *op = storage.Create<PropertyLookup>(identifier, storage.GetPropertyIx(property.first));
-    return Eval(op);
+    auto *op = this->storage.template Create<PropertyLookup>(identifier, this->storage.GetPropertyIx(property.first));
+    return this->Eval(op);
   }
 };
 
-TEST_F(ExpressionEvaluatorPropertyLookup, Vertex) {
-  auto v1 = dba.InsertVertex();
-  ASSERT_TRUE(v1.SetProperty(prop_age.second, memgraph::storage::PropertyValue(10)).HasValue());
-  dba.AdvanceCommand();
-  frame[symbol] = TypedValue(v1);
-  EXPECT_EQ(Value(prop_age).ValueInt(), 10);
-  EXPECT_TRUE(Value(prop_height).IsNull());
+TYPED_TEST_CASE(ExpressionEvaluatorPropertyLookup, StorageTypes);
+
+TYPED_TEST(ExpressionEvaluatorPropertyLookup, Vertex) {
+  auto v1 = this->dba.InsertVertex();
+  ASSERT_TRUE(v1.SetProperty(this->prop_age.second, memgraph::storage::PropertyValue(10)).HasValue());
+  this->dba.AdvanceCommand();
+  this->frame[this->symbol] = TypedValue(v1);
+  EXPECT_EQ(this->Value(this->prop_age).ValueInt(), 10);
+  EXPECT_TRUE(this->Value(this->prop_height).IsNull());
 }
 
-TEST_F(ExpressionEvaluatorPropertyLookup, Duration) {
+TYPED_TEST(ExpressionEvaluatorPropertyLookup, Duration) {
   const memgraph::utils::Duration dur({10, 1, 30, 2, 22, 45});
-  frame[symbol] = TypedValue(dur);
+  this->frame[this->symbol] = TypedValue(dur);
 
-  const std::pair day = std::make_pair("day", dba.NameToProperty("day"));
-  const auto total_days = Value(day);
+  const std::pair day = std::make_pair("day", this->dba.NameToProperty("day"));
+  const auto total_days = this->Value(day);
   EXPECT_TRUE(total_days.IsInt());
   EXPECT_EQ(total_days.ValueInt(), 10);
 
-  const std::pair hour = std::make_pair("hour", dba.NameToProperty("hour"));
-  const auto total_hours = Value(hour);
+  const std::pair hour = std::make_pair("hour", this->dba.NameToProperty("hour"));
+  const auto total_hours = this->Value(hour);
   EXPECT_TRUE(total_hours.IsInt());
   EXPECT_EQ(total_hours.ValueInt(), 1);
 
-  const std::pair minute = std::make_pair("minute", dba.NameToProperty("minute"));
-  const auto total_mins = Value(minute);
+  const std::pair minute = std::make_pair("minute", this->dba.NameToProperty("minute"));
+  const auto total_mins = this->Value(minute);
   EXPECT_TRUE(total_mins.IsInt());
 
   EXPECT_EQ(total_mins.ValueInt(), 1 * 60 + 30);
 
-  const std::pair sec = std::make_pair("second", dba.NameToProperty("second"));
-  const auto total_secs = Value(sec);
+  const std::pair sec = std::make_pair("second", this->dba.NameToProperty("second"));
+  const auto total_secs = this->Value(sec);
   EXPECT_TRUE(total_secs.IsInt());
   const auto expected_secs = total_mins.ValueInt() * 60 + 2;
   EXPECT_EQ(total_secs.ValueInt(), expected_secs);
 
-  const std::pair milli = std::make_pair("millisecond", dba.NameToProperty("millisecond"));
-  const auto total_milli = Value(milli);
+  const std::pair milli = std::make_pair("millisecond", this->dba.NameToProperty("millisecond"));
+  const auto total_milli = this->Value(milli);
   EXPECT_TRUE(total_milli.IsInt());
   const auto expected_milli = total_secs.ValueInt() * 1000 + 22;
   EXPECT_EQ(total_milli.ValueInt(), expected_milli);
 
-  const std::pair micro = std::make_pair("microsecond", dba.NameToProperty("microsecond"));
-  const auto total_micros = Value(micro);
+  const std::pair micro = std::make_pair("microsecond", this->dba.NameToProperty("microsecond"));
+  const auto total_micros = this->Value(micro);
   EXPECT_TRUE(total_micros.IsInt());
   const auto expected_micros = expected_milli * 1000 + 45;
   EXPECT_EQ(total_micros.ValueInt(), expected_micros);
 
-  const std::pair nano = std::make_pair("nanosecond", dba.NameToProperty("nanosecond"));
-  const auto total_nano = Value(nano);
+  const std::pair nano = std::make_pair("nanosecond", this->dba.NameToProperty("nanosecond"));
+  const auto total_nano = this->Value(nano);
   EXPECT_TRUE(total_nano.IsInt());
   const auto expected_nano = expected_micros * 1000;
   EXPECT_EQ(total_nano.ValueInt(), expected_nano);
 }
 
-TEST_F(ExpressionEvaluatorPropertyLookup, Date) {
+TYPED_TEST(ExpressionEvaluatorPropertyLookup, Date) {
   const memgraph::utils::Date date({1996, 11, 22});
-  frame[symbol] = TypedValue(date);
+  this->frame[this->symbol] = TypedValue(date);
 
-  const std::pair year = std::make_pair("year", dba.NameToProperty("year"));
-  const auto y = Value(year);
+  const std::pair year = std::make_pair("year", this->dba.NameToProperty("year"));
+  const auto y = this->Value(year);
   EXPECT_TRUE(y.IsInt());
   EXPECT_EQ(y.ValueInt(), 1996);
 
-  const std::pair month = std::make_pair("month", dba.NameToProperty("month"));
-  const auto m = Value(month);
+  const std::pair month = std::make_pair("month", this->dba.NameToProperty("month"));
+  const auto m = this->Value(month);
   EXPECT_TRUE(m.IsInt());
   EXPECT_EQ(m.ValueInt(), 11);
 
-  const std::pair day = std::make_pair("day", dba.NameToProperty("day"));
-  const auto d = Value(day);
+  const std::pair day = std::make_pair("day", this->dba.NameToProperty("day"));
+  const auto d = this->Value(day);
   EXPECT_TRUE(d.IsInt());
   EXPECT_EQ(d.ValueInt(), 22);
 }
 
-TEST_F(ExpressionEvaluatorPropertyLookup, LocalTime) {
+TYPED_TEST(ExpressionEvaluatorPropertyLookup, LocalTime) {
   const memgraph::utils::LocalTime lt({1, 2, 3, 11, 22});
-  frame[symbol] = TypedValue(lt);
+  this->frame[this->symbol] = TypedValue(lt);
 
-  const std::pair hour = std::make_pair("hour", dba.NameToProperty("hour"));
-  const auto h = Value(hour);
+  const std::pair hour = std::make_pair("hour", this->dba.NameToProperty("hour"));
+  const auto h = this->Value(hour);
   EXPECT_TRUE(h.IsInt());
   EXPECT_EQ(h.ValueInt(), 1);
 
-  const std::pair minute = std::make_pair("minute", dba.NameToProperty("minute"));
-  const auto min = Value(minute);
+  const std::pair minute = std::make_pair("minute", this->dba.NameToProperty("minute"));
+  const auto min = this->Value(minute);
   EXPECT_TRUE(min.IsInt());
   EXPECT_EQ(min.ValueInt(), 2);
 
-  const std::pair second = std::make_pair("second", dba.NameToProperty("second"));
-  const auto sec = Value(second);
+  const std::pair second = std::make_pair("second", this->dba.NameToProperty("second"));
+  const auto sec = this->Value(second);
   EXPECT_TRUE(sec.IsInt());
   EXPECT_EQ(sec.ValueInt(), 3);
 
-  const std::pair millis = std::make_pair("millisecond", dba.NameToProperty("millisecond"));
-  const auto mil = Value(millis);
+  const std::pair millis = std::make_pair("millisecond", this->dba.NameToProperty("millisecond"));
+  const auto mil = this->Value(millis);
   EXPECT_TRUE(mil.IsInt());
   EXPECT_EQ(mil.ValueInt(), 11);
 
-  const std::pair micros = std::make_pair("microsecond", dba.NameToProperty("microsecond"));
-  const auto mic = Value(micros);
+  const std::pair micros = std::make_pair("microsecond", this->dba.NameToProperty("microsecond"));
+  const auto mic = this->Value(micros);
   EXPECT_TRUE(mic.IsInt());
   EXPECT_EQ(mic.ValueInt(), 22);
 }
 
-TEST_F(ExpressionEvaluatorPropertyLookup, LocalDateTime) {
+TYPED_TEST(ExpressionEvaluatorPropertyLookup, LocalDateTime) {
   const memgraph::utils::LocalDateTime ldt({1993, 8, 6}, {2, 3, 4, 55, 40});
-  frame[symbol] = TypedValue(ldt);
+  this->frame[this->symbol] = TypedValue(ldt);
 
-  const std::pair year = std::make_pair("year", dba.NameToProperty("year"));
-  const auto y = Value(year);
+  const std::pair year = std::make_pair("year", this->dba.NameToProperty("year"));
+  const auto y = this->Value(year);
   EXPECT_TRUE(y.IsInt());
   EXPECT_EQ(y.ValueInt(), 1993);
 
-  const std::pair month = std::make_pair("month", dba.NameToProperty("month"));
-  const auto m = Value(month);
+  const std::pair month = std::make_pair("month", this->dba.NameToProperty("month"));
+  const auto m = this->Value(month);
   EXPECT_TRUE(m.IsInt());
   EXPECT_EQ(m.ValueInt(), 8);
 
-  const std::pair day = std::make_pair("day", dba.NameToProperty("day"));
-  const auto d = Value(day);
+  const std::pair day = std::make_pair("day", this->dba.NameToProperty("day"));
+  const auto d = this->Value(day);
   EXPECT_TRUE(d.IsInt());
   EXPECT_EQ(d.ValueInt(), 6);
 
-  const std::pair hour = std::make_pair("hour", dba.NameToProperty("hour"));
-  const auto h = Value(hour);
+  const std::pair hour = std::make_pair("hour", this->dba.NameToProperty("hour"));
+  const auto h = this->Value(hour);
   EXPECT_TRUE(h.IsInt());
   EXPECT_EQ(h.ValueInt(), 2);
 
-  const std::pair minute = std::make_pair("minute", dba.NameToProperty("minute"));
-  const auto min = Value(minute);
+  const std::pair minute = std::make_pair("minute", this->dba.NameToProperty("minute"));
+  const auto min = this->Value(minute);
   EXPECT_TRUE(min.IsInt());
   EXPECT_EQ(min.ValueInt(), 3);
 
-  const std::pair second = std::make_pair("second", dba.NameToProperty("second"));
-  const auto sec = Value(second);
+  const std::pair second = std::make_pair("second", this->dba.NameToProperty("second"));
+  const auto sec = this->Value(second);
   EXPECT_TRUE(sec.IsInt());
   EXPECT_EQ(sec.ValueInt(), 4);
 
-  const std::pair millis = std::make_pair("millisecond", dba.NameToProperty("millisecond"));
-  const auto mil = Value(millis);
+  const std::pair millis = std::make_pair("millisecond", this->dba.NameToProperty("millisecond"));
+  const auto mil = this->Value(millis);
   EXPECT_TRUE(mil.IsInt());
   EXPECT_EQ(mil.ValueInt(), 55);
 
-  const std::pair micros = std::make_pair("microsecond", dba.NameToProperty("microsecond"));
-  const auto mic = Value(micros);
+  const std::pair micros = std::make_pair("microsecond", this->dba.NameToProperty("microsecond"));
+  const auto mic = this->Value(micros);
   EXPECT_TRUE(mic.IsInt());
   EXPECT_EQ(mic.ValueInt(), 40);
 }
 
-TEST_F(ExpressionEvaluatorPropertyLookup, Edge) {
-  auto v1 = dba.InsertVertex();
-  auto v2 = dba.InsertVertex();
-  auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("edge_type"));
+TYPED_TEST(ExpressionEvaluatorPropertyLookup, Edge) {
+  auto v1 = this->dba.InsertVertex();
+  auto v2 = this->dba.InsertVertex();
+  auto e12 = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("edge_type"));
   ASSERT_TRUE(e12.HasValue());
-  ASSERT_TRUE(e12->SetProperty(prop_age.second, memgraph::storage::PropertyValue(10)).HasValue());
-  dba.AdvanceCommand();
-  frame[symbol] = TypedValue(*e12);
-  EXPECT_EQ(Value(prop_age).ValueInt(), 10);
-  EXPECT_TRUE(Value(prop_height).IsNull());
+  ASSERT_TRUE(e12->SetProperty(this->prop_age.second, memgraph::storage::PropertyValue(10)).HasValue());
+  this->dba.AdvanceCommand();
+  this->frame[this->symbol] = TypedValue(*e12);
+  EXPECT_EQ(this->Value(this->prop_age).ValueInt(), 10);
+  EXPECT_TRUE(this->Value(this->prop_height).IsNull());
 }
 
-TEST_F(ExpressionEvaluatorPropertyLookup, Null) {
-  frame[symbol] = TypedValue();
-  EXPECT_TRUE(Value(prop_age).IsNull());
+TYPED_TEST(ExpressionEvaluatorPropertyLookup, Null) {
+  this->frame[this->symbol] = TypedValue();
+  EXPECT_TRUE(this->Value(this->prop_age).IsNull());
 }
 
-TEST_F(ExpressionEvaluatorPropertyLookup, Map) {
-  frame[symbol] = TypedValue(std::map<std::string, TypedValue>{{prop_age.first, TypedValue(10)}});
-  EXPECT_EQ(Value(prop_age).ValueInt(), 10);
-  EXPECT_TRUE(Value(prop_height).IsNull());
+TYPED_TEST(ExpressionEvaluatorPropertyLookup, Map) {
+  this->frame[this->symbol] = TypedValue(std::map<std::string, TypedValue>{{this->prop_age.first, TypedValue(10)}});
+  EXPECT_EQ(this->Value(this->prop_age).ValueInt(), 10);
+  EXPECT_TRUE(this->Value(this->prop_height).IsNull());
 }
 
-class ExpressionEvaluatorAllPropertiesLookup : public ExpressionEvaluatorTest {
+template <typename StorageType>
+class ExpressionEvaluatorAllPropertiesLookup : public ExpressionEvaluatorTest<StorageType> {
  protected:
-  std::pair<std::string, memgraph::storage::PropertyId> prop_age = std::make_pair("age", dba.NameToProperty("age"));
+  std::pair<std::string, memgraph::storage::PropertyId> prop_age =
+      std::make_pair("age", this->dba.NameToProperty("age"));
   std::pair<std::string, memgraph::storage::PropertyId> prop_height =
-      std::make_pair("height", dba.NameToProperty("height"));
-  Identifier *identifier = storage.Create<Identifier>("element");
-  Symbol symbol = symbol_table.CreateSymbol("element", true);
+      std::make_pair("height", this->dba.NameToProperty("height"));
+  Identifier *identifier = this->storage.template Create<Identifier>("element");
+  Symbol symbol = this->symbol_table.CreateSymbol("element", true);
 
   void SetUp() { identifier->MapTo(symbol); }
 
   auto Value() {
-    auto *op = storage.Create<AllPropertiesLookup>(identifier);
-    return Eval(op);
+    auto *op = this->storage.template Create<AllPropertiesLookup>(identifier);
+    return this->Eval(op);
   }
 };
 
-TEST_F(ExpressionEvaluatorAllPropertiesLookup, Vertex) {
-  auto v1 = dba.InsertVertex();
-  ASSERT_TRUE(v1.SetProperty(prop_age.second, memgraph::storage::PropertyValue(10)).HasValue());
-  dba.AdvanceCommand();
-  frame[symbol] = TypedValue(v1);
-  auto all_properties = Value();
+TYPED_TEST_CASE(ExpressionEvaluatorAllPropertiesLookup, StorageTypes);
+
+TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Vertex) {
+  auto v1 = this->dba.InsertVertex();
+  ASSERT_TRUE(v1.SetProperty(this->prop_age.second, memgraph::storage::PropertyValue(10)).HasValue());
+  this->dba.AdvanceCommand();
+  this->frame[this->symbol] = TypedValue(v1);
+  auto all_properties = this->Value();
   EXPECT_TRUE(all_properties.IsMap());
 }
 
-TEST_F(ExpressionEvaluatorAllPropertiesLookup, Edge) {
-  auto v1 = dba.InsertVertex();
-  auto v2 = dba.InsertVertex();
-  auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("edge_type"));
+TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Edge) {
+  auto v1 = this->dba.InsertVertex();
+  auto v2 = this->dba.InsertVertex();
+  auto e12 = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("edge_type"));
   ASSERT_TRUE(e12.HasValue());
-  ASSERT_TRUE(e12->SetProperty(prop_age.second, memgraph::storage::PropertyValue(10)).HasValue());
-  dba.AdvanceCommand();
-  frame[symbol] = TypedValue(*e12);
-  auto all_properties = Value();
+  ASSERT_TRUE(e12->SetProperty(this->prop_age.second, memgraph::storage::PropertyValue(10)).HasValue());
+  this->dba.AdvanceCommand();
+  this->frame[this->symbol] = TypedValue(*e12);
+  auto all_properties = this->Value();
   EXPECT_TRUE(all_properties.IsMap());
 }
 
-TEST_F(ExpressionEvaluatorAllPropertiesLookup, Duration) {
+TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Duration) {
   const memgraph::utils::Duration dur({10, 1, 30, 2, 22, 45});
-  frame[symbol] = TypedValue(dur);
-  auto all_properties = Value();
+  this->frame[this->symbol] = TypedValue(dur);
+  auto all_properties = this->Value();
   EXPECT_TRUE(all_properties.IsMap());
 }
 
-TEST_F(ExpressionEvaluatorAllPropertiesLookup, Date) {
+TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Date) {
   const memgraph::utils::Date date({1996, 11, 22});
-  frame[symbol] = TypedValue(date);
-  auto all_properties = Value();
+  this->frame[this->symbol] = TypedValue(date);
+  auto all_properties = this->Value();
   EXPECT_TRUE(all_properties.IsMap());
 }
 
-TEST_F(ExpressionEvaluatorAllPropertiesLookup, LocalTime) {
+TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, LocalTime) {
   const memgraph::utils::LocalTime lt({1, 2, 3, 11, 22});
-  frame[symbol] = TypedValue(lt);
-  auto all_properties = Value();
+  this->frame[this->symbol] = TypedValue(lt);
+  auto all_properties = this->Value();
   EXPECT_TRUE(all_properties.IsMap());
 }
 
-TEST_F(ExpressionEvaluatorAllPropertiesLookup, LocalDateTime) {
+TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, LocalDateTime) {
   const memgraph::utils::LocalDateTime ldt({1993, 8, 6}, {2, 3, 4, 55, 40});
-  frame[symbol] = TypedValue(ldt);
-  auto all_properties = Value();
+  this->frame[this->symbol] = TypedValue(ldt);
+  auto all_properties = this->Value();
   EXPECT_TRUE(all_properties.IsMap());
 }
 
-TEST_F(ExpressionEvaluatorAllPropertiesLookup, Null) {
-  frame[symbol] = TypedValue();
-  auto all_properties = Value();
+TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Null) {
+  this->frame[this->symbol] = TypedValue();
+  auto all_properties = this->Value();
   EXPECT_TRUE(all_properties.IsNull());
 }
 
-TEST_F(ExpressionEvaluatorAllPropertiesLookup, Map) {
-  frame[symbol] = TypedValue(std::map<std::string, TypedValue>{{prop_age.first, TypedValue(10)}});
-  auto all_properties = Value();
+TYPED_TEST(ExpressionEvaluatorAllPropertiesLookup, Map) {
+  this->frame[this->symbol] = TypedValue(std::map<std::string, TypedValue>{{this->prop_age.first, TypedValue(10)}});
+  auto all_properties = this->Value();
   EXPECT_TRUE(all_properties.IsMap());
 }
 
-class FunctionTest : public ExpressionEvaluatorTest {
+template <typename StorageType>
+class FunctionTest : public ExpressionEvaluatorTest<StorageType> {
  protected:
   std::vector<Expression *> ExpressionsFromTypedValues(const std::vector<TypedValue> &tvs) {
     std::vector<Expression *> expressions;
     expressions.reserve(tvs.size());
 
     for (size_t i = 0; i < tvs.size(); ++i) {
-      auto *ident = storage.Create<Identifier>("arg_" + std::to_string(i), true);
-      auto sym = symbol_table.CreateSymbol("arg_" + std::to_string(i), true);
+      auto *ident = this->storage.template Create<Identifier>("arg_" + std::to_string(i), true);
+      auto sym = this->symbol_table.CreateSymbol("arg_" + std::to_string(i), true);
       ident->MapTo(sym);
-      frame[sym] = tvs[i];
+      this->frame[sym] = tvs[i];
       expressions.push_back(ident);
     }
 
@@ -1373,8 +1478,8 @@ class FunctionTest : public ExpressionEvaluatorTest {
   }
 
   TypedValue EvaluateFunctionWithExprs(const std::string &function_name, const std::vector<Expression *> &expressions) {
-    auto *op = storage.Create<Function>(function_name, expressions);
-    return Eval(op);
+    auto *op = this->storage.template Create<Function>(function_name, expressions);
+    return this->Eval(op);
   }
 
   template <class... TArgs>
@@ -1392,47 +1497,50 @@ class FunctionTest : public ExpressionEvaluatorTest {
   }
 };
 
+TYPED_TEST_CASE(FunctionTest, StorageTypes);
+
 template <class... TArgs>
 static TypedValue MakeTypedValueList(TArgs &&...args) {
   return TypedValue(std::vector<TypedValue>{TypedValue(args)...});
 }
 
-TEST_F(FunctionTest, EndNode) {
-  ASSERT_THROW(EvaluateFunction("ENDNODE"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("ENDNODE", TypedValue()).IsNull());
-  auto v1 = dba.InsertVertex();
-  ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("label1")).HasValue());
-  auto v2 = dba.InsertVertex();
-  ASSERT_TRUE(v2.AddLabel(dba.NameToLabel("label2")).HasValue());
-  auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t"));
+TYPED_TEST(FunctionTest, EndNode) {
+  ASSERT_THROW(this->EvaluateFunction("ENDNODE"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("ENDNODE", TypedValue()).IsNull());
+  auto v1 = this->dba.InsertVertex();
+  ASSERT_TRUE(v1.AddLabel(this->dba.NameToLabel("label1")).HasValue());
+  auto v2 = this->dba.InsertVertex();
+  ASSERT_TRUE(v2.AddLabel(this->dba.NameToLabel("label2")).HasValue());
+  auto e = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("t"));
   ASSERT_TRUE(e.HasValue());
-  ASSERT_TRUE(
-      *EvaluateFunction("ENDNODE", *e).ValueVertex().HasLabel(memgraph::storage::View::NEW, dba.NameToLabel("label2")));
-  ASSERT_THROW(EvaluateFunction("ENDNODE", 2), QueryRuntimeException);
+  ASSERT_TRUE(*this->EvaluateFunction("ENDNODE", *e)
+                   .ValueVertex()
+                   .HasLabel(memgraph::storage::View::NEW, this->dba.NameToLabel("label2")));
+  ASSERT_THROW(this->EvaluateFunction("ENDNODE", 2), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Head) {
-  ASSERT_THROW(EvaluateFunction("HEAD"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("HEAD", TypedValue()).IsNull());
+TYPED_TEST(FunctionTest, Head) {
+  ASSERT_THROW(this->EvaluateFunction("HEAD"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("HEAD", TypedValue()).IsNull());
   auto argument = MakeTypedValueList(3, 4, 5);
-  ASSERT_EQ(EvaluateFunction("HEAD", argument).ValueInt(), 3);
+  ASSERT_EQ(this->EvaluateFunction("HEAD", argument).ValueInt(), 3);
   argument.ValueList().clear();
-  ASSERT_TRUE(EvaluateFunction("HEAD", argument).IsNull());
-  ASSERT_THROW(EvaluateFunction("HEAD", 2), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("HEAD", argument).IsNull());
+  ASSERT_THROW(this->EvaluateFunction("HEAD", 2), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Properties) {
-  ASSERT_THROW(EvaluateFunction("PROPERTIES"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("PROPERTIES", TypedValue()).IsNull());
-  auto v1 = dba.InsertVertex();
-  ASSERT_TRUE(v1.SetProperty(dba.NameToProperty("height"), memgraph::storage::PropertyValue(5)).HasValue());
-  ASSERT_TRUE(v1.SetProperty(dba.NameToProperty("age"), memgraph::storage::PropertyValue(10)).HasValue());
-  auto v2 = dba.InsertVertex();
-  auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("type1"));
+TYPED_TEST(FunctionTest, Properties) {
+  ASSERT_THROW(this->EvaluateFunction("PROPERTIES"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("PROPERTIES", TypedValue()).IsNull());
+  auto v1 = this->dba.InsertVertex();
+  ASSERT_TRUE(v1.SetProperty(this->dba.NameToProperty("height"), memgraph::storage::PropertyValue(5)).HasValue());
+  ASSERT_TRUE(v1.SetProperty(this->dba.NameToProperty("age"), memgraph::storage::PropertyValue(10)).HasValue());
+  auto v2 = this->dba.InsertVertex();
+  auto e = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("type1"));
   ASSERT_TRUE(e.HasValue());
-  ASSERT_TRUE(e->SetProperty(dba.NameToProperty("height"), memgraph::storage::PropertyValue(3)).HasValue());
-  ASSERT_TRUE(e->SetProperty(dba.NameToProperty("age"), memgraph::storage::PropertyValue(15)).HasValue());
-  dba.AdvanceCommand();
+  ASSERT_TRUE(e->SetProperty(this->dba.NameToProperty("height"), memgraph::storage::PropertyValue(3)).HasValue());
+  ASSERT_TRUE(e->SetProperty(this->dba.NameToProperty("age"), memgraph::storage::PropertyValue(15)).HasValue());
+  this->dba.AdvanceCommand();
 
   auto prop_values_to_int = [](TypedValue t) {
     std::unordered_map<std::string, int> properties;
@@ -1442,228 +1550,228 @@ TEST_F(FunctionTest, Properties) {
     return properties;
   };
 
-  ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", v1)),
+  ASSERT_THAT(prop_values_to_int(this->EvaluateFunction("PROPERTIES", v1)),
               UnorderedElementsAre(testing::Pair("height", 5), testing::Pair("age", 10)));
-  ASSERT_THAT(prop_values_to_int(EvaluateFunction("PROPERTIES", *e)),
+  ASSERT_THAT(prop_values_to_int(this->EvaluateFunction("PROPERTIES", *e)),
               UnorderedElementsAre(testing::Pair("height", 3), testing::Pair("age", 15)));
-  ASSERT_THROW(EvaluateFunction("PROPERTIES", 2), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("PROPERTIES", 2), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Last) {
-  ASSERT_THROW(EvaluateFunction("LAST"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("LAST", TypedValue()).IsNull());
+TYPED_TEST(FunctionTest, Last) {
+  ASSERT_THROW(this->EvaluateFunction("LAST"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("LAST", TypedValue()).IsNull());
   auto argument = MakeTypedValueList(3, 4, 5);
-  ASSERT_EQ(EvaluateFunction("LAST", argument).ValueInt(), 5);
+  ASSERT_EQ(this->EvaluateFunction("LAST", argument).ValueInt(), 5);
   argument.ValueList().clear();
-  ASSERT_TRUE(EvaluateFunction("LAST", argument).IsNull());
-  ASSERT_THROW(EvaluateFunction("LAST", 5), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("LAST", argument).IsNull());
+  ASSERT_THROW(this->EvaluateFunction("LAST", 5), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Size) {
-  ASSERT_THROW(EvaluateFunction("SIZE"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("SIZE", TypedValue()).IsNull());
+TYPED_TEST(FunctionTest, Size) {
+  ASSERT_THROW(this->EvaluateFunction("SIZE"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("SIZE", TypedValue()).IsNull());
   auto argument = MakeTypedValueList(3, 4, 5);
-  ASSERT_EQ(EvaluateFunction("SIZE", argument).ValueInt(), 3);
-  ASSERT_EQ(EvaluateFunction("SIZE", "john").ValueInt(), 4);
-  ASSERT_EQ(EvaluateFunction("SIZE",
-                             std::map<std::string, TypedValue>{
-                                 {"a", TypedValue(5)}, {"b", TypedValue(true)}, {"c", TypedValue("123")}})
+  ASSERT_EQ(this->EvaluateFunction("SIZE", argument).ValueInt(), 3);
+  ASSERT_EQ(this->EvaluateFunction("SIZE", "john").ValueInt(), 4);
+  ASSERT_EQ(this->EvaluateFunction("SIZE",
+                                   std::map<std::string, TypedValue>{
+                                       {"a", TypedValue(5)}, {"b", TypedValue(true)}, {"c", TypedValue("123")}})
                 .ValueInt(),
             3);
-  ASSERT_THROW(EvaluateFunction("SIZE", 5), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("SIZE", 5), QueryRuntimeException);
 
-  auto v0 = dba.InsertVertex();
+  auto v0 = this->dba.InsertVertex();
   memgraph::query::Path path(v0);
-  EXPECT_EQ(EvaluateFunction("SIZE", path).ValueInt(), 0);
-  auto v1 = dba.InsertVertex();
-  auto edge = dba.InsertEdge(&v0, &v1, dba.NameToEdgeType("type"));
+  EXPECT_EQ(this->EvaluateFunction("SIZE", path).ValueInt(), 0);
+  auto v1 = this->dba.InsertVertex();
+  auto edge = this->dba.InsertEdge(&v0, &v1, this->dba.NameToEdgeType("type"));
   ASSERT_TRUE(edge.HasValue());
   path.Expand(*edge);
   path.Expand(v1);
-  EXPECT_EQ(EvaluateFunction("SIZE", path).ValueInt(), 1);
+  EXPECT_EQ(this->EvaluateFunction("SIZE", path).ValueInt(), 1);
 }
 
-TEST_F(FunctionTest, StartNode) {
-  ASSERT_THROW(EvaluateFunction("STARTNODE"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("STARTNODE", TypedValue()).IsNull());
-  auto v1 = dba.InsertVertex();
-  ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("label1")).HasValue());
-  auto v2 = dba.InsertVertex();
-  ASSERT_TRUE(v2.AddLabel(dba.NameToLabel("label2")).HasValue());
-  auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t"));
+TYPED_TEST(FunctionTest, StartNode) {
+  ASSERT_THROW(this->EvaluateFunction("STARTNODE"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("STARTNODE", TypedValue()).IsNull());
+  auto v1 = this->dba.InsertVertex();
+  ASSERT_TRUE(v1.AddLabel(this->dba.NameToLabel("label1")).HasValue());
+  auto v2 = this->dba.InsertVertex();
+  ASSERT_TRUE(v2.AddLabel(this->dba.NameToLabel("label2")).HasValue());
+  auto e = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("t"));
   ASSERT_TRUE(e.HasValue());
-  ASSERT_TRUE(*EvaluateFunction("STARTNODE", *e)
+  ASSERT_TRUE(*this->EvaluateFunction("STARTNODE", *e)
                    .ValueVertex()
-                   .HasLabel(memgraph::storage::View::NEW, dba.NameToLabel("label1")));
-  ASSERT_THROW(EvaluateFunction("STARTNODE", 2), QueryRuntimeException);
+                   .HasLabel(memgraph::storage::View::NEW, this->dba.NameToLabel("label1")));
+  ASSERT_THROW(this->EvaluateFunction("STARTNODE", 2), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Degree) {
-  ASSERT_THROW(EvaluateFunction("DEGREE"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("DEGREE", TypedValue()).IsNull());
-  auto v1 = dba.InsertVertex();
-  auto v2 = dba.InsertVertex();
-  auto v3 = dba.InsertVertex();
-  auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t"));
+TYPED_TEST(FunctionTest, Degree) {
+  ASSERT_THROW(this->EvaluateFunction("DEGREE"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("DEGREE", TypedValue()).IsNull());
+  auto v1 = this->dba.InsertVertex();
+  auto v2 = this->dba.InsertVertex();
+  auto v3 = this->dba.InsertVertex();
+  auto e12 = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("t"));
   ASSERT_TRUE(e12.HasValue());
-  ASSERT_TRUE(dba.InsertEdge(&v3, &v2, dba.NameToEdgeType("t")).HasValue());
-  dba.AdvanceCommand();
-  ASSERT_EQ(EvaluateFunction("DEGREE", v1).ValueInt(), 1);
-  ASSERT_EQ(EvaluateFunction("DEGREE", v2).ValueInt(), 2);
-  ASSERT_EQ(EvaluateFunction("DEGREE", v3).ValueInt(), 1);
-  ASSERT_THROW(EvaluateFunction("DEGREE", 2), QueryRuntimeException);
-  ASSERT_THROW(EvaluateFunction("DEGREE", *e12), QueryRuntimeException);
+  ASSERT_TRUE(this->dba.InsertEdge(&v3, &v2, this->dba.NameToEdgeType("t")).HasValue());
+  this->dba.AdvanceCommand();
+  ASSERT_EQ(this->EvaluateFunction("DEGREE", v1).ValueInt(), 1);
+  ASSERT_EQ(this->EvaluateFunction("DEGREE", v2).ValueInt(), 2);
+  ASSERT_EQ(this->EvaluateFunction("DEGREE", v3).ValueInt(), 1);
+  ASSERT_THROW(this->EvaluateFunction("DEGREE", 2), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("DEGREE", *e12), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, InDegree) {
-  ASSERT_THROW(EvaluateFunction("INDEGREE"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("INDEGREE", TypedValue()).IsNull());
-  auto v1 = dba.InsertVertex();
-  auto v2 = dba.InsertVertex();
-  auto v3 = dba.InsertVertex();
-  auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t"));
+TYPED_TEST(FunctionTest, InDegree) {
+  ASSERT_THROW(this->EvaluateFunction("INDEGREE"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("INDEGREE", TypedValue()).IsNull());
+  auto v1 = this->dba.InsertVertex();
+  auto v2 = this->dba.InsertVertex();
+  auto v3 = this->dba.InsertVertex();
+  auto e12 = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("t"));
   ASSERT_TRUE(e12.HasValue());
-  ASSERT_TRUE(dba.InsertEdge(&v3, &v2, dba.NameToEdgeType("t")).HasValue());
-  dba.AdvanceCommand();
-  ASSERT_EQ(EvaluateFunction("INDEGREE", v1).ValueInt(), 0);
-  ASSERT_EQ(EvaluateFunction("INDEGREE", v2).ValueInt(), 2);
-  ASSERT_EQ(EvaluateFunction("INDEGREE", v3).ValueInt(), 0);
-  ASSERT_THROW(EvaluateFunction("INDEGREE", 2), QueryRuntimeException);
-  ASSERT_THROW(EvaluateFunction("INDEGREE", *e12), QueryRuntimeException);
+  ASSERT_TRUE(this->dba.InsertEdge(&v3, &v2, this->dba.NameToEdgeType("t")).HasValue());
+  this->dba.AdvanceCommand();
+  ASSERT_EQ(this->EvaluateFunction("INDEGREE", v1).ValueInt(), 0);
+  ASSERT_EQ(this->EvaluateFunction("INDEGREE", v2).ValueInt(), 2);
+  ASSERT_EQ(this->EvaluateFunction("INDEGREE", v3).ValueInt(), 0);
+  ASSERT_THROW(this->EvaluateFunction("INDEGREE", 2), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("INDEGREE", *e12), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, OutDegree) {
-  ASSERT_THROW(EvaluateFunction("OUTDEGREE"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("OUTDEGREE", TypedValue()).IsNull());
-  auto v1 = dba.InsertVertex();
-  auto v2 = dba.InsertVertex();
-  auto v3 = dba.InsertVertex();
-  auto e12 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("t"));
+TYPED_TEST(FunctionTest, OutDegree) {
+  ASSERT_THROW(this->EvaluateFunction("OUTDEGREE"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("OUTDEGREE", TypedValue()).IsNull());
+  auto v1 = this->dba.InsertVertex();
+  auto v2 = this->dba.InsertVertex();
+  auto v3 = this->dba.InsertVertex();
+  auto e12 = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("t"));
   ASSERT_TRUE(e12.HasValue());
-  ASSERT_TRUE(dba.InsertEdge(&v3, &v2, dba.NameToEdgeType("t")).HasValue());
-  dba.AdvanceCommand();
-  ASSERT_EQ(EvaluateFunction("OUTDEGREE", v1).ValueInt(), 1);
-  ASSERT_EQ(EvaluateFunction("OUTDEGREE", v2).ValueInt(), 0);
-  ASSERT_EQ(EvaluateFunction("OUTDEGREE", v3).ValueInt(), 1);
-  ASSERT_THROW(EvaluateFunction("OUTDEGREE", 2), QueryRuntimeException);
-  ASSERT_THROW(EvaluateFunction("OUTDEGREE", *e12), QueryRuntimeException);
+  ASSERT_TRUE(this->dba.InsertEdge(&v3, &v2, this->dba.NameToEdgeType("t")).HasValue());
+  this->dba.AdvanceCommand();
+  ASSERT_EQ(this->EvaluateFunction("OUTDEGREE", v1).ValueInt(), 1);
+  ASSERT_EQ(this->EvaluateFunction("OUTDEGREE", v2).ValueInt(), 0);
+  ASSERT_EQ(this->EvaluateFunction("OUTDEGREE", v3).ValueInt(), 1);
+  ASSERT_THROW(this->EvaluateFunction("OUTDEGREE", 2), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("OUTDEGREE", *e12), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, ToBoolean) {
-  ASSERT_THROW(EvaluateFunction("TOBOOLEAN"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("TOBOOLEAN", TypedValue()).IsNull());
-  ASSERT_EQ(EvaluateFunction("TOBOOLEAN", 123).ValueBool(), true);
-  ASSERT_EQ(EvaluateFunction("TOBOOLEAN", -213).ValueBool(), true);
-  ASSERT_EQ(EvaluateFunction("TOBOOLEAN", 0).ValueBool(), false);
-  ASSERT_EQ(EvaluateFunction("TOBOOLEAN", " trUE \n\t").ValueBool(), true);
-  ASSERT_EQ(EvaluateFunction("TOBOOLEAN", "\n\tFalsE").ValueBool(), false);
-  ASSERT_TRUE(EvaluateFunction("TOBOOLEAN", "\n\tFALSEA ").IsNull());
-  ASSERT_EQ(EvaluateFunction("TOBOOLEAN", true).ValueBool(), true);
-  ASSERT_EQ(EvaluateFunction("TOBOOLEAN", false).ValueBool(), false);
+TYPED_TEST(FunctionTest, ToBoolean) {
+  ASSERT_THROW(this->EvaluateFunction("TOBOOLEAN"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("TOBOOLEAN", TypedValue()).IsNull());
+  ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", 123).ValueBool(), true);
+  ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", -213).ValueBool(), true);
+  ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", 0).ValueBool(), false);
+  ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", " trUE \n\t").ValueBool(), true);
+  ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", "\n\tFalsE").ValueBool(), false);
+  ASSERT_TRUE(this->EvaluateFunction("TOBOOLEAN", "\n\tFALSEA ").IsNull());
+  ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", true).ValueBool(), true);
+  ASSERT_EQ(this->EvaluateFunction("TOBOOLEAN", false).ValueBool(), false);
 }
 
-TEST_F(FunctionTest, ToFloat) {
-  ASSERT_THROW(EvaluateFunction("TOFLOAT"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("TOFLOAT", TypedValue()).IsNull());
-  ASSERT_EQ(EvaluateFunction("TOFLOAT", " -3.5 \n\t").ValueDouble(), -3.5);
-  ASSERT_EQ(EvaluateFunction("TOFLOAT", "\n\t0.5e-1").ValueDouble(), 0.05);
-  ASSERT_TRUE(EvaluateFunction("TOFLOAT", "\n\t3.4e-3X ").IsNull());
-  ASSERT_EQ(EvaluateFunction("TOFLOAT", -3.5).ValueDouble(), -3.5);
-  ASSERT_EQ(EvaluateFunction("TOFLOAT", -3).ValueDouble(), -3.0);
-  ASSERT_THROW(EvaluateFunction("TOFLOAT", true), QueryRuntimeException);
+TYPED_TEST(FunctionTest, ToFloat) {
+  ASSERT_THROW(this->EvaluateFunction("TOFLOAT"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("TOFLOAT", TypedValue()).IsNull());
+  ASSERT_EQ(this->EvaluateFunction("TOFLOAT", " -3.5 \n\t").ValueDouble(), -3.5);
+  ASSERT_EQ(this->EvaluateFunction("TOFLOAT", "\n\t0.5e-1").ValueDouble(), 0.05);
+  ASSERT_TRUE(this->EvaluateFunction("TOFLOAT", "\n\t3.4e-3X ").IsNull());
+  ASSERT_EQ(this->EvaluateFunction("TOFLOAT", -3.5).ValueDouble(), -3.5);
+  ASSERT_EQ(this->EvaluateFunction("TOFLOAT", -3).ValueDouble(), -3.0);
+  ASSERT_THROW(this->EvaluateFunction("TOFLOAT", true), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, ToInteger) {
-  ASSERT_THROW(EvaluateFunction("TOINTEGER"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("TOINTEGER", TypedValue()).IsNull());
-  ASSERT_EQ(EvaluateFunction("TOINTEGER", false).ValueInt(), 0);
-  ASSERT_EQ(EvaluateFunction("TOINTEGER", true).ValueInt(), 1);
-  ASSERT_EQ(EvaluateFunction("TOINTEGER", "\n\t3").ValueInt(), 3);
-  ASSERT_EQ(EvaluateFunction("TOINTEGER", " -3.5 \n\t").ValueInt(), -3);
-  ASSERT_TRUE(EvaluateFunction("TOINTEGER", "\n\t3X ").IsNull());
-  ASSERT_EQ(EvaluateFunction("TOINTEGER", -3.5).ValueInt(), -3);
-  ASSERT_EQ(EvaluateFunction("TOINTEGER", 3.5).ValueInt(), 3);
+TYPED_TEST(FunctionTest, ToInteger) {
+  ASSERT_THROW(this->EvaluateFunction("TOINTEGER"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("TOINTEGER", TypedValue()).IsNull());
+  ASSERT_EQ(this->EvaluateFunction("TOINTEGER", false).ValueInt(), 0);
+  ASSERT_EQ(this->EvaluateFunction("TOINTEGER", true).ValueInt(), 1);
+  ASSERT_EQ(this->EvaluateFunction("TOINTEGER", "\n\t3").ValueInt(), 3);
+  ASSERT_EQ(this->EvaluateFunction("TOINTEGER", " -3.5 \n\t").ValueInt(), -3);
+  ASSERT_TRUE(this->EvaluateFunction("TOINTEGER", "\n\t3X ").IsNull());
+  ASSERT_EQ(this->EvaluateFunction("TOINTEGER", -3.5).ValueInt(), -3);
+  ASSERT_EQ(this->EvaluateFunction("TOINTEGER", 3.5).ValueInt(), 3);
 }
 
-TEST_F(FunctionTest, Type) {
-  ASSERT_THROW(EvaluateFunction("TYPE"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("TYPE", TypedValue()).IsNull());
-  auto v1 = dba.InsertVertex();
-  ASSERT_TRUE(v1.AddLabel(dba.NameToLabel("label1")).HasValue());
-  auto v2 = dba.InsertVertex();
-  ASSERT_TRUE(v2.AddLabel(dba.NameToLabel("label2")).HasValue());
-  auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("type1"));
+TYPED_TEST(FunctionTest, Type) {
+  ASSERT_THROW(this->EvaluateFunction("TYPE"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("TYPE", TypedValue()).IsNull());
+  auto v1 = this->dba.InsertVertex();
+  ASSERT_TRUE(v1.AddLabel(this->dba.NameToLabel("label1")).HasValue());
+  auto v2 = this->dba.InsertVertex();
+  ASSERT_TRUE(v2.AddLabel(this->dba.NameToLabel("label2")).HasValue());
+  auto e = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("type1"));
   ASSERT_TRUE(e.HasValue());
-  ASSERT_EQ(EvaluateFunction("TYPE", *e).ValueString(), "type1");
-  ASSERT_THROW(EvaluateFunction("TYPE", 2), QueryRuntimeException);
+  ASSERT_EQ(this->EvaluateFunction("TYPE", *e).ValueString(), "type1");
+  ASSERT_THROW(this->EvaluateFunction("TYPE", 2), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, ValueType) {
-  ASSERT_THROW(EvaluateFunction("VALUETYPE"), QueryRuntimeException);
-  ASSERT_THROW(EvaluateFunction("VALUETYPE", TypedValue(), TypedValue()), QueryRuntimeException);
-  ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue()).ValueString(), "NULL");
-  ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(true)).ValueString(), "BOOLEAN");
-  ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(1)).ValueString(), "INTEGER");
-  ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(1.1)).ValueString(), "FLOAT");
-  ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue("test")).ValueString(), "STRING");
-  ASSERT_EQ(
-      EvaluateFunction("VALUETYPE", TypedValue(std::vector<TypedValue>{TypedValue(1), TypedValue(2)})).ValueString(),
-      "LIST");
-  ASSERT_EQ(EvaluateFunction("VALUETYPE", TypedValue(std::map<std::string, TypedValue>{{"test", TypedValue(1)}}))
+TYPED_TEST(FunctionTest, ValueType) {
+  ASSERT_THROW(this->EvaluateFunction("VALUETYPE"), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("VALUETYPE", TypedValue(), TypedValue()), QueryRuntimeException);
+  ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue()).ValueString(), "NULL");
+  ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue(true)).ValueString(), "BOOLEAN");
+  ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue(1)).ValueString(), "INTEGER");
+  ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue(1.1)).ValueString(), "FLOAT");
+  ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue("test")).ValueString(), "STRING");
+  ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue(std::vector<TypedValue>{TypedValue(1), TypedValue(2)}))
+                .ValueString(),
+            "LIST");
+  ASSERT_EQ(this->EvaluateFunction("VALUETYPE", TypedValue(std::map<std::string, TypedValue>{{"test", TypedValue(1)}}))
                 .ValueString(),
             "MAP");
-  auto v1 = dba.InsertVertex();
-  auto v2 = dba.InsertVertex();
-  ASSERT_EQ(EvaluateFunction("VALUETYPE", v1).ValueString(), "NODE");
-  auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("type1"));
+  auto v1 = this->dba.InsertVertex();
+  auto v2 = this->dba.InsertVertex();
+  ASSERT_EQ(this->EvaluateFunction("VALUETYPE", v1).ValueString(), "NODE");
+  auto e = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("type1"));
   ASSERT_TRUE(e.HasValue());
-  ASSERT_EQ(EvaluateFunction("VALUETYPE", *e).ValueString(), "RELATIONSHIP");
+  ASSERT_EQ(this->EvaluateFunction("VALUETYPE", *e).ValueString(), "RELATIONSHIP");
   Path p(v1, *e, v2);
-  ASSERT_EQ(EvaluateFunction("VALUETYPE", p).ValueString(), "PATH");
+  ASSERT_EQ(this->EvaluateFunction("VALUETYPE", p).ValueString(), "PATH");
 }
 
-TEST_F(FunctionTest, Labels) {
-  ASSERT_THROW(EvaluateFunction("LABELS"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("LABELS", TypedValue()).IsNull());
-  auto v = dba.InsertVertex();
-  ASSERT_TRUE(v.AddLabel(dba.NameToLabel("label1")).HasValue());
-  ASSERT_TRUE(v.AddLabel(dba.NameToLabel("label2")).HasValue());
-  dba.AdvanceCommand();
+TYPED_TEST(FunctionTest, Labels) {
+  ASSERT_THROW(this->EvaluateFunction("LABELS"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("LABELS", TypedValue()).IsNull());
+  auto v = this->dba.InsertVertex();
+  ASSERT_TRUE(v.AddLabel(this->dba.NameToLabel("label1")).HasValue());
+  ASSERT_TRUE(v.AddLabel(this->dba.NameToLabel("label2")).HasValue());
+  this->dba.AdvanceCommand();
   std::vector<std::string> labels;
-  auto _labels = EvaluateFunction("LABELS", v).ValueList();
+  auto _labels = this->EvaluateFunction("LABELS", v).ValueList();
   labels.reserve(_labels.size());
   for (auto label : _labels) {
     labels.emplace_back(label.ValueString());
   }
   ASSERT_THAT(labels, UnorderedElementsAre("label1", "label2"));
-  ASSERT_THROW(EvaluateFunction("LABELS", 2), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("LABELS", 2), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, NodesRelationships) {
-  EXPECT_THROW(EvaluateFunction("NODES"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("RELATIONSHIPS"), QueryRuntimeException);
-  EXPECT_TRUE(EvaluateFunction("NODES", TypedValue()).IsNull());
-  EXPECT_TRUE(EvaluateFunction("RELATIONSHIPS", TypedValue()).IsNull());
+TYPED_TEST(FunctionTest, NodesRelationships) {
+  EXPECT_THROW(this->EvaluateFunction("NODES"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("RELATIONSHIPS"), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction("NODES", TypedValue()).IsNull());
+  EXPECT_TRUE(this->EvaluateFunction("RELATIONSHIPS", TypedValue()).IsNull());
 
   {
-    auto v1 = dba.InsertVertex();
-    auto v2 = dba.InsertVertex();
-    auto v3 = dba.InsertVertex();
-    auto e1 = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("Type"));
+    auto v1 = this->dba.InsertVertex();
+    auto v2 = this->dba.InsertVertex();
+    auto v3 = this->dba.InsertVertex();
+    auto e1 = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("Type"));
     ASSERT_TRUE(e1.HasValue());
-    auto e2 = dba.InsertEdge(&v2, &v3, dba.NameToEdgeType("Type"));
+    auto e2 = this->dba.InsertEdge(&v2, &v3, this->dba.NameToEdgeType("Type"));
     ASSERT_TRUE(e2.HasValue());
     memgraph::query::Path path{v1, *e1, v2, *e2, v3};
-    dba.AdvanceCommand();
+    this->dba.AdvanceCommand();
 
-    auto _nodes = EvaluateFunction("NODES", path).ValueList();
+    auto _nodes = this->EvaluateFunction("NODES", path).ValueList();
     std::vector<memgraph::query::VertexAccessor> nodes;
     for (const auto &node : _nodes) {
       nodes.push_back(node.ValueVertex());
     }
     EXPECT_THAT(nodes, ElementsAre(v1, v2, v3));
 
-    auto _edges = EvaluateFunction("RELATIONSHIPS", path).ValueList();
+    auto _edges = this->EvaluateFunction("RELATIONSHIPS", path).ValueList();
     std::vector<memgraph::query::EdgeAccessor> edges;
     for (const auto &edge : _edges) {
       edges.push_back(edge.ValueEdge());
@@ -1671,38 +1779,38 @@ TEST_F(FunctionTest, NodesRelationships) {
     EXPECT_THAT(edges, ElementsAre(*e1, *e2));
   }
 
-  EXPECT_THROW(EvaluateFunction("NODES", 2), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("RELATIONSHIPS", 2), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("NODES", 2), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("RELATIONSHIPS", 2), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Range) {
-  EXPECT_THROW(EvaluateFunction("RANGE"), QueryRuntimeException);
-  EXPECT_TRUE(EvaluateFunction("RANGE", 1, 2, TypedValue()).IsNull());
-  EXPECT_THROW(EvaluateFunction("RANGE", 1, TypedValue(), 1.3), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("RANGE", 1, 2, 0), QueryRuntimeException);
-  EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 1, 3)), ElementsAre(1, 2, 3));
-  EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", -1, 5, 2)), ElementsAre(-1, 1, 3, 5));
-  EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 2, 10, 3)), ElementsAre(2, 5, 8));
-  EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 2, 2, 2)), ElementsAre(2));
-  EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 3, 0, 5)), ElementsAre());
-  EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 5, 1, -2)), ElementsAre(5, 3, 1));
-  EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 6, 1, -2)), ElementsAre(6, 4, 2));
-  EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", 2, 2, -3)), ElementsAre(2));
-  EXPECT_THAT(ToIntList(EvaluateFunction("RANGE", -2, 4, -1)), ElementsAre());
+TYPED_TEST(FunctionTest, Range) {
+  EXPECT_THROW(this->EvaluateFunction("RANGE"), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction("RANGE", 1, 2, TypedValue()).IsNull());
+  EXPECT_THROW(this->EvaluateFunction("RANGE", 1, TypedValue(), 1.3), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("RANGE", 1, 2, 0), QueryRuntimeException);
+  EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 1, 3)), ElementsAre(1, 2, 3));
+  EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", -1, 5, 2)), ElementsAre(-1, 1, 3, 5));
+  EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 2, 10, 3)), ElementsAre(2, 5, 8));
+  EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 2, 2, 2)), ElementsAre(2));
+  EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 3, 0, 5)), ElementsAre());
+  EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 5, 1, -2)), ElementsAre(5, 3, 1));
+  EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 6, 1, -2)), ElementsAre(6, 4, 2));
+  EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", 2, 2, -3)), ElementsAre(2));
+  EXPECT_THAT(ToIntList(this->EvaluateFunction("RANGE", -2, 4, -1)), ElementsAre());
 }
 
-TEST_F(FunctionTest, Keys) {
-  ASSERT_THROW(EvaluateFunction("KEYS"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("KEYS", TypedValue()).IsNull());
-  auto v1 = dba.InsertVertex();
-  ASSERT_TRUE(v1.SetProperty(dba.NameToProperty("height"), memgraph::storage::PropertyValue(5)).HasValue());
-  ASSERT_TRUE(v1.SetProperty(dba.NameToProperty("age"), memgraph::storage::PropertyValue(10)).HasValue());
-  auto v2 = dba.InsertVertex();
-  auto e = dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("type1"));
+TYPED_TEST(FunctionTest, Keys) {
+  ASSERT_THROW(this->EvaluateFunction("KEYS"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("KEYS", TypedValue()).IsNull());
+  auto v1 = this->dba.InsertVertex();
+  ASSERT_TRUE(v1.SetProperty(this->dba.NameToProperty("height"), memgraph::storage::PropertyValue(5)).HasValue());
+  ASSERT_TRUE(v1.SetProperty(this->dba.NameToProperty("age"), memgraph::storage::PropertyValue(10)).HasValue());
+  auto v2 = this->dba.InsertVertex();
+  auto e = this->dba.InsertEdge(&v1, &v2, this->dba.NameToEdgeType("type1"));
   ASSERT_TRUE(e.HasValue());
-  ASSERT_TRUE(e->SetProperty(dba.NameToProperty("width"), memgraph::storage::PropertyValue(3)).HasValue());
-  ASSERT_TRUE(e->SetProperty(dba.NameToProperty("age"), memgraph::storage::PropertyValue(15)).HasValue());
-  dba.AdvanceCommand();
+  ASSERT_TRUE(e->SetProperty(this->dba.NameToProperty("width"), memgraph::storage::PropertyValue(3)).HasValue());
+  ASSERT_TRUE(e->SetProperty(this->dba.NameToProperty("age"), memgraph::storage::PropertyValue(15)).HasValue());
+  this->dba.AdvanceCommand();
 
   auto prop_keys_to_string = [](TypedValue t) {
     std::vector<std::string> keys;
@@ -1711,474 +1819,478 @@ TEST_F(FunctionTest, Keys) {
     }
     return keys;
   };
-  ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", v1)), UnorderedElementsAre("height", "age"));
-  ASSERT_THAT(prop_keys_to_string(EvaluateFunction("KEYS", *e)), UnorderedElementsAre("width", "age"));
-  ASSERT_THROW(EvaluateFunction("KEYS", 2), QueryRuntimeException);
+  ASSERT_THAT(prop_keys_to_string(this->EvaluateFunction("KEYS", v1)), UnorderedElementsAre("height", "age"));
+  ASSERT_THAT(prop_keys_to_string(this->EvaluateFunction("KEYS", *e)), UnorderedElementsAre("width", "age"));
+  ASSERT_THROW(this->EvaluateFunction("KEYS", 2), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Tail) {
-  ASSERT_THROW(EvaluateFunction("TAIL"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("TAIL", TypedValue()).IsNull());
+TYPED_TEST(FunctionTest, Tail) {
+  ASSERT_THROW(this->EvaluateFunction("TAIL"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("TAIL", TypedValue()).IsNull());
   auto argument = MakeTypedValueList();
-  ASSERT_EQ(EvaluateFunction("TAIL", argument).ValueList().size(), 0U);
+  ASSERT_EQ(this->EvaluateFunction("TAIL", argument).ValueList().size(), 0U);
   argument = MakeTypedValueList(3, 4, true, "john");
-  auto list = EvaluateFunction("TAIL", argument).ValueList();
+  auto list = this->EvaluateFunction("TAIL", argument).ValueList();
   ASSERT_EQ(list.size(), 3U);
   ASSERT_EQ(list[0].ValueInt(), 4);
   ASSERT_EQ(list[1].ValueBool(), true);
   ASSERT_EQ(list[2].ValueString(), "john");
-  ASSERT_THROW(EvaluateFunction("TAIL", 2), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("TAIL", 2), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, UniformSample) {
-  ASSERT_THROW(EvaluateFunction("UNIFORMSAMPLE"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", TypedValue(), TypedValue()).IsNull());
-  ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", TypedValue(), 1).IsNull());
-  ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), TypedValue()).IsNull());
-  ASSERT_TRUE(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), 1).IsNull());
-  ASSERT_THROW(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), -1), QueryRuntimeException);
-  ASSERT_EQ(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 0).ValueList().size(), 0);
-  ASSERT_EQ(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 2).ValueList().size(), 2);
-  ASSERT_EQ(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 3).ValueList().size(), 3);
-  ASSERT_EQ(EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 5).ValueList().size(), 5);
+TYPED_TEST(FunctionTest, UniformSample) {
+  ASSERT_THROW(this->EvaluateFunction("UNIFORMSAMPLE"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("UNIFORMSAMPLE", TypedValue(), TypedValue()).IsNull());
+  ASSERT_TRUE(this->EvaluateFunction("UNIFORMSAMPLE", TypedValue(), 1).IsNull());
+  ASSERT_TRUE(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), TypedValue()).IsNull());
+  ASSERT_TRUE(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(), 1).IsNull());
+  ASSERT_THROW(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), -1), QueryRuntimeException);
+  ASSERT_EQ(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 0).ValueList().size(), 0);
+  ASSERT_EQ(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 2).ValueList().size(), 2);
+  ASSERT_EQ(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 3).ValueList().size(), 3);
+  ASSERT_EQ(this->EvaluateFunction("UNIFORMSAMPLE", MakeTypedValueList(1, 2, 3), 5).ValueList().size(), 5);
 }
 
-TEST_F(FunctionTest, Abs) {
-  ASSERT_THROW(EvaluateFunction("ABS"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("ABS", TypedValue()).IsNull());
-  ASSERT_EQ(EvaluateFunction("ABS", -2).ValueInt(), 2);
-  ASSERT_EQ(EvaluateFunction("ABS", -2.5).ValueDouble(), 2.5);
-  ASSERT_THROW(EvaluateFunction("ABS", true), QueryRuntimeException);
+TYPED_TEST(FunctionTest, Abs) {
+  ASSERT_THROW(this->EvaluateFunction("ABS"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("ABS", TypedValue()).IsNull());
+  ASSERT_EQ(this->EvaluateFunction("ABS", -2).ValueInt(), 2);
+  ASSERT_EQ(this->EvaluateFunction("ABS", -2.5).ValueDouble(), 2.5);
+  ASSERT_THROW(this->EvaluateFunction("ABS", true), QueryRuntimeException);
 }
 
 // Test if log works. If it does then all functions wrapped with
 // WRAP_CMATH_FLOAT_FUNCTION macro should work and are not gonna be tested for
 // correctnes..
-TEST_F(FunctionTest, Log) {
-  ASSERT_THROW(EvaluateFunction("LOG"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("LOG", TypedValue()).IsNull());
-  ASSERT_DOUBLE_EQ(EvaluateFunction("LOG", 2).ValueDouble(), log(2));
-  ASSERT_DOUBLE_EQ(EvaluateFunction("LOG", 1.5).ValueDouble(), log(1.5));
+TYPED_TEST(FunctionTest, Log) {
+  ASSERT_THROW(this->EvaluateFunction("LOG"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("LOG", TypedValue()).IsNull());
+  ASSERT_DOUBLE_EQ(this->EvaluateFunction("LOG", 2).ValueDouble(), log(2));
+  ASSERT_DOUBLE_EQ(this->EvaluateFunction("LOG", 1.5).ValueDouble(), log(1.5));
   // Not portable, but should work on most platforms.
-  ASSERT_TRUE(std::isnan(EvaluateFunction("LOG", -1.5).ValueDouble()));
-  ASSERT_THROW(EvaluateFunction("LOG", true), QueryRuntimeException);
+  ASSERT_TRUE(std::isnan(this->EvaluateFunction("LOG", -1.5).ValueDouble()));
+  ASSERT_THROW(this->EvaluateFunction("LOG", true), QueryRuntimeException);
 }
 
 // Function Round wraps round from cmath and will work if FunctionTest.Log test
 // passes. This test is used to show behavior of round since it differs from
 // neo4j's round.
-TEST_F(FunctionTest, Round) {
-  ASSERT_THROW(EvaluateFunction("ROUND"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("ROUND", TypedValue()).IsNull());
-  ASSERT_EQ(EvaluateFunction("ROUND", -2).ValueDouble(), -2);
-  ASSERT_EQ(EvaluateFunction("ROUND", -2.4).ValueDouble(), -2);
-  ASSERT_EQ(EvaluateFunction("ROUND", -2.5).ValueDouble(), -3);
-  ASSERT_EQ(EvaluateFunction("ROUND", -2.6).ValueDouble(), -3);
-  ASSERT_EQ(EvaluateFunction("ROUND", 2.4).ValueDouble(), 2);
-  ASSERT_EQ(EvaluateFunction("ROUND", 2.5).ValueDouble(), 3);
-  ASSERT_EQ(EvaluateFunction("ROUND", 2.6).ValueDouble(), 3);
-  ASSERT_THROW(EvaluateFunction("ROUND", true), QueryRuntimeException);
+TYPED_TEST(FunctionTest, Round) {
+  ASSERT_THROW(this->EvaluateFunction("ROUND"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("ROUND", TypedValue()).IsNull());
+  ASSERT_EQ(this->EvaluateFunction("ROUND", -2).ValueDouble(), -2);
+  ASSERT_EQ(this->EvaluateFunction("ROUND", -2.4).ValueDouble(), -2);
+  ASSERT_EQ(this->EvaluateFunction("ROUND", -2.5).ValueDouble(), -3);
+  ASSERT_EQ(this->EvaluateFunction("ROUND", -2.6).ValueDouble(), -3);
+  ASSERT_EQ(this->EvaluateFunction("ROUND", 2.4).ValueDouble(), 2);
+  ASSERT_EQ(this->EvaluateFunction("ROUND", 2.5).ValueDouble(), 3);
+  ASSERT_EQ(this->EvaluateFunction("ROUND", 2.6).ValueDouble(), 3);
+  ASSERT_THROW(this->EvaluateFunction("ROUND", true), QueryRuntimeException);
 }
 
 // Check if wrapped functions are callable (check if everything was spelled
 // correctly...). Wrapper correctnes is checked in FunctionTest.Log function
 // test.
-TEST_F(FunctionTest, WrappedMathFunctions) {
+TYPED_TEST(FunctionTest, WrappedMathFunctions) {
   for (auto function_name :
        {"FLOOR", "CEIL", "ROUND", "EXP", "LOG", "LOG10", "SQRT", "ACOS", "ASIN", "ATAN", "COS", "SIN", "TAN"}) {
-    EvaluateFunction(function_name, 0.5);
+    this->EvaluateFunction(function_name, 0.5);
   }
 }
 
-TEST_F(FunctionTest, Atan2) {
-  ASSERT_THROW(EvaluateFunction("ATAN2"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("ATAN2", TypedValue(), 1).IsNull());
-  ASSERT_TRUE(EvaluateFunction("ATAN2", 1, TypedValue()).IsNull());
-  ASSERT_DOUBLE_EQ(EvaluateFunction("ATAN2", 2, -1.0).ValueDouble(), atan2(2, -1));
-  ASSERT_THROW(EvaluateFunction("ATAN2", 3.0, true), QueryRuntimeException);
+TYPED_TEST(FunctionTest, Atan2) {
+  ASSERT_THROW(this->EvaluateFunction("ATAN2"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("ATAN2", TypedValue(), 1).IsNull());
+  ASSERT_TRUE(this->EvaluateFunction("ATAN2", 1, TypedValue()).IsNull());
+  ASSERT_DOUBLE_EQ(this->EvaluateFunction("ATAN2", 2, -1.0).ValueDouble(), atan2(2, -1));
+  ASSERT_THROW(this->EvaluateFunction("ATAN2", 3.0, true), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Sign) {
-  ASSERT_THROW(EvaluateFunction("SIGN"), QueryRuntimeException);
-  ASSERT_TRUE(EvaluateFunction("SIGN", TypedValue()).IsNull());
-  ASSERT_EQ(EvaluateFunction("SIGN", -2).ValueInt(), -1);
-  ASSERT_EQ(EvaluateFunction("SIGN", -0.2).ValueInt(), -1);
-  ASSERT_EQ(EvaluateFunction("SIGN", 0.0).ValueInt(), 0);
-  ASSERT_EQ(EvaluateFunction("SIGN", 2.5).ValueInt(), 1);
-  ASSERT_THROW(EvaluateFunction("SIGN", true), QueryRuntimeException);
+TYPED_TEST(FunctionTest, Sign) {
+  ASSERT_THROW(this->EvaluateFunction("SIGN"), QueryRuntimeException);
+  ASSERT_TRUE(this->EvaluateFunction("SIGN", TypedValue()).IsNull());
+  ASSERT_EQ(this->EvaluateFunction("SIGN", -2).ValueInt(), -1);
+  ASSERT_EQ(this->EvaluateFunction("SIGN", -0.2).ValueInt(), -1);
+  ASSERT_EQ(this->EvaluateFunction("SIGN", 0.0).ValueInt(), 0);
+  ASSERT_EQ(this->EvaluateFunction("SIGN", 2.5).ValueInt(), 1);
+  ASSERT_THROW(this->EvaluateFunction("SIGN", true), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, E) {
-  ASSERT_THROW(EvaluateFunction("E", 1), QueryRuntimeException);
-  ASSERT_DOUBLE_EQ(EvaluateFunction("E").ValueDouble(), M_E);
+TYPED_TEST(FunctionTest, E) {
+  ASSERT_THROW(this->EvaluateFunction("E", 1), QueryRuntimeException);
+  ASSERT_DOUBLE_EQ(this->EvaluateFunction("E").ValueDouble(), M_E);
 }
 
-TEST_F(FunctionTest, Pi) {
-  ASSERT_THROW(EvaluateFunction("PI", 1), QueryRuntimeException);
-  ASSERT_DOUBLE_EQ(EvaluateFunction("PI").ValueDouble(), M_PI);
+TYPED_TEST(FunctionTest, Pi) {
+  ASSERT_THROW(this->EvaluateFunction("PI", 1), QueryRuntimeException);
+  ASSERT_DOUBLE_EQ(this->EvaluateFunction("PI").ValueDouble(), M_PI);
 }
 
-TEST_F(FunctionTest, Rand) {
-  ASSERT_THROW(EvaluateFunction("RAND", 1), QueryRuntimeException);
-  ASSERT_GE(EvaluateFunction("RAND").ValueDouble(), 0.0);
-  ASSERT_LT(EvaluateFunction("RAND").ValueDouble(), 1.0);
+TYPED_TEST(FunctionTest, Rand) {
+  ASSERT_THROW(this->EvaluateFunction("RAND", 1), QueryRuntimeException);
+  ASSERT_GE(this->EvaluateFunction("RAND").ValueDouble(), 0.0);
+  ASSERT_LT(this->EvaluateFunction("RAND").ValueDouble(), 1.0);
 }
 
-TEST_F(FunctionTest, StartsWith) {
-  EXPECT_THROW(EvaluateFunction(kStartsWith), QueryRuntimeException);
-  EXPECT_TRUE(EvaluateFunction(kStartsWith, "a", TypedValue()).IsNull());
-  EXPECT_THROW(EvaluateFunction(kStartsWith, TypedValue(), 1.3), QueryRuntimeException);
-  EXPECT_TRUE(EvaluateFunction(kStartsWith, "abc", "abc").ValueBool());
-  EXPECT_TRUE(EvaluateFunction(kStartsWith, "abcdef", "abc").ValueBool());
-  EXPECT_FALSE(EvaluateFunction(kStartsWith, "abcdef", "aBc").ValueBool());
-  EXPECT_FALSE(EvaluateFunction(kStartsWith, "abc", "abcd").ValueBool());
+TYPED_TEST(FunctionTest, StartsWith) {
+  EXPECT_THROW(this->EvaluateFunction(kStartsWith), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction(kStartsWith, "a", TypedValue()).IsNull());
+  EXPECT_THROW(this->EvaluateFunction(kStartsWith, TypedValue(), 1.3), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction(kStartsWith, "abc", "abc").ValueBool());
+  EXPECT_TRUE(this->EvaluateFunction(kStartsWith, "abcdef", "abc").ValueBool());
+  EXPECT_FALSE(this->EvaluateFunction(kStartsWith, "abcdef", "aBc").ValueBool());
+  EXPECT_FALSE(this->EvaluateFunction(kStartsWith, "abc", "abcd").ValueBool());
 }
 
-TEST_F(FunctionTest, EndsWith) {
-  EXPECT_THROW(EvaluateFunction(kEndsWith), QueryRuntimeException);
-  EXPECT_TRUE(EvaluateFunction(kEndsWith, "a", TypedValue()).IsNull());
-  EXPECT_THROW(EvaluateFunction(kEndsWith, TypedValue(), 1.3), QueryRuntimeException);
-  EXPECT_TRUE(EvaluateFunction(kEndsWith, "abc", "abc").ValueBool());
-  EXPECT_TRUE(EvaluateFunction(kEndsWith, "abcdef", "def").ValueBool());
-  EXPECT_FALSE(EvaluateFunction(kEndsWith, "abcdef", "dEf").ValueBool());
-  EXPECT_FALSE(EvaluateFunction(kEndsWith, "bcd", "abcd").ValueBool());
+TYPED_TEST(FunctionTest, EndsWith) {
+  EXPECT_THROW(this->EvaluateFunction(kEndsWith), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction(kEndsWith, "a", TypedValue()).IsNull());
+  EXPECT_THROW(this->EvaluateFunction(kEndsWith, TypedValue(), 1.3), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction(kEndsWith, "abc", "abc").ValueBool());
+  EXPECT_TRUE(this->EvaluateFunction(kEndsWith, "abcdef", "def").ValueBool());
+  EXPECT_FALSE(this->EvaluateFunction(kEndsWith, "abcdef", "dEf").ValueBool());
+  EXPECT_FALSE(this->EvaluateFunction(kEndsWith, "bcd", "abcd").ValueBool());
 }
 
-TEST_F(FunctionTest, Contains) {
-  EXPECT_THROW(EvaluateFunction(kContains), QueryRuntimeException);
-  EXPECT_TRUE(EvaluateFunction(kContains, "a", TypedValue()).IsNull());
-  EXPECT_THROW(EvaluateFunction(kContains, TypedValue(), 1.3), QueryRuntimeException);
-  EXPECT_TRUE(EvaluateFunction(kContains, "abc", "abc").ValueBool());
-  EXPECT_TRUE(EvaluateFunction(kContains, "abcde", "bcd").ValueBool());
-  EXPECT_FALSE(EvaluateFunction(kContains, "cde", "abcdef").ValueBool());
-  EXPECT_FALSE(EvaluateFunction(kContains, "abcdef", "dEf").ValueBool());
+TYPED_TEST(FunctionTest, Contains) {
+  EXPECT_THROW(this->EvaluateFunction(kContains), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction(kContains, "a", TypedValue()).IsNull());
+  EXPECT_THROW(this->EvaluateFunction(kContains, TypedValue(), 1.3), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction(kContains, "abc", "abc").ValueBool());
+  EXPECT_TRUE(this->EvaluateFunction(kContains, "abcde", "bcd").ValueBool());
+  EXPECT_FALSE(this->EvaluateFunction(kContains, "cde", "abcdef").ValueBool());
+  EXPECT_FALSE(this->EvaluateFunction(kContains, "abcdef", "dEf").ValueBool());
 }
 
-TEST_F(FunctionTest, Assert) {
+TYPED_TEST(FunctionTest, Assert) {
   // Invalid calls.
-  ASSERT_THROW(EvaluateFunction("ASSERT"), QueryRuntimeException);
-  ASSERT_THROW(EvaluateFunction("ASSERT", false, false), QueryRuntimeException);
-  ASSERT_THROW(EvaluateFunction("ASSERT", "string", false), QueryRuntimeException);
-  ASSERT_THROW(EvaluateFunction("ASSERT", false, "reason", true), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("ASSERT"), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("ASSERT", false, false), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("ASSERT", "string", false), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("ASSERT", false, "reason", true), QueryRuntimeException);
 
   // Valid calls, assertion fails.
-  ASSERT_THROW(EvaluateFunction("ASSERT", false), QueryRuntimeException);
-  ASSERT_THROW(EvaluateFunction("ASSERT", false, "message"), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("ASSERT", false), QueryRuntimeException);
+  ASSERT_THROW(this->EvaluateFunction("ASSERT", false, "message"), QueryRuntimeException);
   try {
-    EvaluateFunction("ASSERT", false, "bbgba");
+    this->EvaluateFunction("ASSERT", false, "bbgba");
   } catch (QueryRuntimeException &e) {
     ASSERT_TRUE(std::string(e.what()).find("bbgba") != std::string::npos);
   }
 
   // Valid calls, assertion passes.
-  ASSERT_TRUE(EvaluateFunction("ASSERT", true).ValueBool());
-  ASSERT_TRUE(EvaluateFunction("ASSERT", true, "message").ValueBool());
+  ASSERT_TRUE(this->EvaluateFunction("ASSERT", true).ValueBool());
+  ASSERT_TRUE(this->EvaluateFunction("ASSERT", true, "message").ValueBool());
 }
 
-TEST_F(FunctionTest, Counter) {
-  EXPECT_THROW(EvaluateFunction("COUNTER"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("COUNTER", "a"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("COUNTER", "a", "b"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("COUNTER", "a", "b", "c"), QueryRuntimeException);
+TYPED_TEST(FunctionTest, Counter) {
+  EXPECT_THROW(this->EvaluateFunction("COUNTER"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("COUNTER", "a"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("COUNTER", "a", "b"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("COUNTER", "a", "b", "c"), QueryRuntimeException);
 
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 0);
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 1);
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c2", 0).ValueInt(), 0);
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 2);
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c2", 0).ValueInt(), 1);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 0);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 1);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c2", 0).ValueInt(), 0);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c1", 0).ValueInt(), 2);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c2", 0).ValueInt(), 1);
 
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c3", -1).ValueInt(), -1);
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c3", -1).ValueInt(), 0);
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c3", -1).ValueInt(), 1);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c3", -1).ValueInt(), -1);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c3", -1).ValueInt(), 0);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c3", -1).ValueInt(), 1);
 
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 0);
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 5);
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 10);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 0);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 5);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c4", 0, 5).ValueInt(), 10);
 
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), 0);
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), -5);
-  EXPECT_EQ(EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), -10);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), 0);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), -5);
+  EXPECT_EQ(this->EvaluateFunction("COUNTER", "c5", 0, -5).ValueInt(), -10);
 
-  EXPECT_THROW(EvaluateFunction("COUNTER", "c6", 0, 0), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("COUNTER", "c6", 0, 0), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Id) {
-  auto va = dba.InsertVertex();
-  auto ea = dba.InsertEdge(&va, &va, dba.NameToEdgeType("edge"));
+TYPED_TEST(FunctionTest, Id) {
+  auto va = this->dba.InsertVertex();
+  auto ea = this->dba.InsertEdge(&va, &va, this->dba.NameToEdgeType("edge"));
   ASSERT_TRUE(ea.HasValue());
-  auto vb = dba.InsertVertex();
-  dba.AdvanceCommand();
-  EXPECT_TRUE(EvaluateFunction("ID", TypedValue()).IsNull());
-  EXPECT_EQ(EvaluateFunction("ID", va).ValueInt(), 0);
-  EXPECT_EQ(EvaluateFunction("ID", *ea).ValueInt(), 0);
-  EXPECT_EQ(EvaluateFunction("ID", vb).ValueInt(), 1);
-  EXPECT_THROW(EvaluateFunction("ID"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("ID", 0), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("ID", va, *ea), QueryRuntimeException);
+  auto vb = this->dba.InsertVertex();
+  this->dba.AdvanceCommand();
+  EXPECT_TRUE(this->EvaluateFunction("ID", TypedValue()).IsNull());
+  EXPECT_EQ(this->EvaluateFunction("ID", va).ValueInt(), 0);
+  EXPECT_EQ(this->EvaluateFunction("ID", *ea).ValueInt(), 0);
+  EXPECT_EQ(this->EvaluateFunction("ID", vb).ValueInt(), 1);
+  EXPECT_THROW(this->EvaluateFunction("ID"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("ID", 0), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("ID", va, *ea), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, ToStringNull) { EXPECT_TRUE(EvaluateFunction("TOSTRING", TypedValue()).IsNull()); }
+TYPED_TEST(FunctionTest, ToStringNull) { EXPECT_TRUE(this->EvaluateFunction("TOSTRING", TypedValue()).IsNull()); }
 
-TEST_F(FunctionTest, ToStringString) {
-  EXPECT_EQ(EvaluateFunction("TOSTRING", "").ValueString(), "");
-  EXPECT_EQ(EvaluateFunction("TOSTRING", "this is a string").ValueString(), "this is a string");
+TYPED_TEST(FunctionTest, ToStringString) {
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", "").ValueString(), "");
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", "this is a string").ValueString(), "this is a string");
 }
 
-TEST_F(FunctionTest, ToStringInteger) {
-  EXPECT_EQ(EvaluateFunction("TOSTRING", -23321312).ValueString(), "-23321312");
-  EXPECT_EQ(EvaluateFunction("TOSTRING", 0).ValueString(), "0");
-  EXPECT_EQ(EvaluateFunction("TOSTRING", 42).ValueString(), "42");
+TYPED_TEST(FunctionTest, ToStringInteger) {
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", -23321312).ValueString(), "-23321312");
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", 0).ValueString(), "0");
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", 42).ValueString(), "42");
 }
 
-TEST_F(FunctionTest, ToStringDouble) {
-  EXPECT_EQ(EvaluateFunction("TOSTRING", -42.42).ValueString(), "-42.420000");
-  EXPECT_EQ(EvaluateFunction("TOSTRING", 0.0).ValueString(), "0.000000");
-  EXPECT_EQ(EvaluateFunction("TOSTRING", 238910.2313217).ValueString(), "238910.231322");
+TYPED_TEST(FunctionTest, ToStringDouble) {
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", -42.42).ValueString(), "-42.420000");
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", 0.0).ValueString(), "0.000000");
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", 238910.2313217).ValueString(), "238910.231322");
 }
 
-TEST_F(FunctionTest, ToStringBool) {
-  EXPECT_EQ(EvaluateFunction("TOSTRING", true).ValueString(), "true");
-  EXPECT_EQ(EvaluateFunction("TOSTRING", false).ValueString(), "false");
+TYPED_TEST(FunctionTest, ToStringBool) {
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", true).ValueString(), "true");
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", false).ValueString(), "false");
 }
 
-TEST_F(FunctionTest, ToStringDate) {
+TYPED_TEST(FunctionTest, ToStringDate) {
   const auto date = memgraph::utils::Date({1970, 1, 2});
-  EXPECT_EQ(EvaluateFunction("TOSTRING", date).ValueString(), "1970-01-02");
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", date).ValueString(), "1970-01-02");
 }
 
-TEST_F(FunctionTest, ToStringLocalTime) {
+TYPED_TEST(FunctionTest, ToStringLocalTime) {
   const auto lt = memgraph::utils::LocalTime({13, 2, 40, 100, 50});
-  EXPECT_EQ(EvaluateFunction("TOSTRING", lt).ValueString(), "13:02:40.100050");
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", lt).ValueString(), "13:02:40.100050");
 }
 
-TEST_F(FunctionTest, ToStringLocalDateTime) {
+TYPED_TEST(FunctionTest, ToStringLocalDateTime) {
   const auto ldt = memgraph::utils::LocalDateTime({1970, 1, 2}, {23, 02, 59});
-  EXPECT_EQ(EvaluateFunction("TOSTRING", ldt).ValueString(), "1970-01-02T23:02:59.000000");
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", ldt).ValueString(), "1970-01-02T23:02:59.000000");
 }
 
-TEST_F(FunctionTest, ToStringDuration) {
+TYPED_TEST(FunctionTest, ToStringDuration) {
   memgraph::utils::Duration duration{{.minute = 2, .second = 2, .microsecond = 33}};
-  EXPECT_EQ(EvaluateFunction("TOSTRING", duration).ValueString(), "P0DT0H2M2.000033S");
+  EXPECT_EQ(this->EvaluateFunction("TOSTRING", duration).ValueString(), "P0DT0H2M2.000033S");
 }
 
-TEST_F(FunctionTest, ToStringExceptions) { EXPECT_THROW(EvaluateFunction("TOSTRING", 1, 2, 3), QueryRuntimeException); }
-
-TEST_F(FunctionTest, TimestampVoid) {
-  ctx.timestamp = 42;
-  EXPECT_EQ(EvaluateFunction("TIMESTAMP").ValueInt(), 42);
+TYPED_TEST(FunctionTest, ToStringExceptions) {
+  EXPECT_THROW(this->EvaluateFunction("TOSTRING", 1, 2, 3), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, TimestampDate) {
-  ctx.timestamp = 42;
-  EXPECT_EQ(EvaluateFunction("TIMESTAMP", memgraph::utils::Date({1970, 1, 1})).ValueInt(), 0);
-  EXPECT_EQ(EvaluateFunction("TIMESTAMP", memgraph::utils::Date({1971, 1, 1})).ValueInt(), 31536000000000);
+TYPED_TEST(FunctionTest, TimestampVoid) {
+  this->ctx.timestamp = 42;
+  EXPECT_EQ(this->EvaluateFunction("TIMESTAMP").ValueInt(), 42);
 }
 
-TEST_F(FunctionTest, TimestampLocalTime) {
-  ctx.timestamp = 42;
+TYPED_TEST(FunctionTest, TimestampDate) {
+  this->ctx.timestamp = 42;
+  EXPECT_EQ(this->EvaluateFunction("TIMESTAMP", memgraph::utils::Date({1970, 1, 1})).ValueInt(), 0);
+  EXPECT_EQ(this->EvaluateFunction("TIMESTAMP", memgraph::utils::Date({1971, 1, 1})).ValueInt(), 31536000000000);
+}
+
+TYPED_TEST(FunctionTest, TimestampLocalTime) {
+  this->ctx.timestamp = 42;
   const memgraph::utils::LocalTime time(10000);
-  EXPECT_EQ(EvaluateFunction("TIMESTAMP", time).ValueInt(), 10000);
+  EXPECT_EQ(this->EvaluateFunction("TIMESTAMP", time).ValueInt(), 10000);
 }
 
-TEST_F(FunctionTest, TimestampLocalDateTime) {
-  ctx.timestamp = 42;
+TYPED_TEST(FunctionTest, TimestampLocalDateTime) {
+  this->ctx.timestamp = 42;
   const memgraph::utils::LocalDateTime time(20000);
-  EXPECT_EQ(EvaluateFunction("TIMESTAMP", time).ValueInt(), 20000);
+  EXPECT_EQ(this->EvaluateFunction("TIMESTAMP", time).ValueInt(), 20000);
 }
 
-TEST_F(FunctionTest, TimestampDuration) {
-  ctx.timestamp = 42;
+TYPED_TEST(FunctionTest, TimestampDuration) {
+  this->ctx.timestamp = 42;
   const memgraph::utils::Duration time(20000);
-  EXPECT_EQ(EvaluateFunction("TIMESTAMP", time).ValueInt(), 20000);
+  EXPECT_EQ(this->EvaluateFunction("TIMESTAMP", time).ValueInt(), 20000);
 }
 
-TEST_F(FunctionTest, TimestampExceptions) {
-  ctx.timestamp = 42;
-  EXPECT_THROW(EvaluateFunction("TIMESTAMP", 1).ValueInt(), QueryRuntimeException);
+TYPED_TEST(FunctionTest, TimestampExceptions) {
+  this->ctx.timestamp = 42;
+  EXPECT_THROW(this->EvaluateFunction("TIMESTAMP", 1).ValueInt(), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Left) {
-  EXPECT_THROW(EvaluateFunction("LEFT"), QueryRuntimeException);
+TYPED_TEST(FunctionTest, Left) {
+  EXPECT_THROW(this->EvaluateFunction("LEFT"), QueryRuntimeException);
 
-  EXPECT_TRUE(EvaluateFunction("LEFT", TypedValue(), TypedValue()).IsNull());
-  EXPECT_TRUE(EvaluateFunction("LEFT", TypedValue(), 10).IsNull());
-  EXPECT_THROW(EvaluateFunction("LEFT", TypedValue(), -10), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction("LEFT", TypedValue(), TypedValue()).IsNull());
+  EXPECT_TRUE(this->EvaluateFunction("LEFT", TypedValue(), 10).IsNull());
+  EXPECT_THROW(this->EvaluateFunction("LEFT", TypedValue(), -10), QueryRuntimeException);
 
-  EXPECT_EQ(EvaluateFunction("LEFT", "memgraph", 0).ValueString(), "");
-  EXPECT_EQ(EvaluateFunction("LEFT", "memgraph", 3).ValueString(), "mem");
-  EXPECT_EQ(EvaluateFunction("LEFT", "memgraph", 1000).ValueString(), "memgraph");
-  EXPECT_THROW(EvaluateFunction("LEFT", "memgraph", -10), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("LEFT", "memgraph", "graph"), QueryRuntimeException);
+  EXPECT_EQ(this->EvaluateFunction("LEFT", "memgraph", 0).ValueString(), "");
+  EXPECT_EQ(this->EvaluateFunction("LEFT", "memgraph", 3).ValueString(), "mem");
+  EXPECT_EQ(this->EvaluateFunction("LEFT", "memgraph", 1000).ValueString(), "memgraph");
+  EXPECT_THROW(this->EvaluateFunction("LEFT", "memgraph", -10), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("LEFT", "memgraph", "graph"), QueryRuntimeException);
 
-  EXPECT_THROW(EvaluateFunction("LEFT", 132, 10), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("LEFT", 132, 10), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Right) {
-  EXPECT_THROW(EvaluateFunction("RIGHT"), QueryRuntimeException);
+TYPED_TEST(FunctionTest, Right) {
+  EXPECT_THROW(this->EvaluateFunction("RIGHT"), QueryRuntimeException);
 
-  EXPECT_TRUE(EvaluateFunction("RIGHT", TypedValue(), TypedValue()).IsNull());
-  EXPECT_TRUE(EvaluateFunction("RIGHT", TypedValue(), 10).IsNull());
-  EXPECT_THROW(EvaluateFunction("RIGHT", TypedValue(), -10), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction("RIGHT", TypedValue(), TypedValue()).IsNull());
+  EXPECT_TRUE(this->EvaluateFunction("RIGHT", TypedValue(), 10).IsNull());
+  EXPECT_THROW(this->EvaluateFunction("RIGHT", TypedValue(), -10), QueryRuntimeException);
 
-  EXPECT_EQ(EvaluateFunction("RIGHT", "memgraph", 0).ValueString(), "");
-  EXPECT_EQ(EvaluateFunction("RIGHT", "memgraph", 3).ValueString(), "aph");
-  EXPECT_EQ(EvaluateFunction("RIGHT", "memgraph", 1000).ValueString(), "memgraph");
-  EXPECT_THROW(EvaluateFunction("RIGHT", "memgraph", -10), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("RIGHT", "memgraph", "graph"), QueryRuntimeException);
+  EXPECT_EQ(this->EvaluateFunction("RIGHT", "memgraph", 0).ValueString(), "");
+  EXPECT_EQ(this->EvaluateFunction("RIGHT", "memgraph", 3).ValueString(), "aph");
+  EXPECT_EQ(this->EvaluateFunction("RIGHT", "memgraph", 1000).ValueString(), "memgraph");
+  EXPECT_THROW(this->EvaluateFunction("RIGHT", "memgraph", -10), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("RIGHT", "memgraph", "graph"), QueryRuntimeException);
 
-  EXPECT_THROW(EvaluateFunction("RIGHT", 132, 10), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("RIGHT", 132, 10), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Trimming) {
-  EXPECT_TRUE(EvaluateFunction("LTRIM", TypedValue()).IsNull());
-  EXPECT_TRUE(EvaluateFunction("RTRIM", TypedValue()).IsNull());
-  EXPECT_TRUE(EvaluateFunction("TRIM", TypedValue()).IsNull());
+TYPED_TEST(FunctionTest, Trimming) {
+  EXPECT_TRUE(this->EvaluateFunction("LTRIM", TypedValue()).IsNull());
+  EXPECT_TRUE(this->EvaluateFunction("RTRIM", TypedValue()).IsNull());
+  EXPECT_TRUE(this->EvaluateFunction("TRIM", TypedValue()).IsNull());
 
-  EXPECT_EQ(EvaluateFunction("LTRIM", "  abc    ").ValueString(), "abc    ");
-  EXPECT_EQ(EvaluateFunction("RTRIM", " abc ").ValueString(), " abc");
-  EXPECT_EQ(EvaluateFunction("TRIM", "abc").ValueString(), "abc");
+  EXPECT_EQ(this->EvaluateFunction("LTRIM", "  abc    ").ValueString(), "abc    ");
+  EXPECT_EQ(this->EvaluateFunction("RTRIM", " abc ").ValueString(), " abc");
+  EXPECT_EQ(this->EvaluateFunction("TRIM", "abc").ValueString(), "abc");
 
-  EXPECT_THROW(EvaluateFunction("LTRIM", "x", "y"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("RTRIM", "x", "y"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("TRIM", "x", "y"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("LTRIM", "x", "y"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("RTRIM", "x", "y"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("TRIM", "x", "y"), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Reverse) {
-  EXPECT_TRUE(EvaluateFunction("REVERSE", TypedValue()).IsNull());
-  EXPECT_EQ(EvaluateFunction("REVERSE", "abc").ValueString(), "cba");
-  EXPECT_THROW(EvaluateFunction("REVERSE", "x", "y"), QueryRuntimeException);
+TYPED_TEST(FunctionTest, Reverse) {
+  EXPECT_TRUE(this->EvaluateFunction("REVERSE", TypedValue()).IsNull());
+  EXPECT_EQ(this->EvaluateFunction("REVERSE", "abc").ValueString(), "cba");
+  EXPECT_THROW(this->EvaluateFunction("REVERSE", "x", "y"), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Replace) {
-  EXPECT_THROW(EvaluateFunction("REPLACE"), QueryRuntimeException);
-  EXPECT_TRUE(EvaluateFunction("REPLACE", TypedValue(), "l", "w").IsNull());
-  EXPECT_TRUE(EvaluateFunction("REPLACE", "hello", TypedValue(), "w").IsNull());
-  EXPECT_TRUE(EvaluateFunction("REPLACE", "hello", "l", TypedValue()).IsNull());
-  EXPECT_EQ(EvaluateFunction("REPLACE", "hello", "l", "w").ValueString(), "hewwo");
+TYPED_TEST(FunctionTest, Replace) {
+  EXPECT_THROW(this->EvaluateFunction("REPLACE"), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction("REPLACE", TypedValue(), "l", "w").IsNull());
+  EXPECT_TRUE(this->EvaluateFunction("REPLACE", "hello", TypedValue(), "w").IsNull());
+  EXPECT_TRUE(this->EvaluateFunction("REPLACE", "hello", "l", TypedValue()).IsNull());
+  EXPECT_EQ(this->EvaluateFunction("REPLACE", "hello", "l", "w").ValueString(), "hewwo");
 
-  EXPECT_THROW(EvaluateFunction("REPLACE", 1, "l", "w"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("REPLACE", "hello", 1, "w"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("REPLACE", "hello", "l", 1), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("REPLACE", 1, "l", "w"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("REPLACE", "hello", 1, "w"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("REPLACE", "hello", "l", 1), QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Split) {
-  EXPECT_THROW(EvaluateFunction("SPLIT"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("SPLIT", "one,two", 1), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("SPLIT", 1, "one,two"), QueryRuntimeException);
+TYPED_TEST(FunctionTest, Split) {
+  EXPECT_THROW(this->EvaluateFunction("SPLIT"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("SPLIT", "one,two", 1), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("SPLIT", 1, "one,two"), QueryRuntimeException);
 
-  EXPECT_TRUE(EvaluateFunction("SPLIT", TypedValue(), TypedValue()).IsNull());
-  EXPECT_TRUE(EvaluateFunction("SPLIT", "one,two", TypedValue()).IsNull());
-  EXPECT_TRUE(EvaluateFunction("SPLIT", TypedValue(), ",").IsNull());
+  EXPECT_TRUE(this->EvaluateFunction("SPLIT", TypedValue(), TypedValue()).IsNull());
+  EXPECT_TRUE(this->EvaluateFunction("SPLIT", "one,two", TypedValue()).IsNull());
+  EXPECT_TRUE(this->EvaluateFunction("SPLIT", TypedValue(), ",").IsNull());
 
-  auto result = EvaluateFunction("SPLIT", "one,two", ",");
+  auto result = this->EvaluateFunction("SPLIT", "one,two", ",");
   EXPECT_TRUE(result.IsList());
   EXPECT_EQ(result.ValueList()[0].ValueString(), "one");
   EXPECT_EQ(result.ValueList()[1].ValueString(), "two");
 }
 
-TEST_F(FunctionTest, Substring) {
-  EXPECT_THROW(EvaluateFunction("SUBSTRING"), QueryRuntimeException);
+TYPED_TEST(FunctionTest, Substring) {
+  EXPECT_THROW(this->EvaluateFunction("SUBSTRING"), QueryRuntimeException);
 
-  EXPECT_TRUE(EvaluateFunction("SUBSTRING", TypedValue(), 0, 10).IsNull());
-  EXPECT_THROW(EvaluateFunction("SUBSTRING", TypedValue(), TypedValue()), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("SUBSTRING", TypedValue(), -10), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("SUBSTRING", TypedValue(), 0, TypedValue()), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("SUBSTRING", TypedValue(), 0, -10), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction("SUBSTRING", TypedValue(), 0, 10).IsNull());
+  EXPECT_THROW(this->EvaluateFunction("SUBSTRING", TypedValue(), TypedValue()), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("SUBSTRING", TypedValue(), -10), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("SUBSTRING", TypedValue(), 0, TypedValue()), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("SUBSTRING", TypedValue(), 0, -10), QueryRuntimeException);
 
-  EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 2).ValueString(), "llo");
-  EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 10).ValueString(), "");
-  EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 2, 0).ValueString(), "");
-  EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 1, 3).ValueString(), "ell");
-  EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 1, 4).ValueString(), "ello");
-  EXPECT_EQ(EvaluateFunction("SUBSTRING", "hello", 1, 10).ValueString(), "ello");
+  EXPECT_EQ(this->EvaluateFunction("SUBSTRING", "hello", 2).ValueString(), "llo");
+  EXPECT_EQ(this->EvaluateFunction("SUBSTRING", "hello", 10).ValueString(), "");
+  EXPECT_EQ(this->EvaluateFunction("SUBSTRING", "hello", 2, 0).ValueString(), "");
+  EXPECT_EQ(this->EvaluateFunction("SUBSTRING", "hello", 1, 3).ValueString(), "ell");
+  EXPECT_EQ(this->EvaluateFunction("SUBSTRING", "hello", 1, 4).ValueString(), "ello");
+  EXPECT_EQ(this->EvaluateFunction("SUBSTRING", "hello", 1, 10).ValueString(), "ello");
 }
 
-TEST_F(FunctionTest, ToLower) {
-  EXPECT_THROW(EvaluateFunction("TOLOWER"), QueryRuntimeException);
-  EXPECT_TRUE(EvaluateFunction("TOLOWER", TypedValue()).IsNull());
-  EXPECT_EQ(EvaluateFunction("TOLOWER", "Ab__C").ValueString(), "ab__c");
+TYPED_TEST(FunctionTest, ToLower) {
+  EXPECT_THROW(this->EvaluateFunction("TOLOWER"), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction("TOLOWER", TypedValue()).IsNull());
+  EXPECT_EQ(this->EvaluateFunction("TOLOWER", "Ab__C").ValueString(), "ab__c");
 }
 
-TEST_F(FunctionTest, ToUpper) {
-  EXPECT_THROW(EvaluateFunction("TOUPPER"), QueryRuntimeException);
-  EXPECT_TRUE(EvaluateFunction("TOUPPER", TypedValue()).IsNull());
-  EXPECT_EQ(EvaluateFunction("TOUPPER", "Ab__C").ValueString(), "AB__C");
+TYPED_TEST(FunctionTest, ToUpper) {
+  EXPECT_THROW(this->EvaluateFunction("TOUPPER"), QueryRuntimeException);
+  EXPECT_TRUE(this->EvaluateFunction("TOUPPER", TypedValue()).IsNull());
+  EXPECT_EQ(this->EvaluateFunction("TOUPPER", "Ab__C").ValueString(), "AB__C");
 }
 
-TEST_F(FunctionTest, ToByteString) {
-  EXPECT_THROW(EvaluateFunction("TOBYTESTRING"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("TOBYTESTRING", 42), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("TOBYTESTRING", TypedValue()), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("TOBYTESTRING", "", 42), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("TOBYTESTRING", "ff"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("TOBYTESTRING", "00"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("TOBYTESTRING", "0xG"), QueryRuntimeException);
-  EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "").ValueString(), "");
-  EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0x").ValueString(), "");
-  EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0X").ValueString(), "");
-  EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0x0123456789aAbBcCdDeEfF").ValueString(),
+TYPED_TEST(FunctionTest, ToByteString) {
+  EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING", 42), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING", TypedValue()), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING", "", 42), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING", "ff"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING", "00"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("TOBYTESTRING", "0xG"), QueryRuntimeException);
+  EXPECT_EQ(this->EvaluateFunction("TOBYTESTRING", "").ValueString(), "");
+  EXPECT_EQ(this->EvaluateFunction("TOBYTESTRING", "0x").ValueString(), "");
+  EXPECT_EQ(this->EvaluateFunction("TOBYTESTRING", "0X").ValueString(), "");
+  EXPECT_EQ(this->EvaluateFunction("TOBYTESTRING", "0x0123456789aAbBcCdDeEfF").ValueString(),
             "\x01\x23\x45\x67\x89\xAA\xBB\xCC\xDD\xEE\xFF");
-  EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0x042").ValueString().size(), 2);
-  EXPECT_EQ(EvaluateFunction("TOBYTESTRING", "0x042").ValueString(),
+  EXPECT_EQ(this->EvaluateFunction("TOBYTESTRING", "0x042").ValueString().size(), 2);
+  EXPECT_EQ(this->EvaluateFunction("TOBYTESTRING", "0x042").ValueString(),
             memgraph::utils::pmr::string("\x00\x42", 2, memgraph::utils::NewDeleteResource()));
 }
 
-TEST_F(FunctionTest, FromByteString) {
-  EXPECT_THROW(EvaluateFunction("FROMBYTESTRING"), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("FROMBYTESTRING", 42), QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("FROMBYTESTRING", TypedValue()), QueryRuntimeException);
-  EXPECT_EQ(EvaluateFunction("FROMBYTESTRING", "").ValueString(), "");
-  auto bytestring = EvaluateFunction("TOBYTESTRING", "0x123456789aAbBcCdDeEfF");
-  EXPECT_EQ(EvaluateFunction("FROMBYTESTRING", bytestring).ValueString(), "0x0123456789aabbccddeeff");
-  EXPECT_EQ(EvaluateFunction("FROMBYTESTRING", std::string("\x00\x42", 2)).ValueString(), "0x0042");
+TYPED_TEST(FunctionTest, FromByteString) {
+  EXPECT_THROW(this->EvaluateFunction("FROMBYTESTRING"), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("FROMBYTESTRING", 42), QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("FROMBYTESTRING", TypedValue()), QueryRuntimeException);
+  EXPECT_EQ(this->EvaluateFunction("FROMBYTESTRING", "").ValueString(), "");
+  auto bytestring = this->EvaluateFunction("TOBYTESTRING", "0x123456789aAbBcCdDeEfF");
+  EXPECT_EQ(this->EvaluateFunction("FROMBYTESTRING", bytestring).ValueString(), "0x0123456789aabbccddeeff");
+  EXPECT_EQ(this->EvaluateFunction("FROMBYTESTRING", std::string("\x00\x42", 2)).ValueString(), "0x0042");
 }
 
-TEST_F(FunctionTest, Date) {
+TYPED_TEST(FunctionTest, Date) {
   const auto unix_epoch = memgraph::utils::Date({1970, 1, 1});
-  EXPECT_EQ(EvaluateFunction("DATE", "1970-01-01").ValueDate(), unix_epoch);
+  EXPECT_EQ(this->EvaluateFunction("DATE", "1970-01-01").ValueDate(), unix_epoch);
   const auto map_param = TypedValue(
       std::map<std::string, TypedValue>{{"year", TypedValue(1970)}, {"month", TypedValue(1)}, {"day", TypedValue(1)}});
-  EXPECT_EQ(EvaluateFunction("DATE", map_param).ValueDate(), unix_epoch);
+  EXPECT_EQ(this->EvaluateFunction("DATE", map_param).ValueDate(), unix_epoch);
   const auto today = memgraph::utils::CurrentDate();
-  EXPECT_EQ(EvaluateFunction("DATE").ValueDate(), today);
+  EXPECT_EQ(this->EvaluateFunction("DATE").ValueDate(), today);
 
-  EXPECT_THROW(EvaluateFunction("DATE", "{}"), memgraph::utils::BasicException);
-  EXPECT_THROW(EvaluateFunction("DATE", std::map<std::string, TypedValue>{{"years", TypedValue(1970)}}),
+  EXPECT_THROW(this->EvaluateFunction("DATE", "{}"), memgraph::utils::BasicException);
+  EXPECT_THROW(this->EvaluateFunction("DATE", std::map<std::string, TypedValue>{{"years", TypedValue(1970)}}),
                QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("DATE", std::map<std::string, TypedValue>{{"mnths", TypedValue(1970)}}),
+  EXPECT_THROW(this->EvaluateFunction("DATE", std::map<std::string, TypedValue>{{"mnths", TypedValue(1970)}}),
                QueryRuntimeException);
-  EXPECT_THROW(EvaluateFunction("DATE", std::map<std::string, TypedValue>{{"dayz", TypedValue(1970)}}),
+  EXPECT_THROW(this->EvaluateFunction("DATE", std::map<std::string, TypedValue>{{"dayz", TypedValue(1970)}}),
                QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, LocalTime) {
+TYPED_TEST(FunctionTest, LocalTime) {
   const auto local_time = memgraph::utils::LocalTime({13, 3, 2, 0, 0});
-  EXPECT_EQ(EvaluateFunction("LOCALTIME", "130302").ValueLocalTime(), local_time);
+  EXPECT_EQ(this->EvaluateFunction("LOCALTIME", "130302").ValueLocalTime(), local_time);
   const auto one_sec_in_microseconds = 1000000;
   const auto map_param = TypedValue(std::map<std::string, TypedValue>{{"hour", TypedValue(1)},
                                                                       {"minute", TypedValue(2)},
                                                                       {"second", TypedValue(3)},
                                                                       {"millisecond", TypedValue(4)},
                                                                       {"microsecond", TypedValue(5)}});
-  EXPECT_EQ(EvaluateFunction("LOCALTIME", map_param).ValueLocalTime(), memgraph::utils::LocalTime({1, 2, 3, 4, 5}));
+  EXPECT_EQ(this->EvaluateFunction("LOCALTIME", map_param).ValueLocalTime(),
+            memgraph::utils::LocalTime({1, 2, 3, 4, 5}));
   const auto today = memgraph::utils::CurrentLocalTime();
-  EXPECT_NEAR(EvaluateFunction("LOCALTIME").ValueLocalTime().MicrosecondsSinceEpoch(), today.MicrosecondsSinceEpoch(),
-              one_sec_in_microseconds);
+  EXPECT_NEAR(this->EvaluateFunction("LOCALTIME").ValueLocalTime().MicrosecondsSinceEpoch(),
+              today.MicrosecondsSinceEpoch(), one_sec_in_microseconds);
 
-  EXPECT_THROW(EvaluateFunction("LOCALTIME", "{}"), memgraph::utils::BasicException);
-  EXPECT_THROW(EvaluateFunction("LOCALTIME", TypedValue(std::map<std::string, TypedValue>{{"hous", TypedValue(1970)}})),
-               QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("LOCALTIME", "{}"), memgraph::utils::BasicException);
   EXPECT_THROW(
-      EvaluateFunction("LOCALTIME", TypedValue(std::map<std::string, TypedValue>{{"minut", TypedValue(1970)}})),
+      this->EvaluateFunction("LOCALTIME", TypedValue(std::map<std::string, TypedValue>{{"hous", TypedValue(1970)}})),
       QueryRuntimeException);
   EXPECT_THROW(
-      EvaluateFunction("LOCALTIME", TypedValue(std::map<std::string, TypedValue>{{"seconds", TypedValue(1970)}})),
+      this->EvaluateFunction("LOCALTIME", TypedValue(std::map<std::string, TypedValue>{{"minut", TypedValue(1970)}})),
+      QueryRuntimeException);
+  EXPECT_THROW(
+      this->EvaluateFunction("LOCALTIME", TypedValue(std::map<std::string, TypedValue>{{"seconds", TypedValue(1970)}})),
       QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, LocalDateTime) {
+TYPED_TEST(FunctionTest, LocalDateTime) {
   const auto local_date_time = memgraph::utils::LocalDateTime({1970, 1, 1}, {13, 3, 2, 0, 0});
-  EXPECT_EQ(EvaluateFunction("LOCALDATETIME", "1970-01-01T13:03:02").ValueLocalDateTime(), local_date_time);
+  EXPECT_EQ(this->EvaluateFunction("LOCALDATETIME", "1970-01-01T13:03:02").ValueLocalDateTime(), local_date_time);
   const auto today = memgraph::utils::CurrentLocalDateTime();
   const auto one_sec_in_microseconds = 1000000;
   const auto map_param = TypedValue(std::map<std::string, TypedValue>{{"year", TypedValue(1972)},
@@ -2190,20 +2302,20 @@ TEST_F(FunctionTest, LocalDateTime) {
                                                                       {"millisecond", TypedValue(7)},
                                                                       {"microsecond", TypedValue(8)}});
 
-  EXPECT_EQ(EvaluateFunction("LOCALDATETIME", map_param).ValueLocalDateTime(),
+  EXPECT_EQ(this->EvaluateFunction("LOCALDATETIME", map_param).ValueLocalDateTime(),
             memgraph::utils::LocalDateTime({1972, 2, 3}, {4, 5, 6, 7, 8}));
-  EXPECT_NEAR(EvaluateFunction("LOCALDATETIME").ValueLocalDateTime().MicrosecondsSinceEpoch(),
+  EXPECT_NEAR(this->EvaluateFunction("LOCALDATETIME").ValueLocalDateTime().MicrosecondsSinceEpoch(),
               today.MicrosecondsSinceEpoch(), one_sec_in_microseconds);
-  EXPECT_THROW(EvaluateFunction("LOCALDATETIME", "{}"), memgraph::utils::BasicException);
-  EXPECT_THROW(
-      EvaluateFunction("LOCALDATETIME", TypedValue(std::map<std::string, TypedValue>{{"hours", TypedValue(1970)}})),
-      QueryRuntimeException);
-  EXPECT_THROW(
-      EvaluateFunction("LOCALDATETIME", TypedValue(std::map<std::string, TypedValue>{{"seconds", TypedValue(1970)}})),
-      QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("LOCALDATETIME", "{}"), memgraph::utils::BasicException);
+  EXPECT_THROW(this->EvaluateFunction("LOCALDATETIME",
+                                      TypedValue(std::map<std::string, TypedValue>{{"hours", TypedValue(1970)}})),
+               QueryRuntimeException);
+  EXPECT_THROW(this->EvaluateFunction("LOCALDATETIME",
+                                      TypedValue(std::map<std::string, TypedValue>{{"seconds", TypedValue(1970)}})),
+               QueryRuntimeException);
 }
 
-TEST_F(FunctionTest, Duration) {
+TYPED_TEST(FunctionTest, Duration) {
   const auto map_param = TypedValue(std::map<std::string, TypedValue>{{"day", TypedValue(3)},
                                                                       {"hour", TypedValue(4)},
                                                                       {"minute", TypedValue(5)},
@@ -2211,12 +2323,14 @@ TEST_F(FunctionTest, Duration) {
                                                                       {"millisecond", TypedValue(7)},
                                                                       {"microsecond", TypedValue(8)}});
 
-  EXPECT_EQ(EvaluateFunction("DURATION", map_param).ValueDuration(), memgraph::utils::Duration({3, 4, 5, 6, 7, 8}));
-  EXPECT_THROW(EvaluateFunction("DURATION", "{}"), memgraph::utils::BasicException);
-  EXPECT_THROW(EvaluateFunction("DURATION", TypedValue(std::map<std::string, TypedValue>{{"hours", TypedValue(1970)}})),
-               QueryRuntimeException);
+  EXPECT_EQ(this->EvaluateFunction("DURATION", map_param).ValueDuration(),
+            memgraph::utils::Duration({3, 4, 5, 6, 7, 8}));
+  EXPECT_THROW(this->EvaluateFunction("DURATION", "{}"), memgraph::utils::BasicException);
   EXPECT_THROW(
-      EvaluateFunction("DURATION", TypedValue(std::map<std::string, TypedValue>{{"seconds", TypedValue(1970)}})),
+      this->EvaluateFunction("DURATION", TypedValue(std::map<std::string, TypedValue>{{"hours", TypedValue(1970)}})),
+      QueryRuntimeException);
+  EXPECT_THROW(
+      this->EvaluateFunction("DURATION", TypedValue(std::map<std::string, TypedValue>{{"seconds", TypedValue(1970)}})),
       QueryRuntimeException);
 
   const auto map_param_negative = TypedValue(std::map<std::string, TypedValue>{{"day", TypedValue(-3)},
@@ -2225,14 +2339,14 @@ TEST_F(FunctionTest, Duration) {
                                                                                {"second", TypedValue(-6)},
                                                                                {"millisecond", TypedValue(-7)},
                                                                                {"microsecond", TypedValue(-8)}});
-  EXPECT_EQ(EvaluateFunction("DURATION", map_param_negative).ValueDuration(),
+  EXPECT_EQ(this->EvaluateFunction("DURATION", map_param_negative).ValueDuration(),
             memgraph::utils::Duration({-3, -4, -5, -6, -7, -8}));
 
-  EXPECT_EQ(EvaluateFunction("DURATION", "P4DT4H5M6.2S").ValueDuration(),
+  EXPECT_EQ(this->EvaluateFunction("DURATION", "P4DT4H5M6.2S").ValueDuration(),
             memgraph::utils::Duration({4, 4, 5, 6, 0, 200000}));
-  EXPECT_EQ(EvaluateFunction("DURATION", "P3DT4H5M6.100S").ValueDuration(),
+  EXPECT_EQ(this->EvaluateFunction("DURATION", "P3DT4H5M6.100S").ValueDuration(),
             memgraph::utils::Duration({3, 4, 5, 6, 0, 100000}));
-  EXPECT_EQ(EvaluateFunction("DURATION", "P3DT4H5M6.100110S").ValueDuration(),
+  EXPECT_EQ(this->EvaluateFunction("DURATION", "P3DT4H5M6.100110S").ValueDuration(),
             memgraph::utils::Duration({3, 4, 5, 6, 100, 110}));
 }
 }  // namespace
diff --git a/tests/unit/query_plan.cpp b/tests/unit/query_plan.cpp
index 5d2fe6d46..8b14c3bf2 100644
--- a/tests/unit/query_plan.cpp
+++ b/tests/unit/query_plan.cpp
@@ -83,7 +83,10 @@ auto CheckPlan(memgraph::query::CypherQuery *query, AstStorage &storage, TChecke
 }
 
 template <class T>
-class TestPlanner : public ::testing::Test {};
+class TestPlanner : public ::testing::Test {
+ public:
+  AstStorage storage;
+};
 
 using PlannerTypes = ::testing::Types<Planner>;
 
@@ -96,78 +99,66 @@ TYPED_TEST_CASE(TestPlanner, PlannerTypes);
 
 TYPED_TEST(TestPlanner, MatchNodeReturn) {
   // Test MATCH (n) RETURN n
-  AstStorage storage;
+  FakeDbAccessor dba;
   auto *as_n = NEXPR("n", IDENT("n"));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), RETURN(as_n)));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  FakeDbAccessor dba;
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, CreateNodeReturn) {
   // Test CREATE (n) RETURN n AS n
-  AstStorage storage;
+  FakeDbAccessor dba;
   auto ident_n = IDENT("n");
   auto query = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"))), RETURN(ident_n, AS("n"))));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   auto acc = ExpectAccumulate({symbol_table.at(*ident_n)});
-  FakeDbAccessor dba;
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectCreateNode(), acc, ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, CreateExpand) {
   // Test CREATE (n) -[r :rel1]-> (m)
-  AstStorage storage;
-  FakeDbAccessor dba;
   auto relationship = "relationship";
   auto *query = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"), EDGE("r", Direction::OUT, {relationship}), NODE("m")))));
-  CheckPlan<TypeParam>(query, storage, ExpectCreateNode(), ExpectCreateExpand(), ExpectEmptyResult());
+  CheckPlan<TypeParam>(query, this->storage, ExpectCreateNode(), ExpectCreateExpand(), ExpectEmptyResult());
 }
 
 TYPED_TEST(TestPlanner, CreateMultipleNode) {
   // Test CREATE (n), (m)
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n")), PATTERN(NODE("m")))));
-  CheckPlan<TypeParam>(query, storage, ExpectCreateNode(), ExpectCreateNode(), ExpectEmptyResult());
+  CheckPlan<TypeParam>(query, this->storage, ExpectCreateNode(), ExpectCreateNode(), ExpectEmptyResult());
 }
 
 TYPED_TEST(TestPlanner, CreateNodeExpandNode) {
   // Test CREATE (n) -[r :rel]-> (m), (l)
-  AstStorage storage;
-  FakeDbAccessor dba;
   auto relationship = "rel";
   auto *query = QUERY(SINGLE_QUERY(
       CREATE(PATTERN(NODE("n"), EDGE("r", Direction::OUT, {relationship}), NODE("m")), PATTERN(NODE("l")))));
-  CheckPlan<TypeParam>(query, storage, ExpectCreateNode(), ExpectCreateExpand(), ExpectCreateNode(),
+  CheckPlan<TypeParam>(query, this->storage, ExpectCreateNode(), ExpectCreateExpand(), ExpectCreateNode(),
                        ExpectEmptyResult());
 }
 
 TYPED_TEST(TestPlanner, CreateNamedPattern) {
   // Test CREATE p = (n) -[r :rel]-> (m)
-  AstStorage storage;
-  FakeDbAccessor dba;
   auto relationship = "rel";
   auto *query =
       QUERY(SINGLE_QUERY(CREATE(NAMED_PATTERN("p", NODE("n"), EDGE("r", Direction::OUT, {relationship}), NODE("m")))));
-  CheckPlan<TypeParam>(query, storage, ExpectCreateNode(), ExpectCreateExpand(), ExpectConstructNamedPath(),
+  CheckPlan<TypeParam>(query, this->storage, ExpectCreateNode(), ExpectCreateExpand(), ExpectConstructNamedPath(),
                        ExpectEmptyResult());
 }
 
 TYPED_TEST(TestPlanner, MatchCreateExpand) {
   // Test MATCH (n) CREATE (n) -[r :rel1]-> (m)
-  AstStorage storage;
-  FakeDbAccessor dba;
   auto relationship = "relationship";
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))),
                                    CREATE(PATTERN(NODE("n"), EDGE("r", Direction::OUT, {relationship}), NODE("m")))));
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectCreateExpand(), ExpectEmptyResult());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectCreateExpand(), ExpectEmptyResult());
 }
 
 TYPED_TEST(TestPlanner, MatchLabeledNodes) {
   // Test MATCH (n :label) RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto label = "label";
   auto *as_n = NEXPR("n", IDENT("n"));
@@ -175,47 +166,44 @@ TYPED_TEST(TestPlanner, MatchLabeledNodes) {
   {
     // Without created label index
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectFilter(), ExpectProduce());
   }
   {
     // With created label index
     dba.SetIndexCount(dba.Label(label), 0);
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabel(), ExpectProduce());
   }
 }
 
 TYPED_TEST(TestPlanner, MatchPathReturn) {
   // Test MATCH (n) -[r :relationship]- (m) RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto relationship = "relationship";
   auto *as_n = NEXPR("n", IDENT("n"));
   auto *query = QUERY(
       SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r", Direction::BOTH, {relationship}), NODE("m"))), RETURN(as_n)));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectExpand(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchNamedPatternReturn) {
   // Test MATCH p = (n) -[r :relationship]- (m) RETURN p
-  AstStorage storage;
   FakeDbAccessor dba;
   auto relationship = "relationship";
   auto *as_p = NEXPR("p", IDENT("p"));
   auto *query = QUERY(SINGLE_QUERY(
       MATCH(NAMED_PATTERN("p", NODE("n"), EDGE("r", Direction::BOTH, {relationship}), NODE("m"))), RETURN(as_p)));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectExpand(), ExpectConstructNamedPath(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchNamedPatternWithPredicateReturn) {
   // Test MATCH p = (n) -[r :relationship]- (m) WHERE 2 = p RETURN p
-  AstStorage storage;
   FakeDbAccessor dba;
   auto relationship = "relationship";
   auto *as_p = NEXPR("p", IDENT("p"));
@@ -223,14 +211,14 @@ TYPED_TEST(TestPlanner, MatchNamedPatternWithPredicateReturn) {
       QUERY(SINGLE_QUERY(MATCH(NAMED_PATTERN("p", NODE("n"), EDGE("r", Direction::BOTH, {relationship}), NODE("m"))),
                          WHERE(EQ(LITERAL(2), IDENT("p"))), RETURN(as_p)));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectExpand(), ExpectConstructNamedPath(), ExpectFilter(),
             ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, OptionalMatchNamedPatternReturn) {
   // Test OPTIONAL MATCH p = (n) -[r]- (m) RETURN p
-  AstStorage storage;
+  FakeDbAccessor dba;
   auto node_n = NODE("n");
   auto edge = EDGE("r");
   auto node_m = NODE("m");
@@ -240,8 +228,7 @@ TYPED_TEST(TestPlanner, OptionalMatchNamedPatternReturn) {
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   auto get_symbol = [&symbol_table](const auto *ast_node) { return symbol_table.at(*ast_node->identifier_); };
   std::vector<Symbol> optional_symbols{get_symbol(pattern), get_symbol(node_n), get_symbol(edge), get_symbol(node_m)};
-  FakeDbAccessor dba;
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   std::list<BaseOpChecker *> optional{new ExpectScanAll(), new ExpectExpand(), new ExpectConstructNamedPath()};
   CheckPlan(planner.plan(), symbol_table, ExpectOptional(optional_symbols, optional), ExpectProduce());
   DeleteListContent(&optional);
@@ -249,83 +236,76 @@ TYPED_TEST(TestPlanner, OptionalMatchNamedPatternReturn) {
 
 TYPED_TEST(TestPlanner, MatchWhereReturn) {
   // Test MATCH (n) WHERE n.property < 42 RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto property = dba.Property("property");
   auto *as_n = NEXPR("n", IDENT("n"));
-  auto *query = QUERY(
-      SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), WHERE(LESS(PROPERTY_LOOKUP("n", property), LITERAL(42))), RETURN(as_n)));
+  auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))),
+                                   WHERE(LESS(PROPERTY_LOOKUP(dba, "n", property), LITERAL(42))), RETURN(as_n)));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectFilter(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchDelete) {
   // Test MATCH (n) DELETE n
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), DELETE(IDENT("n"))));
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectDelete(), ExpectEmptyResult());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectDelete(), ExpectEmptyResult());
 }
 
 TYPED_TEST(TestPlanner, MatchNodeSet) {
   // Test MATCH (n) SET n.prop = 42, n = n, n :label
-  AstStorage storage;
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
   auto label = "label";
-  auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), SET(PROPERTY_LOOKUP("n", prop), LITERAL(42)),
+  auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), SET(PROPERTY_LOOKUP(dba, "n", prop), LITERAL(42)),
                                    SET("n", IDENT("n")), SET("n", {label})));
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectSetProperty(), ExpectSetProperties(), ExpectSetLabels(),
-                       ExpectEmptyResult());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectSetProperty(), ExpectSetProperties(),
+                       ExpectSetLabels(), ExpectEmptyResult());
 }
 
 TYPED_TEST(TestPlanner, MatchRemove) {
   // Test MATCH (n) REMOVE n.prop REMOVE n :label
-  AstStorage storage;
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
   auto label = "label";
   auto *query =
-      QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), REMOVE(PROPERTY_LOOKUP("n", prop)), REMOVE("n", {label})));
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectRemoveProperty(), ExpectRemoveLabels(),
+      QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), REMOVE(PROPERTY_LOOKUP(dba, "n", prop)), REMOVE("n", {label})));
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectRemoveProperty(), ExpectRemoveLabels(),
                        ExpectEmptyResult());
 }
 
 TYPED_TEST(TestPlanner, MatchMultiPattern) {
   // Test MATCH (n) -[r]- (m), (j) -[e]- (i) RETURN n
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(
       MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m")), PATTERN(NODE("j"), EDGE("e"), NODE("i"))), RETURN("n")));
   // We expect the expansions after the first to have a uniqueness filter in a
   // single MATCH clause.
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectExpand(), ExpectScanAll(), ExpectExpand(),
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectExpand(), ExpectScanAll(), ExpectExpand(),
                        ExpectEdgeUniquenessFilter(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchMultiPatternSameStart) {
   // Test MATCH (n), (n) -[e]- (m) RETURN n
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n")), PATTERN(NODE("n"), EDGE("e"), NODE("m"))), RETURN("n")));
   // We expect the second pattern to generate only an Expand, since another
   // ScanAll would be redundant.
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectExpand(), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectExpand(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchMultiPatternSameExpandStart) {
   // Test MATCH (n) -[r]- (m), (m) -[e]- (l) RETURN n
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(
       MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m")), PATTERN(NODE("m"), EDGE("e"), NODE("l"))), RETURN("n")));
   // We expect the second pattern to generate only an Expand. Another
   // ScanAll would be redundant, as it would generate the nodes obtained from
   // expansion. Additionally, a uniqueness filter is expected.
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectExpand(), ExpectExpand(), ExpectEdgeUniquenessFilter(),
-                       ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectExpand(), ExpectExpand(),
+                       ExpectEdgeUniquenessFilter(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MultiMatch) {
   // Test MATCH (n) -[r]- (m) MATCH (j) -[e]- (i) -[f]- (h) RETURN n
-  AstStorage storage;
+  FakeDbAccessor dba;
   auto *node_n = NODE("n");
   auto *edge_r = EDGE("r");
   auto *node_m = NODE("m");
@@ -337,8 +317,7 @@ TYPED_TEST(TestPlanner, MultiMatch) {
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n, edge_r, node_m)),
                                    MATCH(PATTERN(node_j, edge_e, node_i, edge_f, node_h)), RETURN("n")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  FakeDbAccessor dba;
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   // Multiple MATCH clauses form a Cartesian product, so the uniqueness should
   // not cross MATCH boundaries.
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectExpand(), ExpectScanAll(), ExpectExpand(),
@@ -347,27 +326,25 @@ TYPED_TEST(TestPlanner, MultiMatch) {
 
 TYPED_TEST(TestPlanner, MultiMatchSameStart) {
   // Test MATCH (n) MATCH (n) -[r]- (m) RETURN n
-  AstStorage storage;
+  FakeDbAccessor dba;
   auto *as_n = NEXPR("n", IDENT("n"));
   auto *query =
       QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))), RETURN(as_n)));
   // Similar to MatchMultiPatternSameStart, we expect only Expand from second
   // MATCH clause.
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  FakeDbAccessor dba;
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectExpand(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchWithReturn) {
   // Test MATCH (old) WITH old AS new RETURN new
-  AstStorage storage;
+  FakeDbAccessor dba;
   auto *as_new = NEXPR("new", IDENT("new"));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("old"))), WITH("old", AS("new")), RETURN(as_new)));
   // No accumulation since we only do reads.
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  FakeDbAccessor dba;
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectProduce(), ExpectProduce());
 }
 
@@ -375,25 +352,22 @@ TYPED_TEST(TestPlanner, MatchWithWhereReturn) {
   // Test MATCH (old) WITH old AS new WHERE new.prop < 42 RETURN new
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
   auto *as_new = NEXPR("new", IDENT("new"));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("old"))), WITH("old", AS("new")),
-                                   WHERE(LESS(PROPERTY_LOOKUP("new", prop), LITERAL(42))), RETURN(as_new)));
+                                   WHERE(LESS(PROPERTY_LOOKUP(dba, "new", prop), LITERAL(42))), RETURN(as_new)));
   // No accumulation since we only do reads.
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectProduce(), ExpectFilter(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, CreateMultiExpand) {
   // Test CREATE (n) -[r :r]-> (m), (n) - [p :p]-> (l)
-  FakeDbAccessor dba;
   auto r = "r";
   auto p = "p";
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"), EDGE("r", Direction::OUT, {r}), NODE("m")),
                                           PATTERN(NODE("n"), EDGE("p", Direction::OUT, {p}), NODE("l")))));
-  CheckPlan<TypeParam>(query, storage, ExpectCreateNode(), ExpectCreateExpand(), ExpectCreateExpand(),
+  CheckPlan<TypeParam>(query, this->storage, ExpectCreateNode(), ExpectCreateExpand(), ExpectCreateExpand(),
                        ExpectEmptyResult());
 }
 
@@ -402,13 +376,12 @@ TYPED_TEST(TestPlanner, MatchWithSumWhereReturn) {
   //      RETURN sum AS result
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
-  auto sum = SUM(PROPERTY_LOOKUP("n", prop), false);
+  auto sum = SUM(PROPERTY_LOOKUP(dba, "n", prop), false);
   auto literal = LITERAL(42);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), WITH(ADD(sum, literal), AS("sum")),
                                    WHERE(LESS(IDENT("sum"), LITERAL(42))), RETURN("sum", AS("result"))));
   auto aggr = ExpectAggregate({sum}, {literal});
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), aggr, ExpectProduce(), ExpectFilter(), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), aggr, ExpectProduce(), ExpectFilter(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchReturnSum) {
@@ -416,13 +389,12 @@ TYPED_TEST(TestPlanner, MatchReturnSum) {
   FakeDbAccessor dba;
   auto prop1 = dba.Property("prop1");
   auto prop2 = dba.Property("prop2");
-  AstStorage storage;
-  auto sum = SUM(PROPERTY_LOOKUP("n", prop1), false);
-  auto n_prop2 = PROPERTY_LOOKUP("n", prop2);
+  auto sum = SUM(PROPERTY_LOOKUP(dba, "n", prop1), false);
+  auto n_prop2 = PROPERTY_LOOKUP(dba, "n", prop2);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), RETURN(sum, AS("sum"), n_prop2, AS("group"))));
   auto aggr = ExpectAggregate({sum}, {n_prop2});
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), aggr, ExpectProduce());
 }
 
@@ -430,15 +402,14 @@ TYPED_TEST(TestPlanner, CreateWithSum) {
   // Test CREATE (n) WITH SUM(n.prop) AS sum
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
   auto ident_n = IDENT("n");
-  auto n_prop = PROPERTY_LOOKUP(ident_n, prop);
+  auto n_prop = PROPERTY_LOOKUP(dba, ident_n, prop);
   auto sum = SUM(n_prop, false);
   auto query = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"))), WITH(sum, AS("sum"))));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   auto acc = ExpectAccumulate({symbol_table.at(*ident_n)});
   auto aggr = ExpectAggregate({sum}, {});
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   // We expect both the accumulation and aggregation because the part before
   // WITH updates the database.
   CheckPlan(planner.plan(), symbol_table, ExpectCreateNode(), acc, aggr, ExpectProduce());
@@ -449,13 +420,12 @@ TYPED_TEST(TestPlanner, MatchWithSumWithDistinctWhereReturn) {
   //      RETURN sum AS result
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
-  auto sum = SUM(PROPERTY_LOOKUP("n", prop), true);
+  auto sum = SUM(PROPERTY_LOOKUP(dba, "n", prop), true);
   auto literal = LITERAL(42);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), WITH(ADD(sum, literal), AS("sum")),
                                    WHERE(LESS(IDENT("sum"), LITERAL(42))), RETURN("sum", AS("result"))));
   auto aggr = ExpectAggregate({sum}, {literal});
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), aggr, ExpectProduce(), ExpectFilter(), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), aggr, ExpectProduce(), ExpectFilter(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchReturnSumWithDistinct) {
@@ -463,13 +433,12 @@ TYPED_TEST(TestPlanner, MatchReturnSumWithDistinct) {
   FakeDbAccessor dba;
   auto prop1 = dba.Property("prop1");
   auto prop2 = dba.Property("prop2");
-  AstStorage storage;
-  auto sum = SUM(PROPERTY_LOOKUP("n", prop1), true);
-  auto n_prop2 = PROPERTY_LOOKUP("n", prop2);
+  auto sum = SUM(PROPERTY_LOOKUP(dba, "n", prop1), true);
+  auto n_prop2 = PROPERTY_LOOKUP(dba, "n", prop2);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), RETURN(sum, AS("sum"), n_prop2, AS("group"))));
   auto aggr = ExpectAggregate({sum}, {n_prop2});
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), aggr, ExpectProduce());
 }
 
@@ -477,15 +446,14 @@ TYPED_TEST(TestPlanner, CreateWithSumWithDistinct) {
   // Test CREATE (n) WITH SUM(DISTINCT n.prop) AS sum
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
   auto ident_n = IDENT("n");
-  auto n_prop = PROPERTY_LOOKUP(ident_n, prop);
+  auto n_prop = PROPERTY_LOOKUP(dba, ident_n, prop);
   auto sum = SUM(n_prop, true);
   auto query = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"))), WITH(sum, AS("sum"))));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   auto acc = ExpectAccumulate({symbol_table.at(*ident_n)});
   auto aggr = ExpectAggregate({sum}, {});
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   // We expect both the accumulation and aggregation because the part before
   // WITH updates the database.
   CheckPlan(planner.plan(), symbol_table, ExpectCreateNode(), acc, aggr, ExpectProduce());
@@ -493,35 +461,32 @@ TYPED_TEST(TestPlanner, CreateWithSumWithDistinct) {
 
 TYPED_TEST(TestPlanner, MatchWithCreate) {
   // Test MATCH (n) WITH n AS a CREATE (a) -[r :r]-> (b)
-  FakeDbAccessor dba;
   auto r_type = "r";
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), WITH("n", AS("a")),
                                    CREATE(PATTERN(NODE("a"), EDGE("r", Direction::OUT, {r_type}), NODE("b")))));
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectProduce(), ExpectCreateExpand(), ExpectEmptyResult());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectProduce(), ExpectCreateExpand(),
+                       ExpectEmptyResult());
 }
 
 TYPED_TEST(TestPlanner, MatchReturnSkipLimit) {
   // Test MATCH (n) RETURN n SKIP 2 LIMIT 1
-  AstStorage storage;
+  FakeDbAccessor dba;
   auto *as_n = NEXPR("n", IDENT("n"));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), RETURN(as_n, SKIP(LITERAL(2)), LIMIT(LITERAL(1)))));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  FakeDbAccessor dba;
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectProduce(), ExpectSkip(), ExpectLimit());
 }
 
 TYPED_TEST(TestPlanner, CreateWithSkipReturnLimit) {
   // Test CREATE (n) WITH n AS m SKIP 2 RETURN m LIMIT 1
-  AstStorage storage;
+  FakeDbAccessor dba;
   auto ident_n = IDENT("n");
   auto query = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"))), WITH(ident_n, AS("m"), SKIP(LITERAL(2))),
                                   RETURN("m", LIMIT(LITERAL(1)))));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   auto acc = ExpectAccumulate({symbol_table.at(*ident_n)});
-  FakeDbAccessor dba;
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   // Since we have a write query, we need to have Accumulate. This is a bit
   // different than Neo4j 3.0, which optimizes WITH followed by RETURN as a
   // single RETURN clause and then moves Skip and Limit before Accumulate.
@@ -535,16 +500,15 @@ TYPED_TEST(TestPlanner, CreateReturnSumSkipLimit) {
   // Test CREATE (n) RETURN SUM(n.prop) AS s SKIP 2 LIMIT 1
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
   auto ident_n = IDENT("n");
-  auto n_prop = PROPERTY_LOOKUP(ident_n, prop);
+  auto n_prop = PROPERTY_LOOKUP(dba, ident_n, prop);
   auto sum = SUM(n_prop, false);
   auto query =
       QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"))), RETURN(sum, AS("s"), SKIP(LITERAL(2)), LIMIT(LITERAL(1)))));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   auto acc = ExpectAccumulate({symbol_table.at(*ident_n)});
   auto aggr = ExpectAggregate({sum}, {});
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectCreateNode(), acc, aggr, ExpectProduce(), ExpectSkip(), ExpectLimit());
 }
 
@@ -552,16 +516,15 @@ TYPED_TEST(TestPlanner, CreateReturnSumWithDistinctSkipLimit) {
   // Test CREATE (n) RETURN SUM(n.prop) AS s SKIP 2 LIMIT 1
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
   auto ident_n = IDENT("n");
-  auto n_prop = PROPERTY_LOOKUP(ident_n, prop);
+  auto n_prop = PROPERTY_LOOKUP(dba, ident_n, prop);
   auto sum = SUM(n_prop, true);
   auto query =
       QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"))), RETURN(sum, AS("s"), SKIP(LITERAL(2)), LIMIT(LITERAL(1)))));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   auto acc = ExpectAccumulate({symbol_table.at(*ident_n)});
   auto aggr = ExpectAggregate({sum}, {});
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectCreateNode(), acc, aggr, ExpectProduce(), ExpectSkip(), ExpectLimit());
 }
 
@@ -569,13 +532,12 @@ TYPED_TEST(TestPlanner, MatchReturnOrderBy) {
   // Test MATCH (n) RETURN n AS m ORDER BY n.prop
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
   auto *as_m = NEXPR("m", IDENT("n"));
   auto *node_n = NODE("n");
-  auto ret = RETURN(as_m, ORDER_BY(PROPERTY_LOOKUP("n", prop)));
+  auto ret = RETURN(as_m, ORDER_BY(PROPERTY_LOOKUP(dba, "n", prop)));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n)), ret));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectProduce(), ExpectOrderBy());
 }
 
@@ -585,13 +547,12 @@ TYPED_TEST(TestPlanner, CreateWithOrderByWhere) {
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
   auto r_type = "r";
-  AstStorage storage;
   auto ident_n = IDENT("n");
   auto ident_r = IDENT("r");
   auto ident_m = IDENT("m");
-  auto new_prop = PROPERTY_LOOKUP("new", prop);
-  auto r_prop = PROPERTY_LOOKUP(ident_r, prop);
-  auto m_prop = PROPERTY_LOOKUP(ident_m, prop);
+  auto new_prop = PROPERTY_LOOKUP(dba, "new", prop);
+  auto r_prop = PROPERTY_LOOKUP(dba, ident_r, prop);
+  auto m_prop = PROPERTY_LOOKUP(dba, ident_m, prop);
   auto query =
       QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"), EDGE("r", Direction::OUT, {r_type}), NODE("m"))),
                          WITH(ident_n, AS("new"), ORDER_BY(new_prop, r_prop)), WHERE(LESS(m_prop, LITERAL(42)))));
@@ -602,29 +563,27 @@ TYPED_TEST(TestPlanner, CreateWithOrderByWhere) {
       symbol_table.at(*ident_r),  // `r` in ORDER BY
       symbol_table.at(*ident_m),  // `m` in WHERE
   });
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectCreateNode(), ExpectCreateExpand(), acc, ExpectProduce(),
             ExpectOrderBy(), ExpectFilter(), ExpectEmptyResult());
 }
 
 TYPED_TEST(TestPlanner, ReturnAddSumCountOrderBy) {
   // Test RETURN SUM(1) + COUNT(2) AS result ORDER BY result
-  AstStorage storage;
   auto sum = SUM(LITERAL(1), false);
   auto count = COUNT(LITERAL(2), false);
   auto *query = QUERY(SINGLE_QUERY(RETURN(ADD(sum, count), AS("result"), ORDER_BY(IDENT("result")))));
   auto aggr = ExpectAggregate({sum, count}, {});
-  CheckPlan<TypeParam>(query, storage, aggr, ExpectProduce(), ExpectOrderBy());
+  CheckPlan<TypeParam>(query, this->storage, aggr, ExpectProduce(), ExpectOrderBy());
 }
 
 TYPED_TEST(TestPlanner, ReturnAddSumCountWithDistinctOrderBy) {
   // Test RETURN SUM(1) + COUNT(2) AS result ORDER BY result
-  AstStorage storage;
   auto sum = SUM(LITERAL(1), true);
   auto count = COUNT(LITERAL(2), true);
   auto *query = QUERY(SINGLE_QUERY(RETURN(ADD(sum, count), AS("result"), ORDER_BY(IDENT("result")))));
   auto aggr = ExpectAggregate({sum, count}, {});
-  CheckPlan<TypeParam>(query, storage, aggr, ExpectProduce(), ExpectOrderBy());
+  CheckPlan<TypeParam>(query, this->storage, aggr, ExpectProduce(), ExpectOrderBy());
 }
 
 TYPED_TEST(TestPlanner, MatchMerge) {
@@ -634,19 +593,18 @@ TYPED_TEST(TestPlanner, MatchMerge) {
   FakeDbAccessor dba;
   auto r_type = "r";
   auto prop = dba.Property("prop");
-  AstStorage storage;
   auto ident_n = IDENT("n");
-  auto query =
-      QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))),
-                         MERGE(PATTERN(NODE("n"), EDGE("r", Direction::BOTH, {r_type}), NODE("m")),
-                               ON_MATCH(SET(PROPERTY_LOOKUP("n", prop), LITERAL(42))), ON_CREATE(SET("m", IDENT("n")))),
-                         RETURN(ident_n, AS("n"))));
+  auto query = QUERY(
+      SINGLE_QUERY(MATCH(PATTERN(NODE("n"))),
+                   MERGE(PATTERN(NODE("n"), EDGE("r", Direction::BOTH, {r_type}), NODE("m")),
+                         ON_MATCH(SET(PROPERTY_LOOKUP(dba, "n", prop), LITERAL(42))), ON_CREATE(SET("m", IDENT("n")))),
+                   RETURN(ident_n, AS("n"))));
   std::list<BaseOpChecker *> on_match{new ExpectExpand(), new ExpectSetProperty()};
   std::list<BaseOpChecker *> on_create{new ExpectCreateExpand(), new ExpectSetProperties()};
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   // We expect Accumulate after Merge, because it is considered as a write.
   auto acc = ExpectAccumulate({symbol_table.at(*ident_n)});
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectMerge(on_match, on_create), acc, ExpectProduce());
   DeleteListContent(&on_match);
   DeleteListContent(&on_create);
@@ -656,32 +614,29 @@ TYPED_TEST(TestPlanner, MatchOptionalMatchWhereReturn) {
   // Test MATCH (n) OPTIONAL MATCH (n) -[r]- (m) WHERE m.prop < 42 RETURN r
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), OPTIONAL_MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
-                                   WHERE(LESS(PROPERTY_LOOKUP("m", prop), LITERAL(42))), RETURN("r")));
+                                   WHERE(LESS(PROPERTY_LOOKUP(dba, "m", prop), LITERAL(42))), RETURN("r")));
   std::list<BaseOpChecker *> optional{new ExpectScanAll(), new ExpectExpand(), new ExpectFilter()};
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectOptional(optional), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectOptional(optional), ExpectProduce());
   DeleteListContent(&optional);
 }
 
 TYPED_TEST(TestPlanner, MatchOptionalMatchNodePropertyWithIndex) {
   // Test MATCH (n:Label) OPTIONAL MATCH (m:Label) WHERE n.prop = m.prop RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
-
   const auto label_name = "label";
   const auto label = dba.Label(label_name);
-  const auto property = PROPERTY_PAIR("prop");
+  const auto property = PROPERTY_PAIR(dba, "prop");
   dba.SetIndexCount(label, property.second, 0);
 
   auto *query = QUERY(SINGLE_QUERY(
       MATCH(PATTERN(NODE("n", label_name))), OPTIONAL_MATCH(PATTERN(NODE("m", label_name))),
-      WHERE(EQ(PROPERTY_LOOKUP("n", property.second), PROPERTY_LOOKUP("m", property.second))), RETURN("n")));
+      WHERE(EQ(PROPERTY_LOOKUP(dba, "n", property.second), PROPERTY_LOOKUP(dba, "m", property.second))), RETURN("n")));
 
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
 
-  auto m_prop = PROPERTY_LOOKUP("m", property);
+  auto m_prop = PROPERTY_LOOKUP(dba, "m", property);
   std::list<BaseOpChecker *> optional{new ExpectScanAllByLabelPropertyValue(label, property, m_prop)};
 
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectFilter(), ExpectOptional(optional), ExpectProduce());
@@ -690,38 +645,36 @@ TYPED_TEST(TestPlanner, MatchOptionalMatchNodePropertyWithIndex) {
 
 TYPED_TEST(TestPlanner, MatchUnwindReturn) {
   // Test MATCH (n) UNWIND [1,2,3] AS x RETURN n, x
-  AstStorage storage;
+  FakeDbAccessor dba;
   auto *as_n = NEXPR("n", IDENT("n"));
   auto *as_x = NEXPR("x", IDENT("x"));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), UNWIND(LIST(LITERAL(1), LITERAL(2), LITERAL(3)), AS("x")),
                                    RETURN(as_n, as_x)));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  FakeDbAccessor dba;
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectUnwind(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, ReturnDistinctOrderBySkipLimit) {
   // Test RETURN DISTINCT 1 ORDER BY 1 SKIP 1 LIMIT 1
-  AstStorage storage;
   auto *query = QUERY(
       SINGLE_QUERY(RETURN_DISTINCT(LITERAL(1), AS("1"), ORDER_BY(LITERAL(1)), SKIP(LITERAL(1)), LIMIT(LITERAL(1)))));
-  CheckPlan<TypeParam>(query, storage, ExpectProduce(), ExpectDistinct(), ExpectOrderBy(), ExpectSkip(), ExpectLimit());
+  CheckPlan<TypeParam>(query, this->storage, ExpectProduce(), ExpectDistinct(), ExpectOrderBy(), ExpectSkip(),
+                       ExpectLimit());
 }
 
 TYPED_TEST(TestPlanner, CreateWithDistinctSumWhereReturn) {
   // Test CREATE (n) WITH DISTINCT SUM(n.prop) AS s WHERE s < 42 RETURN s
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
   auto node_n = NODE("n");
-  auto sum = SUM(PROPERTY_LOOKUP("n", prop), false);
+  auto sum = SUM(PROPERTY_LOOKUP(dba, "n", prop), false);
   auto query = QUERY(SINGLE_QUERY(CREATE(PATTERN(node_n)), WITH_DISTINCT(sum, AS("s")),
                                   WHERE(LESS(IDENT("s"), LITERAL(42))), RETURN("s")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   auto acc = ExpectAccumulate({symbol_table.at(*node_n->identifier_)});
   auto aggr = ExpectAggregate({sum}, {});
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectCreateNode(), acc, aggr, ExpectProduce(), ExpectDistinct(),
             ExpectFilter(), ExpectProduce());
 }
@@ -729,58 +682,56 @@ TYPED_TEST(TestPlanner, CreateWithDistinctSumWhereReturn) {
 TYPED_TEST(TestPlanner, MatchCrossReferenceVariable) {
   // Test MATCH (n {prop: m.prop}), (m {prop: n.prop}) RETURN n
   FakeDbAccessor dba;
-  auto prop = PROPERTY_PAIR("prop");
-  AstStorage storage;
+  auto prop = PROPERTY_PAIR(dba, "prop");
   auto node_n = NODE("n");
-  auto m_prop = PROPERTY_LOOKUP("m", prop.second);
-  std::get<0>(node_n->properties_)[storage.GetPropertyIx(prop.first)] = m_prop;
+  auto m_prop = PROPERTY_LOOKUP(dba, "m", prop.second);
+  std::get<0>(node_n->properties_)[this->storage.GetPropertyIx(prop.first)] = m_prop;
   auto node_m = NODE("m");
-  auto n_prop = PROPERTY_LOOKUP("n", prop.second);
-  std::get<0>(node_m->properties_)[storage.GetPropertyIx(prop.first)] = n_prop;
+  auto n_prop = PROPERTY_LOOKUP(dba, "n", prop.second);
+  std::get<0>(node_m->properties_)[this->storage.GetPropertyIx(prop.first)] = n_prop;
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n), PATTERN(node_m)), RETURN("n")));
   // We expect both ScanAll to come before filters (2 are joined into one),
   // because they need to populate the symbol values.
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectScanAll(), ExpectFilter(), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectScanAll(), ExpectFilter(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchWhereBeforeExpand) {
   // Test MATCH (n) -[r]- (m) WHERE n.prop < 42 RETURN n
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
   auto *as_n = NEXPR("n", IDENT("n"));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
-                                   WHERE(LESS(PROPERTY_LOOKUP("n", prop), LITERAL(42))), RETURN(as_n)));
+                                   WHERE(LESS(PROPERTY_LOOKUP(dba, "n", prop), LITERAL(42))), RETURN(as_n)));
   // We expect Filter to come immediately after ScanAll, since it only uses `n`.
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectFilter(), ExpectExpand(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchFilterPropIsNotNull) {
   FakeDbAccessor dba;
   auto label = dba.Label("label");
-  auto prop = PROPERTY_PAIR("prop");
+  auto prop = PROPERTY_PAIR(dba, "prop");
   dba.SetIndexCount(label, 1);
   dba.SetIndexCount(label, prop.second, 1);
-  AstStorage storage;
 
   {
     // Test MATCH (n :label) -[r]- (m) WHERE n.prop IS NOT NULL RETURN n
     auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"), EDGE("r"), NODE("m"))),
-                                     WHERE(NOT(IS_NULL(PROPERTY_LOOKUP("n", prop)))), RETURN("n")));
+                                     WHERE(NOT(IS_NULL(PROPERTY_LOOKUP(dba, "n", prop)))), RETURN("n")));
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     // We expect ScanAllByLabelProperty to come instead of ScanAll > Filter.
     CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelProperty(label, prop), ExpectExpand(), ExpectProduce());
   }
 
   {
     // Test MATCH (n :label) -[r]- (m) WHERE n.prop IS NOT NULL OR true RETURN n
-    auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"), EDGE("r"), NODE("m"))),
-                                     WHERE(OR(NOT(IS_NULL(PROPERTY_LOOKUP("n", prop))), LITERAL(true))), RETURN("n")));
+    auto *query =
+        QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"), EDGE("r"), NODE("m"))),
+                           WHERE(OR(NOT(IS_NULL(PROPERTY_LOOKUP(dba, "n", prop))), LITERAL(true))), RETURN("n")));
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     // We expect ScanAllBy > Filter because of the "or true" condition.
     CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectFilter(), ExpectExpand(), ExpectProduce());
   }
@@ -788,13 +739,13 @@ TYPED_TEST(TestPlanner, MatchFilterPropIsNotNull) {
   {
     // Test MATCH (n :label) -[r]- (m)
     //      WHERE n.prop IS NOT NULL AND n.x = 2 RETURN n
-    auto prop_x = PROPERTY_PAIR("x");
-    auto *query = QUERY(
-        SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"), EDGE("r"), NODE("m"))),
-                     WHERE(AND(NOT(IS_NULL(PROPERTY_LOOKUP("n", prop))), EQ(PROPERTY_LOOKUP("n", prop_x), LITERAL(2)))),
-                     RETURN("n")));
+    auto prop_x = PROPERTY_PAIR(dba, "x");
+    auto *query = QUERY(SINGLE_QUERY(
+        MATCH(PATTERN(NODE("n", "label"), EDGE("r"), NODE("m"))),
+        WHERE(AND(NOT(IS_NULL(PROPERTY_LOOKUP(dba, "n", prop))), EQ(PROPERTY_LOOKUP(dba, "n", prop_x), LITERAL(2)))),
+        RETURN("n")));
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     // We expect ScanAllByLabelProperty > Filter
     // to come instead of ScanAll > Filter.
     CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelProperty(label, prop), ExpectFilter(), ExpectExpand(),
@@ -806,12 +757,11 @@ TYPED_TEST(TestPlanner, MultiMatchWhere) {
   // Test MATCH (n) -[r]- (m) MATCH (l) WHERE n.prop < 42 RETURN n
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))), MATCH(PATTERN(NODE("l"))),
-                                   WHERE(LESS(PROPERTY_LOOKUP("n", prop), LITERAL(42))), RETURN("n")));
+                                   WHERE(LESS(PROPERTY_LOOKUP(dba, "n", prop), LITERAL(42))), RETURN("n")));
   // Even though WHERE is in the second MATCH clause, we expect Filter to come
   // before second ScanAll, since it only uses the value from first ScanAll.
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectFilter(), ExpectExpand(), ExpectScanAll(),
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectFilter(), ExpectExpand(), ExpectScanAll(),
                        ExpectProduce());
 }
 
@@ -819,14 +769,14 @@ TYPED_TEST(TestPlanner, MatchOptionalMatchWhere) {
   // Test MATCH (n) -[r]- (m) OPTIONAL MATCH (l) WHERE n.prop < 42 RETURN n
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))), OPTIONAL_MATCH(PATTERN(NODE("l"))),
-                                   WHERE(LESS(PROPERTY_LOOKUP("n", prop), LITERAL(42))), RETURN("n")));
+                                   WHERE(LESS(PROPERTY_LOOKUP(dba, "n", prop), LITERAL(42))), RETURN("n")));
   // Even though WHERE is in the second MATCH clause, and it uses the value from
   // first ScanAll, it must remain part of the Optional. It should come before
   // optional ScanAll.
   std::list<BaseOpChecker *> optional{new ExpectFilter(), new ExpectScanAll()};
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectExpand(), ExpectOptional(optional), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectExpand(), ExpectOptional(optional),
+                       ExpectProduce());
   DeleteListContent(&optional);
 }
 
@@ -834,12 +784,11 @@ TYPED_TEST(TestPlanner, MatchReturnAsterisk) {
   // Test MATCH (n) -[e]- (m) RETURN *, m.prop
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
-  auto ret = RETURN(PROPERTY_LOOKUP("m", prop), AS("m.prop"));
+  auto ret = RETURN(PROPERTY_LOOKUP(dba, "m", prop), AS("m.prop"));
   ret->body_.all_identifiers = true;
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("e"), NODE("m"))), ret));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectExpand(), ExpectProduce());
   std::vector<std::string> output_names;
   for (const auto &output_symbol : planner.plan().OutputSymbols(symbol_table)) {
@@ -853,13 +802,12 @@ TYPED_TEST(TestPlanner, MatchReturnAsteriskSum) {
   // Test MATCH (n) RETURN *, SUM(n.prop) AS s
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
-  AstStorage storage;
-  auto sum = SUM(PROPERTY_LOOKUP("n", prop), false);
+  auto sum = SUM(PROPERTY_LOOKUP(dba, "n", prop), false);
   auto ret = RETURN(sum, AS("s"));
   ret->body_.all_identifiers = true;
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), ret));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   auto *produce = dynamic_cast<Produce *>(&planner.plan());
   ASSERT_TRUE(produce);
   const auto &named_expressions = produce->named_expressions_;
@@ -878,33 +826,30 @@ TYPED_TEST(TestPlanner, MatchReturnAsteriskSum) {
 
 TYPED_TEST(TestPlanner, UnwindMergeNodeProperty) {
   // Test UNWIND [1] AS i MERGE (n {prop: i})
-  AstStorage storage;
-  FakeDbAccessor dba;
   auto node_n = NODE("n");
-  std::get<0>(node_n->properties_)[storage.GetPropertyIx("prop")] = IDENT("i");
+  std::get<0>(node_n->properties_)[this->storage.GetPropertyIx("prop")] = IDENT("i");
   auto *query = QUERY(SINGLE_QUERY(UNWIND(LIST(LITERAL(1)), AS("i")), MERGE(PATTERN(node_n))));
   std::list<BaseOpChecker *> on_match{new ExpectScanAll(), new ExpectFilter()};
   std::list<BaseOpChecker *> on_create{new ExpectCreateNode()};
-  CheckPlan<TypeParam>(query, storage, ExpectUnwind(), ExpectMerge(on_match, on_create), ExpectEmptyResult());
+  CheckPlan<TypeParam>(query, this->storage, ExpectUnwind(), ExpectMerge(on_match, on_create), ExpectEmptyResult());
   DeleteListContent(&on_match);
   DeleteListContent(&on_create);
 }
 
 TYPED_TEST(TestPlanner, UnwindMergeNodePropertyWithIndex) {
   // Test UNWIND [1] AS i MERGE (n :label {prop: i}) with label-property index
-  AstStorage storage;
   FakeDbAccessor dba;
   const auto label_name = "label";
   const auto label = dba.Label(label_name);
-  const auto property = PROPERTY_PAIR("prop");
+  const auto property = PROPERTY_PAIR(dba, "prop");
   dba.SetIndexCount(label, property.second, 1);
   auto node_n = NODE("n", label_name);
-  std::get<0>(node_n->properties_)[storage.GetPropertyIx(property.first)] = IDENT("i");
+  std::get<0>(node_n->properties_)[this->storage.GetPropertyIx(property.first)] = IDENT("i");
   auto *query = QUERY(SINGLE_QUERY(UNWIND(LIST(LITERAL(1)), AS("i")), MERGE(PATTERN(node_n))));
   std::list<BaseOpChecker *> on_match{new ExpectScanAllByLabelPropertyValue(label, property, IDENT("i"))};
   std::list<BaseOpChecker *> on_create{new ExpectCreateNode()};
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectUnwind(), ExpectMerge(on_match, on_create), ExpectEmptyResult());
   DeleteListContent(&on_match);
   DeleteListContent(&on_create);
@@ -912,51 +857,45 @@ TYPED_TEST(TestPlanner, UnwindMergeNodePropertyWithIndex) {
 
 TYPED_TEST(TestPlanner, MultipleOptionalMatchReturn) {
   // Test OPTIONAL MATCH (n) OPTIONAL MATCH (m) RETURN n
-  AstStorage storage;
   auto *query =
       QUERY(SINGLE_QUERY(OPTIONAL_MATCH(PATTERN(NODE("n"))), OPTIONAL_MATCH(PATTERN(NODE("m"))), RETURN("n")));
   std::list<BaseOpChecker *> optional{new ExpectScanAll()};
-  CheckPlan<TypeParam>(query, storage, ExpectOptional(optional), ExpectOptional(optional), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectOptional(optional), ExpectOptional(optional), ExpectProduce());
   DeleteListContent(&optional);
 }
 
 TYPED_TEST(TestPlanner, FunctionAggregationReturn) {
   // Test RETURN sqrt(SUM(2)) AS result, 42 AS group_by
-  AstStorage storage;
   auto sum = SUM(LITERAL(2), false);
   auto group_by_literal = LITERAL(42);
   auto *query = QUERY(SINGLE_QUERY(RETURN(FN("sqrt", sum), AS("result"), group_by_literal, AS("group_by"))));
   auto aggr = ExpectAggregate({sum}, {group_by_literal});
-  CheckPlan<TypeParam>(query, storage, aggr, ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, aggr, ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, FunctionWithoutArguments) {
   // Test RETURN pi() AS pi
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(RETURN(FN("pi"), AS("pi"))));
-  CheckPlan<TypeParam>(query, storage, ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, ListLiteralAggregationReturn) {
   // Test RETURN [SUM(2)] AS result, 42 AS group_by
-  AstStorage storage;
   auto sum = SUM(LITERAL(2), false);
   auto group_by_literal = LITERAL(42);
   auto *query = QUERY(SINGLE_QUERY(RETURN(LIST(sum), AS("result"), group_by_literal, AS("group_by"))));
   auto aggr = ExpectAggregate({sum}, {group_by_literal});
-  CheckPlan<TypeParam>(query, storage, aggr, ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, aggr, ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MapLiteralAggregationReturn) {
   // Test RETURN {sum: SUM(2)} AS result, 42 AS group_by
-  AstStorage storage;
-  FakeDbAccessor dba;
   auto sum = SUM(LITERAL(2), false);
   auto group_by_literal = LITERAL(42);
-  auto *query = QUERY(
-      SINGLE_QUERY(RETURN(MAP({storage.GetPropertyIx("sum"), sum}), AS("result"), group_by_literal, AS("group_by"))));
+  auto *query = QUERY(SINGLE_QUERY(
+      RETURN(MAP({this->storage.GetPropertyIx("sum"), sum}), AS("result"), group_by_literal, AS("group_by"))));
   auto aggr = ExpectAggregate({sum}, {group_by_literal});
-  CheckPlan<TypeParam>(query, storage, aggr, ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, aggr, ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MapProjectionLiteralAggregationReturn) {
@@ -975,22 +914,21 @@ TYPED_TEST(TestPlanner, MapProjectionLiteralAggregationReturn) {
 
 TYPED_TEST(TestPlanner, EmptyListIndexAggregation) {
   // Test RETURN [][SUM(2)] AS result, 42 AS group_by
-  AstStorage storage;
   auto sum = SUM(LITERAL(2), false);
   auto empty_list = LIST();
   auto group_by_literal = LITERAL(42);
-  auto *query = QUERY(SINGLE_QUERY(RETURN(storage.Create<memgraph::query::SubscriptOperator>(empty_list, sum),
-                                          AS("result"), group_by_literal, AS("group_by"))));
+  auto *query =
+      QUERY(SINGLE_QUERY(RETURN(this->storage.template Create<memgraph::query::SubscriptOperator>(empty_list, sum),
+                                AS("result"), group_by_literal, AS("group_by"))));
   // We expect to group by '42' and the empty list, because it is a
   // sub-expression of a binary operator which contains an aggregation. This is
   // similar to grouping by '1' in `RETURN 1 + SUM(2)`.
   auto aggr = ExpectAggregate({sum}, {empty_list, group_by_literal});
-  CheckPlan<TypeParam>(query, storage, aggr, ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, aggr, ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, ListSliceAggregationReturn) {
   // Test RETURN [1, 2][0..SUM(2)] AS result, 42 AS group_by
-  AstStorage storage;
   auto sum = SUM(LITERAL(2), false);
   auto list = LIST(LITERAL(1), LITERAL(2));
   auto group_by_literal = LITERAL(42);
@@ -999,40 +937,37 @@ TYPED_TEST(TestPlanner, ListSliceAggregationReturn) {
   // Similarly to EmptyListIndexAggregation test, we expect grouping by list and
   // '42', because slicing is an operator.
   auto aggr = ExpectAggregate({sum}, {list, group_by_literal});
-  CheckPlan<TypeParam>(query, storage, aggr, ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, aggr, ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, ListWithAggregationAndGroupBy) {
   // Test RETURN [sum(2), 42]
-  AstStorage storage;
   auto sum = SUM(LITERAL(2), false);
   auto group_by_literal = LITERAL(42);
   auto *query = QUERY(SINGLE_QUERY(RETURN(LIST(sum, group_by_literal), AS("result"))));
   auto aggr = ExpectAggregate({sum}, {group_by_literal});
-  CheckPlan<TypeParam>(query, storage, aggr, ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, aggr, ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, AggregatonWithListWithAggregationAndGroupBy) {
   // Test RETURN sum(2), [sum(3), 42]
-  AstStorage storage;
   auto sum2 = SUM(LITERAL(2), false);
   auto sum3 = SUM(LITERAL(3), false);
   auto group_by_literal = LITERAL(42);
   auto *query = QUERY(SINGLE_QUERY(RETURN(sum2, AS("sum2"), LIST(sum3, group_by_literal), AS("list"))));
   auto aggr = ExpectAggregate({sum2, sum3}, {group_by_literal});
-  CheckPlan<TypeParam>(query, storage, aggr, ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, aggr, ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MapWithAggregationAndGroupBy) {
-  // Test RETURN {lit: 42, sum: sum(2)} AS result
-  AstStorage storage;
-  FakeDbAccessor dba;
+  // Test RETURN {lit: 42, sum: sum(2)}
   auto sum = SUM(LITERAL(2), false);
   auto group_by_literal = LITERAL(42);
-  auto *query = QUERY(SINGLE_QUERY(RETURN(
-      MAP({storage.GetPropertyIx("sum"), sum}, {storage.GetPropertyIx("lit"), group_by_literal}), AS("result"))));
+  auto *query = QUERY(SINGLE_QUERY(
+      RETURN(MAP({this->storage.GetPropertyIx("sum"), sum}, {this->storage.GetPropertyIx("lit"), group_by_literal}),
+             AS("result"))));
   auto aggr = ExpectAggregate({sum}, {group_by_literal});
-  CheckPlan<TypeParam>(query, storage, aggr, ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, aggr, ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MapProjectionWithAggregationAndGroupBy) {
@@ -1051,80 +986,76 @@ TYPED_TEST(TestPlanner, MapProjectionWithAggregationAndGroupBy) {
 
 TYPED_TEST(TestPlanner, AtomIndexedLabelProperty) {
   // Test MATCH (n :label {property: 42, not_indexed: 0}) RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto label = dba.Label("label");
-  auto property = PROPERTY_PAIR("property");
-  auto not_indexed = PROPERTY_PAIR("not_indexed");
+  auto property = PROPERTY_PAIR(dba, "property");
+  auto not_indexed = PROPERTY_PAIR(dba, "not_indexed");
   dba.SetIndexCount(label, 1);
   dba.SetIndexCount(label, property.second, 1);
   auto node = NODE("n", "label");
   auto lit_42 = LITERAL(42);
-  std::get<0>(node->properties_)[storage.GetPropertyIx(property.first)] = lit_42;
-  std::get<0>(node->properties_)[storage.GetPropertyIx(not_indexed.first)] = LITERAL(0);
+  std::get<0>(node->properties_)[this->storage.GetPropertyIx(property.first)] = lit_42;
+  std::get<0>(node->properties_)[this->storage.GetPropertyIx(not_indexed.first)] = LITERAL(0);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node)), RETURN("n")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelPropertyValue(label, property, lit_42), ExpectFilter(),
             ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, AtomPropertyWhereLabelIndexing) {
   // Test MATCH (n {property: 42}) WHERE n.not_indexed AND n:label RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto label = dba.Label("label");
-  auto property = PROPERTY_PAIR("property");
-  auto not_indexed = PROPERTY_PAIR("not_indexed");
+  auto property = PROPERTY_PAIR(dba, "property");
+  auto not_indexed = PROPERTY_PAIR(dba, "not_indexed");
   dba.SetIndexCount(label, property.second, 0);
   auto node = NODE("n");
   auto lit_42 = LITERAL(42);
-  std::get<0>(node->properties_)[storage.GetPropertyIx(property.first)] = lit_42;
-  auto *query = QUERY(
-      SINGLE_QUERY(MATCH(PATTERN(node)),
-                   WHERE(AND(PROPERTY_LOOKUP("n", not_indexed),
-                             storage.Create<memgraph::query::LabelsTest>(
-                                 IDENT("n"), std::vector<memgraph::query::LabelIx>{storage.GetLabelIx("label")}))),
-                   RETURN("n")));
+  std::get<0>(node->properties_)[this->storage.GetPropertyIx(property.first)] = lit_42;
+  auto *query = QUERY(SINGLE_QUERY(
+      MATCH(PATTERN(node)),
+      WHERE(AND(PROPERTY_LOOKUP(dba, "n", not_indexed),
+                this->storage.template Create<memgraph::query::LabelsTest>(
+                    IDENT("n"), std::vector<memgraph::query::LabelIx>{this->storage.GetLabelIx("label")}))),
+      RETURN("n")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelPropertyValue(label, property, lit_42), ExpectFilter(),
             ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, WhereIndexedLabelProperty) {
   // Test MATCH (n :label) WHERE n.property = 42 RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto label = dba.Label("label");
-  auto property = PROPERTY_PAIR("property");
+  auto property = PROPERTY_PAIR(dba, "property");
   dba.SetIndexCount(label, property.second, 0);
   auto lit_42 = LITERAL(42);
-  auto *query = QUERY(
-      SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))), WHERE(EQ(PROPERTY_LOOKUP("n", property), lit_42)), RETURN("n")));
+  auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))),
+                                   WHERE(EQ(PROPERTY_LOOKUP(dba, "n", property), lit_42)), RETURN("n")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelPropertyValue(label, property, lit_42), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, BestPropertyIndexed) {
   // Test MATCH (n :label) WHERE n.property = 1 AND n.better = 42 RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto label = dba.Label("label");
   auto property = dba.Property("property");
   // Add a vertex with :label+property combination, so that the best
   // :label+better remains empty and thus better choice.
   dba.SetIndexCount(label, property, 1);
-  auto better = PROPERTY_PAIR("better");
+  auto better = PROPERTY_PAIR(dba, "better");
   dba.SetIndexCount(label, better.second, 0);
   auto lit_42 = LITERAL(42);
-  auto *query = QUERY(
-      SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))),
-                   WHERE(AND(EQ(PROPERTY_LOOKUP("n", property), LITERAL(1)), EQ(PROPERTY_LOOKUP("n", better), lit_42))),
-                   RETURN("n")));
+  auto *query = QUERY(SINGLE_QUERY(
+      MATCH(PATTERN(NODE("n", "label"))),
+      WHERE(AND(EQ(PROPERTY_LOOKUP(dba, "n", property), LITERAL(1)), EQ(PROPERTY_LOOKUP(dba, "n", better), lit_42))),
+      RETURN("n")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelPropertyValue(label, better, lit_42), ExpectFilter(),
             ExpectProduce());
 }
@@ -1135,18 +1066,18 @@ TYPED_TEST(TestPlanner, MultiPropertyIndexScan) {
   FakeDbAccessor dba;
   auto label1 = dba.Label("label1");
   auto label2 = dba.Label("label2");
-  auto prop1 = PROPERTY_PAIR("prop1");
-  auto prop2 = PROPERTY_PAIR("prop2");
+  auto prop1 = PROPERTY_PAIR(dba, "prop1");
+  auto prop2 = PROPERTY_PAIR(dba, "prop2");
   dba.SetIndexCount(label1, prop1.second, 0);
   dba.SetIndexCount(label2, prop2.second, 0);
-  AstStorage storage;
   auto lit_1 = LITERAL(1);
   auto lit_2 = LITERAL(2);
-  auto *query = QUERY(SINGLE_QUERY(
-      MATCH(PATTERN(NODE("n", "label1")), PATTERN(NODE("m", "label2"))),
-      WHERE(AND(EQ(PROPERTY_LOOKUP("n", prop1), lit_1), EQ(PROPERTY_LOOKUP("m", prop2), lit_2))), RETURN("n", "m")));
+  auto *query = QUERY(
+      SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label1")), PATTERN(NODE("m", "label2"))),
+                   WHERE(AND(EQ(PROPERTY_LOOKUP(dba, "n", prop1), lit_1), EQ(PROPERTY_LOOKUP(dba, "m", prop2), lit_2))),
+                   RETURN("n", "m")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelPropertyValue(label1, prop1, lit_1),
             ExpectScanAllByLabelPropertyValue(label2, prop2, lit_2), ExpectProduce());
 }
@@ -1158,17 +1089,15 @@ TYPED_TEST(TestPlanner, WhereIndexedLabelPropertyRange) {
   auto label = dba.Label("label");
   auto property = dba.Property("property");
   dba.SetIndexCount(label, property, 0);
-  AstStorage storage;
   auto lit_42 = LITERAL(42);
-  auto n_prop = PROPERTY_LOOKUP("n", property);
-  auto check_planned_range = [&label, &property, &dba](const auto &rel_expr, auto lower_bound, auto upper_bound) {
-    // Shadow the first storage, so that the query is created in this one.
-    AstStorage storage;
-    storage.GetLabelIx("label");
-    storage.GetPropertyIx("property");
+  auto n_prop = PROPERTY_LOOKUP(dba, "n", property);
+  auto check_planned_range = [&, this](const auto &rel_expr, auto lower_bound, auto upper_bound) {
+    // Shadow the first this->storage, so that the query is created in this one.
+    this->storage.GetLabelIx("label");
+    this->storage.GetPropertyIx("property");
     auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))), WHERE(rel_expr), RETURN("n")));
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     CheckPlan(planner.plan(), symbol_table,
               ExpectScanAllByLabelPropertyRange(label, property, lower_bound, upper_bound), ExpectProduce());
   };
@@ -1198,18 +1127,17 @@ TYPED_TEST(TestPlanner, WhereIndexedLabelPropertyRange) {
 
 TYPED_TEST(TestPlanner, WherePreferEqualityIndexOverRange) {
   // Test MATCH (n :label) WHERE n.property = 42 AND n.property > 0 RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto label = dba.Label("label");
-  auto property = PROPERTY_PAIR("property");
+  auto property = PROPERTY_PAIR(dba, "property");
   dba.SetIndexCount(label, property.second, 0);
   auto lit_42 = LITERAL(42);
-  auto *query = QUERY(SINGLE_QUERY(
-      MATCH(PATTERN(NODE("n", "label"))),
-      WHERE(AND(EQ(PROPERTY_LOOKUP("n", property), lit_42), GREATER(PROPERTY_LOOKUP("n", property), LITERAL(0)))),
-      RETURN("n")));
+  auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))),
+                                   WHERE(AND(EQ(PROPERTY_LOOKUP(dba, "n", property), lit_42),
+                                             GREATER(PROPERTY_LOOKUP(dba, "n", property), LITERAL(0)))),
+                                   RETURN("n")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelPropertyValue(label, property, lit_42), ExpectFilter(),
             ExpectProduce());
 }
@@ -1221,12 +1149,11 @@ TYPED_TEST(TestPlanner, UnableToUsePropertyIndex) {
   auto property = dba.Property("property");
   dba.SetIndexCount(label, 0);
   dba.SetIndexCount(label, property, 0);
-  AstStorage storage;
-  auto *query =
-      QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))),
-                         WHERE(EQ(PROPERTY_LOOKUP("n", property), PROPERTY_LOOKUP("n", property))), RETURN("n")));
+  auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))),
+                                   WHERE(EQ(PROPERTY_LOOKUP(dba, "n", property), PROPERTY_LOOKUP(dba, "n", property))),
+                                   RETURN("n")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   // We can only get ScanAllByLabelIndex, because we are comparing properties
   // with those on the same node.
   CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabel(), ExpectFilter(), ExpectProduce());
@@ -1236,16 +1163,15 @@ TYPED_TEST(TestPlanner, SecondPropertyIndex) {
   // Test MATCH (n :label), (m :label) WHERE m.property = n.property RETURN n
   FakeDbAccessor dba;
   auto label = dba.Label("label");
-  auto property = PROPERTY_PAIR("property");
+  auto property = PROPERTY_PAIR(dba, "property");
   dba.SetIndexCount(label, 0);
   dba.SetIndexCount(label, dba.Property("property"), 0);
-  AstStorage storage;
-  auto n_prop = PROPERTY_LOOKUP("n", property);
-  auto m_prop = PROPERTY_LOOKUP("m", property);
+  auto n_prop = PROPERTY_LOOKUP(dba, "n", property);
+  auto m_prop = PROPERTY_LOOKUP(dba, "m", property);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label")), PATTERN(NODE("m", "label"))),
                                    WHERE(EQ(m_prop, n_prop)), RETURN("n")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabel(),
             // Note: We are scanning for m, therefore property should equal n_prop.
             ExpectScanAllByLabelPropertyValue(label, property, n_prop), ExpectProduce());
@@ -1253,41 +1179,37 @@ TYPED_TEST(TestPlanner, SecondPropertyIndex) {
 
 TYPED_TEST(TestPlanner, ReturnSumGroupByAll) {
   // Test RETURN sum([1,2,3]), all(x in [1] where x = 1)
-  AstStorage storage;
   auto sum = SUM(LIST(LITERAL(1), LITERAL(2), LITERAL(3)), false);
   auto *all = ALL("x", LIST(LITERAL(1)), WHERE(EQ(IDENT("x"), LITERAL(1))));
   auto *query = QUERY(SINGLE_QUERY(RETURN(sum, AS("sum"), all, AS("all"))));
   auto aggr = ExpectAggregate({sum}, {all});
-  CheckPlan<TypeParam>(query, storage, aggr, ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, aggr, ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchExpandVariable) {
   // Test MATCH (n) -[r *..3]-> (m) RETURN r
-  AstStorage storage;
   auto edge = EDGE_VARIABLE("r");
   edge->upper_bound_ = LITERAL(3);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), RETURN("r")));
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectExpandVariable(), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectExpandVariable(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchExpandVariableNoBounds) {
   // Test MATCH (n) -[r *]-> (m) RETURN r
-  AstStorage storage;
   auto edge = EDGE_VARIABLE("r");
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), RETURN("r")));
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectExpandVariable(), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectExpandVariable(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchExpandVariableInlinedFilter) {
   // Test MATCH (n) -[r :type * {prop: 42}]-> (m) RETURN r
   FakeDbAccessor dba;
   auto type = "type";
-  auto prop = PROPERTY_PAIR("prop");
-  AstStorage storage;
+  auto prop = PROPERTY_PAIR(dba, "prop");
   auto edge = EDGE_VARIABLE("r", Type::DEPTH_FIRST, Direction::BOTH, {type});
-  std::get<0>(edge->properties_)[storage.GetPropertyIx(prop.first)] = LITERAL(42);
+  std::get<0>(edge->properties_)[this->storage.GetPropertyIx(prop.first)] = LITERAL(42);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), RETURN("r")));
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(),
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(),
                        ExpectExpandVariable(),  // Filter is both inlined and post-expand
                        ExpectFilter(), ExpectProduce());
 }
@@ -1296,25 +1218,23 @@ TYPED_TEST(TestPlanner, MatchExpandVariableNotInlinedFilter) {
   // Test MATCH (n) -[r :type * {prop: m.prop}]-> (m) RETURN r
   FakeDbAccessor dba;
   auto type = "type";
-  auto prop = PROPERTY_PAIR("prop");
-  AstStorage storage;
+  auto prop = PROPERTY_PAIR(dba, "prop");
   auto edge = EDGE_VARIABLE("r", Type::DEPTH_FIRST, Direction::BOTH, {type});
-  std::get<0>(edge->properties_)[storage.GetPropertyIx(prop.first)] = EQ(PROPERTY_LOOKUP("m", prop), LITERAL(42));
+  std::get<0>(edge->properties_)[this->storage.GetPropertyIx(prop.first)] =
+      EQ(PROPERTY_LOOKUP(dba, "m", prop), LITERAL(42));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), RETURN("r")));
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectExpandVariable(), ExpectFilter(), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectExpandVariable(), ExpectFilter(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchExpandVariableTotalWeightSymbol) {
   // Test MATCH p = (a {id: 0})-[r* wShortest (e, v | 1) total_weight]->(b)
   // RETURN *
   FakeDbAccessor dba;
-  AstStorage storage;
-
   auto edge = EDGE_VARIABLE("r", Type::WEIGHTED_SHORTEST_PATH, Direction::BOTH, {}, nullptr, nullptr, nullptr, nullptr,
                             nullptr, IDENT("total_weight"));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), RETURN("*")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   auto *root = dynamic_cast<Produce *>(&planner.plan());
 
   ASSERT_TRUE(root);
@@ -1334,23 +1254,21 @@ TYPED_TEST(TestPlanner, MatchExpandVariableTotalWeightSymbol) {
 
 TYPED_TEST(TestPlanner, UnwindMatchVariable) {
   // Test UNWIND [1,2,3] AS depth MATCH (n) -[r*d]-> (m) RETURN r
-  AstStorage storage;
   auto edge = EDGE_VARIABLE("r", Type::DEPTH_FIRST, Direction::OUT);
   edge->lower_bound_ = IDENT("d");
   edge->upper_bound_ = IDENT("d");
   auto *query = QUERY(SINGLE_QUERY(UNWIND(LIST(LITERAL(1), LITERAL(2), LITERAL(3)), AS("d")),
                                    MATCH(PATTERN(NODE("n"), edge, NODE("m"))), RETURN("r")));
-  CheckPlan<TypeParam>(query, storage, ExpectUnwind(), ExpectScanAll(), ExpectExpandVariable(), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectUnwind(), ExpectScanAll(), ExpectExpandVariable(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, MatchBfs) {
   // Test MATCH (n) -[r:type *..10 (r, n|n)]-> (m) RETURN r
   FakeDbAccessor dba;
-  AstStorage storage;
-  auto edge_type = storage.GetEdgeTypeIx("type");
-  auto *bfs =
-      storage.Create<memgraph::query::EdgeAtom>(IDENT("r"), memgraph::query::EdgeAtom::Type::BREADTH_FIRST,
-                                                Direction::OUT, std::vector<memgraph::query::EdgeTypeIx>{edge_type});
+  auto edge_type = this->storage.GetEdgeTypeIx("type");
+  auto *bfs = this->storage.template Create<memgraph::query::EdgeAtom>(
+      IDENT("r"), memgraph::query::EdgeAtom::Type::BREADTH_FIRST, Direction::OUT,
+      std::vector<memgraph::query::EdgeTypeIx>{edge_type});
   bfs->filter_lambda_.inner_edge = IDENT("r");
   bfs->filter_lambda_.inner_node = IDENT("n");
   bfs->filter_lambda_.expression = IDENT("n");
@@ -1358,7 +1276,7 @@ TYPED_TEST(TestPlanner, MatchBfs) {
   auto *as_r = NEXPR("r", IDENT("r"));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), bfs, NODE("m"))), RETURN(as_r)));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectExpandBfs(), ExpectProduce());
 }
 
@@ -1367,10 +1285,9 @@ TYPED_TEST(TestPlanner, MatchDoubleScanToExpandExisting) {
   FakeDbAccessor dba;
   auto label = "label";
   dba.SetIndexCount(dba.Label(label), 0);
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m", label))), RETURN("r")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   // We expect 2x ScanAll and then Expand, since we are guessing that is
   // faster (due to low label index vertex count).
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectScanAllByLabel(), ExpectExpand(), ExpectProduce());
@@ -1384,12 +1301,11 @@ TYPED_TEST(TestPlanner, MatchScanToExpand) {
   // Fill vertices to the max + 1.
   dba.SetIndexCount(label, property, FLAGS_query_vertex_count_to_expand_existing + 1);
   dba.SetIndexCount(label, FLAGS_query_vertex_count_to_expand_existing + 1);
-  AstStorage storage;
   auto node_m = NODE("m", "label");
-  std::get<0>(node_m->properties_)[storage.GetPropertyIx("property")] = LITERAL(1);
+  std::get<0>(node_m->properties_)[this->storage.GetPropertyIx("property")] = LITERAL(1);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), node_m)), RETURN("r")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   // We expect 1x ScanAll and then Expand, since we are guessing that
   // is faster (due to high label index vertex count).
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectExpand(), ExpectFilter(), ExpectProduce());
@@ -1398,28 +1314,27 @@ TYPED_TEST(TestPlanner, MatchScanToExpand) {
 TYPED_TEST(TestPlanner, MatchWhereAndSplit) {
   // Test MATCH (n) -[r]- (m) WHERE n.prop AND r.prop RETURN m
   FakeDbAccessor dba;
-  auto prop = PROPERTY_PAIR("prop");
-  AstStorage storage;
-  auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
-                                   WHERE(AND(PROPERTY_LOOKUP("n", prop), PROPERTY_LOOKUP("r", prop))), RETURN("m")));
+  auto prop = PROPERTY_PAIR(dba, "prop");
+  auto *query =
+      QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
+                         WHERE(AND(PROPERTY_LOOKUP(dba, "n", prop), PROPERTY_LOOKUP(dba, "r", prop))), RETURN("m")));
   // We expect `n.prop` filter right after scanning `n`.
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectFilter(), ExpectExpand(), ExpectFilter(),
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectFilter(), ExpectExpand(), ExpectFilter(),
                        ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, ReturnAsteriskOmitsLambdaSymbols) {
   // Test MATCH (n) -[r* (ie, in | true)]- (m) RETURN *
-  AstStorage storage;
+  FakeDbAccessor dba;
   auto edge = EDGE_VARIABLE("r", Type::DEPTH_FIRST, Direction::BOTH);
   edge->filter_lambda_.inner_edge = IDENT("ie");
   edge->filter_lambda_.inner_node = IDENT("in");
   edge->filter_lambda_.expression = LITERAL(true);
-  auto ret = storage.Create<memgraph::query::Return>();
+  auto ret = this->storage.template Create<memgraph::query::Return>();
   ret->body_.all_identifiers = true;
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), ret));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  FakeDbAccessor dba;
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   auto *produce = dynamic_cast<Produce *>(&planner.plan());
   ASSERT_TRUE(produce);
   std::vector<std::string> outputs;
@@ -1435,40 +1350,40 @@ TYPED_TEST(TestPlanner, ReturnAsteriskOmitsLambdaSymbols) {
 
 TYPED_TEST(TestPlanner, FilterRegexMatchIndex) {
   // Test MATCH (n :label) WHERE n.prop =~ "regex" RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
   auto label = dba.Label("label");
   dba.SetIndexCount(label, 0);
   dba.SetIndexCount(label, prop, 0);
-  auto *regex_match = storage.Create<memgraph::query::RegexMatch>(PROPERTY_LOOKUP("n", prop), LITERAL("regex"));
+  auto *regex_match =
+      this->storage.template Create<memgraph::query::RegexMatch>(PROPERTY_LOOKUP(dba, "n", prop), LITERAL("regex"));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))), WHERE(regex_match), RETURN("n")));
   // We expect that we use index by property range where lower bound is an empty
   // string. Filter must still remain in place, because we don't have regex
   // based index.
   Bound lower_bound(LITERAL(""), Bound::Type::INCLUSIVE);
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelPropertyRange(label, prop, lower_bound, std::nullopt),
             ExpectFilter(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, FilterRegexMatchPreferEqualityIndex) {
   // Test MATCH (n :label) WHERE n.prop =~ "regex" AND n.prop = 42 RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
-  auto prop = PROPERTY_PAIR("prop");
+  auto prop = PROPERTY_PAIR(dba, "prop");
   auto label = dba.Label("label");
   dba.SetIndexCount(label, 0);
   dba.SetIndexCount(label, prop.second, 0);
-  auto *regex_match = storage.Create<memgraph::query::RegexMatch>(PROPERTY_LOOKUP("n", prop), LITERAL("regex"));
+  auto *regex_match =
+      this->storage.template Create<memgraph::query::RegexMatch>(PROPERTY_LOOKUP(dba, "n", prop), LITERAL("regex"));
   auto *lit_42 = LITERAL(42);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))),
-                                   WHERE(AND(regex_match, EQ(PROPERTY_LOOKUP("n", prop), lit_42))), RETURN("n")));
+                                   WHERE(AND(regex_match, EQ(PROPERTY_LOOKUP(dba, "n", prop), lit_42))), RETURN("n")));
   // We expect that we use index by property value equal to 42, because that's
   // much better than property range for regex matching.
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelPropertyValue(label, prop, lit_42), ExpectFilter(),
             ExpectProduce());
 }
@@ -1476,51 +1391,52 @@ TYPED_TEST(TestPlanner, FilterRegexMatchPreferEqualityIndex) {
 TYPED_TEST(TestPlanner, FilterRegexMatchPreferEqualityIndex2) {
   // Test MATCH (n :label)
   // WHERE n.prop =~ "regex" AND n.prop = 42 AND n.prop > 0 RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
-  auto prop = PROPERTY_PAIR("prop");
+  auto prop = PROPERTY_PAIR(dba, "prop");
   auto label = dba.Label("label");
   dba.SetIndexCount(label, 0);
   dba.SetIndexCount(label, prop.second, 0);
-  auto *regex_match = storage.Create<memgraph::query::RegexMatch>(PROPERTY_LOOKUP("n", prop), LITERAL("regex"));
+  auto *regex_match =
+      this->storage.template Create<memgraph::query::RegexMatch>(PROPERTY_LOOKUP(dba, "n", prop), LITERAL("regex"));
   auto *lit_42 = LITERAL(42);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))),
-                                   WHERE(AND(AND(regex_match, EQ(PROPERTY_LOOKUP("n", prop), lit_42)),
-                                             GREATER(PROPERTY_LOOKUP("n", prop), LITERAL(0)))),
+                                   WHERE(AND(AND(regex_match, EQ(PROPERTY_LOOKUP(dba, "n", prop), lit_42)),
+                                             GREATER(PROPERTY_LOOKUP(dba, "n", prop), LITERAL(0)))),
                                    RETURN("n")));
   // We expect that we use index by property value equal to 42, because that's
   // much better than property range.
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelPropertyValue(label, prop, lit_42), ExpectFilter(),
             ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, FilterRegexMatchPreferRangeIndex) {
   // Test MATCH (n :label) WHERE n.prop =~ "regex" AND n.prop > 42 RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto prop = dba.Property("prop");
   auto label = dba.Label("label");
   dba.SetIndexCount(label, 0);
   dba.SetIndexCount(label, prop, 0);
-  auto *regex_match = storage.Create<memgraph::query::RegexMatch>(PROPERTY_LOOKUP("n", prop), LITERAL("regex"));
+  auto *regex_match =
+      this->storage.template Create<memgraph::query::RegexMatch>(PROPERTY_LOOKUP(dba, "n", prop), LITERAL("regex"));
   auto *lit_42 = LITERAL(42);
-  auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))),
-                                   WHERE(AND(regex_match, GREATER(PROPERTY_LOOKUP("n", prop), lit_42))), RETURN("n")));
+  auto *query =
+      QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))),
+                         WHERE(AND(regex_match, GREATER(PROPERTY_LOOKUP(dba, "n", prop), lit_42))), RETURN("n")));
   // We expect that we use index by property range on a concrete value (42), as
   // it is much better than using a range from empty string for regex matching.
   Bound lower_bound(lit_42, Bound::Type::EXCLUSIVE);
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabelPropertyRange(label, prop, lower_bound, std::nullopt),
             ExpectFilter(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, CallProcedureStandalone) {
   // Test CALL proc(1,2,3) YIELD field AS result
-  AstStorage storage;
-  auto *ast_call = storage.Create<memgraph::query::CallProcedure>();
+  FakeDbAccessor dba;
+  auto *ast_call = this->storage.template Create<memgraph::query::CallProcedure>();
   ast_call->procedure_name_ = "proc";
   ast_call->arguments_ = {LITERAL(1), LITERAL(2), LITERAL(3)};
   ast_call->result_fields_ = {"field"};
@@ -1532,8 +1448,7 @@ TYPED_TEST(TestPlanner, CallProcedureStandalone) {
   for (const auto *ident : ast_call->result_identifiers_) {
     result_syms.push_back(symbol_table.at(*ident));
   }
-  FakeDbAccessor dba;
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(
       planner.plan(), symbol_table,
       ExpectCallProcedure(ast_call->procedure_name_, ast_call->arguments_, ast_call->result_fields_, result_syms));
@@ -1541,8 +1456,8 @@ TYPED_TEST(TestPlanner, CallProcedureStandalone) {
 
 TYPED_TEST(TestPlanner, CallProcedureAfterScanAll) {
   // Test MATCH (n) CALL proc(n) YIELD field AS result RETURN result
-  AstStorage storage;
-  auto *ast_call = storage.Create<memgraph::query::CallProcedure>();
+  FakeDbAccessor dba;
+  auto *ast_call = this->storage.template Create<memgraph::query::CallProcedure>();
   ast_call->procedure_name_ = "proc";
   ast_call->arguments_ = {IDENT("n")};
   ast_call->result_fields_ = {"field"};
@@ -1554,8 +1469,7 @@ TYPED_TEST(TestPlanner, CallProcedureAfterScanAll) {
   for (const auto *ident : ast_call->result_identifiers_) {
     result_syms.push_back(symbol_table.at(*ident));
   }
-  FakeDbAccessor dba;
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table, ExpectScanAll(),
             ExpectCallProcedure(ast_call->procedure_name_, ast_call->arguments_, ast_call->result_fields_, result_syms),
             ExpectProduce());
@@ -1563,22 +1477,21 @@ TYPED_TEST(TestPlanner, CallProcedureAfterScanAll) {
 
 TYPED_TEST(TestPlanner, CallProcedureBeforeScanAll) {
   // Test CALL proc() YIELD field MATCH (n) WHERE n.prop = field RETURN n
-  AstStorage storage;
-  auto *ast_call = storage.Create<memgraph::query::CallProcedure>();
+  FakeDbAccessor dba;
+  auto *ast_call = this->storage.template Create<memgraph::query::CallProcedure>();
   ast_call->procedure_name_ = "proc";
   ast_call->result_fields_ = {"field"};
   ast_call->result_identifiers_ = {IDENT("field")};
-  FakeDbAccessor dba;
   auto property = dba.Property("prop");
   auto *query = QUERY(SINGLE_QUERY(ast_call, MATCH(PATTERN(NODE("n"))),
-                                   WHERE(EQ(PROPERTY_LOOKUP("n", property), IDENT("field"))), RETURN("n")));
+                                   WHERE(EQ(PROPERTY_LOOKUP(dba, "n", property), IDENT("field"))), RETURN("n")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   std::vector<Symbol> result_syms;
   result_syms.reserve(ast_call->result_identifiers_.size());
   for (const auto *ident : ast_call->result_identifiers_) {
     result_syms.push_back(symbol_table.at(*ident));
   }
-  auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+  auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
   CheckPlan(planner.plan(), symbol_table,
             ExpectCallProcedure(ast_call->procedure_name_, ast_call->arguments_, ast_call->result_fields_, result_syms),
             ExpectScanAll(), ExpectFilter(), ExpectProduce());
@@ -1586,57 +1499,53 @@ TYPED_TEST(TestPlanner, CallProcedureBeforeScanAll) {
 
 TYPED_TEST(TestPlanner, ScanAllById) {
   // Test MATCH (n) WHERE id(n) = 42 RETURN n
-  AstStorage storage;
   auto *query =
       QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), WHERE(EQ(FN("id", IDENT("n")), LITERAL(42))), RETURN("n")));
-  CheckPlan<TypeParam>(query, storage, ExpectScanAllById(), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAllById(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, ScanAllByIdExpandToExisting) {
   // Test MATCH (n)-[r]-(m) WHERE id(m) = 42 RETURN r
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
                                    WHERE(EQ(FN("id", IDENT("m")), LITERAL(42))), RETURN("r")));
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectScanAllById(), ExpectExpand(), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectScanAllById(), ExpectExpand(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, BfsToExisting) {
   // Test MATCH (n)-[r *bfs]-(m) WHERE id(m) = 42 RETURN r
-  AstStorage storage;
-  auto *bfs = storage.Create<memgraph::query::EdgeAtom>(IDENT("r"), memgraph::query::EdgeAtom::Type::BREADTH_FIRST,
-                                                        Direction::BOTH);
+  auto *bfs = this->storage.template Create<memgraph::query::EdgeAtom>(
+      IDENT("r"), memgraph::query::EdgeAtom::Type::BREADTH_FIRST, Direction::BOTH);
   bfs->filter_lambda_.inner_edge = IDENT("ie");
   bfs->filter_lambda_.inner_node = IDENT("in");
   bfs->filter_lambda_.expression = LITERAL(true);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), bfs, NODE("m"))),
                                    WHERE(EQ(FN("id", IDENT("m")), LITERAL(42))), RETURN("r")));
-  CheckPlan<TypeParam>(query, storage, ExpectScanAll(), ExpectScanAllById(), ExpectExpandBfs(), ExpectProduce());
+  CheckPlan<TypeParam>(query, this->storage, ExpectScanAll(), ExpectScanAllById(), ExpectExpandBfs(), ExpectProduce());
 }
 
 TYPED_TEST(TestPlanner, LabelPropertyInListValidOptimization) {
   // Test MATCH (n:label) WHERE n.property IN ['a'] RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto label = dba.Label("label");
-  auto property = PROPERTY_PAIR("property");
+  auto property = PROPERTY_PAIR(dba, "property");
   auto *lit_list_a = LIST(LITERAL('a'));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))),
-                                   WHERE(IN_LIST(PROPERTY_LOOKUP("n", property), lit_list_a)), RETURN("n")));
+                                   WHERE(IN_LIST(PROPERTY_LOOKUP(dba, "n", property), lit_list_a)), RETURN("n")));
   {
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectFilter(), ExpectProduce());
   }
   {
     dba.SetIndexCount(label, 1);
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabel(), ExpectFilter(), ExpectProduce());
   }
   {
     dba.SetIndexCount(label, property.second, 1);
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     CheckPlan(planner.plan(), symbol_table, ExpectUnwind(),
               ExpectScanAllByLabelPropertyValue(label, property, lit_list_a), ExpectProduce());
   }
@@ -1644,51 +1553,48 @@ TYPED_TEST(TestPlanner, LabelPropertyInListValidOptimization) {
 
 TYPED_TEST(TestPlanner, LabelPropertyInListWhereLabelPropertyOnLeftNotListOnRight) {
   // Test MATCH (n:label) WHERE n.property IN 'a' RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto label = dba.Label("label");
-  auto property = PROPERTY_PAIR("property");
+  auto property = PROPERTY_PAIR(dba, "property");
   auto *lit_a = LITERAL('a');
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))),
-                                   WHERE(IN_LIST(PROPERTY_LOOKUP("n", property), lit_a)), RETURN("n")));
+                                   WHERE(IN_LIST(PROPERTY_LOOKUP(dba, "n", property), lit_a)), RETURN("n")));
   {
     dba.SetIndexCount(label, property.second, 1);
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectFilter(), ExpectProduce());
   }
 }
 
 TYPED_TEST(TestPlanner, LabelPropertyInListWhereLabelPropertyOnRight) {
   // Test MATCH (n:label) WHERE ['a'] IN n.property RETURN n
-  AstStorage storage;
   FakeDbAccessor dba;
   auto label = dba.Label("label");
-  auto property = PROPERTY_PAIR("property");
+  auto property = PROPERTY_PAIR(dba, "property");
   auto *lit_list_a = LIST(LITERAL('a'));
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n", "label"))),
-                                   WHERE(IN_LIST(lit_list_a, PROPERTY_LOOKUP("n", property))), RETURN("n")));
+                                   WHERE(IN_LIST(lit_list_a, PROPERTY_LOOKUP(dba, "n", property))), RETURN("n")));
   {
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectFilter(), ExpectProduce());
   }
   {
     dba.SetIndexCount(label, 1);
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabel(), ExpectFilter(), ExpectProduce());
   }
   {
     dba.SetIndexCount(label, property.second, 1);
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     CheckPlan(planner.plan(), symbol_table, ExpectScanAllByLabel(), ExpectFilter(), ExpectProduce());
   }
 }
 
 TYPED_TEST(TestPlanner, Foreach) {
-  AstStorage storage;
   FakeDbAccessor dba;
   {
     auto *i = NEXPR("i", IDENT("i"));
@@ -1696,7 +1602,7 @@ TYPED_TEST(TestPlanner, Foreach) {
     auto create = ExpectCreateNode();
     std::list<BaseOpChecker *> updates{&create};
     std::list<BaseOpChecker *> input;
-    CheckPlan<TypeParam>(query, storage, ExpectForeach(input, updates), ExpectEmptyResult());
+    CheckPlan<TypeParam>(query, this->storage, ExpectForeach(input, updates), ExpectEmptyResult());
   }
   {
     auto *i = NEXPR("i", IDENT("i"));
@@ -1704,16 +1610,16 @@ TYPED_TEST(TestPlanner, Foreach) {
     auto del = ExpectDelete();
     std::list<BaseOpChecker *> updates{&del};
     std::list<BaseOpChecker *> input;
-    CheckPlan<TypeParam>(query, storage, ExpectForeach({input}, updates), ExpectEmptyResult());
+    CheckPlan<TypeParam>(query, this->storage, ExpectForeach({input}, updates), ExpectEmptyResult());
   }
   {
     auto prop = dba.Property("prop");
     auto *i = NEXPR("i", IDENT("i"));
-    auto *query = QUERY(SINGLE_QUERY(FOREACH(i, {SET(PROPERTY_LOOKUP("i", prop), LITERAL(10))})));
+    auto *query = QUERY(SINGLE_QUERY(FOREACH(i, {SET(PROPERTY_LOOKUP(dba, "i", prop), LITERAL(10))})));
     auto set_prop = ExpectSetProperty();
     std::list<BaseOpChecker *> updates{&set_prop};
     std::list<BaseOpChecker *> input;
-    CheckPlan<TypeParam>(query, storage, ExpectForeach({input}, updates), ExpectEmptyResult());
+    CheckPlan<TypeParam>(query, this->storage, ExpectForeach({input}, updates), ExpectEmptyResult());
   }
   {
     auto *i = NEXPR("i", IDENT("i"));
@@ -1725,7 +1631,7 @@ TYPED_TEST(TestPlanner, Foreach) {
     std::list<BaseOpChecker *> nested_updates{{&create, &del}};
     auto nested_foreach = ExpectForeach(input, nested_updates);
     std::list<BaseOpChecker *> updates{&nested_foreach};
-    CheckPlan<TypeParam>(query, storage, ExpectForeach(input, updates), ExpectEmptyResult());
+    CheckPlan<TypeParam>(query, this->storage, ExpectForeach(input, updates), ExpectEmptyResult());
   }
   {
     auto *i = NEXPR("i", IDENT("i"));
@@ -1737,7 +1643,7 @@ TYPED_TEST(TestPlanner, Foreach) {
     std::list<BaseOpChecker *> input{&input_op};
     auto *query =
         QUERY(SINGLE_QUERY(FOREACH(i, {CREATE(PATTERN(NODE("n")))}), FOREACH(j, {CREATE(PATTERN(NODE("n")))})));
-    CheckPlan<TypeParam>(query, storage, ExpectForeach(input, updates), ExpectEmptyResult());
+    CheckPlan<TypeParam>(query, this->storage, ExpectForeach(input, updates), ExpectEmptyResult());
   }
 
   {
@@ -1751,7 +1657,7 @@ TYPED_TEST(TestPlanner, Foreach) {
     auto *query = QUERY(SINGLE_QUERY(FOREACH(n, {MERGE(PATTERN(NODE("v", label_name)))})));
 
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
 
     std::list<BaseOpChecker *> on_match{new ExpectScanAllByLabel()};
     std::list<BaseOpChecker *> on_create{new ExpectCreateNode()};
@@ -1767,10 +1673,8 @@ TYPED_TEST(TestPlanner, Foreach) {
 }
 
 TYPED_TEST(TestPlanner, Exists) {
-  AstStorage storage;
-  FakeDbAccessor dba;
-
   // MATCH (n) WHERE exists((n)-[]-())
+  FakeDbAccessor dba;
   {
     auto *query = QUERY(SINGLE_QUERY(
         MATCH(PATTERN(NODE("n"))),
@@ -1779,7 +1683,7 @@ TYPED_TEST(TestPlanner, Exists) {
         RETURN("n")));
 
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     std::list<BaseOpChecker *> pattern_filter{new ExpectExpand(), new ExpectLimit(), new ExpectEvaluatePatternFilter()};
 
     CheckPlan(planner.plan(), symbol_table, ExpectScanAll(),
@@ -1797,7 +1701,7 @@ TYPED_TEST(TestPlanner, Exists) {
         RETURN("n")));
 
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     std::list<BaseOpChecker *> pattern_filter{new ExpectExpand(), new ExpectFilter(), new ExpectLimit(),
                                               new ExpectEvaluatePatternFilter()};
 
@@ -1818,7 +1722,7 @@ TYPED_TEST(TestPlanner, Exists) {
         RETURN("n")));
 
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     std::list<BaseOpChecker *> pattern_filter_with_types{new ExpectExpand(), new ExpectFilter(), new ExpectLimit(),
                                                          new ExpectEvaluatePatternFilter()};
     std::list<BaseOpChecker *> pattern_filter_without_types{new ExpectExpand(), new ExpectLimit(),
@@ -1840,11 +1744,11 @@ TYPED_TEST(TestPlanner, Exists) {
         MATCH(PATTERN(NODE("n"))),
         WHERE(AND(EXISTS(PATTERN(NODE("n"), EDGE("edge", memgraph::query::EdgeAtom::Direction::BOTH, {"TYPE"}, false),
                                  NODE("node", "Two", false))),
-                  PROPERTY_LOOKUP("n", property))),
+                  PROPERTY_LOOKUP(dba, "n", property))),
         RETURN("n")));
 
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     std::list<BaseOpChecker *> pattern_filter{new ExpectExpand(), new ExpectFilter(), new ExpectLimit(),
                                               new ExpectEvaluatePatternFilter()};
 
@@ -1865,7 +1769,7 @@ TYPED_TEST(TestPlanner, Exists) {
         RETURN("n")));
 
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     std::list<BaseOpChecker *> pattern_filter_with_types{new ExpectExpand(), new ExpectFilter(), new ExpectLimit(),
                                                          new ExpectEvaluatePatternFilter()};
     std::list<BaseOpChecker *> pattern_filter_without_types{new ExpectExpand(), new ExpectLimit(),
@@ -1882,16 +1786,14 @@ TYPED_TEST(TestPlanner, Exists) {
 }
 
 TYPED_TEST(TestPlanner, Subqueries) {
-  AstStorage storage;
-  FakeDbAccessor dba;
-
   // MATCH (n) CALL { MATCH (m) RETURN (m) } RETURN n, m
+  FakeDbAccessor dba;
   {
     auto *subquery = SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), RETURN("n"));
     auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("m"))), CALL_SUBQUERY(subquery), RETURN("m", "n")));
 
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     std::list<BaseOpChecker *> subquery_plan{new ExpectScanAll(), new ExpectProduce()};
 
     CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectApply(subquery_plan), ExpectProduce());
@@ -1905,7 +1807,7 @@ TYPED_TEST(TestPlanner, Subqueries) {
     auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("m"))), CALL_SUBQUERY(subquery), RETURN("m", "n")));
 
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     std::list<BaseOpChecker *> subquery_plan{new ExpectScanAll(), new ExpectExpand(), new ExpectProduce()};
 
     CheckPlan(planner.plan(), symbol_table, ExpectScanAll(), ExpectApply(subquery_plan), ExpectProduce());
@@ -1917,11 +1819,11 @@ TYPED_TEST(TestPlanner, Subqueries) {
   {
     auto property = dba.Property("prop");
     auto *subquery = SINGLE_QUERY(MATCH(PATTERN(NODE("p"), EDGE("r", Direction::OUT), NODE("s"))),
-                                  WHERE(EQ(PROPERTY_LOOKUP("s", property), LITERAL(2))), RETURN("p"));
+                                  WHERE(EQ(PROPERTY_LOOKUP(dba, "s", property), LITERAL(2))), RETURN("p"));
     auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), CALL_SUBQUERY(subquery), RETURN("n", "p")));
 
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     std::list<BaseOpChecker *> subquery_plan{new ExpectScanAll(), new ExpectExpand(), new ExpectFilter(),
                                              new ExpectProduce()};
 
@@ -1937,7 +1839,7 @@ TYPED_TEST(TestPlanner, Subqueries) {
     auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("m"))), CALL_SUBQUERY(subquery), RETURN("m", "n", "o")));
 
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
     std::list<BaseOpChecker *> subquery_inside_subquery_plan{new ExpectScanAll(), new ExpectProduce()};
     std::list<BaseOpChecker *> subquery_plan{new ExpectScanAll(), new ExpectApply(subquery_inside_subquery_plan),
                                              new ExpectProduce()};
@@ -1955,7 +1857,7 @@ TYPED_TEST(TestPlanner, Subqueries) {
     auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("m"))), CALL_SUBQUERY(subquery), RETURN("m", "n")));
 
     auto symbol_table = memgraph::query::MakeSymbolTable(query);
-    auto planner = MakePlanner<TypeParam>(&dba, storage, symbol_table, query);
+    auto planner = MakePlanner<TypeParam>(&dba, this->storage, symbol_table, query);
 
     std::list<BaseOpChecker *> left_subquery_part{new ExpectScanAll(), new ExpectProduce()};
     std::list<BaseOpChecker *> right_subquery_part{new ExpectScanAll(), new ExpectProduce()};
diff --git a/tests/unit/query_plan_accumulate_aggregate.cpp b/tests/unit/query_plan_accumulate_aggregate.cpp
index 7fb9e7987..e271e0f6a 100644
--- a/tests/unit/query_plan_accumulate_aggregate.cpp
+++ b/tests/unit/query_plan_accumulate_aggregate.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -14,6 +14,7 @@
 #include <memory>
 #include <vector>
 
+#include "disk_test_utils.hpp"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
@@ -21,6 +22,8 @@
 #include "query/exceptions.hpp"
 #include "query/plan/operator.hpp"
 #include "query_plan_common.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 using namespace memgraph::query;
 using namespace memgraph::query::plan;
@@ -28,7 +31,60 @@ using memgraph::query::test_common::ToIntList;
 using memgraph::query::test_common::ToIntMap;
 using testing::UnorderedElementsAre;
 
-TEST(QueryPlan, Accumulate) {
+template <typename StorageType>
+class QueryPlanTest : public testing::Test {
+ public:
+  const std::string testSuite = "query_plan_accumulate_aggregate";
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db = std::make_unique<StorageType>(config);
+  AstStorage storage;
+
+  void TearDown() override { CleanStorageDirs(); }
+
+  void CleanStorageDirs() {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+
+  std::shared_ptr<Produce> MakeAggregationProduce(std::shared_ptr<LogicalOperator> input, SymbolTable &symbol_table,
+                                                  const std::vector<Expression *> aggr_inputs,
+                                                  const std::vector<Aggregation::Op> aggr_ops,
+                                                  const std::vector<Expression *> group_by_exprs,
+                                                  const std::vector<Symbol> remember, const bool distinct) {
+    // prepare all the aggregations
+    std::vector<Aggregate::Element> aggregates;
+    std::vector<NamedExpression *> named_expressions;
+
+    auto aggr_inputs_it = aggr_inputs.begin();
+    for (auto aggr_op : aggr_ops) {
+      // TODO change this from using IDENT to using AGGREGATION
+      // once AGGREGATION is handled properly in ExpressionEvaluation
+      auto aggr_sym = symbol_table.CreateSymbol("aggregation", true);
+      auto named_expr =
+          NEXPR("", IDENT("aggregation")->MapTo(aggr_sym))->MapTo(symbol_table.CreateSymbol("named_expression", true));
+      named_expressions.push_back(named_expr);
+      // the key expression is only used in COLLECT_MAP
+      Expression *key_expr_ptr = aggr_op == Aggregation::Op::COLLECT_MAP ? LITERAL("key") : nullptr;
+      aggregates.emplace_back(Aggregate::Element{*aggr_inputs_it++, key_expr_ptr, aggr_op, aggr_sym, distinct});
+    }
+
+    // Produce will also evaluate group_by expressions and return them after the
+    // aggregations.
+    for (auto group_by_expr : group_by_exprs) {
+      auto named_expr = NEXPR("", group_by_expr)->MapTo(symbol_table.CreateSymbol("named_expression", true));
+      named_expressions.push_back(named_expr);
+    }
+    auto aggregation = std::make_shared<Aggregate>(input, aggregates, group_by_exprs, remember);
+    return std::make_shared<Produce>(aggregation, named_expressions);
+  }
+};
+
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+
+TYPED_TEST_CASE(QueryPlanTest, StorageTypes);
+
+TYPED_TEST(QueryPlanTest, Accumulate) {
   // simulate the following two query execution on an empty db
   // CREATE ({x:0})-[:T]->({x:0})
   // MATCH (n)--(m) SET n.x = n.x + 1, m.x = m.x + 1 RETURN n.x, m.x
@@ -36,9 +92,11 @@ TEST(QueryPlan, Accumulate) {
   // with accumulation we expect them to be [[2, 2], [2, 2]]
 
   auto check = [&](bool accumulate) {
-    memgraph::storage::Storage db;
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    this->db.reset(nullptr);
+    this->CleanStorageDirs();
+    this->db = std::make_unique<TypeParam>(this->config);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto prop = dba.NameToProperty("x");
 
     auto v1 = dba.InsertVertex();
@@ -48,17 +106,16 @@ TEST(QueryPlan, Accumulate) {
     ASSERT_TRUE(dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("T")).HasValue());
     dba.AdvanceCommand();
 
-    AstStorage storage;
     SymbolTable symbol_table;
 
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m", false,
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m", false,
                           memgraph::storage::View::OLD);
 
     auto one = LITERAL(1);
-    auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
+    auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
     auto set_n_p = std::make_shared<plan::SetProperty>(r_m.op_, prop, n_p, ADD(n_p, one));
-    auto m_p = PROPERTY_LOOKUP(IDENT("m")->MapTo(r_m.node_sym_), prop);
+    auto m_p = PROPERTY_LOOKUP(dba, IDENT("m")->MapTo(r_m.node_sym_), prop);
     auto set_m_p = std::make_shared<plan::SetProperty>(set_n_p, prop, m_p, ADD(m_p, one));
 
     std::shared_ptr<LogicalOperator> last_op = set_m_p;
@@ -69,7 +126,7 @@ TEST(QueryPlan, Accumulate) {
     auto n_p_ne = NEXPR("n.p", n_p)->MapTo(symbol_table.CreateSymbol("n_p_ne", true));
     auto m_p_ne = NEXPR("m.p", m_p)->MapTo(symbol_table.CreateSymbol("m_p_ne", true));
     auto produce = MakeProduce(last_op, n_p_ne, m_p_ne);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     auto results = CollectProduce(*produce, &context);
     std::vector<int> results_data;
     for (const auto &row : results)
@@ -84,68 +141,36 @@ TEST(QueryPlan, Accumulate) {
   check(true);
 }
 
-TEST(QueryPlan, AccumulateAdvance) {
+TYPED_TEST(QueryPlanTest, AccumulateAdvance) {
   // we simulate 'CREATE (n) WITH n AS n MATCH (m) RETURN m'
   // to get correct results we need to advance the command
   auto check = [&](bool advance) {
-    memgraph::storage::Storage db;
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
-    AstStorage storage;
+    this->db.reset();
+    this->CleanStorageDirs();
+    this->db = std::make_unique<TypeParam>(this->config);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     SymbolTable symbol_table;
     NodeCreationInfo node;
     node.symbol = symbol_table.CreateSymbol("n", true);
     auto create = std::make_shared<CreateNode>(nullptr, node);
     auto accumulate = std::make_shared<Accumulate>(create, std::vector<Symbol>{node.symbol}, advance);
-    auto match = MakeScanAll(storage, symbol_table, "m", accumulate);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto match = MakeScanAll(this->storage, symbol_table, "m", accumulate);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_EQ(advance ? 1 : 0, PullAll(*match.op_, &context));
   };
   check(false);
   check(true);
 }
 
-std::shared_ptr<Produce> MakeAggregationProduce(std::shared_ptr<LogicalOperator> input, SymbolTable &symbol_table,
-                                                AstStorage &storage, const std::vector<Expression *> aggr_inputs,
-                                                const std::vector<Aggregation::Op> aggr_ops,
-                                                const std::vector<Expression *> group_by_exprs,
-                                                const std::vector<Symbol> remember, const bool distinct) {
-  // prepare all the aggregations
-  std::vector<Aggregate::Element> aggregates;
-  std::vector<NamedExpression *> named_expressions;
-
-  auto aggr_inputs_it = aggr_inputs.begin();
-  for (auto aggr_op : aggr_ops) {
-    // TODO change this from using IDENT to using AGGREGATION
-    // once AGGREGATION is handled properly in ExpressionEvaluation
-    auto aggr_sym = symbol_table.CreateSymbol("aggregation", true);
-    auto named_expr =
-        NEXPR("", IDENT("aggregation")->MapTo(aggr_sym))->MapTo(symbol_table.CreateSymbol("named_expression", true));
-    named_expressions.push_back(named_expr);
-    // the key expression is only used in COLLECT_MAP
-    Expression *key_expr_ptr = aggr_op == Aggregation::Op::COLLECT_MAP ? LITERAL("key") : nullptr;
-    aggregates.emplace_back(Aggregate::Element{*aggr_inputs_it++, key_expr_ptr, aggr_op, aggr_sym, distinct});
-  }
-
-  // Produce will also evaluate group_by expressions and return them after the
-  // aggregations.
-  for (auto group_by_expr : group_by_exprs) {
-    auto named_expr = NEXPR("", group_by_expr)->MapTo(symbol_table.CreateSymbol("named_expression", true));
-    named_expressions.push_back(named_expr);
-  }
-  auto aggregation = std::make_shared<Aggregate>(input, aggregates, group_by_exprs, remember);
-  return std::make_shared<Produce>(aggregation, named_expressions);
-}
-
 /** Test fixture for all the aggregation ops in one return. */
-class QueryPlanAggregateOps : public ::testing::Test {
+template <typename StorageType>
+class QueryPlanAggregateOps : public QueryPlanTest<StorageType> {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
-  memgraph::storage::PropertyId prop = db.NameToProperty("prop");
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{this->db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
+  memgraph::storage::PropertyId prop = this->db->NameToProperty("prop");
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   void AddData() {
@@ -171,23 +196,25 @@ class QueryPlanAggregateOps : public ::testing::Test {
                               Aggregation::Op::MAX, Aggregation::Op::SUM, Aggregation::Op::AVG,
                               Aggregation::Op::COLLECT_LIST, Aggregation::Op::COLLECT_MAP}) {
     // match all nodes and perform aggregations
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
 
     std::vector<Expression *> aggregation_expressions(ops.size(), n_p);
     std::vector<Expression *> group_bys;
     if (with_group_by) group_bys.push_back(n_p);
     aggregation_expressions[0] = nullptr;
     auto produce =
-        MakeAggregationProduce(n.op_, symbol_table, storage, aggregation_expressions, ops, group_bys, {}, distinct);
-    auto context = MakeContext(storage, symbol_table, &dba);
+        this->MakeAggregationProduce(n.op_, symbol_table, aggregation_expressions, ops, group_bys, {}, distinct);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     return CollectProduce(*produce, &context);
   }
 };
 
-TEST_F(QueryPlanAggregateOps, WithData) {
-  AddData();
-  auto results = AggregationResults(false, false);
+TYPED_TEST_CASE(QueryPlanAggregateOps, StorageTypes);
+
+TYPED_TEST(QueryPlanAggregateOps, WithData) {
+  this->AddData();
+  auto results = this->AggregationResults(false, false);
 
   ASSERT_EQ(results.size(), 1);
   ASSERT_EQ(results[0].size(), 8);
@@ -220,48 +247,48 @@ TEST_F(QueryPlanAggregateOps, WithData) {
   EXPECT_FALSE(std::set<int>({5, 7, 12}).insert(map.begin()->second).second);
 }
 
-TEST_F(QueryPlanAggregateOps, WithoutDataWithGroupBy) {
+TYPED_TEST(QueryPlanAggregateOps, WithoutDataWithGroupBy) {
   {
-    auto results = AggregationResults(true, false, {Aggregation::Op::COUNT});
+    auto results = this->AggregationResults(true, false, {Aggregation::Op::COUNT});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::Int);
     EXPECT_EQ(results[0][0].ValueInt(), 0);
   }
   {
-    auto results = AggregationResults(true, false, {Aggregation::Op::SUM});
+    auto results = this->AggregationResults(true, false, {Aggregation::Op::SUM});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::Int);
     EXPECT_EQ(results[0][0].ValueInt(), 0);
   }
   {
-    auto results = AggregationResults(true, false, {Aggregation::Op::AVG});
+    auto results = this->AggregationResults(true, false, {Aggregation::Op::AVG});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::Null);
   }
   {
-    auto results = AggregationResults(true, false, {Aggregation::Op::MIN});
+    auto results = this->AggregationResults(true, false, {Aggregation::Op::MIN});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::Null);
   }
   {
-    auto results = AggregationResults(true, false, {Aggregation::Op::MAX});
+    auto results = this->AggregationResults(true, false, {Aggregation::Op::MAX});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::Null);
   }
   {
-    auto results = AggregationResults(true, false, {Aggregation::Op::COLLECT_LIST});
+    auto results = this->AggregationResults(true, false, {Aggregation::Op::COLLECT_LIST});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::List);
   }
   {
-    auto results = AggregationResults(true, false, {Aggregation::Op::COLLECT_MAP});
+    auto results = this->AggregationResults(true, false, {Aggregation::Op::COLLECT_MAP});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::Map);
   }
 }
 
-TEST_F(QueryPlanAggregateOps, WithoutDataWithoutGroupBy) {
-  auto results = AggregationResults(false, false);
+TYPED_TEST(QueryPlanAggregateOps, WithoutDataWithoutGroupBy) {
+  auto results = this->AggregationResults(false, false);
   ASSERT_EQ(results.size(), 1);
   ASSERT_EQ(results[0].size(), 8);
   // count(*)
@@ -286,13 +313,12 @@ TEST_F(QueryPlanAggregateOps, WithoutDataWithoutGroupBy) {
   EXPECT_EQ(ToIntMap(results[0][7]).size(), 0);
 }
 
-TEST(QueryPlan, AggregateGroupByValues) {
+TYPED_TEST(QueryPlanTest, AggregateGroupByValues) {
   // Tests that distinct groups are aggregated properly for values of all types.
   // Also test the "remember" part of the Aggregation API as final results are
   // obtained via a property lookup of a remembered node.
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // a vector of memgraph::storage::PropertyValue to be set as property values on vertices
   // most of them should result in a distinct group (commented where not)
@@ -324,17 +350,16 @@ TEST(QueryPlan, AggregateGroupByValues) {
     ASSERT_TRUE(dba.InsertVertex().SetProperty(prop, group_by_vals[i % group_by_vals.size()]).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   // match all nodes and perform aggregations
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
 
   auto produce =
-      MakeAggregationProduce(n.op_, symbol_table, storage, {n_p}, {Aggregation::Op::COUNT}, {n_p}, {n.sym_}, false);
+      this->MakeAggregationProduce(n.op_, symbol_table, {n_p}, {Aggregation::Op::COUNT}, {n_p}, {n.sym_}, false);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(results.size(), group_by_vals.size() - 2);
   std::unordered_set<TypedValue, TypedValue::Hash, TypedValue::BoolEqual> result_group_bys;
@@ -350,13 +375,12 @@ TEST(QueryPlan, AggregateGroupByValues) {
                                   TypedValue::BoolEqual{}));
 }
 
-TEST(QueryPlan, AggregateMultipleGroupBy) {
+TYPED_TEST(QueryPlanTest, AggregateMultipleGroupBy) {
   // in this test we have 3 different properties that have different values
   // for different records and assert that we get the correct combination
   // of values in our groups
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto prop1 = dba.NameToProperty("prop1");
   auto prop2 = dba.NameToProperty("prop2");
@@ -369,33 +393,30 @@ TEST(QueryPlan, AggregateMultipleGroupBy) {
   }
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   // match all nodes and perform aggregations
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_p1 = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop1);
-  auto n_p2 = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop2);
-  auto n_p3 = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop3);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_p1 = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop1);
+  auto n_p2 = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop2);
+  auto n_p3 = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop3);
 
-  auto produce = MakeAggregationProduce(n.op_, symbol_table, storage, {n_p1}, {Aggregation::Op::COUNT},
-                                        {n_p1, n_p2, n_p3}, {n.sym_}, false);
+  auto produce = this->MakeAggregationProduce(n.op_, symbol_table, {n_p1}, {Aggregation::Op::COUNT}, {n_p1, n_p2, n_p3},
+                                              {n.sym_}, false);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 2 * 3 * 5);
 }
 
-TEST(QueryPlan, AggregateNoInput) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+TYPED_TEST(QueryPlanTest, AggregateNoInput) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
 
   auto two = LITERAL(2);
-  auto produce = MakeAggregationProduce(nullptr, symbol_table, storage, {two}, {Aggregation::Op::COUNT}, {}, {}, false);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto produce = this->MakeAggregationProduce(nullptr, symbol_table, {two}, {Aggregation::Op::COUNT}, {}, {}, false);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(1, results.size());
   EXPECT_EQ(1, results[0].size());
@@ -403,7 +424,7 @@ TEST(QueryPlan, AggregateNoInput) {
   EXPECT_EQ(1, results[0][0].ValueInt());
 }
 
-TEST(QueryPlan, AggregateCountEdgeCases) {
+TYPED_TEST(QueryPlanTest, AggregateCountEdgeCases) {
   // tests for detected bugs in the COUNT aggregation behavior
   // ensure that COUNT returns correctly for
   //  - 0 vertices in database
@@ -412,22 +433,20 @@ TEST(QueryPlan, AggregateCountEdgeCases) {
   //  - 2 vertices in database, property set on one
   //  - 2 vertices in database, property set on both
 
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto prop = dba.NameToProperty("prop");
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
 
   // returns -1 when there are no results
   // otherwise returns MATCH (n) RETURN count(n.prop)
   auto count = [&]() {
-    auto produce = MakeAggregationProduce(n.op_, symbol_table, storage, {n_p}, {Aggregation::Op::COUNT}, {}, {}, false);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto produce = this->MakeAggregationProduce(n.op_, symbol_table, {n_p}, {Aggregation::Op::COUNT}, {}, {}, false);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     auto results = CollectProduce(*produce, &context);
     if (results.size() == 0) return -1L;
     EXPECT_EQ(1, results.size());
@@ -462,13 +481,12 @@ TEST(QueryPlan, AggregateCountEdgeCases) {
   EXPECT_EQ(2, count());
 }
 
-TEST(QueryPlan, AggregateFirstValueTypes) {
+TYPED_TEST(QueryPlanTest, AggregateFirstValueTypes) {
   // testing exceptions that get emitted by the first-value
   // type check
 
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto v1 = dba.InsertVertex();
   auto prop_string = dba.NameToProperty("string");
@@ -477,17 +495,16 @@ TEST(QueryPlan, AggregateFirstValueTypes) {
   ASSERT_TRUE(v1.SetProperty(prop_int, memgraph::storage::PropertyValue(12)).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_prop_string = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop_string);
-  auto n_prop_int = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop_int);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_prop_string = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop_string);
+  auto n_prop_int = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop_int);
   auto n_id = n_prop_string->expression_;
 
   auto aggregate = [&](Expression *expression, Aggregation::Op aggr_op) {
-    auto produce = MakeAggregationProduce(n.op_, symbol_table, storage, {expression}, {aggr_op}, {}, {}, false);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto produce = this->MakeAggregationProduce(n.op_, symbol_table, {expression}, {aggr_op}, {}, {}, false);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     CollectProduce(*produce, &context);
   };
 
@@ -515,14 +532,13 @@ TEST(QueryPlan, AggregateFirstValueTypes) {
   aggregate(n_prop_int, Aggregation::Op::COLLECT_MAP);
 }
 
-TEST(QueryPlan, AggregateTypes) {
+TYPED_TEST(QueryPlanTest, AggregateTypes) {
   // testing exceptions that can get emitted by an aggregation
   // does not check all combinations that can result in an exception
   // (that logic is defined and tested by TypedValue)
 
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto p1 = dba.NameToProperty("p1");  // has only string props
   ASSERT_TRUE(dba.InsertVertex().SetProperty(p1, memgraph::storage::PropertyValue("string")).HasValue());
@@ -532,16 +548,15 @@ TEST(QueryPlan, AggregateTypes) {
   ASSERT_TRUE(dba.InsertVertex().SetProperty(p2, memgraph::storage::PropertyValue(true)).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_p1 = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), p1);
-  auto n_p2 = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), p2);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_p1 = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), p1);
+  auto n_p2 = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), p2);
 
   auto aggregate = [&](Expression *expression, Aggregation::Op aggr_op) {
-    auto produce = MakeAggregationProduce(n.op_, symbol_table, storage, {expression}, {aggr_op}, {}, {}, false);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto produce = this->MakeAggregationProduce(n.op_, symbol_table, {expression}, {aggr_op}, {}, {}, false);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     CollectProduce(*produce, &context);
   };
 
@@ -574,15 +589,13 @@ TEST(QueryPlan, AggregateTypes) {
   EXPECT_THROW(aggregate(n_p2, Aggregation::Op::SUM), QueryRuntimeException);
 }
 
-TEST(QueryPlan, Unwind) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+TYPED_TEST(QueryPlanTest, Unwind) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
 
   // UNWIND [ [1, true, "x"], [], ["bla"] ] AS x UNWIND x as y RETURN x, y
-  auto input_expr = storage.Create<PrimitiveLiteral>(std::vector<memgraph::storage::PropertyValue>{
+  auto input_expr = this->storage.template Create<PrimitiveLiteral>(std::vector<memgraph::storage::PropertyValue>{
       memgraph::storage::PropertyValue(std::vector<memgraph::storage::PropertyValue>{
           memgraph::storage::PropertyValue(1), memgraph::storage::PropertyValue(true),
           memgraph::storage::PropertyValue("x")}),
@@ -600,7 +613,7 @@ TEST(QueryPlan, Unwind) {
   auto y_ne = NEXPR("y", IDENT("y")->MapTo(y))->MapTo(symbol_table.CreateSymbol("y_ne", true));
   auto produce = MakeProduce(unwind_1, x_ne, y_ne);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(4, results.size());
   const std::vector<int> expected_x_card{3, 3, 3, 1};
@@ -617,9 +630,9 @@ TEST(QueryPlan, Unwind) {
   }
 }
 
-TEST_F(QueryPlanAggregateOps, WithDataDistinct) {
-  AddData();
-  auto results = AggregationResults(false, true);
+TYPED_TEST(QueryPlanAggregateOps, WithDataDistinct) {
+  this->AddData();
+  auto results = this->AggregationResults(false, true);
 
   ASSERT_EQ(results.size(), 1);
   ASSERT_EQ(results[0].size(), 8);
@@ -652,48 +665,48 @@ TEST_F(QueryPlanAggregateOps, WithDataDistinct) {
   EXPECT_FALSE(std::set<int>({5, 7, 12}).insert(map.begin()->second).second);
 }
 
-TEST_F(QueryPlanAggregateOps, WithoutDataWithDistinctAndWithGroupBy) {
+TYPED_TEST(QueryPlanAggregateOps, WithoutDataWithDistinctAndWithGroupBy) {
   {
-    auto results = AggregationResults(true, true, {Aggregation::Op::COUNT});
+    auto results = this->AggregationResults(true, true, {Aggregation::Op::COUNT});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::Int);
     EXPECT_EQ(results[0][0].ValueInt(), 0);
   }
   {
-    auto results = AggregationResults(true, true, {Aggregation::Op::SUM});
+    auto results = this->AggregationResults(true, true, {Aggregation::Op::SUM});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::Int);
     EXPECT_EQ(results[0][0].ValueInt(), 0);
   }
   {
-    auto results = AggregationResults(true, true, {Aggregation::Op::AVG});
+    auto results = this->AggregationResults(true, true, {Aggregation::Op::AVG});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::Null);
   }
   {
-    auto results = AggregationResults(true, true, {Aggregation::Op::MIN});
+    auto results = this->AggregationResults(true, true, {Aggregation::Op::MIN});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::Null);
   }
   {
-    auto results = AggregationResults(true, true, {Aggregation::Op::MAX});
+    auto results = this->AggregationResults(true, true, {Aggregation::Op::MAX});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::Null);
   }
   {
-    auto results = AggregationResults(true, true, {Aggregation::Op::COLLECT_LIST});
+    auto results = this->AggregationResults(true, true, {Aggregation::Op::COLLECT_LIST});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::List);
   }
   {
-    auto results = AggregationResults(true, true, {Aggregation::Op::COLLECT_MAP});
+    auto results = this->AggregationResults(true, true, {Aggregation::Op::COLLECT_MAP});
     EXPECT_EQ(results.size(), 1);
     EXPECT_EQ(results[0][0].type(), TypedValue::Type::Map);
   }
 }
 
-TEST_F(QueryPlanAggregateOps, WithoutDataWithDistinctAndWithoutGroupBy) {
-  auto results = AggregationResults(false, true);
+TYPED_TEST(QueryPlanAggregateOps, WithoutDataWithDistinctAndWithoutGroupBy) {
+  auto results = this->AggregationResults(false, true);
   ASSERT_EQ(results.size(), 1);
   ASSERT_EQ(results[0].size(), 8);
   // count(*)
@@ -718,13 +731,12 @@ TEST_F(QueryPlanAggregateOps, WithoutDataWithDistinctAndWithoutGroupBy) {
   EXPECT_EQ(ToIntMap(results[0][7]).size(), 0);
 }
 
-TEST(QueryPlan, AggregateGroupByValuesWithDistinct) {
+TYPED_TEST(QueryPlanTest, AggregateGroupByValuesWithDistinct) {
   // Tests that distinct groups are aggregated properly for values of all types.
   // Also test the "remember" part of the Aggregation API as final results are
   // obtained via a property lookup of a remembered node.
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // a vector of memgraph::storage::PropertyValue to be set as property values on vertices
   // most of them should result in a distinct group (commented where not)
@@ -756,17 +768,16 @@ TEST(QueryPlan, AggregateGroupByValuesWithDistinct) {
     ASSERT_TRUE(dba.InsertVertex().SetProperty(prop, group_by_vals[i % group_by_vals.size()]).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   // match all nodes and perform aggregations
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
 
   auto produce =
-      MakeAggregationProduce(n.op_, symbol_table, storage, {n_p}, {Aggregation::Op::COUNT}, {n_p}, {n.sym_}, true);
+      this->MakeAggregationProduce(n.op_, symbol_table, {n_p}, {Aggregation::Op::COUNT}, {n_p}, {n.sym_}, true);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(results.size(), group_by_vals.size() - 2);
   std::unordered_set<TypedValue, TypedValue::Hash, TypedValue::BoolEqual> result_group_bys;
@@ -785,13 +796,12 @@ TEST(QueryPlan, AggregateGroupByValuesWithDistinct) {
                                   TypedValue::BoolEqual{}));
 }
 
-TEST(QueryPlan, AggregateMultipleGroupByWithDistinct) {
+TYPED_TEST(QueryPlanTest, AggregateMultipleGroupByWithDistinct) {
   // in this test we have 3 different properties that have different values
   // for different records and assert that we get the correct combination
   // of values in our groups
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto prop1 = dba.NameToProperty("prop1");
   auto prop2 = dba.NameToProperty("prop2");
@@ -804,34 +814,31 @@ TEST(QueryPlan, AggregateMultipleGroupByWithDistinct) {
   }
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   // match all nodes and perform aggregations
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_p1 = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop1);
-  auto n_p2 = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop2);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_p1 = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop1);
+  auto n_p2 = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop2);
 
-  auto produce = MakeAggregationProduce(n.op_, symbol_table, storage, {n_p1}, {Aggregation::Op::COUNT}, {n_p1, n_p2},
-                                        {n.sym_}, true);
+  auto produce =
+      this->MakeAggregationProduce(n.op_, symbol_table, {n_p1}, {Aggregation::Op::COUNT}, {n_p1, n_p2}, {n.sym_}, true);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   for (const auto &row : results) {
     ASSERT_EQ(1, row[0].ValueInt());
   }
 }
 
-TEST(QueryPlan, AggregateNoInputWithDistinct) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+TYPED_TEST(QueryPlanTest, AggregateNoInputWithDistinct) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
 
   auto two = LITERAL(2);
-  auto produce = MakeAggregationProduce(nullptr, symbol_table, storage, {two}, {Aggregation::Op::COUNT}, {}, {}, true);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto produce = this->MakeAggregationProduce(nullptr, symbol_table, {two}, {Aggregation::Op::COUNT}, {}, {}, true);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(1, results.size());
   EXPECT_EQ(1, results[0].size());
@@ -839,7 +846,7 @@ TEST(QueryPlan, AggregateNoInputWithDistinct) {
   EXPECT_EQ(1, results[0][0].ValueInt());
 }
 
-TEST(QueryPlan, AggregateCountEdgeCasesWithDistinct) {
+TYPED_TEST(QueryPlanTest, AggregateCountEdgeCasesWithDistinct) {
   // tests for detected bugs in the COUNT aggregation behavior
   // ensure that COUNT returns correctly for
   //  - 0 vertices in database
@@ -848,22 +855,20 @@ TEST(QueryPlan, AggregateCountEdgeCasesWithDistinct) {
   //  - 2 vertices in database, property set on one
   //  - 2 vertices in database, property set on both
 
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto prop = dba.NameToProperty("prop");
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
 
   // returns -1 when there are no results
   // otherwise returns MATCH (n) RETURN count(n.prop)
   auto count = [&]() {
-    auto produce = MakeAggregationProduce(n.op_, symbol_table, storage, {n_p}, {Aggregation::Op::COUNT}, {}, {}, true);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto produce = this->MakeAggregationProduce(n.op_, symbol_table, {n_p}, {Aggregation::Op::COUNT}, {}, {}, true);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     auto results = CollectProduce(*produce, &context);
     if (results.size() == 0) return -1L;
     EXPECT_EQ(1, results.size());
@@ -898,13 +903,12 @@ TEST(QueryPlan, AggregateCountEdgeCasesWithDistinct) {
   EXPECT_EQ(1, count());
 }
 
-TEST(QueryPlan, AggregateFirstValueTypesWithDistinct) {
+TYPED_TEST(QueryPlanTest, AggregateFirstValueTypesWithDistinct) {
   // testing exceptions that get emitted by the first-value
   // type check
 
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto v1 = dba.InsertVertex();
   auto prop_string = dba.NameToProperty("string");
@@ -913,17 +917,16 @@ TEST(QueryPlan, AggregateFirstValueTypesWithDistinct) {
   ASSERT_TRUE(v1.SetProperty(prop_int, memgraph::storage::PropertyValue(12)).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_prop_string = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop_string);
-  auto n_prop_int = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop_int);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_prop_string = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop_string);
+  auto n_prop_int = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop_int);
   auto n_id = n_prop_string->expression_;
 
   auto aggregate = [&](Expression *expression, Aggregation::Op aggr_op) {
-    auto produce = MakeAggregationProduce(n.op_, symbol_table, storage, {expression}, {aggr_op}, {}, {}, true);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto produce = this->MakeAggregationProduce(n.op_, symbol_table, {expression}, {aggr_op}, {}, {}, true);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     CollectProduce(*produce, &context);
   };
 
@@ -951,14 +954,13 @@ TEST(QueryPlan, AggregateFirstValueTypesWithDistinct) {
   aggregate(n_prop_int, Aggregation::Op::COLLECT_MAP);
 }
 
-TEST(QueryPlan, AggregateTypesWithDistinct) {
+TYPED_TEST(QueryPlanTest, AggregateTypesWithDistinct) {
   // testing exceptions that can get emitted by an aggregation
   // does not check all combinations that can result in an exception
   // (that logic is defined and tested by TypedValue)
 
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto p1 = dba.NameToProperty("p1");  // has only string props
   ASSERT_TRUE(dba.InsertVertex().SetProperty(p1, memgraph::storage::PropertyValue("string")).HasValue());
@@ -968,16 +970,15 @@ TEST(QueryPlan, AggregateTypesWithDistinct) {
   ASSERT_TRUE(dba.InsertVertex().SetProperty(p2, memgraph::storage::PropertyValue(true)).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_p1 = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), p1);
-  auto n_p2 = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), p2);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_p1 = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), p1);
+  auto n_p2 = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), p2);
 
   auto aggregate = [&](Expression *expression, Aggregation::Op aggr_op) {
-    auto produce = MakeAggregationProduce(n.op_, symbol_table, storage, {expression}, {aggr_op}, {}, {}, true);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto produce = this->MakeAggregationProduce(n.op_, symbol_table, {expression}, {aggr_op}, {}, {}, true);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     CollectProduce(*produce, &context);
   };
 
diff --git a/tests/unit/query_plan_bag_semantics.cpp b/tests/unit/query_plan_bag_semantics.cpp
index f0b0916f4..6b1c7ab64 100644
--- a/tests/unit/query_plan_bag_semantics.cpp
+++ b/tests/unit/query_plan_bag_semantics.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -19,30 +19,48 @@
 #include <memory>
 #include <vector>
 
+#include "disk_test_utils.hpp"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 #include "query/context.hpp"
 #include "query/exceptions.hpp"
 #include "query/plan/operator.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 #include "query_plan_common.hpp"
 
 using namespace memgraph::query;
 using namespace memgraph::query::plan;
 
-TEST(QueryPlan, Skip) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-
+template <typename StorageType>
+class QueryPlanTest : public testing::Test {
+ public:
+  const std::string testSuite = "query_plan_bag_semantics";
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db = std::make_unique<StorageType>(config);
   AstStorage storage;
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+};
+
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(QueryPlanTest, StorageTypes);
+
+TYPED_TEST(QueryPlanTest, Skip) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n1");
+  auto n = MakeScanAll(this->storage, symbol_table, "n1");
   auto skip = std::make_shared<plan::Skip>(n.op_, LITERAL(2));
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(0, PullAll(*skip, &context));
 
   dba.InsertVertex();
@@ -62,18 +80,15 @@ TEST(QueryPlan, Skip) {
   EXPECT_EQ(11, PullAll(*skip, &context));
 }
 
-TEST(QueryPlan, Limit) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-
-  AstStorage storage;
+TYPED_TEST(QueryPlanTest, Limit) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n1");
+  auto n = MakeScanAll(this->storage, symbol_table, "n1");
   auto skip = std::make_shared<plan::Limit>(n.op_, LITERAL(2));
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(0, PullAll(*skip, &context));
 
   dba.InsertVertex();
@@ -93,37 +108,33 @@ TEST(QueryPlan, Limit) {
   EXPECT_EQ(2, PullAll(*skip, &context));
 }
 
-TEST(QueryPlan, CreateLimit) {
+TYPED_TEST(QueryPlanTest, CreateLimit) {
   // CREATE (n), (m)
   // MATCH (n) CREATE (m) LIMIT 1
   // in the end we need to have 3 vertices in the db
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   dba.InsertVertex();
   dba.InsertVertex();
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n1");
+  auto n = MakeScanAll(this->storage, symbol_table, "n1");
   NodeCreationInfo m;
   m.symbol = symbol_table.CreateSymbol("m", true);
   auto c = std::make_shared<CreateNode>(n.op_, m);
   auto skip = std::make_shared<plan::Limit>(c, LITERAL(1));
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(1, PullAll(*skip, &context));
   dba.AdvanceCommand();
   EXPECT_EQ(3, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
 }
 
-TEST(QueryPlan, OrderBy) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+TYPED_TEST(QueryPlanTest, OrderBy) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
   auto prop = dba.NameToProperty("prop");
 
@@ -179,24 +190,22 @@ TEST(QueryPlan, OrderBy) {
     dba.AdvanceCommand();
 
     // order by and collect results
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
     auto order_by = std::make_shared<plan::OrderBy>(n.op_, std::vector<SortItem>{{order_value_pair.first, n_p}},
                                                     std::vector<Symbol>{n.sym_});
     auto n_p_ne = NEXPR("n.p", n_p)->MapTo(symbol_table.CreateSymbol("n.p", true));
     auto produce = MakeProduce(order_by, n_p_ne);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     auto results = CollectProduce(*produce, &context);
     ASSERT_EQ(values.size(), results.size());
     for (int j = 0; j < results.size(); ++j) EXPECT_TRUE(TypedValue::BoolEqual{}(results[j][0], values[j]));
   }
 }
 
-TEST(QueryPlan, OrderByMultiple) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+TYPED_TEST(QueryPlanTest, OrderByMultiple) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
 
   auto p1 = dba.NameToProperty("p1");
@@ -218,9 +227,9 @@ TEST(QueryPlan, OrderByMultiple) {
   dba.AdvanceCommand();
 
   // order by and collect results
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_p1 = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), p1);
-  auto n_p2 = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), p2);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_p1 = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), p1);
+  auto n_p2 = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), p2);
   // order the results so we get
   // (p1: 0, p2: N-1)
   // (p1: 0, p2: N-2)
@@ -235,7 +244,7 @@ TEST(QueryPlan, OrderByMultiple) {
   auto n_p1_ne = NEXPR("n.p1", n_p1)->MapTo(symbol_table.CreateSymbol("n.p1", true));
   auto n_p2_ne = NEXPR("n.p2", n_p2)->MapTo(symbol_table.CreateSymbol("n.p2", true));
   auto produce = MakeProduce(order_by, n_p1_ne, n_p2_ne);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(N * N, results.size());
   for (int j = 0; j < N * N; ++j) {
@@ -246,11 +255,9 @@ TEST(QueryPlan, OrderByMultiple) {
   }
 }
 
-TEST(QueryPlan, OrderByExceptions) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+TYPED_TEST(QueryPlanTest, OrderByExceptions) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
   auto prop = dba.NameToProperty("prop");
 
@@ -292,11 +299,11 @@ TEST(QueryPlan, OrderByExceptions) {
                 memgraph::storage::PropertyValue::Type::Null);
 
     // order by and expect an exception
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
     auto order_by =
         std::make_shared<plan::OrderBy>(n.op_, std::vector<SortItem>{{Ordering::ASC, n_p}}, std::vector<Symbol>{});
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_THROW(PullAll(*order_by, &context), QueryRuntimeException);
   }
 }
diff --git a/tests/unit/query_plan_common.hpp b/tests/unit/query_plan_common.hpp
index 975099a0e..cf5f2224d 100644
--- a/tests/unit/query_plan_common.hpp
+++ b/tests/unit/query_plan_common.hpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -83,7 +83,9 @@ int PullAll(const LogicalOperator &logical_op, ExecutionContext *context) {
   Frame frame(context->symbol_table.max_position());
   auto cursor = logical_op.MakeCursor(memgraph::utils::NewDeleteResource());
   int count = 0;
-  while (cursor->Pull(frame, *context)) count++;
+  while (cursor->Pull(frame, *context)) {
+    count++;
+  }
   return count;
 }
 
@@ -107,7 +109,7 @@ struct ScanAllTuple {
 ScanAllTuple MakeScanAll(AstStorage &storage, SymbolTable &symbol_table, const std::string &identifier,
                          std::shared_ptr<LogicalOperator> input = {nullptr},
                          memgraph::storage::View view = memgraph::storage::View::OLD) {
-  auto node = NODE(identifier);
+  auto node = memgraph::query::test_common::GetNode(storage, identifier);
   auto symbol = symbol_table.CreateSymbol(identifier, true);
   node->identifier_->MapTo(symbol);
   auto logical_op = std::make_shared<ScanAll>(input, symbol, view);
@@ -123,7 +125,7 @@ ScanAllTuple MakeScanAll(AstStorage &storage, SymbolTable &symbol_table, const s
 ScanAllTuple MakeScanAllByLabel(AstStorage &storage, SymbolTable &symbol_table, const std::string &identifier,
                                 memgraph::storage::LabelId label, std::shared_ptr<LogicalOperator> input = {nullptr},
                                 memgraph::storage::View view = memgraph::storage::View::OLD) {
-  auto node = NODE(identifier);
+  auto node = memgraph::query::test_common::GetNode(storage, identifier);
   auto symbol = symbol_table.CreateSymbol(identifier, true);
   node->identifier_->MapTo(symbol);
   auto logical_op = std::make_shared<ScanAllByLabel>(input, symbol, label, view);
@@ -142,7 +144,7 @@ ScanAllTuple MakeScanAllByLabelPropertyRange(AstStorage &storage, SymbolTable &s
                                              std::optional<Bound> upper_bound,
                                              std::shared_ptr<LogicalOperator> input = {nullptr},
                                              memgraph::storage::View view = memgraph::storage::View::OLD) {
-  auto node = NODE(identifier);
+  auto node = memgraph::query::test_common::GetNode(storage, identifier);
   auto symbol = symbol_table.CreateSymbol(identifier, true);
   node->identifier_->MapTo(symbol);
   auto logical_op = std::make_shared<ScanAllByLabelPropertyRange>(input, symbol, label, property, property_name,
@@ -161,7 +163,7 @@ ScanAllTuple MakeScanAllByLabelPropertyValue(AstStorage &storage, SymbolTable &s
                                              const std::string &property_name, Expression *value,
                                              std::shared_ptr<LogicalOperator> input = {nullptr},
                                              memgraph::storage::View view = memgraph::storage::View::OLD) {
-  auto node = NODE(identifier);
+  auto node = memgraph::query::test_common::GetNode(storage, identifier);
   auto symbol = symbol_table.CreateSymbol(identifier, true);
   node->identifier_->MapTo(symbol);
   auto logical_op =
@@ -181,11 +183,11 @@ ExpandTuple MakeExpand(AstStorage &storage, SymbolTable &symbol_table, std::shar
                        Symbol input_symbol, const std::string &edge_identifier, EdgeAtom::Direction direction,
                        const std::vector<memgraph::storage::EdgeTypeId> &edge_types, const std::string &node_identifier,
                        bool existing_node, memgraph::storage::View view) {
-  auto edge = EDGE(edge_identifier, direction);
+  auto edge = memgraph::query::test_common::GetEdge(storage, edge_identifier, direction);
   auto edge_sym = symbol_table.CreateSymbol(edge_identifier, true);
   edge->identifier_->MapTo(edge_sym);
 
-  auto node = NODE(node_identifier);
+  auto node = memgraph::query::test_common::GetNode(storage, node_identifier);
   auto node_sym = symbol_table.CreateSymbol(node_identifier, true);
   node->identifier_->MapTo(node_sym);
 
@@ -219,6 +221,7 @@ auto CountIterable(TIterable &&iterable) {
 inline uint64_t CountEdges(memgraph::query::DbAccessor *dba, memgraph::storage::View view) {
   uint64_t count = 0;
   for (auto vertex : dba->Vertices(view)) {
+    dba->PrefetchOutEdges(vertex);
     auto maybe_edges = vertex.OutEdges(view);
     MG_ASSERT(maybe_edges.HasValue());
     count += CountIterable(*maybe_edges);
diff --git a/tests/unit/query_plan_create_set_remove_delete.cpp b/tests/unit/query_plan_create_set_remove_delete.cpp
index 54c643ae1..088f6f8cc 100644
--- a/tests/unit/query_plan_create_set_remove_delete.cpp
+++ b/tests/unit/query_plan_create_set_remove_delete.cpp
@@ -16,6 +16,7 @@
 #include <vector>
 
 #include "auth/models.hpp"
+#include "disk_test_utils.hpp"
 #include "glue/auth_checker.hpp"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
@@ -23,28 +24,47 @@
 #include "license/license.hpp"
 #include "query/context.hpp"
 #include "query/exceptions.hpp"
+#include "query/frontend/ast/ast.hpp"
 #include "query/interpret/frame.hpp"
 #include "query/plan/operator.hpp"
 
 #include "query_plan_common.hpp"
+#include "storage/v2/disk/storage.hpp"
 #include "storage/v2/id_types.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/property_value.hpp"
+#include "storage/v2/storage.hpp"
 #include "storage/v2/vertex_accessor.hpp"
 
 using namespace memgraph::query;
 using namespace memgraph::query::plan;
 
-TEST(QueryPlan, CreateNodeWithAttributes) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+template <typename StorageType>
+class QueryPlanTest : public testing::Test {
+ public:
+  const std::string testSuite = "query_plan_create_set_remove_delete";
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db = std::make_unique<StorageType>(config);
+  AstStorage storage;
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+};
+
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(QueryPlanTest, StorageTypes);
+
+TYPED_TEST(QueryPlanTest, CreateNodeWithAttributes) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   memgraph::storage::LabelId label = dba.NameToLabel("Person");
-  auto property = PROPERTY_PAIR("prop");
+  auto property = PROPERTY_PAIR(dba, "prop");
 
-  AstStorage storage;
   SymbolTable symbol_table;
-
   NodeCreationInfo node;
   node.symbol = symbol_table.CreateSymbol("n", true);
   node.labels.emplace_back(label);
@@ -52,7 +72,7 @@ TEST(QueryPlan, CreateNodeWithAttributes) {
       .emplace_back(property.second, LITERAL(42));
 
   auto create = std::make_shared<CreateNode>(nullptr, node);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   PullAll(*create, &context);
   dba.AdvanceCommand();
 
@@ -79,26 +99,24 @@ TEST(QueryPlan, CreateNodeWithAttributes) {
 }
 
 #ifdef MG_ENTERPRISE
-TEST(QueryPlan, FineGrainedCreateNodeWithAttributes) {
+TYPED_TEST(QueryPlanTest, FineGrainedCreateNodeWithAttributes) {
   memgraph::license::global_license_checker.EnableTesting();
-  memgraph::query::AstStorage ast;
   memgraph::query::SymbolTable symbol_table;
-  memgraph::storage::Storage db;
-  auto dba = db.Access();
-  DbAccessor execution_dba(&dba);
-  const auto label = dba.NameToLabel("label1");
+  auto dba = this->db->Access();
+  DbAccessor execution_dba(dba.get());
+  const auto label = dba->NameToLabel("label1");
   const auto property = memgraph::storage::PropertyId::FromInt(1);
 
   memgraph::query::plan::NodeCreationInfo node;
   std::get<std::vector<std::pair<memgraph::storage::PropertyId, Expression *>>>(node.properties)
-      .emplace_back(property, ast.Create<PrimitiveLiteral>(42));
+      .emplace_back(property, this->storage.template Create<PrimitiveLiteral>(42));
 
   node.symbol = symbol_table.CreateSymbol("n", true);
   node.labels.emplace_back(label);
 
   const auto test_create = [&](memgraph::auth::User &user) {
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &execution_dba};
-    auto context = MakeContextWithFineGrainedChecker(ast, symbol_table, &execution_dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &execution_dba, &auth_checker);
     auto create = std::make_shared<CreateNode>(nullptr, node);
 
     return PullAll(*create, &context);
@@ -122,18 +140,15 @@ TEST(QueryPlan, FineGrainedCreateNodeWithAttributes) {
 }
 #endif
 
-TEST(QueryPlan, CreateReturn) {
+TYPED_TEST(QueryPlanTest, CreateReturn) {
   // test CREATE (n:Person {age: 42}) RETURN n, n.age
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   memgraph::storage::LabelId label = dba.NameToLabel("Person");
-  auto property = PROPERTY_PAIR("property");
+  auto property = PROPERTY_PAIR(dba, "property");
 
-  AstStorage storage;
   SymbolTable symbol_table;
-
   NodeCreationInfo node;
   node.symbol = symbol_table.CreateSymbol("n", true);
   node.labels.emplace_back(label);
@@ -143,11 +158,11 @@ TEST(QueryPlan, CreateReturn) {
   auto create = std::make_shared<CreateNode>(nullptr, node);
   auto named_expr_n =
       NEXPR("n", IDENT("n")->MapTo(node.symbol))->MapTo(symbol_table.CreateSymbol("named_expr_n", true));
-  auto prop_lookup = PROPERTY_LOOKUP(IDENT("n")->MapTo(node.symbol), property);
+  auto prop_lookup = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(node.symbol), property);
   auto named_expr_n_p = NEXPR("n", prop_lookup)->MapTo(symbol_table.CreateSymbol("named_expr_n_p", true));
 
   auto produce = MakeProduce(create, named_expr_n, named_expr_n_p);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(1, results.size());
   EXPECT_EQ(2, results[0].size());
@@ -163,20 +178,17 @@ TEST(QueryPlan, CreateReturn) {
 }
 
 #ifdef MG_ENTERPRISE
-TEST(QueryPlan, FineGrainedCreateReturn) {
+TYPED_TEST(QueryPlanTest, FineGrainedCreateReturn) {
   memgraph::license::global_license_checker.EnableTesting();
 
   // test CREATE (n:Person {age: 42}) RETURN n, n.age
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   const auto label = dba.NameToLabel("label");
-  const auto property = PROPERTY_PAIR("property");
+  const auto property = PROPERTY_PAIR(dba, "property");
 
-  AstStorage storage;
   SymbolTable symbol_table;
-
   NodeCreationInfo node;
   node.symbol = symbol_table.CreateSymbol("n", true);
   node.labels.emplace_back(label);
@@ -186,7 +198,7 @@ TEST(QueryPlan, FineGrainedCreateReturn) {
   auto create = std::make_shared<CreateNode>(nullptr, node);
   auto named_expr_n =
       NEXPR("n", IDENT("n")->MapTo(node.symbol))->MapTo(symbol_table.CreateSymbol("named_expr_n", true));
-  auto prop_lookup = PROPERTY_LOOKUP(IDENT("n")->MapTo(node.symbol), property);
+  auto prop_lookup = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(node.symbol), property);
   auto named_expr_n_p = NEXPR("n", prop_lookup)->MapTo(symbol_table.CreateSymbol("named_expr_n_p", true));
 
   auto produce = MakeProduce(create, named_expr_n, named_expr_n_p);
@@ -197,7 +209,7 @@ TEST(QueryPlan, FineGrainedCreateReturn) {
     user.fine_grained_access_handler().label_permissions().Grant("label",
                                                                  memgraph::auth::FineGrainedPermission::CREATE_DELETE);
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
     auto results = CollectProduce(*produce, &context);
     EXPECT_EQ(1, results.size());
     EXPECT_EQ(2, results[0].size());
@@ -218,24 +230,22 @@ TEST(QueryPlan, FineGrainedCreateReturn) {
     user.fine_grained_access_handler().label_permissions().Grant("label",
                                                                  memgraph::auth::FineGrainedPermission::UPDATE);
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
     ASSERT_THROW(CollectProduce(*produce, &context), QueryRuntimeException);
   }
 }
 #endif
 
-TEST(QueryPlan, CreateExpand) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, CreateExpand) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   memgraph::storage::LabelId label_node_1 = dba.NameToLabel("Node1");
   memgraph::storage::LabelId label_node_2 = dba.NameToLabel("Node2");
-  auto property = PROPERTY_PAIR("property");
+  auto property = PROPERTY_PAIR(dba, "property");
   memgraph::storage::EdgeTypeId edge_type = dba.NameToEdgeType("edge_type");
 
   SymbolTable symbol_table;
-  AstStorage storage;
 
   auto test_create_path = [&](bool cycle, int expected_nodes_created, int expected_edges_created) {
     int before_v = CountIterable(dba.Vertices(memgraph::storage::View::OLD));
@@ -262,7 +272,7 @@ TEST(QueryPlan, CreateExpand) {
 
     auto create_op = std::make_shared<CreateNode>(nullptr, n);
     auto create_expand = std::make_shared<CreateExpand>(m, r, create_op, n.symbol, cycle);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     PullAll(*create_expand, &context);
     dba.AdvanceCommand();
 
@@ -291,6 +301,7 @@ TEST(QueryPlan, CreateExpand) {
     }
 
     for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
+      dba.PrefetchOutEdges(vertex);
       auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
       MG_ASSERT(maybe_edges.HasValue());
       for (auto edge : *maybe_edges) {
@@ -302,12 +313,11 @@ TEST(QueryPlan, CreateExpand) {
 }
 
 #ifdef MG_ENTERPRISE
-class CreateExpandWithAuthFixture : public testing::Test {
+template <typename StorageType>
+class CreateExpandWithAuthFixture : public QueryPlanTest<StorageType> {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
-  AstStorage storage;
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{this->db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
   SymbolTable symbol_table;
 
   void SetUp() override { memgraph::license::global_license_checker.EnableTesting(); }
@@ -315,7 +325,7 @@ class CreateExpandWithAuthFixture : public testing::Test {
   void ExecuteCreateExpand(bool cycle, memgraph::auth::User &user) {
     const auto label_node_1 = dba.NameToLabel("Node1");
     const auto label_node_2 = dba.NameToLabel("Node2");
-    const auto property = PROPERTY_PAIR("property");
+    const auto property = PROPERTY_PAIR(dba, "property");
     const auto edge_type = dba.NameToEdgeType("edge_type");
 
     // data for the first node
@@ -340,7 +350,7 @@ class CreateExpandWithAuthFixture : public testing::Test {
     auto create_op = std::make_shared<CreateNode>(nullptr, n);
     auto create_expand = std::make_shared<CreateExpand>(m, r, create_op, n.symbol, cycle);
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
     PullAll(*create_expand, &context);
     dba.AdvanceCommand();
   }
@@ -351,38 +361,40 @@ class CreateExpandWithAuthFixture : public testing::Test {
   }
 };
 
-TEST_F(CreateExpandWithAuthFixture, CreateExpandWithNoGrantsOnCreateDelete) {
+TYPED_TEST_CASE(CreateExpandWithAuthFixture, StorageTypes);
+
+TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithNoGrantsOnCreateDelete) {
   // All labels denied, All edge types denied
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
   user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
-  ASSERT_THROW(ExecuteCreateExpand(false, user), QueryRuntimeException);
-  ASSERT_THROW(ExecuteCreateExpand(true, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteCreateExpand(false, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteCreateExpand(true, user), QueryRuntimeException);
 }
 
-TEST_F(CreateExpandWithAuthFixture, CreateExpandWithLabelsGrantedOnly) {
+TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithLabelsGrantedOnly) {
   // All labels granted, All edge types denied
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*",
                                                                memgraph::auth::FineGrainedPermission::CREATE_DELETE);
   user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
 
-  ASSERT_THROW(ExecuteCreateExpand(false, user), QueryRuntimeException);
-  ASSERT_THROW(ExecuteCreateExpand(true, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteCreateExpand(false, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteCreateExpand(true, user), QueryRuntimeException);
 }
 
-TEST_F(CreateExpandWithAuthFixture, CreateExpandWithEdgeTypesGrantedOnly) {
+TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithEdgeTypesGrantedOnly) {
   // All labels denied, All edge types granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-  ASSERT_THROW(ExecuteCreateExpand(false, user), QueryRuntimeException);
-  ASSERT_THROW(ExecuteCreateExpand(true, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteCreateExpand(false, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteCreateExpand(true, user), QueryRuntimeException);
 }
 
-TEST_F(CreateExpandWithAuthFixture, CreateExpandWithFirstLabelGranted) {
+TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithFirstLabelGranted) {
   // First label granted, All edge types granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("Node1",
@@ -392,11 +404,11 @@ TEST_F(CreateExpandWithAuthFixture, CreateExpandWithFirstLabelGranted) {
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-  ASSERT_THROW(ExecuteCreateExpand(false, user), QueryRuntimeException);
-  ASSERT_THROW(ExecuteCreateExpand(true, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteCreateExpand(false, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteCreateExpand(true, user), QueryRuntimeException);
 }
 
-TEST_F(CreateExpandWithAuthFixture, CreateExpandWithSecondLabelGranted) {
+TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithSecondLabelGranted) {
   // Second label granted, All edge types granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("Node2",
@@ -405,11 +417,11 @@ TEST_F(CreateExpandWithAuthFixture, CreateExpandWithSecondLabelGranted) {
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-  ASSERT_THROW(ExecuteCreateExpand(false, user), QueryRuntimeException);
-  ASSERT_THROW(ExecuteCreateExpand(true, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteCreateExpand(false, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteCreateExpand(true, user), QueryRuntimeException);
 }
 
-TEST_F(CreateExpandWithAuthFixture, CreateExpandWithoutCycleWithEverythingGranted) {
+TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithoutCycleWithEverythingGranted) {
   // All labels granted, All edge types granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*",
@@ -417,11 +429,11 @@ TEST_F(CreateExpandWithAuthFixture, CreateExpandWithoutCycleWithEverythingGrante
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-  ExecuteCreateExpand(false, user);
-  TestCreateExpandHypothesis(2, 1);
+  this->ExecuteCreateExpand(false, user);
+  this->TestCreateExpandHypothesis(2, 1);
 }
 
-TEST_F(CreateExpandWithAuthFixture, CreateExpandWithCycleWithEverythingGranted) {
+TYPED_TEST(CreateExpandWithAuthFixture, CreateExpandWithCycleWithEverythingGranted) {
   // All labels granted, All edge types granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*",
@@ -429,14 +441,13 @@ TEST_F(CreateExpandWithAuthFixture, CreateExpandWithCycleWithEverythingGranted)
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-  ExecuteCreateExpand(true, user);
-  TestCreateExpandHypothesis(1, 1);
+  this->ExecuteCreateExpand(true, user);
+  this->TestCreateExpandHypothesis(1, 1);
 }
 
-TEST(QueryPlan, MatchCreateNode) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, MatchCreateNode) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // add three nodes we'll match and expand-create from
   dba.InsertVertex();
@@ -445,10 +456,8 @@ TEST(QueryPlan, MatchCreateNode) {
   dba.AdvanceCommand();
 
   SymbolTable symbol_table;
-  AstStorage storage;
-
   // first node
-  auto n_scan_all = MakeScanAll(storage, symbol_table, "n");
+  auto n_scan_all = MakeScanAll(this->storage, symbol_table, "n");
   // second node
   NodeCreationInfo m;
   m.symbol = symbol_table.CreateSymbol("m", true);
@@ -456,18 +465,17 @@ TEST(QueryPlan, MatchCreateNode) {
   auto create_node = std::make_shared<CreateNode>(n_scan_all.op_, m);
 
   EXPECT_EQ(CountIterable(dba.Vertices(memgraph::storage::View::OLD)), 3);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   PullAll(*create_node, &context);
   dba.AdvanceCommand();
   EXPECT_EQ(CountIterable(dba.Vertices(memgraph::storage::View::OLD)), 6);
 }
 
-class MatchCreateNodeWithAuthFixture : public testing::Test {
+template <typename StorageType>
+class MatchCreateNodeWithAuthFixture : public QueryPlanTest<StorageType> {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
-  AstStorage storage;
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{this->db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
   SymbolTable symbol_table;
 
   void SetUp() override { memgraph::license::global_license_checker.EnableTesting(); }
@@ -484,7 +492,7 @@ class MatchCreateNodeWithAuthFixture : public testing::Test {
   }
 
   void ExecuteMatchCreate(memgraph::auth::User &user) {
-    auto n_scan_all = MakeScanAll(storage, symbol_table, "n");
+    auto n_scan_all = MakeScanAll(this->storage, symbol_table, "n");
     // second node
     NodeCreationInfo m{};
 
@@ -494,7 +502,7 @@ class MatchCreateNodeWithAuthFixture : public testing::Test {
     // creation op
     auto create_node = std::make_shared<CreateNode>(n_scan_all.op_, m);
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
 
     PullAll(*create_node, &context);
     dba.AdvanceCommand();
@@ -511,25 +519,27 @@ class MatchCreateNodeWithAuthFixture : public testing::Test {
   }
 };
 
-TEST_F(MatchCreateNodeWithAuthFixture, MatchCreateWithAllLabelsDeniedThrows) {
+TYPED_TEST_CASE(MatchCreateNodeWithAuthFixture, StorageTypes);
+
+TYPED_TEST(MatchCreateNodeWithAuthFixture, MatchCreateWithAllLabelsDeniedThrows) {
   // All labels denied
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
 
-  ASSERT_THROW(ExecuteMatchCreateTestSuite(user, 3), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteMatchCreateTestSuite(user, 3), QueryRuntimeException);
 }
 
-TEST_F(MatchCreateNodeWithAuthFixture, MatchCreateWithAllLabelsGrantedExecutes) {
+TYPED_TEST(MatchCreateNodeWithAuthFixture, MatchCreateWithAllLabelsGrantedExecutes) {
   // All labels granteddenieddenieddenied
 
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*",
                                                                memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-  ExecuteMatchCreateTestSuite(user, 6);
+  this->ExecuteMatchCreateTestSuite(user, 6);
 }
 
-TEST_F(MatchCreateNodeWithAuthFixture, MatchCreateWithOneLabelDeniedThrows) {
+TYPED_TEST(MatchCreateNodeWithAuthFixture, MatchCreateWithOneLabelDeniedThrows) {
   // Label2 denied
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("l1",
@@ -539,14 +549,13 @@ TEST_F(MatchCreateNodeWithAuthFixture, MatchCreateWithOneLabelDeniedThrows) {
 
   user.fine_grained_access_handler().label_permissions().Grant("l2", memgraph::auth::FineGrainedPermission::UPDATE);
 
-  ASSERT_THROW(ExecuteMatchCreateTestSuite(user, 3), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteMatchCreateTestSuite(user, 3), QueryRuntimeException);
 }
 #endif
 
-TEST(QueryPlan, MatchCreateExpand) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, MatchCreateExpand) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // add three nodes we'll match and expand-create from
   dba.InsertVertex();
@@ -558,16 +567,14 @@ TEST(QueryPlan, MatchCreateExpand) {
   //  memgraph::storage::LabelId label_node_2 = dba.NameToLabel("Node2");
   //  memgraph::storage::PropertyId property = dba.NameToLabel("prop");
   memgraph::storage::EdgeTypeId edge_type = dba.NameToEdgeType("edge_type");
-
   SymbolTable symbol_table;
-  AstStorage storage;
 
   auto test_create_path = [&](bool cycle, int expected_nodes_created, int expected_edges_created) {
     int before_v = CountIterable(dba.Vertices(memgraph::storage::View::OLD));
     int before_e = CountEdges(&dba, memgraph::storage::View::OLD);
 
     // data for the first node
-    auto n_scan_all = MakeScanAll(storage, symbol_table, "n");
+    auto n_scan_all = MakeScanAll(this->storage, symbol_table, "n");
 
     // data for the second node
     NodeCreationInfo m;
@@ -579,7 +586,7 @@ TEST(QueryPlan, MatchCreateExpand) {
     r.edge_type = edge_type;
 
     auto create_expand = std::make_shared<CreateExpand>(m, r, n_scan_all.op_, n_scan_all.sym_, cycle);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     PullAll(*create_expand, &context);
     dba.AdvanceCommand();
 
@@ -592,12 +599,11 @@ TEST(QueryPlan, MatchCreateExpand) {
 }
 
 #ifdef MG_ENTERPRISE
-class MatchCreateExpandWithAuthFixture : public testing::Test {
+template <typename StorageType>
+class MatchCreateExpandWithAuthFixture : public QueryPlanTest<StorageType> {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
-  AstStorage storage;
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{this->db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
   SymbolTable symbol_table;
 
   void SetUp() override { memgraph::license::global_license_checker.EnableTesting(); }
@@ -616,7 +622,7 @@ class MatchCreateExpandWithAuthFixture : public testing::Test {
 
   void ExecuteMatchCreateExpand(memgraph::auth::User &user, bool cycle) {
     // data for the first node
-    auto n_scan_all = MakeScanAll(storage, symbol_table, "n");
+    auto n_scan_all = MakeScanAll(this->storage, symbol_table, "n");
 
     // data for the second node
     NodeCreationInfo m;
@@ -632,7 +638,7 @@ class MatchCreateExpandWithAuthFixture : public testing::Test {
 
     auto create_expand = std::make_shared<CreateExpand>(m, r, n_scan_all.op_, n_scan_all.sym_, cycle);
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
     PullAll(*create_expand, &context);
     dba.AdvanceCommand();
   }
@@ -650,36 +656,38 @@ class MatchCreateExpandWithAuthFixture : public testing::Test {
   }
 };
 
-TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedEverything) {
+TYPED_TEST_CASE(MatchCreateExpandWithAuthFixture, StorageTypes);
+
+TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedEverything) {
   // All labels denied, All edge types denied
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
   user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
-  ASSERT_THROW(ExecuteMatchCreateExpandTestSuite(false, 0, 0, user), QueryRuntimeException);
-  ASSERT_THROW(ExecuteMatchCreateExpandTestSuite(true, 0, 0, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(false, 0, 0, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(true, 0, 0, user), QueryRuntimeException);
 }
 
-TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedEdgeTypes) {
+TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedEdgeTypes) {
   // All labels granted, All edge types denied
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*",
                                                                memgraph::auth::FineGrainedPermission::CREATE_DELETE);
   user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
-  ASSERT_THROW(ExecuteMatchCreateExpandTestSuite(false, 0, 0, user), QueryRuntimeException);
-  ASSERT_THROW(ExecuteMatchCreateExpandTestSuite(true, 0, 0, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(false, 0, 0, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(true, 0, 0, user), QueryRuntimeException);
 }
 
-TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedLabels) {
+TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedLabels) {
   // All labels denied, All edge types granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
-  ASSERT_THROW(ExecuteMatchCreateExpandTestSuite(false, 0, 0, user), QueryRuntimeException);
-  ASSERT_THROW(ExecuteMatchCreateExpandTestSuite(true, 0, 0, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(false, 0, 0, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(true, 0, 0, user), QueryRuntimeException);
 }
 
-TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedOneLabel) {
+TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedOneLabel) {
   // First two label granted, All edge types granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("l1", memgraph::auth::FineGrainedPermission::UPDATE);
@@ -689,11 +697,11 @@ TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandThrowsWhenDeniedOneLab
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-  ASSERT_THROW(ExecuteMatchCreateExpandTestSuite(false, 0, 0, user), QueryRuntimeException);
-  ASSERT_THROW(ExecuteMatchCreateExpandTestSuite(true, 0, 0, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(false, 0, 0, user), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteMatchCreateExpandTestSuite(true, 0, 0, user), QueryRuntimeException);
 }
 
-TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithoutCycleExecutesWhenGrantedSpecificallyEverything) {
+TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithoutCycleExecutesWhenGrantedSpecificallyEverything) {
   // All label granted, Specific edge type granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*",
@@ -701,10 +709,10 @@ TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithoutCycleExecutesWh
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "edge_type", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-  ExecuteMatchCreateExpandTestSuite(false, 6, 3, user);
+  this->ExecuteMatchCreateExpandTestSuite(false, 6, 3, user);
 }
 
-TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithCycleExecutesWhenGrantedSpecificallyEverything) {
+TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithCycleExecutesWhenGrantedSpecificallyEverything) {
   // All label granted, Specific edge type granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*",
@@ -712,10 +720,10 @@ TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithCycleExecutesWhenG
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "edge_type", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-  ExecuteMatchCreateExpandTestSuite(true, 3, 3, user);
+  this->ExecuteMatchCreateExpandTestSuite(true, 3, 3, user);
 }
 
-TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithoutCycleExecutesWhenGrantedEverything) {
+TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithoutCycleExecutesWhenGrantedEverything) {
   // All labels granted, All edge types granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*",
@@ -723,10 +731,10 @@ TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithoutCycleExecutesWh
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-  ExecuteMatchCreateExpandTestSuite(false, 6, 3, user);
+  this->ExecuteMatchCreateExpandTestSuite(false, 6, 3, user);
 }
 
-TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithCycleExecutesWhenGrantedEverything) {
+TYPED_TEST(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithCycleExecutesWhenGrantedEverything) {
   // All labels granted, All edge types granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*",
@@ -734,14 +742,13 @@ TEST_F(MatchCreateExpandWithAuthFixture, MatchCreateExpandWithCycleExecutesWhenG
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-  ExecuteMatchCreateExpandTestSuite(true, 3, 3, user);
+  this->ExecuteMatchCreateExpandTestSuite(true, 3, 3, user);
 }
 #endif
 
-TEST(QueryPlan, Delete) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, Delete) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // make a fully-connected (one-direction, no cycles) with 4 nodes
   std::vector<memgraph::query::VertexAccessor> vertices;
@@ -754,15 +761,13 @@ TEST(QueryPlan, Delete) {
   EXPECT_EQ(4, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
   EXPECT_EQ(6, CountEdges(&dba, memgraph::storage::View::OLD));
 
-  AstStorage storage;
   SymbolTable symbol_table;
-
   // attempt to delete a vertex, and fail
   {
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
     auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_THROW(PullAll(*delete_op, &context), QueryRuntimeException);
     dba.AdvanceCommand();
     EXPECT_EQ(4, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
@@ -771,11 +776,11 @@ TEST(QueryPlan, Delete) {
 
   // detach delete a single vertex
   {
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
     auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, true);
     Frame frame(symbol_table.max_position());
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     delete_op->MakeCursor(memgraph::utils::NewDeleteResource())->Pull(frame, context);
     dba.AdvanceCommand();
     EXPECT_EQ(3, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
@@ -784,12 +789,12 @@ TEST(QueryPlan, Delete) {
 
   // delete all remaining edges
   {
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
                           memgraph::storage::View::NEW);
-    auto r_get = storage.Create<Identifier>("r")->MapTo(r_m.edge_sym_);
+    auto r_get = this->storage.template Create<Identifier>("r")->MapTo(r_m.edge_sym_);
     auto delete_op = std::make_shared<plan::Delete>(r_m.op_, std::vector<Expression *>{r_get}, false);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     PullAll(*delete_op, &context);
     dba.AdvanceCommand();
     EXPECT_EQ(3, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
@@ -798,10 +803,10 @@ TEST(QueryPlan, Delete) {
 
   // delete all remaining vertices
   {
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
     auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     PullAll(*delete_op, &context);
     dba.AdvanceCommand();
     EXPECT_EQ(0, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
@@ -810,12 +815,11 @@ TEST(QueryPlan, Delete) {
 }
 
 #ifdef MG_ENTERPRISE
-class DeleteOperatorWithAuthFixture : public testing::Test {
+template <typename StorageType>
+class DeleteOperatorWithAuthFixture : public QueryPlanTest<StorageType> {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
-  AstStorage storage;
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{this->db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
   SymbolTable symbol_table;
 
   void SetUp() override { memgraph::license::global_license_checker.EnableTesting(); }
@@ -844,11 +848,11 @@ class DeleteOperatorWithAuthFixture : public testing::Test {
   };
 
   void DeleteAllNodes(memgraph::auth::User &user) {
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
     Frame frame(symbol_table.max_position());
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
     auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, true);
     PullAll(*delete_op, &context);
     dba.AdvanceCommand();
@@ -866,13 +870,13 @@ class DeleteOperatorWithAuthFixture : public testing::Test {
   };
 
   void DeleteAllEdges(memgraph::auth::User &user) {
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
                           memgraph::storage::View::NEW);
-    auto r_get = storage.Create<Identifier>("r")->MapTo(r_m.edge_sym_);
+    auto r_get = this->storage.template Create<Identifier>("r")->MapTo(r_m.edge_sym_);
     auto delete_op = std::make_shared<plan::Delete>(r_m.op_, std::vector<Expression *>{r_get}, false);
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
     PullAll(*delete_op, &context);
     dba.AdvanceCommand();
   };
@@ -891,39 +895,41 @@ class DeleteOperatorWithAuthFixture : public testing::Test {
   }
 };
 
-TEST_F(DeleteOperatorWithAuthFixture, DeleteNodeThrowsExceptionWhenAllLabelsDenied) {
+TYPED_TEST_CASE(DeleteOperatorWithAuthFixture, StorageTypes);
+
+TYPED_TEST(DeleteOperatorWithAuthFixture, DeleteNodeThrowsExceptionWhenAllLabelsDenied) {
   // All labels denied
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
 
-  ASSERT_THROW(ExecuteDeleteNodesTestSuite(user, 0), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteDeleteNodesTestSuite(user, 0), QueryRuntimeException);
 }
 
-TEST_F(DeleteOperatorWithAuthFixture, DeleteNodeThrowsExceptionWhenPartialLabelsGranted) {
+TYPED_TEST(DeleteOperatorWithAuthFixture, DeleteNodeThrowsExceptionWhenPartialLabelsGranted) {
   // One Label granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("l1",
                                                                memgraph::auth::FineGrainedPermission::CREATE_DELETE);
   user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
-  ASSERT_THROW(ExecuteDeleteNodesTestSuite(user, 0), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteDeleteNodesTestSuite(user, 0), QueryRuntimeException);
 }
 
-TEST_F(DeleteOperatorWithAuthFixture, DeleteNodeExecutesWhenGrantedAllLabels) {
+TYPED_TEST(DeleteOperatorWithAuthFixture, DeleteNodeExecutesWhenGrantedAllLabels) {
   // All labels granted
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*",
                                                                memgraph::auth::FineGrainedPermission::CREATE_DELETE);
-  ExecuteDeleteNodesTestSuite(user, 0);
+  this->ExecuteDeleteNodesTestSuite(user, 0);
 }
-TEST_F(DeleteOperatorWithAuthFixture, DeleteNodeThrowsExceptionWhenEdgeTypesNotGranted) {
+TYPED_TEST(DeleteOperatorWithAuthFixture, DeleteNodeThrowsExceptionWhenEdgeTypesNotGranted) {
   // All labels granted,All edge types denied
   memgraph::auth::User user{"test"};
   user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
   user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
 
-  ASSERT_THROW(ExecuteDeleteNodesTestSuite(user, 0), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteDeleteNodesTestSuite(user, 0), QueryRuntimeException);
 }
-TEST_F(DeleteOperatorWithAuthFixture, DeleteEdgesThrowsErrorWhenPartialGrant) {
+TYPED_TEST(DeleteOperatorWithAuthFixture, DeleteEdgesThrowsErrorWhenPartialGrant) {
   // Specific label granted, Specific edge types granted
 
   memgraph::auth::User user{"test"};
@@ -940,10 +946,10 @@ TEST_F(DeleteOperatorWithAuthFixture, DeleteEdgesThrowsErrorWhenPartialGrant) {
   user.fine_grained_access_handler().edge_type_permissions().Grant("type3",
                                                                    memgraph::auth::FineGrainedPermission::UPDATE);
 
-  ASSERT_THROW(ExecuteDeleteEdgesTestSuite(user, 0), QueryRuntimeException);
+  ASSERT_THROW(this->ExecuteDeleteEdgesTestSuite(user, 0), QueryRuntimeException);
 }
 
-TEST_F(DeleteOperatorWithAuthFixture, DeleteNodeAndDeleteEdgePerformWhenGranted) {
+TYPED_TEST(DeleteOperatorWithAuthFixture, DeleteNodeAndDeleteEdgePerformWhenGranted) {
   // All labels granted, All edge_types granted
 
   memgraph::auth::User user{"test"};
@@ -952,14 +958,14 @@ TEST_F(DeleteOperatorWithAuthFixture, DeleteNodeAndDeleteEdgePerformWhenGranted)
   user.fine_grained_access_handler().edge_type_permissions().Grant(
       "*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-  InitGraph();
-  DeleteAllNodes(user);
-  TestDeleteNodesHypothesis(0);
-  TestDeleteEdgesHypothesis(0);
+  this->InitGraph();
+  this->DeleteAllNodes(user);
+  this->TestDeleteNodesHypothesis(0);
+  this->TestDeleteEdgesHypothesis(0);
 }
 #endif
 
-TEST(QueryPlan, DeleteTwiceDeleteBlockingEdge) {
+TYPED_TEST(QueryPlanTest, DeleteTwiceDeleteBlockingEdge) {
   // test deleting the same vertex and edge multiple times
   //
   // also test vertex deletion succeeds if the prohibiting
@@ -971,10 +977,9 @@ TEST(QueryPlan, DeleteTwiceDeleteBlockingEdge) {
   // CREATE ()-[:T]->()
   // MATCH (n)-[r]-(m) [DETACH] DELETE n, r, m
 
-  auto test_delete = [](bool detach) {
-    memgraph::storage::Storage db;
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+  auto test_delete = [this](bool detach) {
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
 
     auto v1 = dba.InsertVertex();
     auto v2 = dba.InsertVertex();
@@ -983,20 +988,19 @@ TEST(QueryPlan, DeleteTwiceDeleteBlockingEdge) {
     EXPECT_EQ(2, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
     EXPECT_EQ(1, CountEdges(&dba, memgraph::storage::View::OLD));
 
-    AstStorage storage;
     SymbolTable symbol_table;
 
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m", false,
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m", false,
                           memgraph::storage::View::OLD);
 
     // getter expressions for deletion
-    auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
-    auto r_get = storage.Create<Identifier>("r")->MapTo(r_m.edge_sym_);
-    auto m_get = storage.Create<Identifier>("m")->MapTo(r_m.node_sym_);
+    auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
+    auto r_get = this->storage.template Create<Identifier>("r")->MapTo(r_m.edge_sym_);
+    auto m_get = this->storage.template Create<Identifier>("m")->MapTo(r_m.node_sym_);
 
     auto delete_op = std::make_shared<plan::Delete>(r_m.op_, std::vector<Expression *>{n_get, r_get, m_get}, detach);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_EQ(2, PullAll(*delete_op, &context));
     dba.AdvanceCommand();
     EXPECT_EQ(0, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
@@ -1007,13 +1011,12 @@ TEST(QueryPlan, DeleteTwiceDeleteBlockingEdge) {
   test_delete(false);
 }
 
-TEST(QueryPlan, DeleteReturn) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, DeleteReturn) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // make a fully-connected (one-direction, no cycles) with 4 nodes
-  auto prop = PROPERTY_PAIR("property");
+  auto prop = PROPERTY_PAIR(dba, "property");
   for (int i = 0; i < 4; ++i) {
     auto va = dba.InsertVertex();
     ASSERT_TRUE(va.SetProperty(prop.second, memgraph::storage::PropertyValue(42)).HasValue());
@@ -1023,77 +1026,71 @@ TEST(QueryPlan, DeleteReturn) {
   EXPECT_EQ(4, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
   EXPECT_EQ(0, CountEdges(&dba, memgraph::storage::View::OLD));
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
 
-  auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
+  auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
   auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, true);
 
-  auto prop_lookup = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
-  auto n_p = storage.Create<NamedExpression>("n", prop_lookup)->MapTo(symbol_table.CreateSymbol("bla", true));
+  auto prop_lookup = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
+  auto n_p =
+      this->storage.template Create<NamedExpression>("n", prop_lookup)->MapTo(symbol_table.CreateSymbol("bla", true));
   auto produce = MakeProduce(delete_op, n_p);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   ASSERT_THROW(CollectProduce(*produce, &context), QueryRuntimeException);
 }
 
-TEST(QueryPlan, DeleteNull) {
+TYPED_TEST(QueryPlanTest, DeleteNull) {
   // test (simplified) WITH Null as x delete x
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
 
   auto once = std::make_shared<Once>();
   auto delete_op = std::make_shared<plan::Delete>(once, std::vector<Expression *>{LITERAL(TypedValue())}, false);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(1, PullAll(*delete_op, &context));
 }
 
-TEST(QueryPlan, DeleteAdvance) {
+TYPED_TEST(QueryPlanTest, DeleteAdvance) {
   // test queries on empty DB:
   // CREATE (n)
   // MATCH (n) DELETE n WITH n ...
   // this fails only if the deleted record `n` is actually used in subsequent
   // clauses, which is compatible with Neo's behavior.
-  memgraph::storage::Storage db;
-
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
   auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
   auto advance = std::make_shared<Accumulate>(delete_op, std::vector<Symbol>{n.sym_}, true);
   auto res_sym = symbol_table.CreateSymbol("res", true);
   {
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     dba.InsertVertex();
     dba.AdvanceCommand();
     auto produce = MakeProduce(advance, NEXPR("res", LITERAL(42))->MapTo(res_sym));
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_EQ(1, PullAll(*produce, &context));
   }
   {
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     dba.InsertVertex();
     dba.AdvanceCommand();
-    auto n_prop = PROPERTY_LOOKUP(n_get, dba.NameToProperty("prop"));
+    auto n_prop = PROPERTY_LOOKUP(dba, n_get, dba.NameToProperty("prop"));
     auto produce = MakeProduce(advance, NEXPR("res", n_prop)->MapTo(res_sym));
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_THROW(PullAll(*produce, &context), QueryRuntimeException);
   }
 }
 
-TEST(QueryPlan, SetProperty) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, SetProperty) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // graph with 4 vertices in connected pairs
   // the origin vertex in each par and both edges
@@ -1107,29 +1104,29 @@ TEST(QueryPlan, SetProperty) {
   ASSERT_TRUE(dba.InsertEdge(&v2, &v4, edge_type).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   // scan (n)-[r]->(m)
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
                         memgraph::storage::View::OLD);
 
   // set prop1 to 42 on n and r
   auto prop1 = dba.NameToProperty("prop1");
   auto literal = LITERAL(42);
 
-  auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop1);
+  auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop1);
   auto set_n_p = std::make_shared<plan::SetProperty>(r_m.op_, prop1, n_p, literal);
 
-  auto r_p = PROPERTY_LOOKUP(IDENT("r")->MapTo(r_m.edge_sym_), prop1);
+  auto r_p = PROPERTY_LOOKUP(dba, IDENT("r")->MapTo(r_m.edge_sym_), prop1);
   auto set_r_p = std::make_shared<plan::SetProperty>(set_n_p, prop1, r_p, literal);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(2, PullAll(*set_r_p, &context));
   dba.AdvanceCommand();
 
   EXPECT_EQ(CountEdges(&dba, memgraph::storage::View::OLD), 2);
   for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
+    dba.PrefetchOutEdges(vertex);
     auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
     ASSERT_TRUE(maybe_edges.HasValue());
     for (auto edge : *maybe_edges) {
@@ -1147,11 +1144,10 @@ TEST(QueryPlan, SetProperty) {
   }
 }
 
-TEST(QueryPlan, SetProperties) {
-  auto test_set_properties = [](bool update) {
-    memgraph::storage::Storage db;
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, SetProperties) {
+  auto test_set_properties = [this](bool update) {
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
 
     // graph: ({a: 0})-[:R {b:1}]->({c:2})
     auto prop_a = dba.NameToProperty("a");
@@ -1165,12 +1161,11 @@ TEST(QueryPlan, SetProperties) {
     ASSERT_TRUE(v2.SetProperty(prop_c, memgraph::storage::PropertyValue(2)).HasValue());
     dba.AdvanceCommand();
 
-    AstStorage storage;
     SymbolTable symbol_table;
 
     // scan (n)-[r]->(m)
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
                           memgraph::storage::View::OLD);
 
     auto op = update ? plan::SetProperties::Op::UPDATE : plan::SetProperties::Op::REPLACE;
@@ -1180,12 +1175,13 @@ TEST(QueryPlan, SetProperties) {
     auto m_ident = IDENT("m")->MapTo(r_m.node_sym_);
     auto set_r_to_n = std::make_shared<plan::SetProperties>(r_m.op_, n.sym_, r_ident, op);
     auto set_m_to_r = std::make_shared<plan::SetProperties>(set_r_to_n, r_m.edge_sym_, m_ident, op);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_EQ(1, PullAll(*set_m_to_r, &context));
     dba.AdvanceCommand();
 
     EXPECT_EQ(CountEdges(&dba, memgraph::storage::View::OLD), 1);
     for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
+      dba.PrefetchOutEdges(vertex);
       auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
       ASSERT_TRUE(maybe_edges.HasValue());
       for (auto edge : *maybe_edges) {
@@ -1223,10 +1219,9 @@ TEST(QueryPlan, SetProperties) {
   test_set_properties(false);
 }
 
-TEST(QueryPlan, SetLabels) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, SetLabels) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto label1 = dba.NameToLabel("label1");
   auto label2 = dba.NameToLabel("label2");
@@ -1235,13 +1230,12 @@ TEST(QueryPlan, SetLabels) {
   ASSERT_TRUE(dba.InsertVertex().AddLabel(label1).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
   auto label_set =
       std::make_shared<plan::SetLabels>(n.op_, n.sym_, std::vector<memgraph::storage::LabelId>{label2, label3});
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(2, PullAll(*label_set, &context));
 
   for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
@@ -1252,7 +1246,7 @@ TEST(QueryPlan, SetLabels) {
 }
 
 #ifdef MG_ENTERPRISE
-TEST(QueryPlan, SetLabelsWithFineGrained) {
+TYPED_TEST(QueryPlanTest, SetLabelsWithFineGrained) {
   memgraph::license::global_license_checker.EnableTesting();
   auto set_labels = [&](memgraph::auth::User user, memgraph::query::DbAccessor dba,
                         std::vector<memgraph::storage::LabelId> labels) {
@@ -1260,14 +1254,13 @@ TEST(QueryPlan, SetLabelsWithFineGrained) {
     ASSERT_TRUE(dba.InsertVertex().AddLabel(labels[0]).HasValue());
     dba.AdvanceCommand();
 
-    AstStorage storage;
     SymbolTable symbol_table;
 
-    auto n = MakeScanAll(storage, symbol_table, "n");
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
     auto label_set =
         std::make_shared<plan::SetLabels>(n.op_, n.sym_, std::vector<memgraph::storage::LabelId>{labels[1], labels[2]});
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
 
     PullAll(*label_set, &context);
   };
@@ -1277,9 +1270,8 @@ TEST(QueryPlan, SetLabelsWithFineGrained) {
     memgraph::auth::User user{"test"};
     user.fine_grained_access_handler().label_permissions().Grant("*",
                                                                  memgraph::auth::FineGrainedPermission::CREATE_DELETE);
-    memgraph::storage::Storage db;
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto label1 = dba.NameToLabel("label1");
     auto label2 = dba.NameToLabel("label2");
     auto label3 = dba.NameToLabel("label3");
@@ -1295,9 +1287,8 @@ TEST(QueryPlan, SetLabelsWithFineGrained) {
   {
     memgraph::auth::User user{"test"};
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
-    memgraph::storage::Storage db;
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto label1 = dba.NameToLabel("label1");
     auto label2 = dba.NameToLabel("label2");
     auto label3 = dba.NameToLabel("label3");
@@ -1315,9 +1306,8 @@ TEST(QueryPlan, SetLabelsWithFineGrained) {
     user.fine_grained_access_handler().label_permissions().Grant("label3",
                                                                  memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-    memgraph::storage::Storage db;
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto label1 = dba.NameToLabel("label1");
     auto label2 = dba.NameToLabel("label2");
     auto label3 = dba.NameToLabel("label3");
@@ -1327,10 +1317,9 @@ TEST(QueryPlan, SetLabelsWithFineGrained) {
 }
 #endif
 
-TEST(QueryPlan, RemoveProperty) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, RemoveProperty) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // graph with 4 vertices in connected pairs
   // the origin vertex in each par and both edges
@@ -1355,25 +1344,25 @@ TEST(QueryPlan, RemoveProperty) {
   ASSERT_TRUE(v2.SetProperty(prop2, memgraph::storage::PropertyValue(0)).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   // scan (n)-[r]->(m)
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
                         memgraph::storage::View::OLD);
 
-  auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop1);
+  auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop1);
   auto set_n_p = std::make_shared<plan::RemoveProperty>(r_m.op_, prop1, n_p);
 
-  auto r_p = PROPERTY_LOOKUP(IDENT("r")->MapTo(r_m.edge_sym_), prop1);
+  auto r_p = PROPERTY_LOOKUP(dba, IDENT("r")->MapTo(r_m.edge_sym_), prop1);
   auto set_r_p = std::make_shared<plan::RemoveProperty>(set_n_p, prop1, r_p);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(2, PullAll(*set_r_p, &context));
   dba.AdvanceCommand();
 
   EXPECT_EQ(CountEdges(&dba, memgraph::storage::View::OLD), 2);
   for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
+    dba.PrefetchOutEdges(vertex);
     auto maybe_edges = vertex.OutEdges(memgraph::storage::View::OLD);
     ASSERT_TRUE(maybe_edges.HasValue());
     for (auto edge : *maybe_edges) {
@@ -1391,10 +1380,9 @@ TEST(QueryPlan, RemoveProperty) {
   }
 }
 
-TEST(QueryPlan, RemoveLabels) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, RemoveLabels) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto label1 = dba.NameToLabel("label1");
   auto label2 = dba.NameToLabel("label2");
@@ -1408,13 +1396,12 @@ TEST(QueryPlan, RemoveLabels) {
   ASSERT_TRUE(v2.AddLabel(label3).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
   auto label_remove =
       std::make_shared<plan::RemoveLabels>(n.op_, n.sym_, std::vector<memgraph::storage::LabelId>{label1, label2});
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(2, PullAll(*label_remove, &context));
 
   for (auto vertex : dba.Vertices(memgraph::storage::View::OLD)) {
@@ -1425,7 +1412,7 @@ TEST(QueryPlan, RemoveLabels) {
 }
 
 #ifdef MG_ENTERPRISE
-TEST(QueryPlan, RemoveLabelsFineGrainedFiltering) {
+TYPED_TEST(QueryPlanTest, RemoveLabelsFineGrainedFiltering) {
   memgraph::license::global_license_checker.EnableTesting();
   auto remove_labels = [&](memgraph::auth::User user, memgraph::query::DbAccessor dba,
                            std::vector<memgraph::storage::LabelId> labels) {
@@ -1438,15 +1425,14 @@ TEST(QueryPlan, RemoveLabelsFineGrainedFiltering) {
     ASSERT_TRUE(v2.AddLabel(labels[2]).HasValue());
     dba.AdvanceCommand();
 
-    AstStorage storage;
     SymbolTable symbol_table;
 
-    auto n = MakeScanAll(storage, symbol_table, "n");
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
     auto label_remove = std::make_shared<plan::RemoveLabels>(
         n.op_, n.sym_, std::vector<memgraph::storage::LabelId>{labels[0], labels[1]});
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
 
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
 
     PullAll(*label_remove, &context);
   };
@@ -1456,9 +1442,8 @@ TEST(QueryPlan, RemoveLabelsFineGrainedFiltering) {
     memgraph::auth::User user{"test"};
     user.fine_grained_access_handler().label_permissions().Grant("*",
                                                                  memgraph::auth::FineGrainedPermission::CREATE_DELETE);
-    memgraph::storage::Storage db;
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto label1 = dba.NameToLabel("label1");
     auto label2 = dba.NameToLabel("label2");
     auto label3 = dba.NameToLabel("label3");
@@ -1474,9 +1459,8 @@ TEST(QueryPlan, RemoveLabelsFineGrainedFiltering) {
   {
     memgraph::auth::User user{"test"};
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
-    memgraph::storage::Storage db;
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto label1 = dba.NameToLabel("label1");
     auto label2 = dba.NameToLabel("label2");
     auto label3 = dba.NameToLabel("label3");
@@ -1494,9 +1478,8 @@ TEST(QueryPlan, RemoveLabelsFineGrainedFiltering) {
     user.fine_grained_access_handler().label_permissions().Grant("label3",
                                                                  memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-    memgraph::storage::Storage db;
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto label1 = dba.NameToLabel("label1");
     auto label2 = dba.NameToLabel("label2");
     auto label3 = dba.NameToLabel("label3");
@@ -1506,13 +1489,12 @@ TEST(QueryPlan, RemoveLabelsFineGrainedFiltering) {
 }
 #endif
 
-TEST(QueryPlan, NodeFilterSet) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, NodeFilterSet) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Create a graph such that (v1 {prop: 42}) is connected to v2 and v3.
   auto v1 = dba.InsertVertex();
-  auto prop = PROPERTY_PAIR("property");
+  auto prop = PROPERTY_PAIR(dba, "property");
   ASSERT_TRUE(v1.SetProperty(prop.second, memgraph::storage::PropertyValue(42)).HasValue());
   auto v2 = dba.InsertVertex();
   auto v3 = dba.InsertVertex();
@@ -1523,21 +1505,21 @@ TEST(QueryPlan, NodeFilterSet) {
   // Create operations which match (v1 {prop: 42}) -- (v) and increment the
   // v1.prop. The expected result is two incremenentations, since v1 is matched
   // twice for 2 edges it has.
-  AstStorage storage;
   SymbolTable symbol_table;
   // MATCH (n {prop: 42}) -[r]- (m)
-  auto scan_all = MakeScanAll(storage, symbol_table, "n");
-  std::get<0>(scan_all.node_->properties_)[storage.GetPropertyIx(prop.first)] = LITERAL(42);
-  auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m",
-                           false, memgraph::storage::View::OLD);
-  auto *filter_expr =
-      EQ(storage.Create<PropertyLookup>(scan_all.node_->identifier_, storage.GetPropertyIx(prop.first)), LITERAL(42));
+  auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
+  std::get<0>(scan_all.node_->properties_)[this->storage.GetPropertyIx(prop.first)] = LITERAL(42);
+  auto expand = MakeExpand(this->storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::BOTH, {},
+                           "m", false, memgraph::storage::View::OLD);
+  auto *filter_expr = EQ(this->storage.template Create<PropertyLookup>(scan_all.node_->identifier_,
+                                                                       this->storage.GetPropertyIx(prop.first)),
+                         LITERAL(42));
   auto node_filter = std::make_shared<Filter>(expand.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
   // SET n.prop = n.prop + 1
-  auto set_prop = PROPERTY_LOOKUP(IDENT("n")->MapTo(scan_all.sym_), prop);
+  auto set_prop = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(scan_all.sym_), prop);
   auto add = ADD(set_prop, LITERAL(1));
   auto set = std::make_shared<plan::SetProperty>(node_filter, prop.second, set_prop, add);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(2, PullAll(*set, &context));
   dba.AdvanceCommand();
   auto prop_eq = TypedValue(*v1.GetProperty(memgraph::storage::View::OLD, prop.second)) == TypedValue(42 + 2);
@@ -1545,13 +1527,12 @@ TEST(QueryPlan, NodeFilterSet) {
   EXPECT_TRUE(prop_eq.ValueBool());
 }
 
-TEST(QueryPlan, FilterRemove) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, FilterRemove) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Create a graph such that (v1 {prop: 42}) is connected to v2 and v3.
   auto v1 = dba.InsertVertex();
-  auto prop = PROPERTY_PAIR("property");
+  auto prop = PROPERTY_PAIR(dba, "property");
   ASSERT_TRUE(v1.SetProperty(prop.second, memgraph::storage::PropertyValue(42)).HasValue());
   auto v2 = dba.InsertVertex();
   auto v3 = dba.InsertVertex();
@@ -1561,85 +1542,80 @@ TEST(QueryPlan, FilterRemove) {
   dba.AdvanceCommand();
   // Create operations which match (v1 {prop: 42}) -- (v) and remove v1.prop.
   // The expected result is two matches, for each edge of v1.
-  AstStorage storage;
   SymbolTable symbol_table;
   // MATCH (n) -[r]- (m) WHERE n.prop < 43
-  auto scan_all = MakeScanAll(storage, symbol_table, "n");
-  std::get<0>(scan_all.node_->properties_)[storage.GetPropertyIx(prop.first)] = LITERAL(42);
-  auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::BOTH, {}, "m",
-                           false, memgraph::storage::View::OLD);
-  auto filter_prop = PROPERTY_LOOKUP(IDENT("n")->MapTo(scan_all.sym_), prop);
+  auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
+  std::get<0>(scan_all.node_->properties_)[this->storage.GetPropertyIx(prop.first)] = LITERAL(42);
+  auto expand = MakeExpand(this->storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::BOTH, {},
+                           "m", false, memgraph::storage::View::OLD);
+  auto filter_prop = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(scan_all.sym_), prop);
   auto filter = std::make_shared<Filter>(expand.op_, std::vector<std::shared_ptr<LogicalOperator>>{},
                                          LESS(filter_prop, LITERAL(43)));
   // REMOVE n.prop
-  auto rem_prop = PROPERTY_LOOKUP(IDENT("n")->MapTo(scan_all.sym_), prop);
+  auto rem_prop = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(scan_all.sym_), prop);
   auto rem = std::make_shared<plan::RemoveProperty>(filter, prop.second, rem_prop);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(2, PullAll(*rem, &context));
   dba.AdvanceCommand();
   EXPECT_EQ(v1.GetProperty(memgraph::storage::View::OLD, prop.second)->type(),
             memgraph::storage::PropertyValue::Type::Null);
 }
 
-TEST(QueryPlan, SetRemove) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, SetRemove) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto v = dba.InsertVertex();
   auto label1 = dba.NameToLabel("label1");
   auto label2 = dba.NameToLabel("label2");
   dba.AdvanceCommand();
   // Create operations which match (v) and set and remove v :label.
   // The expected result is single (v) as it was at the start.
-  AstStorage storage;
   SymbolTable symbol_table;
   // MATCH (n) SET n :label1 :label2 REMOVE n :label1 :label2
-  auto scan_all = MakeScanAll(storage, symbol_table, "n");
+  auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
   auto set = std::make_shared<plan::SetLabels>(scan_all.op_, scan_all.sym_,
                                                std::vector<memgraph::storage::LabelId>{label1, label2});
   auto rem =
       std::make_shared<plan::RemoveLabels>(set, scan_all.sym_, std::vector<memgraph::storage::LabelId>{label1, label2});
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(1, PullAll(*rem, &context));
   dba.AdvanceCommand();
   EXPECT_FALSE(*v.HasLabel(memgraph::storage::View::OLD, label1));
   EXPECT_FALSE(*v.HasLabel(memgraph::storage::View::OLD, label2));
 }
 
-TEST(QueryPlan, Merge) {
+TYPED_TEST(QueryPlanTest, Merge) {
   // test setup:
   //  - three nodes, two of them connected with T
   //  - merge input branch matches all nodes
   //  - merge_match branch looks for an expansion (any direction)
   //    and sets some property (for result validation)
   //  - merge_create branch just sets some other property
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
   ASSERT_TRUE(dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("Type")).HasValue());
   auto v3 = dba.InsertVertex();
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto prop = PROPERTY_PAIR("property");
-  auto n = MakeScanAll(storage, symbol_table, "n");
+  auto prop = PROPERTY_PAIR(dba, "property");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
 
   // merge_match branch
-  auto r_m = MakeExpand(storage, symbol_table, std::make_shared<Once>(), n.sym_, "r", EdgeAtom::Direction::BOTH, {},
-                        "m", false, memgraph::storage::View::OLD);
-  auto m_p = PROPERTY_LOOKUP(IDENT("m")->MapTo(r_m.node_sym_), prop);
+  auto r_m = MakeExpand(this->storage, symbol_table, std::make_shared<Once>(), n.sym_, "r", EdgeAtom::Direction::BOTH,
+                        {}, "m", false, memgraph::storage::View::OLD);
+  auto m_p = PROPERTY_LOOKUP(dba, IDENT("m")->MapTo(r_m.node_sym_), prop);
   auto m_set = std::make_shared<plan::SetProperty>(r_m.op_, prop.second, m_p, LITERAL(1));
 
   // merge_create branch
-  auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
+  auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
   auto n_set = std::make_shared<plan::SetProperty>(std::make_shared<Once>(), prop.second, n_p, LITERAL(2));
 
   auto merge = std::make_shared<plan::Merge>(n.op_, m_set, n_set);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   ASSERT_EQ(3, PullAll(*merge, &context));
   dba.AdvanceCommand();
 
@@ -1654,13 +1630,11 @@ TEST(QueryPlan, Merge) {
   ASSERT_EQ(v3.GetProperty(memgraph::storage::View::OLD, prop.second)->ValueInt(), 2);
 }
 
-TEST(QueryPlan, MergeNoInput) {
+TYPED_TEST(QueryPlanTest, MergeNoInput) {
   // merge with no input, creates a single node
 
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
 
   NodeCreationInfo node;
@@ -1669,144 +1643,129 @@ TEST(QueryPlan, MergeNoInput) {
   auto merge = std::make_shared<plan::Merge>(nullptr, create, create);
 
   EXPECT_EQ(0, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(1, PullAll(*merge, &context));
   dba.AdvanceCommand();
   EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
 }
 
-TEST(QueryPlan, SetPropertyOnNull) {
+TYPED_TEST(QueryPlanTest, SetPropertyOnNull) {
   // SET (Null).prop = 42
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
-  auto prop = PROPERTY_PAIR("property");
+  auto prop = PROPERTY_PAIR(dba, "property");
   auto null = LITERAL(TypedValue());
   auto literal = LITERAL(42);
-  auto n_prop = PROPERTY_LOOKUP(null, prop);
+  auto n_prop = PROPERTY_LOOKUP(dba, null, prop);
   auto once = std::make_shared<Once>();
   auto set_op = std::make_shared<plan::SetProperty>(once, prop.second, n_prop, literal);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(1, PullAll(*set_op, &context));
 }
 
-TEST(QueryPlan, SetPropertiesOnNull) {
+TYPED_TEST(QueryPlanTest, SetPropertiesOnNull) {
   // OPTIONAL MATCH (n) SET n = n
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
-  auto n = MakeScanAll(storage, symbol_table, "n");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
   auto n_ident = IDENT("n")->MapTo(n.sym_);
   auto optional = std::make_shared<plan::Optional>(nullptr, n.op_, std::vector<Symbol>{n.sym_});
   auto set_op = std::make_shared<plan::SetProperties>(optional, n.sym_, n_ident, plan::SetProperties::Op::REPLACE);
   EXPECT_EQ(0, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(1, PullAll(*set_op, &context));
 }
 
-TEST(QueryPlan, SetLabelsOnNull) {
+TYPED_TEST(QueryPlanTest, SetLabelsOnNull) {
   // OPTIONAL MATCH (n) SET n :label
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto label = dba.NameToLabel("label");
-  AstStorage storage;
   SymbolTable symbol_table;
-  auto n = MakeScanAll(storage, symbol_table, "n");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
   auto optional = std::make_shared<plan::Optional>(nullptr, n.op_, std::vector<Symbol>{n.sym_});
   auto set_op = std::make_shared<plan::SetLabels>(optional, n.sym_, std::vector<memgraph::storage::LabelId>{label});
   EXPECT_EQ(0, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(1, PullAll(*set_op, &context));
 }
 
-TEST(QueryPlan, RemovePropertyOnNull) {
+TYPED_TEST(QueryPlanTest, RemovePropertyOnNull) {
   // REMOVE (Null).prop
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
-  auto prop = PROPERTY_PAIR("property");
+  auto prop = PROPERTY_PAIR(dba, "property");
   auto null = LITERAL(TypedValue());
-  auto n_prop = PROPERTY_LOOKUP(null, prop);
+  auto n_prop = PROPERTY_LOOKUP(dba, null, prop);
   auto once = std::make_shared<Once>();
   auto remove_op = std::make_shared<plan::RemoveProperty>(once, prop.second, n_prop);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(1, PullAll(*remove_op, &context));
 }
 
-TEST(QueryPlan, RemoveLabelsOnNull) {
+TYPED_TEST(QueryPlanTest, RemoveLabelsOnNull) {
   // OPTIONAL MATCH (n) REMOVE n :label
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto label = dba.NameToLabel("label");
-  AstStorage storage;
   SymbolTable symbol_table;
-  auto n = MakeScanAll(storage, symbol_table, "n");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
   auto optional = std::make_shared<plan::Optional>(nullptr, n.op_, std::vector<Symbol>{n.sym_});
   auto remove_op =
       std::make_shared<plan::RemoveLabels>(optional, n.sym_, std::vector<memgraph::storage::LabelId>{label});
   EXPECT_EQ(0, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(1, PullAll(*remove_op, &context));
 }
 
-TEST(QueryPlan, DeleteSetProperty) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, DeleteSetProperty) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Add a single vertex.
   dba.InsertVertex();
   dba.AdvanceCommand();
   EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
-  AstStorage storage;
   SymbolTable symbol_table;
   // MATCH (n) DELETE n SET n.property = 42
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
   auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
-  auto prop = PROPERTY_PAIR("property");
-  auto n_prop = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
+  auto prop = PROPERTY_PAIR(dba, "property");
+  auto n_prop = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
   auto set_op = std::make_shared<plan::SetProperty>(delete_op, prop.second, n_prop, LITERAL(42));
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_THROW(PullAll(*set_op, &context), QueryRuntimeException);
 }
 
-TEST(QueryPlan, DeleteSetPropertiesFromMap) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, DeleteSetPropertiesFromMap) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Add a single vertex.
   dba.InsertVertex();
   dba.AdvanceCommand();
   EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
-  AstStorage storage;
   SymbolTable symbol_table;
   // MATCH (n) DELETE n SET n = {property: 42}
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
   auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
-  auto prop = PROPERTY_PAIR("property");
+  auto prop = PROPERTY_PAIR(dba, "property");
   std::unordered_map<PropertyIx, Expression *> prop_map;
-  prop_map.emplace(storage.GetPropertyIx(prop.first), LITERAL(42));
-  auto *rhs = storage.Create<MapLiteral>(prop_map);
+  prop_map.emplace(this->storage.GetPropertyIx(prop.first), LITERAL(42));
+  auto *rhs = this->storage.template Create<MapLiteral>(prop_map);
   for (auto op_type : {plan::SetProperties::Op::REPLACE, plan::SetProperties::Op::UPDATE}) {
     auto set_op = std::make_shared<plan::SetProperties>(delete_op, n.sym_, rhs, op_type);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_THROW(PullAll(*set_op, &context), QueryRuntimeException);
   }
 }
 
-TEST(QueryPlan, DeleteSetPropertiesFrom) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, DeleteSetPropertiesFrom) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Add a single vertex.
   {
     auto v = dba.InsertVertex();
@@ -1814,58 +1773,53 @@ TEST(QueryPlan, DeleteSetPropertiesFrom) {
   }
   dba.AdvanceCommand();
   EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
-  AstStorage storage;
   SymbolTable symbol_table;
   // MATCH (n) DELETE n SET n = n
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
   auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
   auto *rhs = IDENT("n")->MapTo(n.sym_);
   for (auto op_type : {plan::SetProperties::Op::REPLACE, plan::SetProperties::Op::UPDATE}) {
     auto set_op = std::make_shared<plan::SetProperties>(delete_op, n.sym_, rhs, op_type);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_THROW(PullAll(*set_op, &context), QueryRuntimeException);
   }
 }
 
-TEST(QueryPlan, DeleteRemoveLabels) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, DeleteRemoveLabels) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Add a single vertex.
   dba.InsertVertex();
   dba.AdvanceCommand();
   EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
-  AstStorage storage;
   SymbolTable symbol_table;
   // MATCH (n) DELETE n REMOVE n :label
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
   auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
   std::vector<memgraph::storage::LabelId> labels{dba.NameToLabel("label")};
   auto rem_op = std::make_shared<plan::RemoveLabels>(delete_op, n.sym_, labels);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_THROW(PullAll(*rem_op, &context), QueryRuntimeException);
 }
 
-TEST(QueryPlan, DeleteRemoveProperty) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlanTest, DeleteRemoveProperty) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Add a single vertex.
   dba.InsertVertex();
   dba.AdvanceCommand();
   EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
-  AstStorage storage;
   SymbolTable symbol_table;
   // MATCH (n) DELETE n REMOVE n.property
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto n_get = storage.Create<Identifier>("n")->MapTo(n.sym_);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto n_get = this->storage.template Create<Identifier>("n")->MapTo(n.sym_);
   auto delete_op = std::make_shared<plan::Delete>(n.op_, std::vector<Expression *>{n_get}, false);
-  auto prop = PROPERTY_PAIR("property");
-  auto n_prop = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), prop);
+  auto prop = PROPERTY_PAIR(dba, "property");
+  auto n_prop = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), prop);
   auto rem_op = std::make_shared<plan::RemoveProperty>(delete_op, prop.second, n_prop);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_THROW(PullAll(*rem_op, &context), QueryRuntimeException);
 }
 
@@ -1873,12 +1827,11 @@ TEST(QueryPlan, DeleteRemoveProperty) {
 ////     FINE GRAINED AUTHORIZATION      /////
 //////////////////////////////////////////////
 #ifdef MG_ENTERPRISE
-class UpdatePropertiesWithAuthFixture : public testing::Test {
+template <typename StorageType>
+class UpdatePropertiesWithAuthFixture : public QueryPlanTest<StorageType> {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
-  AstStorage storage;
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{this->db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
   SymbolTable symbol_table;
 
   const std::string vertex_label_name = "l1";
@@ -1909,10 +1862,10 @@ class UpdatePropertiesWithAuthFixture : public testing::Test {
 
   void ExecuteSetPropertyOnVertex(memgraph::auth::User user, int new_property_value) {
     // MATCH (n) SET n.prop = 2
-    auto scan_all = MakeScanAll(storage, symbol_table, "n");
+    auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
 
     auto literal = LITERAL(new_property_value);
-    auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(scan_all.sym_), entity_prop);
+    auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(scan_all.sym_), entity_prop);
     auto set_property = std::make_shared<plan::SetProperty>(scan_all.op_, entity_prop, n_p, literal);
 
     // produce the node
@@ -1921,7 +1874,7 @@ class UpdatePropertiesWithAuthFixture : public testing::Test {
     auto produce = MakeProduce(set_property, output);
 
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
 
     PullAll(*produce, &context);
     dba.AdvanceCommand();
@@ -1929,16 +1882,16 @@ class UpdatePropertiesWithAuthFixture : public testing::Test {
 
   void ExecuteSetPropertyOnEdge(memgraph::auth::User user, int new_property_value) {
     // MATCH (n)-[r]->(m) SET r.prop = 2
-    auto scan_all = MakeScanAll(storage, symbol_table, "n");
-    auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::OUT, {}, "m",
-                             false, memgraph::storage::View::OLD);
+    auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
+    auto expand = MakeExpand(this->storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::OUT,
+                             {}, "m", false, memgraph::storage::View::OLD);
     // set property to 2 on n
     auto literal = LITERAL(new_property_value);
-    auto n_p = PROPERTY_LOOKUP(IDENT("r")->MapTo(expand.edge_sym_), entity_prop);
+    auto n_p = PROPERTY_LOOKUP(dba, IDENT("r")->MapTo(expand.edge_sym_), entity_prop);
     auto set_property = std::make_shared<plan::SetProperty>(expand.op_, entity_prop, n_p, literal);
 
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
 
     PullAll(*set_property, &context);
     dba.AdvanceCommand();
@@ -1946,12 +1899,12 @@ class UpdatePropertiesWithAuthFixture : public testing::Test {
 
   void ExecuteSetPropertiesOnVertex(memgraph::auth::User user, int new_property_value) {
     // MATCH (n) SET n = {prop: 2};
-    auto scan_all = MakeScanAll(storage, symbol_table, "n");
+    auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
 
-    auto prop = PROPERTY_PAIR(entity_prop_name);
+    auto prop = PROPERTY_PAIR(dba, entity_prop_name);
     std::unordered_map<PropertyIx, Expression *> prop_map;
-    prop_map.emplace(storage.GetPropertyIx(prop.first), LITERAL(new_property_value));
-    auto *rhs = storage.Create<MapLiteral>(prop_map);
+    prop_map.emplace(this->storage.GetPropertyIx(prop.first), LITERAL(new_property_value));
+    auto *rhs = this->storage.template Create<MapLiteral>(prop_map);
     auto set_properties =
         std::make_shared<plan::SetProperties>(scan_all.op_, scan_all.sym_, rhs, plan::SetProperties::Op::UPDATE);
 
@@ -1960,7 +1913,7 @@ class UpdatePropertiesWithAuthFixture : public testing::Test {
     auto produce = MakeProduce(set_properties, output);
 
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
 
     PullAll(*produce, &context);
     dba.AdvanceCommand();
@@ -1968,14 +1921,14 @@ class UpdatePropertiesWithAuthFixture : public testing::Test {
 
   void ExecuteSetPropertiesOnEdge(memgraph::auth::User user, int new_property_value) {
     // MATCH (n)-[r]->(m) SET r = {prop: 2};
-    auto scan_all = MakeScanAll(storage, symbol_table, "n");
-    auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::OUT, {}, "m",
-                             false, memgraph::storage::View::OLD);
+    auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
+    auto expand = MakeExpand(this->storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::OUT,
+                             {}, "m", false, memgraph::storage::View::OLD);
 
-    auto prop = PROPERTY_PAIR(entity_prop_name);
+    auto prop = PROPERTY_PAIR(dba, entity_prop_name);
     std::unordered_map<PropertyIx, Expression *> prop_map;
-    prop_map.emplace(storage.GetPropertyIx(prop.first), LITERAL(new_property_value));
-    auto *rhs = storage.Create<MapLiteral>(prop_map);
+    prop_map.emplace(this->storage.GetPropertyIx(prop.first), LITERAL(new_property_value));
+    auto *rhs = this->storage.template Create<MapLiteral>(prop_map);
     auto set_properties =
         std::make_shared<plan::SetProperties>(expand.op_, expand.edge_sym_, rhs, plan::SetProperties::Op::UPDATE);
 
@@ -1984,7 +1937,7 @@ class UpdatePropertiesWithAuthFixture : public testing::Test {
     auto produce = MakeProduce(set_properties, output);
 
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
 
     PullAll(*produce, &context);
     dba.AdvanceCommand();
@@ -1992,9 +1945,9 @@ class UpdatePropertiesWithAuthFixture : public testing::Test {
 
   void ExecuteRemovePropertyOnVertex(memgraph::auth::User user) {
     // MATCH (n) REMOVE n.prop
-    auto scan_all = MakeScanAll(storage, symbol_table, "n");
+    auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
 
-    auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(scan_all.sym_), entity_prop);
+    auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(scan_all.sym_), entity_prop);
     auto remove_property = std::make_shared<plan::RemoveProperty>(scan_all.op_, entity_prop, n_p);
 
     // produce the node
@@ -2003,7 +1956,7 @@ class UpdatePropertiesWithAuthFixture : public testing::Test {
     auto produce = MakeProduce(remove_property, output);
 
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
 
     PullAll(*produce, &context);
     dba.AdvanceCommand();
@@ -2011,43 +1964,45 @@ class UpdatePropertiesWithAuthFixture : public testing::Test {
 
   void ExecuteRemovePropertyOnEdge(memgraph::auth::User user) {
     // MATCH (n)-[r]->(m) REMOVE r.prop
-    auto scan_all = MakeScanAll(storage, symbol_table, "n");
-    auto expand = MakeExpand(storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::OUT, {}, "m",
-                             false, memgraph::storage::View::OLD);
+    auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
+    auto expand = MakeExpand(this->storage, symbol_table, scan_all.op_, scan_all.sym_, "r", EdgeAtom::Direction::OUT,
+                             {}, "m", false, memgraph::storage::View::OLD);
     // set property to 2 on n
-    auto n_p = PROPERTY_LOOKUP(IDENT("n")->MapTo(expand.edge_sym_), entity_prop);
+    auto n_p = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(expand.edge_sym_), entity_prop);
     auto remove_property = std::make_shared<plan::RemoveProperty>(expand.op_, entity_prop, n_p);
 
     memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    auto context = MakeContextWithFineGrainedChecker(this->storage, symbol_table, &dba, &auth_checker);
 
     PullAll(*remove_property, &context);
     dba.AdvanceCommand();
   };
 };
 
-TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyWithAuthChecker) {
-  // Add a single vertex
-  auto v = dba.InsertVertex();
-  ASSERT_TRUE(v.AddLabel(vertex_label).HasValue());
-  ASSERT_TRUE(v.SetProperty(entity_prop, entity_prop_value).HasValue());
-  dba.AdvanceCommand();
+TYPED_TEST_CASE(UpdatePropertiesWithAuthFixture, StorageTypes);
 
-  EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
+TYPED_TEST(UpdatePropertiesWithAuthFixture, SetPropertyWithAuthChecker) {
+  // Add a single vertex
+  auto v = this->dba.InsertVertex();
+  ASSERT_TRUE(v.AddLabel(this->vertex_label).HasValue());
+  ASSERT_TRUE(v.SetProperty(this->entity_prop, this->entity_prop_value).HasValue());
+  this->dba.AdvanceCommand();
+
+  EXPECT_EQ(1, CountIterable(this->dba.Vertices(memgraph::storage::View::OLD)));
 
   auto test_hypothesis = [&](int expected_property_value) {
-    auto vertex = *dba.Vertices(memgraph::storage::View::NEW).begin();
+    auto vertex = *this->dba.Vertices(memgraph::storage::View::NEW).begin();
     auto maybe_properties = vertex.Properties(memgraph::storage::View::NEW);
     ASSERT_TRUE(maybe_properties.HasValue());
     const auto &properties = *maybe_properties;
     EXPECT_EQ(properties.size(), 1);
-    auto maybe_prop = vertex.GetProperty(memgraph::storage::View::NEW, entity_prop);
+    auto maybe_prop = vertex.GetProperty(memgraph::storage::View::NEW, this->entity_prop);
     ASSERT_TRUE(maybe_prop.HasValue());
     ASSERT_EQ(maybe_prop->ValueInt(), expected_property_value);
   };
 
   auto test_remove_hypothesis = [&](int properties_size) {
-    auto vertex = *dba.Vertices(memgraph::storage::View::NEW).begin();
+    auto vertex = *this->dba.Vertices(memgraph::storage::View::NEW).begin();
     auto maybe_properties = vertex.Properties(memgraph::storage::View::NEW);
     ASSERT_TRUE(maybe_properties.HasValue());
     const auto &properties = *maybe_properties;
@@ -2059,35 +2014,35 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyWithAuthChecker) {
 
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertyOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertyOnVertex(user, 2);
     test_hypothesis(1);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertiesOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertiesOnVertex(user, 2);
     test_hypothesis(1);
 
-    SetVertexProperty(v);
-    ExecuteRemovePropertyOnVertex(user);
+    this->SetVertexProperty(v);
+    this->ExecuteRemovePropertyOnVertex(user);
     test_remove_hypothesis(1);
   }
 
   {
     auto user = memgraph::auth::User{"denied_label"};
 
-    user.fine_grained_access_handler().label_permissions().Grant(vertex_label_name,
+    user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
                                                                  memgraph::auth::FineGrainedPermission::NOTHING);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertyOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertyOnVertex(user, 2);
     test_hypothesis(1);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertiesOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertiesOnVertex(user, 2);
     test_hypothesis(1);
 
-    SetVertexProperty(v);
-    ExecuteRemovePropertyOnVertex(user);
+    this->SetVertexProperty(v);
+    this->ExecuteRemovePropertyOnVertex(user);
     test_remove_hypothesis(1);
   }
 
@@ -2096,54 +2051,54 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyWithAuthChecker) {
 
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertyOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertyOnVertex(user, 2);
     test_hypothesis(2);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertiesOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertiesOnVertex(user, 2);
     test_hypothesis(2);
 
-    SetVertexProperty(v);
-    ExecuteRemovePropertyOnVertex(user);
+    this->SetVertexProperty(v);
+    this->ExecuteRemovePropertyOnVertex(user);
     test_remove_hypothesis(0);
   }
 
   {
     auto user = memgraph::auth::User{"granted_label"};
 
-    user.fine_grained_access_handler().label_permissions().Grant(vertex_label_name,
+    user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
                                                                  memgraph::auth::FineGrainedPermission::UPDATE);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertyOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertyOnVertex(user, 2);
     test_hypothesis(2);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertiesOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertiesOnVertex(user, 2);
     test_hypothesis(2);
 
-    SetVertexProperty(v);
-    ExecuteRemovePropertyOnVertex(user);
+    this->SetVertexProperty(v);
+    this->ExecuteRemovePropertyOnVertex(user);
     test_remove_hypothesis(0);
   }
 
   {
     auto user = memgraph::auth::User{"granted_read_label"};
 
-    user.fine_grained_access_handler().label_permissions().Grant(vertex_label_name,
+    user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
                                                                  memgraph::auth::FineGrainedPermission::READ);
 
-    SetVertexProperty(v);
-    ASSERT_THROW(ExecuteSetPropertyOnVertex(user, 2), QueryRuntimeException);
+    this->SetVertexProperty(v);
+    ASSERT_THROW(this->ExecuteSetPropertyOnVertex(user, 2), QueryRuntimeException);
     test_hypothesis(1);
 
-    SetVertexProperty(v);
-    ASSERT_THROW(ExecuteSetPropertiesOnVertex(user, 2), QueryRuntimeException);
+    this->SetVertexProperty(v);
+    ASSERT_THROW(this->ExecuteSetPropertiesOnVertex(user, 2), QueryRuntimeException);
     test_hypothesis(1);
 
-    SetVertexProperty(v);
-    ASSERT_THROW(ExecuteRemovePropertyOnVertex(user), QueryRuntimeException);
+    this->SetVertexProperty(v);
+    ASSERT_THROW(this->ExecuteRemovePropertyOnVertex(user), QueryRuntimeException);
     test_remove_hypothesis(1);
   }
 
@@ -2152,120 +2107,120 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyWithAuthChecker) {
 
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
 
-    SetVertexProperty(v);
-    ASSERT_THROW(ExecuteSetPropertyOnVertex(user, 2), QueryRuntimeException);
+    this->SetVertexProperty(v);
+    ASSERT_THROW(this->ExecuteSetPropertyOnVertex(user, 2), QueryRuntimeException);
     test_hypothesis(1);
 
-    SetVertexProperty(v);
-    ASSERT_THROW(ExecuteSetPropertiesOnVertex(user, 2), QueryRuntimeException);
+    this->SetVertexProperty(v);
+    ASSERT_THROW(this->ExecuteSetPropertiesOnVertex(user, 2), QueryRuntimeException);
     test_hypothesis(1);
 
-    SetVertexProperty(v);
-    ASSERT_THROW(ExecuteRemovePropertyOnVertex(user), QueryRuntimeException);
+    this->SetVertexProperty(v);
+    ASSERT_THROW(this->ExecuteRemovePropertyOnVertex(user), QueryRuntimeException);
     test_remove_hypothesis(1);
   }
 
   {
     auto user = memgraph::auth::User{"granted_update_label_denied_read_global"};
 
-    user.fine_grained_access_handler().label_permissions().Grant(vertex_label_name,
+    user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
                                                                  memgraph::auth::FineGrainedPermission::UPDATE);
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertyOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertyOnVertex(user, 2);
     test_hypothesis(2);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertiesOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertiesOnVertex(user, 2);
     test_hypothesis(2);
 
-    SetVertexProperty(v);
-    ExecuteRemovePropertyOnVertex(user);
+    this->SetVertexProperty(v);
+    this->ExecuteRemovePropertyOnVertex(user);
     test_remove_hypothesis(0);
   }
 
   {
     auto user = memgraph::auth::User{"granted_update_global_denied_read_label"};
 
-    user.fine_grained_access_handler().label_permissions().Grant(vertex_label_name,
+    user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
                                                                  memgraph::auth::FineGrainedPermission::NOTHING);
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::UPDATE);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertyOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertyOnVertex(user, 2);
     test_hypothesis(1);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertiesOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertiesOnVertex(user, 2);
     test_hypothesis(1);
 
-    SetVertexProperty(v);
-    ExecuteRemovePropertyOnVertex(user);
+    this->SetVertexProperty(v);
+    this->ExecuteRemovePropertyOnVertex(user);
     test_remove_hypothesis(1);
   }
 
   {
     auto user = memgraph::auth::User{"granted_create_delete_label_denied_read_global"};
 
-    user.fine_grained_access_handler().label_permissions().Grant(vertex_label_name,
+    user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
                                                                  memgraph::auth::FineGrainedPermission::CREATE_DELETE);
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertyOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertyOnVertex(user, 2);
     test_hypothesis(2);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertiesOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertiesOnVertex(user, 2);
     test_hypothesis(2);
 
-    SetVertexProperty(v);
-    ExecuteRemovePropertyOnVertex(user);
+    this->SetVertexProperty(v);
+    this->ExecuteRemovePropertyOnVertex(user);
     test_remove_hypothesis(0);
   }
 
   {
     auto user = memgraph::auth::User{"granted_create_delete_global_denied_read_label"};
 
-    user.fine_grained_access_handler().label_permissions().Grant(vertex_label_name,
+    user.fine_grained_access_handler().label_permissions().Grant(this->vertex_label_name,
                                                                  memgraph::auth::FineGrainedPermission::NOTHING);
     user.fine_grained_access_handler().label_permissions().Grant("*",
                                                                  memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertyOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertyOnVertex(user, 2);
     test_hypothesis(1);
 
-    SetVertexProperty(v);
-    ExecuteSetPropertiesOnVertex(user, 2);
+    this->SetVertexProperty(v);
+    this->ExecuteSetPropertiesOnVertex(user, 2);
     test_hypothesis(1);
 
-    SetVertexProperty(v);
-    ExecuteRemovePropertyOnVertex(user);
+    this->SetVertexProperty(v);
+    this->ExecuteRemovePropertyOnVertex(user);
     test_remove_hypothesis(1);
   }
 }
 
-TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
+TYPED_TEST(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
   // Add a single vertex
-  auto v1 = dba.InsertVertex();
-  ASSERT_TRUE(v1.AddLabel(label_1).HasValue());
+  auto v1 = this->dba.InsertVertex();
+  ASSERT_TRUE(v1.AddLabel(this->label_1).HasValue());
 
-  auto v2 = dba.InsertVertex();
-  ASSERT_TRUE(v2.AddLabel(label_2).HasValue());
+  auto v2 = this->dba.InsertVertex();
+  ASSERT_TRUE(v2.AddLabel(this->label_2).HasValue());
 
   auto edge_type_name = "edge_type";
-  auto edge_type_id = dba.NameToEdgeType(edge_type_name);
-  auto edge = dba.InsertEdge(&v1, &v2, edge_type_id);
+  auto edge_type_id = this->dba.NameToEdgeType(edge_type_name);
+  auto edge = this->dba.InsertEdge(&v1, &v2, edge_type_id);
   ASSERT_TRUE(edge.HasValue());
-  ASSERT_TRUE(edge->SetProperty(entity_prop, entity_prop_value).HasValue());
-  dba.AdvanceCommand();
+  ASSERT_TRUE(edge->SetProperty(this->entity_prop, this->entity_prop_value).HasValue());
+  this->dba.AdvanceCommand();
 
-  EXPECT_EQ(2, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
+  EXPECT_EQ(2, CountIterable(this->dba.Vertices(memgraph::storage::View::OLD)));
 
   auto test_hypothesis = [&](int expected_property_value) {
-    for (auto vertex : dba.Vertices(memgraph::storage::View::NEW)) {
+    for (auto vertex : this->dba.Vertices(memgraph::storage::View::NEW)) {
       if (vertex.OutEdges(memgraph::storage::View::NEW).HasValue()) {
         auto maybe_edges = vertex.OutEdges(memgraph::storage::View::NEW);
         for (auto edge : *maybe_edges) {
@@ -2274,7 +2229,7 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
           ASSERT_TRUE(maybe_properties.HasValue());
           const auto &properties = *maybe_properties;
           EXPECT_EQ(properties.size(), 1);
-          auto maybe_prop = edge.GetProperty(memgraph::storage::View::NEW, entity_prop);
+          auto maybe_prop = edge.GetProperty(memgraph::storage::View::NEW, this->entity_prop);
           ASSERT_TRUE(maybe_prop.HasValue());
           ASSERT_EQ(maybe_prop->ValueInt(), expected_property_value);
         }
@@ -2283,7 +2238,7 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
   };
 
   auto test_remove_hypothesis = [&](int properties_size) {
-    for (auto vertex : dba.Vertices(memgraph::storage::View::NEW)) {
+    for (auto vertex : this->dba.Vertices(memgraph::storage::View::NEW)) {
       if (vertex.OutEdges(memgraph::storage::View::NEW).HasValue()) {
         auto maybe_edges = vertex.OutEdges(memgraph::storage::View::NEW);
         for (auto edge : *maybe_edges) {
@@ -2304,16 +2259,16 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
     user.fine_grained_access_handler().edge_type_permissions().Grant("*",
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertyOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertyOnEdge(user, 2);
     test_hypothesis(1);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertiesOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertiesOnEdge(user, 2);
     test_hypothesis(1);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteRemovePropertyOnEdge(user);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteRemovePropertyOnEdge(user);
     test_remove_hypothesis(1);
   }
 
@@ -2324,16 +2279,16 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
     user.fine_grained_access_handler().edge_type_permissions().Grant(edge_type_name,
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertyOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertyOnEdge(user, 2);
     test_hypothesis(1);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertiesOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertiesOnEdge(user, 2);
     test_hypothesis(1);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteRemovePropertyOnEdge(user);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteRemovePropertyOnEdge(user);
     test_remove_hypothesis(1);
   }
 
@@ -2344,16 +2299,16 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
     user.fine_grained_access_handler().edge_type_permissions().Grant("*",
                                                                      memgraph::auth::FineGrainedPermission::UPDATE);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertyOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertyOnEdge(user, 2);
     test_hypothesis(2);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertiesOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertiesOnEdge(user, 2);
     test_hypothesis(2);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteRemovePropertyOnEdge(user);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteRemovePropertyOnEdge(user);
     test_remove_hypothesis(0);
   }
 
@@ -2364,16 +2319,16 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
     user.fine_grained_access_handler().edge_type_permissions().Grant(edge_type_name,
                                                                      memgraph::auth::FineGrainedPermission::UPDATE);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertyOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertyOnEdge(user, 2);
     test_hypothesis(2);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertiesOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertiesOnEdge(user, 2);
     test_hypothesis(2);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteRemovePropertyOnEdge(user);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteRemovePropertyOnEdge(user);
     test_remove_hypothesis(0);
   }
 
@@ -2386,16 +2341,16 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
     user.fine_grained_access_handler().edge_type_permissions().Grant("*",
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertyOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertyOnEdge(user, 2);
     test_hypothesis(2);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertiesOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertiesOnEdge(user, 2);
     test_hypothesis(2);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteRemovePropertyOnEdge(user);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteRemovePropertyOnEdge(user);
     test_remove_hypothesis(0);
   }
 
@@ -2406,16 +2361,16 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
     user.fine_grained_access_handler().edge_type_permissions().Grant(edge_type_name,
                                                                      memgraph::auth::FineGrainedPermission::READ);
 
-    SetEdgeProperty(edge.GetValue());
-    ASSERT_THROW(ExecuteSetPropertyOnEdge(user, 2), QueryRuntimeException);
+    this->SetEdgeProperty(edge.GetValue());
+    ASSERT_THROW(this->ExecuteSetPropertyOnEdge(user, 2), QueryRuntimeException);
     test_hypothesis(1);
 
-    SetEdgeProperty(edge.GetValue());
-    ASSERT_THROW(ExecuteSetPropertiesOnEdge(user, 2), QueryRuntimeException);
+    this->SetEdgeProperty(edge.GetValue());
+    ASSERT_THROW(this->ExecuteSetPropertiesOnEdge(user, 2), QueryRuntimeException);
     test_hypothesis(1);
 
-    SetEdgeProperty(edge.GetValue());
-    ASSERT_THROW(ExecuteRemovePropertyOnEdge(user), QueryRuntimeException);
+    this->SetEdgeProperty(edge.GetValue());
+    ASSERT_THROW(this->ExecuteRemovePropertyOnEdge(user), QueryRuntimeException);
     test_remove_hypothesis(1);
   }
 
@@ -2425,16 +2380,16 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
 
-    SetEdgeProperty(edge.GetValue());
-    ASSERT_THROW(ExecuteSetPropertyOnEdge(user, 2), QueryRuntimeException);
+    this->SetEdgeProperty(edge.GetValue());
+    ASSERT_THROW(this->ExecuteSetPropertyOnEdge(user, 2), QueryRuntimeException);
     test_hypothesis(1);
 
-    SetEdgeProperty(edge.GetValue());
-    ASSERT_THROW(ExecuteSetPropertiesOnEdge(user, 2), QueryRuntimeException);
+    this->SetEdgeProperty(edge.GetValue());
+    ASSERT_THROW(this->ExecuteSetPropertiesOnEdge(user, 2), QueryRuntimeException);
     test_hypothesis(1);
 
-    SetEdgeProperty(edge.GetValue());
-    ASSERT_THROW(ExecuteRemovePropertyOnEdge(user), QueryRuntimeException);
+    this->SetEdgeProperty(edge.GetValue());
+    ASSERT_THROW(this->ExecuteRemovePropertyOnEdge(user), QueryRuntimeException);
     test_remove_hypothesis(1);
   }
 
@@ -2447,16 +2402,16 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
     user.fine_grained_access_handler().edge_type_permissions().Grant("*",
                                                                      memgraph::auth::FineGrainedPermission::UPDATE);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertyOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertyOnEdge(user, 2);
     test_hypothesis(1);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertiesOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertiesOnEdge(user, 2);
     test_hypothesis(1);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteRemovePropertyOnEdge(user);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteRemovePropertyOnEdge(user);
     test_remove_hypothesis(1);
   }
 
@@ -2469,16 +2424,16 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
     user.fine_grained_access_handler().edge_type_permissions().Grant("*",
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertyOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertyOnEdge(user, 2);
     test_hypothesis(2);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertiesOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertiesOnEdge(user, 2);
     test_hypothesis(2);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteRemovePropertyOnEdge(user);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteRemovePropertyOnEdge(user);
     test_remove_hypothesis(0);
   }
 
@@ -2491,16 +2446,16 @@ TEST_F(UpdatePropertiesWithAuthFixture, SetPropertyExpandWithAuthChecker) {
     user.fine_grained_access_handler().edge_type_permissions().Grant(
         "*", memgraph::auth::FineGrainedPermission::CREATE_DELETE);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertyOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertyOnEdge(user, 2);
     test_hypothesis(1);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteSetPropertiesOnEdge(user, 2);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteSetPropertiesOnEdge(user, 2);
     test_hypothesis(1);
 
-    SetEdgeProperty(edge.GetValue());
-    ExecuteRemovePropertyOnEdge(user);
+    this->SetEdgeProperty(edge.GetValue());
+    this->ExecuteRemovePropertyOnEdge(user);
     test_remove_hypothesis(1);
   }
 }
diff --git a/tests/unit/query_plan_edge_cases.cpp b/tests/unit/query_plan_edge_cases.cpp
index fe9f0e5f5..9d09918e2 100644
--- a/tests/unit/query_plan_edge_cases.cpp
+++ b/tests/unit/query_plan_edge_cases.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -14,35 +14,42 @@
 // easy testing and latter readability they are tested end-to-end.
 
 #include <filesystem>
+#include <memory>
 #include <optional>
 
+#include "disk_test_utils.hpp"
 #include "gmock/gmock.h"
 #include "gtest/gtest.h"
 
 #include "communication/result_stream_faker.hpp"
 #include "query/interpreter.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/storage.hpp"
 
 DECLARE_bool(query_cost_planner);
 
+template <typename StorageType>
 class QueryExecution : public testing::Test {
  protected:
-  std::optional<memgraph::storage::Storage> db_;
+  const std::string testSuite = "query_plan_edge_cases";
   std::optional<memgraph::query::InterpreterContext> interpreter_context_;
   std::optional<memgraph::query::Interpreter> interpreter_;
 
   std::filesystem::path data_directory{std::filesystem::temp_directory_path() / "MG_tests_unit_query_plan_edge_cases"};
 
   void SetUp() {
-    db_.emplace();
-    interpreter_context_.emplace(&*db_, memgraph::query::InterpreterConfig{}, data_directory);
+    interpreter_context_.emplace(std::make_unique<StorageType>(disk_test_utils::GenerateOnDiskConfig(testSuite)),
+                                 memgraph::query::InterpreterConfig{}, data_directory);
     interpreter_.emplace(&*interpreter_context_);
   }
 
   void TearDown() {
     interpreter_ = std::nullopt;
     interpreter_context_ = std::nullopt;
-    db_ = std::nullopt;
+
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
   }
 
   /**
@@ -51,7 +58,7 @@ class QueryExecution : public testing::Test {
    * Return the query results.
    */
   auto Execute(const std::string &query) {
-    ResultStreamFaker stream(&*db_);
+    ResultStreamFaker stream(this->interpreter_context_->db.get());
 
     auto [header, _, qid] = interpreter_->Prepare(query, {}, nullptr);
     stream.Header(header);
@@ -62,24 +69,28 @@ class QueryExecution : public testing::Test {
   }
 };
 
-TEST_F(QueryExecution, MissingOptionalIntoExpand) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(QueryExecution, StorageTypes);
+
+TYPED_TEST(QueryExecution, MissingOptionalIntoExpand) {
   // validating bug where expanding from Null (due to a preceeding optional
   // match) exhausts the expansion cursor, even if it's input is still not
   // exhausted
-  Execute(
+  this->Execute(
       "CREATE (a:Person {id: 1}), (b:Person "
       "{id:2})-[:Has]->(:Dog)-[:Likes]->(:Food )");
-  ASSERT_EQ(Execute("MATCH (n) RETURN n").size(), 4);
+  ASSERT_EQ(this->Execute("MATCH (n) RETURN n").size(), 4);
 
   auto Exec = [this](bool desc, const std::string &edge_pattern) {
     // this test depends on left-to-right query planning
     FLAGS_query_cost_planner = false;
-    return Execute(std::string("MATCH (p:Person) WITH p ORDER BY p.id ") + (desc ? "DESC " : "") +
-                   "OPTIONAL MATCH (p)-->(d:Dog) WITH p, d "
-                   "MATCH (d)" +
-                   edge_pattern +
-                   "(f:Food) "
-                   "RETURN p, d, f")
+    return this
+        ->Execute(std::string("MATCH (p:Person) WITH p ORDER BY p.id ") + (desc ? "DESC " : "") +
+                  "OPTIONAL MATCH (p)-->(d:Dog) WITH p, d "
+                  "MATCH (d)" +
+                  edge_pattern +
+                  "(f:Food) "
+                  "RETURN p, d, f")
         .size();
   };
 
@@ -94,14 +105,14 @@ TEST_F(QueryExecution, MissingOptionalIntoExpand) {
   EXPECT_EQ(Exec(true, bfs), 1);
 }
 
-TEST_F(QueryExecution, EdgeUniquenessInOptional) {
+TYPED_TEST(QueryExecution, EdgeUniquenessInOptional) {
   // Validating that an edge uniqueness check can't fail when the edge is Null
   // due to optonal match. Since edge-uniqueness only happens in one OPTIONAL
   // MATCH, we only need to check that scenario.
-  Execute("CREATE (), ()-[:Type]->()");
-  ASSERT_EQ(Execute("MATCH (n) RETURN n").size(), 3);
-  EXPECT_EQ(Execute("MATCH (n) OPTIONAL MATCH (n)-[r1]->(), (n)-[r2]->() "
-                    "RETURN n, r1, r2")
+  this->Execute("CREATE (), ()-[:Type]->()");
+  ASSERT_EQ(this->Execute("MATCH (n) RETURN n").size(), 3);
+  EXPECT_EQ(this->Execute("MATCH (n) OPTIONAL MATCH (n)-[r1]->(), (n)-[r2]->() "
+                          "RETURN n, r1, r2")
                 .size(),
             3);
 }
diff --git a/tests/unit/query_plan_match_filter_return.cpp b/tests/unit/query_plan_match_filter_return.cpp
index f6a1ede52..e4e240a0f 100644
--- a/tests/unit/query_plan_match_filter_return.cpp
+++ b/tests/unit/query_plan_match_filter_return.cpp
@@ -9,6 +9,8 @@
 // by the Apache License, Version 2.0, included in the file
 // licenses/APL.txt.
 
+#include "disk_test_utils.hpp"
+#include "query/frontend/ast/ast.hpp"
 #include "query_plan_common.hpp"
 
 #include <iterator>
@@ -34,23 +36,36 @@
 #include "query/context.hpp"
 #include "query/exceptions.hpp"
 #include "query/plan/operator.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "utils/synchronized.hpp"
 
 using namespace memgraph::query;
 using namespace memgraph::query::plan;
 
+const std::string testSuite = "query_plan_match_filter_return";
+
+template <typename StorageType>
 class MatchReturnFixture : public testing::Test {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
   AstStorage storage;
   SymbolTable symbol_table;
 
+  void SetUp() override { memgraph::license::global_license_checker.EnableTesting(); }
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+
   void AddVertices(int count) {
     for (int i = 0; i < count; i++) dba.InsertVertex();
   }
-  void SetUp() override { memgraph::license::global_license_checker.EnableTesting(); }
 
   std::vector<Path> PathResults(std::shared_ptr<Produce> &op) {
     std::vector<Path> res;
@@ -72,60 +87,63 @@ class MatchReturnFixture : public testing::Test {
   }
 };
 
-TEST_F(MatchReturnFixture, MatchReturn) {
-  AddVertices(2);
-  dba.AdvanceCommand();
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(MatchReturnFixture, StorageTypes);
+
+TYPED_TEST(MatchReturnFixture, MatchReturn) {
+  this->AddVertices(2);
+  this->dba.AdvanceCommand();
 
   auto test_pull_count = [&](memgraph::storage::View view) {
-    auto scan_all = MakeScanAll(storage, symbol_table, "n", nullptr, view);
-    auto output =
-        NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+    auto scan_all = MakeScanAll(this->storage, this->symbol_table, "n", nullptr, view);
+    auto output = NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))
+                      ->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(scan_all.op_, output);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
     return PullAll(*produce, &context);
   };
 
   EXPECT_EQ(2, test_pull_count(memgraph::storage::View::NEW));
   EXPECT_EQ(2, test_pull_count(memgraph::storage::View::OLD));
-  dba.InsertVertex();
+  this->dba.InsertVertex();
   EXPECT_EQ(3, test_pull_count(memgraph::storage::View::NEW));
   EXPECT_EQ(2, test_pull_count(memgraph::storage::View::OLD));
-  dba.AdvanceCommand();
+  this->dba.AdvanceCommand();
   EXPECT_EQ(3, test_pull_count(memgraph::storage::View::OLD));
 }
 
-TEST_F(MatchReturnFixture, MatchReturnPath) {
-  AddVertices(2);
-  dba.AdvanceCommand();
+TYPED_TEST(MatchReturnFixture, MatchReturnPath) {
+  this->AddVertices(2);
+  this->dba.AdvanceCommand();
 
-  auto scan_all = MakeScanAll(storage, symbol_table, "n", nullptr);
-  Symbol path_sym = symbol_table.CreateSymbol("path", true);
+  auto scan_all = MakeScanAll(this->storage, this->symbol_table, "n", nullptr);
+  Symbol path_sym = this->symbol_table.CreateSymbol("path", true);
   auto make_path = std::make_shared<ConstructNamedPath>(scan_all.op_, path_sym, std::vector<Symbol>{scan_all.sym_});
   auto output =
-      NEXPR("path", IDENT("path")->MapTo(path_sym))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+      NEXPR("path", IDENT("path")->MapTo(path_sym))->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
   auto produce = MakeProduce(make_path, output);
-  auto results = PathResults(produce);
+  auto results = this->PathResults(produce);
   ASSERT_EQ(results.size(), 2);
   std::vector<memgraph::query::Path> expected_paths;
-  for (const auto &v : dba.Vertices(memgraph::storage::View::OLD)) expected_paths.emplace_back(v);
+  for (const auto &v : this->dba.Vertices(memgraph::storage::View::OLD)) expected_paths.emplace_back(v);
   ASSERT_EQ(expected_paths.size(), 2);
   EXPECT_TRUE(std::is_permutation(expected_paths.begin(), expected_paths.end(), results.begin()));
 }
 
 #ifdef MG_ENTERPRISE
-TEST_F(MatchReturnFixture, ScanAllWithAuthChecker) {
+TYPED_TEST(MatchReturnFixture, ScanAllWithAuthChecker) {
   std::string labelName = "l1";
-  const auto label = dba.NameToLabel(labelName);
+  const auto label = this->dba.NameToLabel(labelName);
 
-  ASSERT_TRUE(dba.InsertVertex().AddLabel(label).HasValue());
-  dba.AdvanceCommand();
+  ASSERT_TRUE(this->dba.InsertVertex().AddLabel(label).HasValue());
+  this->dba.AdvanceCommand();
 
   auto test_hypothesis = [&](memgraph::auth::User user, memgraph::storage::View view, int expected_pull_count) {
-    auto scan_all = MakeScanAll(storage, symbol_table, "n", nullptr, view);
-    ASSERT_EQ(expected_pull_count, PullCountAuthorized(scan_all, user));
+    auto scan_all = MakeScanAll(this->storage, this->symbol_table, "n", nullptr, view);
+    ASSERT_EQ(expected_pull_count, this->PullCountAuthorized(scan_all, user));
 
-    scan_all = MakeScanAll(storage, symbol_table, "n", nullptr, view);
-    ASSERT_EQ(expected_pull_count, PullCountAuthorized(scan_all, user));
+    scan_all = MakeScanAll(this->storage, this->symbol_table, "n", nullptr, view);
+    ASSERT_EQ(expected_pull_count, this->PullCountAuthorized(scan_all, user));
   };
 
   {
@@ -204,24 +222,38 @@ TEST_F(MatchReturnFixture, ScanAllWithAuthChecker) {
 }
 #endif
 
-TEST(QueryPlan, MatchReturnCartesian) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+template <typename StorageType>
+class QueryPlan : public testing::Test {
+ public:
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  AstStorage storage;
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+};
+
+TYPED_TEST_CASE(QueryPlan, StorageTypes);
+
+TYPED_TEST(QueryPlan, MatchReturnCartesian) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   ASSERT_TRUE(dba.InsertVertex().AddLabel(dba.NameToLabel("l1")).HasValue());
   ASSERT_TRUE(dba.InsertVertex().AddLabel(dba.NameToLabel("l2")).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto m = MakeScanAll(storage, symbol_table, "m", n.op_);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto m = MakeScanAll(this->storage, symbol_table, "m", n.op_);
   auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto return_m = NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_2", true));
   auto produce = MakeProduce(m.op_, return_n, return_m);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 4);
   // ensure the result ordering is OK:
@@ -230,38 +262,35 @@ TEST(QueryPlan, MatchReturnCartesian) {
   EXPECT_NE(results[0][1].ValueVertex(), results[1][1].ValueVertex());
 }
 
-TEST(QueryPlan, StandaloneReturn) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, StandaloneReturn) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // add a few nodes to the database
   dba.InsertVertex();
   dba.InsertVertex();
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   auto output = NEXPR("n", LITERAL(42));
   auto produce = MakeProduce(std::shared_ptr<LogicalOperator>(nullptr), output);
   output->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 1);
   EXPECT_EQ(results[0].size(), 1);
   EXPECT_EQ(results[0][0].ValueInt(), 42);
 }
 
-TEST(QueryPlan, NodeFilterLabelsAndProperties) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, NodeFilterLabelsAndProperties) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // add a few nodes to the database
   memgraph::storage::LabelId label = dba.NameToLabel("Label");
-  auto property = PROPERTY_PAIR("Property");
+  auto property = PROPERTY_PAIR(dba, "Property");
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
   auto v3 = dba.InsertVertex();
@@ -281,24 +310,23 @@ TEST(QueryPlan, NodeFilterLabelsAndProperties) {
   ASSERT_TRUE(v5.SetProperty(property.second, memgraph::storage::PropertyValue(1)).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   // make a scan all
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  n.node_->labels_.emplace_back(storage.GetLabelIx(dba.LabelToName(label)));
-  std::get<0>(n.node_->properties_)[storage.GetPropertyIx(property.first)] = LITERAL(42);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  n.node_->labels_.emplace_back(this->storage.GetLabelIx(dba.LabelToName(label)));
+  std::get<0>(n.node_->properties_)[this->storage.GetPropertyIx(property.first)] = LITERAL(42);
 
   // node filtering
-  auto *filter_expr = AND(storage.Create<LabelsTest>(n.node_->identifier_, n.node_->labels_),
-                          EQ(PROPERTY_LOOKUP(n.node_->identifier_, property), LITERAL(42)));
+  auto *filter_expr = AND(this->storage.template Create<LabelsTest>(n.node_->identifier_, n.node_->labels_),
+                          EQ(PROPERTY_LOOKUP(dba, n.node_->identifier_, property), LITERAL(42)));
   auto node_filter = std::make_shared<Filter>(n.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
 
   // make a named expression and a produce
   auto output = NEXPR("x", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto produce = MakeProduce(node_filter, output);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(1, PullAll(*produce, &context));
 
   //  test that filtering works with old records
@@ -308,10 +336,9 @@ TEST(QueryPlan, NodeFilterLabelsAndProperties) {
   EXPECT_EQ(2, PullAll(*produce, &context));
 }
 
-TEST(QueryPlan, NodeFilterMultipleLabels) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, NodeFilterMultipleLabels) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // add a few nodes to the database
   memgraph::storage::LabelId label1 = dba.NameToLabel("label1");
@@ -334,31 +361,29 @@ TEST(QueryPlan, NodeFilterMultipleLabels) {
   ASSERT_TRUE(v3.AddLabel(label3).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   // make a scan all
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  n.node_->labels_.emplace_back(storage.GetLabelIx(dba.LabelToName(label1)));
-  n.node_->labels_.emplace_back(storage.GetLabelIx(dba.LabelToName(label2)));
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  n.node_->labels_.emplace_back(this->storage.GetLabelIx(dba.LabelToName(label1)));
+  n.node_->labels_.emplace_back(this->storage.GetLabelIx(dba.LabelToName(label2)));
 
   // node filtering
-  auto *filter_expr = storage.Create<LabelsTest>(n.node_->identifier_, n.node_->labels_);
+  auto *filter_expr = this->storage.template Create<LabelsTest>(n.node_->identifier_, n.node_->labels_);
   auto node_filter = std::make_shared<Filter>(n.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
 
   // make a named expression and a produce
   auto output = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto produce = MakeProduce(node_filter, output);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 2);
 }
 
-TEST(QueryPlan, Cartesian) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, Cartesian) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto add_vertex = [&dba](std::string label) {
     auto vertex = dba.InsertVertex();
@@ -369,11 +394,10 @@ TEST(QueryPlan, Cartesian) {
   std::vector<memgraph::query::VertexAccessor> vertices{add_vertex("v1"), add_vertex("v2"), add_vertex("v3")};
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto m = MakeScanAll(storage, symbol_table, "m");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto m = MakeScanAll(this->storage, symbol_table, "m");
   auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto return_m = NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_2", true));
 
@@ -383,7 +407,7 @@ TEST(QueryPlan, Cartesian) {
 
   auto produce = MakeProduce(cartesian_op, return_n, return_m);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 9);
   for (int i = 0; i < 3; ++i) {
@@ -394,16 +418,13 @@ TEST(QueryPlan, Cartesian) {
   }
 }
 
-TEST(QueryPlan, CartesianEmptySet) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-
-  AstStorage storage;
+TYPED_TEST(QueryPlan, CartesianEmptySet) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto m = MakeScanAll(storage, symbol_table, "m");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto m = MakeScanAll(this->storage, symbol_table, "m");
   auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto return_m = NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_2", true));
 
@@ -412,15 +433,14 @@ TEST(QueryPlan, CartesianEmptySet) {
   auto cartesian_op = std::make_shared<Cartesian>(n.op_, left_symbols, m.op_, right_symbols);
 
   auto produce = MakeProduce(cartesian_op, return_n, return_m);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 0);
 }
 
-TEST(QueryPlan, CartesianThreeWay) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, CartesianThreeWay) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto add_vertex = [&dba](std::string label) {
     auto vertex = dba.InsertVertex();
     MG_ASSERT(vertex.AddLabel(dba.NameToLabel(label)).HasValue());
@@ -430,12 +450,11 @@ TEST(QueryPlan, CartesianThreeWay) {
   std::vector<memgraph::query::VertexAccessor> vertices{add_vertex("v1"), add_vertex("v2"), add_vertex("v3")};
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto m = MakeScanAll(storage, symbol_table, "m");
-  auto l = MakeScanAll(storage, symbol_table, "l");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto m = MakeScanAll(this->storage, symbol_table, "m");
+  auto l = MakeScanAll(this->storage, symbol_table, "l");
   auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto return_m = NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_2", true));
   auto return_l = NEXPR("l", IDENT("l")->MapTo(l.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_3", true));
@@ -449,7 +468,7 @@ TEST(QueryPlan, CartesianThreeWay) {
   auto cartesian_op_2 = std::make_shared<Cartesian>(cartesian_op_1, n_m_symbols, l.op_, l_symbols);
 
   auto produce = MakeProduce(cartesian_op_2, return_n, return_m, return_l);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 27);
   int id = 0;
@@ -465,19 +484,21 @@ TEST(QueryPlan, CartesianThreeWay) {
   }
 }
 
+template <typename StorageType>
 class ExpandFixture : public testing::Test {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
-  AstStorage storage;
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
   SymbolTable symbol_table;
+  AstStorage storage;
 
   // make a V-graph (v3)<-[r2]-(v1)-[r1]->(v2)
   memgraph::query::VertexAccessor v1{dba.InsertVertex()};
   memgraph::query::VertexAccessor v2{dba.InsertVertex()};
   memgraph::query::VertexAccessor v3{dba.InsertVertex()};
-  memgraph::storage::EdgeTypeId edge_type{db.NameToEdgeType("Edge")};
+  memgraph::storage::EdgeTypeId edge_type{db->NameToEdgeType("Edge")};
   memgraph::query::EdgeAccessor r1{*dba.InsertEdge(&v1, &v2, edge_type)};
   memgraph::query::EdgeAccessor r2{*dba.InsertEdge(&v1, &v3, edge_type)};
 
@@ -489,48 +510,57 @@ class ExpandFixture : public testing::Test {
 
     dba.AdvanceCommand();
   }
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
 };
 
-TEST_F(ExpandFixture, Expand) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(ExpandFixture, StorageTypes);
+
+TYPED_TEST(ExpandFixture, Expand) {
   auto test_expand = [&](EdgeAtom::Direction direction, memgraph::storage::View view) {
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", direction, {}, "m", false, view);
+    auto n = MakeScanAll(this->storage, this->symbol_table, "n");
+    auto r_m = MakeExpand(this->storage, this->symbol_table, n.op_, n.sym_, "r", direction, {}, "m", false, view);
 
     // make a named expression and a produce
-    auto output =
-        NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+    auto output = NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))
+                      ->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(r_m.op_, output);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
     return PullAll(*produce, &context);
   };
 
   // test that expand works well for both old and new graph state
-  ASSERT_TRUE(dba.InsertEdge(&v1, &v2, edge_type).HasValue());
-  ASSERT_TRUE(dba.InsertEdge(&v1, &v3, edge_type).HasValue());
+  ASSERT_TRUE(this->dba.InsertEdge(&this->v1, &this->v2, this->edge_type).HasValue());
+  ASSERT_TRUE(this->dba.InsertEdge(&this->v1, &this->v3, this->edge_type).HasValue());
   EXPECT_EQ(2, test_expand(EdgeAtom::Direction::OUT, memgraph::storage::View::OLD));
   EXPECT_EQ(2, test_expand(EdgeAtom::Direction::IN, memgraph::storage::View::OLD));
   EXPECT_EQ(4, test_expand(EdgeAtom::Direction::BOTH, memgraph::storage::View::OLD));
   EXPECT_EQ(4, test_expand(EdgeAtom::Direction::OUT, memgraph::storage::View::NEW));
   EXPECT_EQ(4, test_expand(EdgeAtom::Direction::IN, memgraph::storage::View::NEW));
   EXPECT_EQ(8, test_expand(EdgeAtom::Direction::BOTH, memgraph::storage::View::NEW));
-  dba.AdvanceCommand();
+  this->dba.AdvanceCommand();
   EXPECT_EQ(4, test_expand(EdgeAtom::Direction::OUT, memgraph::storage::View::OLD));
   EXPECT_EQ(4, test_expand(EdgeAtom::Direction::IN, memgraph::storage::View::OLD));
   EXPECT_EQ(8, test_expand(EdgeAtom::Direction::BOTH, memgraph::storage::View::OLD));
 }
 
 #ifdef MG_ENTERPRISE
-TEST_F(ExpandFixture, ExpandWithEdgeFiltering) {
+TYPED_TEST(ExpandFixture, ExpandWithEdgeFiltering) {
   auto test_expand = [&](memgraph::auth::User user, EdgeAtom::Direction direction, memgraph::storage::View view) {
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", direction, {}, "m", false, view);
+    auto n = MakeScanAll(this->storage, this->symbol_table, "n");
+    auto r_m = MakeExpand(this->storage, this->symbol_table, n.op_, n.sym_, "r", direction, {}, "m", false, view);
 
     // make a named expression and a produce
-    auto output =
-        NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+    auto output = NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))
+                      ->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(r_m.op_, output);
-    memgraph::glue::FineGrainedAuthChecker auth_checker{user, &dba};
-    auto context = MakeContextWithFineGrainedChecker(storage, symbol_table, &dba, &auth_checker);
+    memgraph::glue::FineGrainedAuthChecker auth_checker{user, &this->dba};
+    auto context = MakeContextWithFineGrainedChecker(this->storage, this->symbol_table, &this->dba, &auth_checker);
     return PullAll(*produce, &context);
   };
 
@@ -542,10 +572,10 @@ TEST_F(ExpandFixture, ExpandWithEdgeFiltering) {
                                                                    memgraph::auth::FineGrainedPermission::NOTHING);
   user.fine_grained_access_handler().label_permissions().Grant("*",
                                                                memgraph::auth::FineGrainedPermission::CREATE_DELETE);
-  memgraph::storage::EdgeTypeId edge_type_test{db.NameToEdgeType("edge_type_test")};
+  memgraph::storage::EdgeTypeId edge_type_test{this->db->NameToEdgeType("edge_type_test")};
 
-  ASSERT_TRUE(dba.InsertEdge(&v1, &v2, edge_type_test).HasValue());
-  ASSERT_TRUE(dba.InsertEdge(&v1, &v3, edge_type_test).HasValue());
+  ASSERT_TRUE(this->dba.InsertEdge(&this->v1, &this->v2, edge_type_test).HasValue());
+  ASSERT_TRUE(this->dba.InsertEdge(&this->v1, &this->v3, edge_type_test).HasValue());
   // test that expand works well for both old and new graph state
   EXPECT_EQ(2, test_expand(user, EdgeAtom::Direction::OUT, memgraph::storage::View::OLD));
   EXPECT_EQ(2, test_expand(user, EdgeAtom::Direction::IN, memgraph::storage::View::OLD));
@@ -554,7 +584,7 @@ TEST_F(ExpandFixture, ExpandWithEdgeFiltering) {
   EXPECT_EQ(2, test_expand(user, EdgeAtom::Direction::IN, memgraph::storage::View::NEW));
   EXPECT_EQ(4, test_expand(user, EdgeAtom::Direction::BOTH, memgraph::storage::View::NEW));
 
-  dba.AdvanceCommand();
+  this->dba.AdvanceCommand();
 
   EXPECT_EQ(2, test_expand(user, EdgeAtom::Direction::OUT, memgraph::storage::View::OLD));
   EXPECT_EQ(2, test_expand(user, EdgeAtom::Direction::IN, memgraph::storage::View::OLD));
@@ -576,20 +606,20 @@ TEST_F(ExpandFixture, ExpandWithEdgeFiltering) {
 }
 #endif
 
-TEST_F(ExpandFixture, ExpandPath) {
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
+TYPED_TEST(ExpandFixture, ExpandPath) {
+  auto n = MakeScanAll(this->storage, this->symbol_table, "n");
+  auto r_m = MakeExpand(this->storage, this->symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
                         memgraph::storage::View::OLD);
-  Symbol path_sym = symbol_table.CreateSymbol("path", true);
+  Symbol path_sym = this->symbol_table.CreateSymbol("path", true);
   auto path = std::make_shared<ConstructNamedPath>(r_m.op_, path_sym,
                                                    std::vector<Symbol>{n.sym_, r_m.edge_sym_, r_m.node_sym_});
   auto output =
-      NEXPR("path", IDENT("path")->MapTo(path_sym))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+      NEXPR("path", IDENT("path")->MapTo(path_sym))->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
   auto produce = MakeProduce(path, output);
 
-  std::vector<memgraph::query::Path> expected_paths{memgraph::query::Path(v1, r2, v3),
-                                                    memgraph::query::Path(v1, r1, v2)};
-  auto context = MakeContext(storage, symbol_table, &dba);
+  std::vector<memgraph::query::Path> expected_paths{memgraph::query::Path(this->v1, this->r2, this->v3),
+                                                    memgraph::query::Path(this->v1, this->r1, this->v2)};
+  auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(results.size(), 2);
   std::vector<memgraph::query::Path> results_paths;
@@ -609,15 +639,18 @@ TEST_F(ExpandFixture, ExpandPath) {
  * member in this class). Edges have properties set that
  * indicate origin and destination vertex for debugging.
  */
+using map_int = std::unordered_map<int, int>;
+
+template <typename StorageType>
 class QueryPlanExpandVariable : public testing::Test {
  protected:
   // type returned by the GetEdgeListSizes function, used
   // a lot below in test declaration
-  using map_int = std::unordered_map<int, int>;
 
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
   // labels for layers in the double chain
   std::vector<memgraph::storage::LabelId> labels;
 
@@ -627,7 +660,7 @@ class QueryPlanExpandVariable : public testing::Test {
   // using std::nullopt
   std::nullopt_t nullopt = std::nullopt;
 
-  void SetUp() {
+  void SetUp() override {
     memgraph::license::global_license_checker.EnableTesting();
 
     // create the graph
@@ -659,6 +692,12 @@ class QueryPlanExpandVariable : public testing::Test {
     ASSERT_EQ(CountEdges(&dba, memgraph::storage::View::OLD), 4 * (chain_length - 1));
   }
 
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+
   /**
    * Expands the given LogicalOperator input with a match
    * (ScanAll->Filter(label)->Expand). Can create both VariableExpand
@@ -773,13 +812,17 @@ class QueryPlanExpandVariable : public testing::Test {
   }
 };
 
-TEST_F(QueryPlanExpandVariable, OneVariableExpansion) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(QueryPlanExpandVariable, StorageTypes);
+
+TYPED_TEST(QueryPlanExpandVariable, OneVariableExpansion) {
   auto test_expand = [&](int layer, EdgeAtom::Direction direction, std::optional<size_t> lower,
                          std::optional<size_t> upper, bool reverse) {
-    auto e = Edge("r", direction);
-    return GetEdgeListSizes(AddMatch<ExpandVariable>(nullptr, "n", layer, direction, {}, lower, upper, e, "m",
-                                                     memgraph::storage::View::OLD, reverse),
-                            e);
+    auto e = this->Edge("r", direction);
+    return this->GetEdgeListSizes(
+        this->template AddMatch<ExpandVariable>(nullptr, "n", layer, direction, {}, lower, upper, e, "m",
+                                                memgraph::storage::View::OLD, reverse),
+        e);
   };
 
   for (int reverse = 0; reverse < 2; ++reverse) {
@@ -800,23 +843,24 @@ TEST_F(QueryPlanExpandVariable, OneVariableExpansion) {
     EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, 4, reverse), (map_int{{4, 24}}));
 
     // default bound values (lower default is 1, upper default is inf)
-    EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, nullopt, 0, reverse), (map_int{}));
-    EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, nullopt, 1, reverse), (map_int{{1, 4}}));
-    EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, nullopt, 2, reverse), (map_int{{1, 4}, {2, 8}}));
-    EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 7, nullopt, reverse), (map_int{{7, 24}, {8, 24}}));
-    EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 8, nullopt, reverse), (map_int{{8, 24}}));
-    EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 9, nullopt, reverse), (map_int{}));
+    EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, this->nullopt, 0, reverse), (map_int{}));
+    EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, this->nullopt, 1, reverse), (map_int{{1, 4}}));
+    EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, this->nullopt, 2, reverse), (map_int{{1, 4}, {2, 8}}));
+    EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 7, this->nullopt, reverse), (map_int{{7, 24}, {8, 24}}));
+    EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 8, this->nullopt, reverse), (map_int{{8, 24}}));
+    EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 9, this->nullopt, reverse), (map_int{}));
   }
 }
 
 #ifdef MG_ENTERPRISE
-TEST_F(QueryPlanExpandVariable, FineGrainedOneVariableExpansion) {
+TYPED_TEST(QueryPlanExpandVariable, FineGrainedOneVariableExpansion) {
   auto test_expand = [&](int layer, EdgeAtom::Direction direction, std::optional<size_t> lower,
                          std::optional<size_t> upper, bool reverse, memgraph::auth::User &user) {
-    auto e = Edge("r", direction);
-    return GetEdgeListSizes(AddMatch<ExpandVariable>(nullptr, "n", layer, direction, {}, lower, upper, e, "m",
-                                                     memgraph::storage::View::OLD, reverse),
-                            e, &user);
+    auto e = this->Edge("r", direction);
+    return this->GetEdgeListSizes(
+        this->template AddMatch<ExpandVariable>(nullptr, "n", layer, direction, {}, lower, upper, e, "m",
+                                                memgraph::storage::View::OLD, reverse),
+        e, &user);
   };
 
   // All labels, All edge types granted
@@ -843,12 +887,13 @@ TEST_F(QueryPlanExpandVariable, FineGrainedOneVariableExpansion) {
       EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 4, 4, reverse, user), (map_int{{4, 24}}));
 
       // default bound values (lower default is 1, upper default is inf)
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, nullopt, 0, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, nullopt, 1, reverse, user), (map_int{{1, 4}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, nullopt, 2, reverse, user), (map_int{{1, 4}, {2, 8}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 7, nullopt, reverse, user), (map_int{{7, 24}, {8, 24}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 8, nullopt, reverse, user), (map_int{{8, 24}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 9, nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, this->nullopt, 0, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, this->nullopt, 1, reverse, user), (map_int{{1, 4}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, this->nullopt, 2, reverse, user), (map_int{{1, 4}, {2, 8}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 7, this->nullopt, reverse, user),
+                (map_int{{7, 24}, {8, 24}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 8, this->nullopt, reverse, user), (map_int{{8, 24}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 9, this->nullopt, reverse, user), (map_int{}));
     }
   }
 
@@ -859,12 +904,12 @@ TEST_F(QueryPlanExpandVariable, FineGrainedOneVariableExpansion) {
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
     for (auto reverse : {false, true}) {
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user), (map_int{}));
     }
   }
 
@@ -875,18 +920,18 @@ TEST_F(QueryPlanExpandVariable, FineGrainedOneVariableExpansion) {
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
     for (auto reverse : {false, true}) {
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user), (map_int{}));
     }
   }
 
@@ -896,12 +941,12 @@ TEST_F(QueryPlanExpandVariable, FineGrainedOneVariableExpansion) {
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
     for (auto reverse : {false, true}) {
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user), (map_int{}));
     }
   }
 
@@ -914,18 +959,18 @@ TEST_F(QueryPlanExpandVariable, FineGrainedOneVariableExpansion) {
     user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::NOTHING);
 
     for (auto reverse : {false, true}) {
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 2, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 2, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 2, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 2, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 2, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 2, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user), (map_int{}));
     }
   }
 
@@ -939,20 +984,20 @@ TEST_F(QueryPlanExpandVariable, FineGrainedOneVariableExpansion) {
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
 
     for (auto reverse : {false, true}) {
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{1, 4}, {0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user),
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, this->nullopt, reverse, user), (map_int{{1, 4}, {0, 2}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, this->nullopt, reverse, user),
                 (map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{{1, 4}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user),
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{{1, 4}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user),
                 (map_int{{1, 4}, {2, 4}, {3, 4}, {4, 4}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user), (map_int{}));
     }
   }
 
@@ -965,21 +1010,21 @@ TEST_F(QueryPlanExpandVariable, FineGrainedOneVariableExpansion) {
     user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::NOTHING);
 
     for (auto reverse : {false, true}) {
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}, {1, 4}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user),
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, this->nullopt, reverse, user), (map_int{{0, 2}, {1, 4}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, this->nullopt, reverse, user),
                 (map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{1, 4}, {0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user),
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, this->nullopt, reverse, user), (map_int{{1, 4}, {0, 2}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, this->nullopt, reverse, user),
                 (map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{{1, 4}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user),
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{{1, 4}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user),
                 (map_int{{1, 4}, {2, 4}, {3, 4}, {4, 4}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{{1, 4}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user),
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{{1, 4}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user),
                 (map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}}));
     }
   }
@@ -994,48 +1039,48 @@ TEST_F(QueryPlanExpandVariable, FineGrainedOneVariableExpansion) {
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
 
     for (auto reverse : {false, true}) {
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{1, 4}, {0, 2}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user),
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 0, this->nullopt, reverse, user), (map_int{{1, 4}, {0, 2}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 0, this->nullopt, reverse, user),
                 (map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, nullopt, reverse, user), (map_int{{1, 4}, {0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, nullopt, reverse, user), (map_int{{0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, nullopt, reverse, user),
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 0, this->nullopt, reverse, user), (map_int{{1, 4}, {0, 2}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 0, this->nullopt, reverse, user), (map_int{{0, 2}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 0, this->nullopt, reverse, user),
                 (map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}, {0, 2}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{{1, 4}}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user),
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{{1, 4}}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(1, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user),
                 (map_int{{1, 4}, {2, 4}, {3, 4}, {4, 4}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, nullopt, reverse, user), (map_int{}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, nullopt, reverse, user), (map_int{{1, 4}}));
-      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, nullopt, reverse, user),
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::IN, 1, this->nullopt, reverse, user), (map_int{}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 1, this->nullopt, reverse, user), (map_int{{1, 4}}));
+      EXPECT_EQ(test_expand(0, EdgeAtom::Direction::BOTH, 1, this->nullopt, reverse, user),
                 (map_int{{4, 4}, {3, 4}, {2, 4}, {1, 4}}));
     }
   }
 }
 #endif
 
-TEST_F(QueryPlanExpandVariable, EdgeUniquenessSingleAndVariableExpansion) {
+TYPED_TEST(QueryPlanExpandVariable, EdgeUniquenessSingleAndVariableExpansion) {
   auto test_expand = [&](int layer, EdgeAtom::Direction direction, std::optional<size_t> lower,
                          std::optional<size_t> upper, bool single_expansion_before, bool add_uniqueness_check) {
     std::shared_ptr<LogicalOperator> last_op{nullptr};
     std::vector<Symbol> symbols;
 
     if (single_expansion_before) {
-      symbols.push_back(Edge("r0", direction));
-      last_op = AddMatch<Expand>(last_op, "n0", layer, direction, {}, lower, upper, symbols.back(), "m0",
-                                 memgraph::storage::View::OLD);
+      symbols.push_back(this->Edge("r0", direction));
+      last_op = this->template AddMatch<Expand>(last_op, "n0", layer, direction, {}, lower, upper, symbols.back(), "m0",
+                                                memgraph::storage::View::OLD);
     }
 
-    auto var_length_sym = Edge("r1", direction);
+    auto var_length_sym = this->Edge("r1", direction);
     symbols.push_back(var_length_sym);
-    last_op = AddMatch<ExpandVariable>(last_op, "n1", layer, direction, {}, lower, upper, var_length_sym, "m1",
-                                       memgraph::storage::View::OLD);
+    last_op = this->template AddMatch<ExpandVariable>(last_op, "n1", layer, direction, {}, lower, upper, var_length_sym,
+                                                      "m1", memgraph::storage::View::OLD);
 
     if (!single_expansion_before) {
-      symbols.push_back(Edge("r2", direction));
-      last_op = AddMatch<Expand>(last_op, "n2", layer, direction, {}, lower, upper, symbols.back(), "m2",
-                                 memgraph::storage::View::OLD);
+      symbols.push_back(this->Edge("r2", direction));
+      last_op = this->template AddMatch<Expand>(last_op, "n2", layer, direction, {}, lower, upper, symbols.back(), "m2",
+                                                memgraph::storage::View::OLD);
     }
 
     if (add_uniqueness_check) {
@@ -1044,7 +1089,7 @@ TEST_F(QueryPlanExpandVariable, EdgeUniquenessSingleAndVariableExpansion) {
       last_op = std::make_shared<EdgeUniquenessFilter>(last_op, last_symbol, symbols);
     }
 
-    return GetEdgeListSizes(last_op, var_length_sym);
+    return this->GetEdgeListSizes(last_op, var_length_sym);
   };
 
   // no uniqueness between variable and single expansion
@@ -1054,20 +1099,20 @@ TEST_F(QueryPlanExpandVariable, EdgeUniquenessSingleAndVariableExpansion) {
   EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 2, 3, false, true), (map_int{{2, 3 * 8}}));
 }
 
-TEST_F(QueryPlanExpandVariable, EdgeUniquenessTwoVariableExpansions) {
+TYPED_TEST(QueryPlanExpandVariable, EdgeUniquenessTwoVariableExpansions) {
   auto test_expand = [&](int layer, EdgeAtom::Direction direction, std::optional<size_t> lower,
                          std::optional<size_t> upper, bool add_uniqueness_check) {
-    auto e1 = Edge("r1", direction);
-    auto first = AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, {}, lower, upper, e1, "m1",
-                                          memgraph::storage::View::OLD);
-    auto e2 = Edge("r2", direction);
-    auto last_op = AddMatch<ExpandVariable>(first, "n2", layer, direction, {}, lower, upper, e2, "m2",
-                                            memgraph::storage::View::OLD);
+    auto e1 = this->Edge("r1", direction);
+    auto first = this->template AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, {}, lower, upper, e1, "m1",
+                                                         memgraph::storage::View::OLD);
+    auto e2 = this->Edge("r2", direction);
+    auto last_op = this->template AddMatch<ExpandVariable>(first, "n2", layer, direction, {}, lower, upper, e2, "m2",
+                                                           memgraph::storage::View::OLD);
     if (add_uniqueness_check) {
       last_op = std::make_shared<EdgeUniquenessFilter>(last_op, e2, std::vector<Symbol>{e1});
     }
 
-    return GetEdgeListSizes(last_op, e2);
+    return this->GetEdgeListSizes(last_op, e2);
   };
 
   EXPECT_EQ(test_expand(0, EdgeAtom::Direction::OUT, 2, 2, false), (map_int{{2, 8 * 8}}));
@@ -1075,20 +1120,20 @@ TEST_F(QueryPlanExpandVariable, EdgeUniquenessTwoVariableExpansions) {
 }
 
 #ifdef MG_ENTERPRISE
-TEST_F(QueryPlanExpandVariable, FineGrainedEdgeUniquenessTwoVariableExpansions) {
+TYPED_TEST(QueryPlanExpandVariable, FineGrainedEdgeUniquenessTwoVariableExpansions) {
   auto test_expand = [&](int layer, EdgeAtom::Direction direction, std::optional<size_t> lower,
                          std::optional<size_t> upper, bool add_uniqueness_check, memgraph::auth::User &user) {
-    auto e1 = Edge("r1", direction);
-    auto first = AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, {}, lower, upper, e1, "m1",
-                                          memgraph::storage::View::OLD);
-    auto e2 = Edge("r2", direction);
-    auto last_op = AddMatch<ExpandVariable>(first, "n2", layer, direction, {}, lower, upper, e2, "m2",
-                                            memgraph::storage::View::OLD);
+    auto e1 = this->Edge("r1", direction);
+    auto first = this->template AddMatch<ExpandVariable>(nullptr, "n1", layer, direction, {}, lower, upper, e1, "m1",
+                                                         memgraph::storage::View::OLD);
+    auto e2 = this->Edge("r2", direction);
+    auto last_op = this->template AddMatch<ExpandVariable>(first, "n2", layer, direction, {}, lower, upper, e2, "m2",
+                                                           memgraph::storage::View::OLD);
     if (add_uniqueness_check) {
       last_op = std::make_shared<EdgeUniquenessFilter>(last_op, e2, std::vector<Symbol>{e1});
     }
 
-    return GetEdgeListSizes(last_op, e2, &user);
+    return this->GetEdgeListSizes(last_op, e2, &user);
   };
 
   // All labels granted, All edge types granted
@@ -1168,23 +1213,23 @@ TEST_F(QueryPlanExpandVariable, FineGrainedEdgeUniquenessTwoVariableExpansions)
 }
 #endif
 
-TEST_F(QueryPlanExpandVariable, NamedPath) {
-  auto e = Edge("r", EdgeAtom::Direction::OUT);
-  auto expand = AddMatch<ExpandVariable>(nullptr, "n", 0, EdgeAtom::Direction::OUT, {}, 2, 2, e, "m",
-                                         memgraph::storage::View::OLD);
+TYPED_TEST(QueryPlanExpandVariable, NamedPath) {
+  auto e = this->Edge("r", EdgeAtom::Direction::OUT);
+  auto expand = this->template AddMatch<ExpandVariable>(nullptr, "n", 0, EdgeAtom::Direction::OUT, {}, 2, 2, e, "m",
+                                                        memgraph::storage::View::OLD);
   auto find_symbol = [this](const std::string &name) {
-    for (const auto &sym : symbol_table.table())
+    for (const auto &sym : this->symbol_table.table())
       if (sym.second.name() == name) return sym.second;
     throw std::runtime_error("Symbol not found");
   };
 
-  auto path_symbol = symbol_table.CreateSymbol("path", true, Symbol::Type::PATH);
+  auto path_symbol = this->symbol_table.CreateSymbol("path", true, Symbol::Type::PATH);
   auto create_path = std::make_shared<ConstructNamedPath>(expand, path_symbol,
                                                           std::vector<Symbol>{find_symbol("n"), e, find_symbol("m")});
 
   std::vector<memgraph::query::Path> expected_paths;
-  for (const auto &v : dba.Vertices(memgraph::storage::View::OLD)) {
-    if (!*v.HasLabel(memgraph::storage::View::OLD, labels[0])) continue;
+  for (const auto &v : this->dba.Vertices(memgraph::storage::View::OLD)) {
+    if (!*v.HasLabel(memgraph::storage::View::OLD, this->labels[0])) continue;
     auto maybe_edges1 = v.OutEdges(memgraph::storage::View::OLD);
     for (const auto &e1 : *maybe_edges1) {
       auto maybe_edges2 = e1.To().OutEdges(memgraph::storage::View::OLD);
@@ -1195,23 +1240,23 @@ TEST_F(QueryPlanExpandVariable, NamedPath) {
   }
   ASSERT_EQ(expected_paths.size(), 8);
 
-  auto results = GetPathResults(create_path, path_symbol);
+  auto results = this->GetPathResults(create_path, path_symbol);
   ASSERT_EQ(results.size(), 8);
   EXPECT_TRUE(std::is_permutation(results.begin(), results.end(), expected_paths.begin()));
 }
 
 #ifdef MG_ENTERPRISE
-TEST_F(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
-  auto e = Edge("r", EdgeAtom::Direction::OUT);
-  auto expand = AddMatch<ExpandVariable>(nullptr, "n", 0, EdgeAtom::Direction::OUT, {}, 0, 2, e, "m",
-                                         memgraph::storage::View::OLD);
+TYPED_TEST(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
+  auto e = this->Edge("r", EdgeAtom::Direction::OUT);
+  auto expand = this->template AddMatch<ExpandVariable>(nullptr, "n", 0, EdgeAtom::Direction::OUT, {}, 0, 2, e, "m",
+                                                        memgraph::storage::View::OLD);
   auto find_symbol = [this](const std::string &name) {
-    for (const auto &sym : symbol_table.table())
+    for (const auto &sym : this->symbol_table.table())
       if (sym.second.name() == name) return sym.second;
     throw std::runtime_error("Symbol not found");
   };
 
-  auto path_symbol = symbol_table.CreateSymbol("path", true, Symbol::Type::PATH);
+  auto path_symbol = this->symbol_table.CreateSymbol("path", true, Symbol::Type::PATH);
   auto create_path = std::make_shared<ConstructNamedPath>(expand, path_symbol,
                                                           std::vector<Symbol>{find_symbol("n"), e, find_symbol("m")});
 
@@ -1220,7 +1265,7 @@ TEST_F(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
     memgraph::auth::User user{"test"};
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
-    auto results = GetPathResults(create_path, path_symbol, &user);
+    auto results = this->GetPathResults(create_path, path_symbol, &user);
     ASSERT_EQ(results.size(), 14);
   }
 
@@ -1231,7 +1276,7 @@ TEST_F(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
 
-    auto results = GetPathResults(create_path, path_symbol, &user);
+    auto results = this->GetPathResults(create_path, path_symbol, &user);
     ASSERT_EQ(results.size(), 0);
   }
 
@@ -1241,7 +1286,7 @@ TEST_F(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
 
-    auto results = GetPathResults(create_path, path_symbol, &user);
+    auto results = this->GetPathResults(create_path, path_symbol, &user);
     ASSERT_EQ(results.size(), 0);
   }
 
@@ -1252,7 +1297,7 @@ TEST_F(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
 
-    auto results = GetPathResults(create_path, path_symbol, &user);
+    auto results = this->GetPathResults(create_path, path_symbol, &user);
     ASSERT_EQ(results.size(), 2);
   }
 
@@ -1264,7 +1309,7 @@ TEST_F(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
     user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
 
-    auto results = GetPathResults(create_path, path_symbol, &user);
+    auto results = this->GetPathResults(create_path, path_symbol, &user);
     ASSERT_EQ(results.size(), 0);
   }
 
@@ -1276,7 +1321,7 @@ TEST_F(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
     user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::NOTHING);
     user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::READ);
 
-    auto results = GetPathResults(create_path, path_symbol, &user);
+    auto results = this->GetPathResults(create_path, path_symbol, &user);
     ASSERT_EQ(results.size(), 2);
   }
 
@@ -1288,12 +1333,12 @@ TEST_F(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
     user.fine_grained_access_handler().label_permissions().Grant("1", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().label_permissions().Grant("2", memgraph::auth::FineGrainedPermission::NOTHING);
 
-    auto results = GetPathResults(create_path, path_symbol, &user);
+    auto results = this->GetPathResults(create_path, path_symbol, &user);
     ASSERT_EQ(results.size(), 6);
 
     std::vector<memgraph::query::Path> expected_paths;
-    for (const auto &v : dba.Vertices(memgraph::storage::View::OLD)) {
-      if (!*v.HasLabel(memgraph::storage::View::OLD, labels[0])) continue;
+    for (const auto &v : this->dba.Vertices(memgraph::storage::View::OLD)) {
+      if (!*v.HasLabel(memgraph::storage::View::OLD, this->labels[0])) continue;
       expected_paths.emplace_back(v);
       auto maybe_edges1 = v.OutEdges(memgraph::storage::View::OLD);
       for (const auto &e1 : *maybe_edges1) {
@@ -1312,7 +1357,7 @@ TEST_F(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
                                                                      memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
 
-    auto results = GetPathResults(create_path, path_symbol, &user);
+    auto results = this->GetPathResults(create_path, path_symbol, &user);
     ASSERT_EQ(results.size(), 2);
   }
 
@@ -1325,12 +1370,12 @@ TEST_F(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
 
-    auto results = GetPathResults(create_path, path_symbol, &user);
+    auto results = this->GetPathResults(create_path, path_symbol, &user);
     ASSERT_EQ(results.size(), 6);
 
     std::vector<memgraph::query::Path> expected_paths;
-    for (const auto &v : dba.Vertices(memgraph::storage::View::OLD)) {
-      if (!*v.HasLabel(memgraph::storage::View::OLD, labels[0])) continue;
+    for (const auto &v : this->dba.Vertices(memgraph::storage::View::OLD)) {
+      if (!*v.HasLabel(memgraph::storage::View::OLD, this->labels[0])) continue;
       expected_paths.emplace_back(v);
       auto maybe_edges1 = v.OutEdges(memgraph::storage::View::OLD);
       for (const auto &e1 : *maybe_edges1) {
@@ -1343,35 +1388,37 @@ TEST_F(QueryPlanExpandVariable, FineGrainedFilterNamedPath) {
 }
 #endif
 
-TEST_F(QueryPlanExpandVariable, ExpandToSameSymbol) {
+TYPED_TEST(QueryPlanExpandVariable, ExpandToSameSymbol) {
   auto test_expand = [&](int layer, EdgeAtom::Direction direction, std::optional<size_t> lower,
                          std::optional<size_t> upper, bool reverse) {
-    auto e = Edge("r", direction);
+    auto e = this->Edge("r", direction);
 
     auto node = NODE("n");
-    auto symbol = symbol_table.CreateSymbol("n", true);
+    auto symbol = this->symbol_table.CreateSymbol("n", true);
     node->identifier_->MapTo(symbol);
     auto logical_op = std::make_shared<ScanAll>(nullptr, symbol, memgraph::storage::View::OLD);
     auto n_from = ScanAllTuple{node, logical_op, symbol};
 
     auto filter_op = std::make_shared<Filter>(
         n_from.op_, std::vector<std::shared_ptr<LogicalOperator>>{},
-        storage.Create<memgraph::query::LabelsTest>(
-            n_from.node_->identifier_, std::vector<LabelIx>{storage.GetLabelIx(dba.LabelToName(labels[layer]))}));
+        this->storage.template Create<memgraph::query::LabelsTest>(
+            n_from.node_->identifier_,
+            std::vector<LabelIx>{this->storage.GetLabelIx(this->dba.LabelToName(this->labels[layer]))}));
 
     // convert optional ints to optional expressions
     auto convert = [this](std::optional<size_t> bound) {
       return bound ? LITERAL(static_cast<int64_t>(bound.value())) : nullptr;
     };
 
-    return GetEdgeListSizes(std::make_shared<ExpandVariable>(
-                                filter_op, symbol, symbol, e, EdgeAtom::Type::DEPTH_FIRST, direction,
-                                std::vector<memgraph::storage::EdgeTypeId>{}, reverse, convert(lower), convert(upper),
-                                /* existing = */ true,
-                                ExpansionLambda{symbol_table.CreateSymbol("inner_edge", false),
-                                                symbol_table.CreateSymbol("inner_node", false), nullptr},
-                                std::nullopt, std::nullopt),
-                            e);
+    return this->GetEdgeListSizes(
+        std::make_shared<ExpandVariable>(filter_op, symbol, symbol, e, EdgeAtom::Type::DEPTH_FIRST, direction,
+                                         std::vector<memgraph::storage::EdgeTypeId>{}, reverse, convert(lower),
+                                         convert(upper),
+                                         /* existing = */ true,
+                                         ExpansionLambda{this->symbol_table.CreateSymbol("inner_edge", false),
+                                                         this->symbol_table.CreateSymbol("inner_node", false), nullptr},
+                                         std::nullopt, std::nullopt),
+        e);
   };
 
   // The graph is a double chain:
@@ -1534,35 +1581,37 @@ TEST_F(QueryPlanExpandVariable, ExpandToSameSymbol) {
 }
 
 #ifdef MG_ENTERPRISE
-TEST_F(QueryPlanExpandVariable, FineGrainedExpandToSameSymbol) {
+TYPED_TEST(QueryPlanExpandVariable, FineGrainedExpandToSameSymbol) {
   auto test_expand = [&](int layer, EdgeAtom::Direction direction, std::optional<size_t> lower,
                          std::optional<size_t> upper, bool reverse, memgraph::auth::User &user) {
-    auto e = Edge("r", direction);
+    auto e = this->Edge("r", direction);
 
     auto node = NODE("n");
-    auto symbol = symbol_table.CreateSymbol("n", true);
+    auto symbol = this->symbol_table.CreateSymbol("n", true);
     node->identifier_->MapTo(symbol);
     auto logical_op = std::make_shared<ScanAll>(nullptr, symbol, memgraph::storage::View::OLD);
     auto n_from = ScanAllTuple{node, logical_op, symbol};
 
     auto filter_op = std::make_shared<Filter>(
         n_from.op_, std::vector<std::shared_ptr<LogicalOperator>>{},
-        storage.Create<memgraph::query::LabelsTest>(
-            n_from.node_->identifier_, std::vector<LabelIx>{storage.GetLabelIx(dba.LabelToName(labels[layer]))}));
+        this->storage.template Create<memgraph::query::LabelsTest>(
+            n_from.node_->identifier_,
+            std::vector<LabelIx>{this->storage.GetLabelIx(this->dba.LabelToName(this->labels[layer]))}));
 
     // convert optional ints to optional expressions
     auto convert = [this](std::optional<size_t> bound) {
       return bound ? LITERAL(static_cast<int64_t>(bound.value())) : nullptr;
     };
 
-    return GetEdgeListSizes(std::make_shared<ExpandVariable>(
-                                filter_op, symbol, symbol, e, EdgeAtom::Type::DEPTH_FIRST, direction,
-                                std::vector<memgraph::storage::EdgeTypeId>{}, reverse, convert(lower), convert(upper),
-                                /* existing = */ true,
-                                ExpansionLambda{symbol_table.CreateSymbol("inner_edge", false),
-                                                symbol_table.CreateSymbol("inner_node", false), nullptr},
-                                std::nullopt, std::nullopt),
-                            e, &user);
+    return this->GetEdgeListSizes(
+        std::make_shared<ExpandVariable>(filter_op, symbol, symbol, e, EdgeAtom::Type::DEPTH_FIRST, direction,
+                                         std::vector<memgraph::storage::EdgeTypeId>{}, reverse, convert(lower),
+                                         convert(upper),
+                                         /* existing = */ true,
+                                         ExpansionLambda{this->symbol_table.CreateSymbol("inner_edge", false),
+                                                         this->symbol_table.CreateSymbol("inner_node", false), nullptr},
+                                         std::nullopt, std::nullopt),
+        e, &user);
   };
 
   // All labels granted, All edge types granted
@@ -1743,6 +1792,7 @@ struct hash<std::pair<int, int>> {
 }  // namespace std
 
 /** A test fixture for weighted shortest path expansion */
+template <typename StorageType>
 class QueryPlanExpandWeightedShortestPath : public testing::Test {
  public:
   struct ResultType {
@@ -1752,10 +1802,11 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
   };
 
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
-  std::pair<std::string, memgraph::storage::PropertyId> prop = PROPERTY_PAIR("property");
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
+  std::pair<std::string, memgraph::storage::PropertyId> prop = PROPERTY_PAIR(dba, "property");
   memgraph::storage::EdgeTypeId edge_type = dba.NameToEdgeType("edge_type");
 
   // make 5 vertices because we'll need to compare against them exactly
@@ -1777,14 +1828,14 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
 
   Symbol total_weight = symbol_table.CreateSymbol("total_weight", true);
 
-  void SetUp() {
+  void SetUp() override {
     memgraph::license::global_license_checker.EnableTesting();
 
     for (int i = 0; i < 5; i++) {
       v.push_back(dba.InsertVertex());
       ASSERT_TRUE(v.back().SetProperty(prop.second, memgraph::storage::PropertyValue(i)).HasValue());
       auto label = fmt::format("l{}", i);
-      ASSERT_TRUE(v.back().AddLabel(db.NameToLabel(label)).HasValue());
+      ASSERT_TRUE(v.back().AddLabel(db->NameToLabel(label)).HasValue());
     }
 
     auto add_edge = [&](int from, int to, double weight) {
@@ -1803,6 +1854,12 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
     dba.AdvanceCommand();
   }
 
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+
   // defines and performs a weighted shortest expansion with the given
   // params returns a vector of pairs. each pair is (vector-of-edges,
   // vertex)
@@ -1814,7 +1871,7 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
     auto last_op = n.op_;
     if (node_id) {
       last_op = std::make_shared<Filter>(last_op, std::vector<std::shared_ptr<LogicalOperator>>{},
-                                         EQ(PROPERTY_LOOKUP(n.node_->identifier_, prop), LITERAL(*node_id)));
+                                         EQ(PROPERTY_LOOKUP(dba, n.node_->identifier_, prop), LITERAL(*node_id)));
     }
 
     auto ident_e = IDENT("e");
@@ -1827,7 +1884,7 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
         last_op, n.sym_, node_sym, edge_list_sym, EdgeAtom::Type::WEIGHTED_SHORTEST_PATH, direction,
         std::vector<memgraph::storage::EdgeTypeId>{}, false, nullptr, max_depth ? LITERAL(max_depth.value()) : nullptr,
         existing_node_input != nullptr, ExpansionLambda{filter_edge, filter_node, where},
-        ExpansionLambda{weight_edge, weight_node, PROPERTY_LOOKUP(ident_e, prop)}, total_weight);
+        ExpansionLambda{weight_edge, weight_node, PROPERTY_LOOKUP(dba, ident_e, prop)}, total_weight);
 
     Frame frame(symbol_table.max_position());
     auto cursor = last_op->MakeCursor(memgraph::utils::NewDeleteResource());
@@ -1865,10 +1922,13 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
   Expression *PropNe(Symbol symbol, int value) {
     auto ident = IDENT("inner_element");
     ident->MapTo(symbol);
-    return NEQ(PROPERTY_LOOKUP(ident, prop), LITERAL(value));
+    return NEQ(PROPERTY_LOOKUP(dba, ident, prop), LITERAL(value));
   }
 };
 
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(QueryPlanExpandWeightedShortestPath, StorageTypes);
+
 // Testing weighted shortest path on this graph:
 //
 //      5            5
@@ -1881,221 +1941,222 @@ class QueryPlanExpandWeightedShortestPath : public testing::Test {
 //      \->[2]->-[3]->/
 //      3      3     3
 
-TEST_F(QueryPlanExpandWeightedShortestPath, Basic) {
-  auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true));
+TYPED_TEST(QueryPlanExpandWeightedShortestPath, Basic) {
+  auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true));
 
   ASSERT_EQ(results.size(), 4);
 
   // check end nodes
-  EXPECT_EQ(GetProp(results[0].vertex), 2);
-  EXPECT_EQ(GetProp(results[1].vertex), 1);
-  EXPECT_EQ(GetProp(results[2].vertex), 3);
-  EXPECT_EQ(GetProp(results[3].vertex), 4);
+  EXPECT_EQ(this->GetProp(results[0].vertex), 2);
+  EXPECT_EQ(this->GetProp(results[1].vertex), 1);
+  EXPECT_EQ(this->GetProp(results[2].vertex), 3);
+  EXPECT_EQ(this->GetProp(results[3].vertex), 4);
 
   // check paths and total weights
   EXPECT_EQ(results[0].path.size(), 1);
-  EXPECT_EQ(GetDoubleProp(results[0].path[0]), 3);
+  EXPECT_EQ(this->GetDoubleProp(results[0].path[0]), 3);
   EXPECT_EQ(results[0].total_weight, 3);
 
   EXPECT_EQ(results[1].path.size(), 1);
-  EXPECT_EQ(GetDoubleProp(results[1].path[0]), 5);
+  EXPECT_EQ(this->GetDoubleProp(results[1].path[0]), 5);
   EXPECT_EQ(results[1].total_weight, 5);
 
   EXPECT_EQ(results[2].path.size(), 2);
-  EXPECT_EQ(GetDoubleProp(results[2].path[0]), 3);
-  EXPECT_EQ(GetDoubleProp(results[2].path[1]), 3);
+  EXPECT_EQ(this->GetDoubleProp(results[2].path[0]), 3);
+  EXPECT_EQ(this->GetDoubleProp(results[2].path[1]), 3);
   EXPECT_EQ(results[2].total_weight, 6);
 
   EXPECT_EQ(results[3].path.size(), 3);
-  EXPECT_EQ(GetDoubleProp(results[3].path[0]), 3);
-  EXPECT_EQ(GetDoubleProp(results[3].path[1]), 3);
-  EXPECT_EQ(GetDoubleProp(results[3].path[2]), 3);
+  EXPECT_EQ(this->GetDoubleProp(results[3].path[0]), 3);
+  EXPECT_EQ(this->GetDoubleProp(results[3].path[1]), 3);
+  EXPECT_EQ(this->GetDoubleProp(results[3].path[2]), 3);
   EXPECT_EQ(results[3].total_weight, 9);
 }
 
-TEST_F(QueryPlanExpandWeightedShortestPath, EdgeDirection) {
+TYPED_TEST(QueryPlanExpandWeightedShortestPath, EdgeDirection) {
   {
-    auto results = ExpandWShortest(EdgeAtom::Direction::OUT, 1000, LITERAL(true));
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::OUT, 1000, LITERAL(true));
     ASSERT_EQ(results.size(), 4);
-    EXPECT_EQ(GetProp(results[0].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 2);
     EXPECT_EQ(results[0].total_weight, 3);
-    EXPECT_EQ(GetProp(results[1].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 1);
     EXPECT_EQ(results[1].total_weight, 5);
-    EXPECT_EQ(GetProp(results[2].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 3);
     EXPECT_EQ(results[2].total_weight, 6);
-    EXPECT_EQ(GetProp(results[3].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[3].vertex), 4);
     EXPECT_EQ(results[3].total_weight, 9);
   }
   {
-    auto results = ExpandWShortest(EdgeAtom::Direction::IN, 1000, LITERAL(true));
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::IN, 1000, LITERAL(true));
     ASSERT_EQ(results.size(), 4);
-    EXPECT_EQ(GetProp(results[0].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 4);
     EXPECT_EQ(results[0].total_weight, 12);
-    EXPECT_EQ(GetProp(results[1].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 3);
     EXPECT_EQ(results[1].total_weight, 15);
-    EXPECT_EQ(GetProp(results[2].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 1);
     EXPECT_EQ(results[2].total_weight, 17);
-    EXPECT_EQ(GetProp(results[3].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[3].vertex), 2);
     EXPECT_EQ(results[3].total_weight, 18);
   }
 }
 
-TEST_F(QueryPlanExpandWeightedShortestPath, Where) {
+TYPED_TEST(QueryPlanExpandWeightedShortestPath, Where) {
   {
-    auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, PropNe(filter_node, 2));
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, this->PropNe(this->filter_node, 2));
     ASSERT_EQ(results.size(), 3);
-    EXPECT_EQ(GetProp(results[0].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 1);
     EXPECT_EQ(results[0].total_weight, 5);
-    EXPECT_EQ(GetProp(results[1].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 4);
     EXPECT_EQ(results[1].total_weight, 10);
-    EXPECT_EQ(GetProp(results[2].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 3);
     EXPECT_EQ(results[2].total_weight, 13);
   }
   {
-    auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, PropNe(filter_node, 1));
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, this->PropNe(this->filter_node, 1));
     ASSERT_EQ(results.size(), 3);
-    EXPECT_EQ(GetProp(results[0].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 2);
     EXPECT_EQ(results[0].total_weight, 3);
-    EXPECT_EQ(GetProp(results[1].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 3);
     EXPECT_EQ(results[1].total_weight, 6);
-    EXPECT_EQ(GetProp(results[2].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 4);
     EXPECT_EQ(results[2].total_weight, 9);
   }
 }
 
-TEST_F(QueryPlanExpandWeightedShortestPath, ExistingNode) {
+TYPED_TEST(QueryPlanExpandWeightedShortestPath, ExistingNode) {
   auto ExpandPreceeding = [this](std::optional<int> preceeding_node_id) {
     // scan the nodes optionally filtering on property value
-    auto n0 = MakeScanAll(storage, symbol_table, "n0");
+    auto n0 = MakeScanAll(this->storage, this->symbol_table, "n0");
     if (preceeding_node_id) {
-      auto filter =
-          std::make_shared<Filter>(n0.op_, std::vector<std::shared_ptr<LogicalOperator>>{},
-                                   EQ(PROPERTY_LOOKUP(n0.node_->identifier_, prop), LITERAL(*preceeding_node_id)));
+      auto filter = std::make_shared<Filter>(
+          n0.op_, std::vector<std::shared_ptr<LogicalOperator>>{},
+          EQ(PROPERTY_LOOKUP(this->dba, n0.node_->identifier_, this->prop), LITERAL(*preceeding_node_id)));
       // inject the filter op into the ScanAllTuple. that way the filter
       // op can be passed into the ExpandWShortest function without too
       // much refactor
       n0.op_ = filter;
     }
 
-    return ExpandWShortest(EdgeAtom::Direction::OUT, 1000, LITERAL(true), std::nullopt, &n0);
+    return this->ExpandWShortest(EdgeAtom::Direction::OUT, 1000, LITERAL(true), std::nullopt, &n0);
   };
 
   EXPECT_EQ(ExpandPreceeding(std::nullopt).size(), 20);
   {
     auto results = ExpandPreceeding(3);
     ASSERT_EQ(results.size(), 4);
-    for (int i = 0; i < 4; i++) EXPECT_EQ(GetProp(results[i].vertex), 3);
+    for (int i = 0; i < 4; i++) EXPECT_EQ(this->GetProp(results[i].vertex), 3);
   }
 }
 
-TEST_F(QueryPlanExpandWeightedShortestPath, UpperBound) {
+TYPED_TEST(QueryPlanExpandWeightedShortestPath, UpperBound) {
   {
-    auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, std::nullopt, LITERAL(true));
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, std::nullopt, LITERAL(true));
     ASSERT_EQ(results.size(), 4);
-    EXPECT_EQ(GetProp(results[0].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 2);
     EXPECT_EQ(results[0].total_weight, 3);
-    EXPECT_EQ(GetProp(results[1].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 1);
     EXPECT_EQ(results[1].total_weight, 5);
-    EXPECT_EQ(GetProp(results[2].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 3);
     EXPECT_EQ(results[2].total_weight, 6);
-    EXPECT_EQ(GetProp(results[3].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[3].vertex), 4);
     EXPECT_EQ(results[3].total_weight, 9);
   }
   {
-    auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 2, LITERAL(true));
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 2, LITERAL(true));
     ASSERT_EQ(results.size(), 4);
-    EXPECT_EQ(GetProp(results[0].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 2);
     EXPECT_EQ(results[0].total_weight, 3);
-    EXPECT_EQ(GetProp(results[1].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 1);
     EXPECT_EQ(results[1].total_weight, 5);
-    EXPECT_EQ(GetProp(results[2].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 3);
     EXPECT_EQ(results[2].total_weight, 6);
-    EXPECT_EQ(GetProp(results[3].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[3].vertex), 4);
     EXPECT_EQ(results[3].total_weight, 10);
   }
   {
-    auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1, LITERAL(true));
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1, LITERAL(true));
     ASSERT_EQ(results.size(), 3);
-    EXPECT_EQ(GetProp(results[0].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 2);
     EXPECT_EQ(results[0].total_weight, 3);
-    EXPECT_EQ(GetProp(results[1].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 1);
     EXPECT_EQ(results[1].total_weight, 5);
-    EXPECT_EQ(GetProp(results[2].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 4);
     EXPECT_EQ(results[2].total_weight, 12);
   }
   {
-    auto new_vertex = dba.InsertVertex();
-    ASSERT_TRUE(new_vertex.SetProperty(prop.second, memgraph::storage::PropertyValue(5)).HasValue());
-    auto edge = dba.InsertEdge(&v[4], &new_vertex, edge_type);
+    auto new_vertex = this->dba.InsertVertex();
+    ASSERT_TRUE(new_vertex.SetProperty(this->prop.second, memgraph::storage::PropertyValue(5)).HasValue());
+    auto edge = this->dba.InsertEdge(&this->v[4], &new_vertex, this->edge_type);
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_TRUE(edge->SetProperty(prop.second, memgraph::storage::PropertyValue(2)).HasValue());
-    dba.AdvanceCommand();
+    ASSERT_TRUE(edge->SetProperty(this->prop.second, memgraph::storage::PropertyValue(2)).HasValue());
+    this->dba.AdvanceCommand();
 
-    auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 3, LITERAL(true));
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 3, LITERAL(true));
 
     ASSERT_EQ(results.size(), 5);
-    EXPECT_EQ(GetProp(results[0].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 2);
     EXPECT_EQ(results[0].total_weight, 3);
-    EXPECT_EQ(GetProp(results[1].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 1);
     EXPECT_EQ(results[1].total_weight, 5);
-    EXPECT_EQ(GetProp(results[2].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 3);
     EXPECT_EQ(results[2].total_weight, 6);
-    EXPECT_EQ(GetProp(results[3].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[3].vertex), 4);
     EXPECT_EQ(results[3].total_weight, 9);
-    EXPECT_EQ(GetProp(results[4].vertex), 5);
+    EXPECT_EQ(this->GetProp(results[4].vertex), 5);
     EXPECT_EQ(results[4].total_weight, 12);
   }
 }
 
-TEST_F(QueryPlanExpandWeightedShortestPath, NonNumericWeight) {
-  auto new_vertex = dba.InsertVertex();
-  ASSERT_TRUE(new_vertex.SetProperty(prop.second, memgraph::storage::PropertyValue(5)).HasValue());
-  auto edge = dba.InsertEdge(&v[4], &new_vertex, edge_type);
+TYPED_TEST(QueryPlanExpandWeightedShortestPath, NonNumericWeight) {
+  auto new_vertex = this->dba.InsertVertex();
+  ASSERT_TRUE(new_vertex.SetProperty(this->prop.second, memgraph::storage::PropertyValue(5)).HasValue());
+  auto edge = this->dba.InsertEdge(&this->v[4], &new_vertex, this->edge_type);
   ASSERT_TRUE(edge.HasValue());
-  ASSERT_TRUE(edge->SetProperty(prop.second, memgraph::storage::PropertyValue("not a number")).HasValue());
-  dba.AdvanceCommand();
-  EXPECT_THROW(ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true)), QueryRuntimeException);
+  ASSERT_TRUE(edge->SetProperty(this->prop.second, memgraph::storage::PropertyValue("not a number")).HasValue());
+  this->dba.AdvanceCommand();
+  EXPECT_THROW(this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true)), QueryRuntimeException);
 }
 
-TEST_F(QueryPlanExpandWeightedShortestPath, NegativeWeight) {
-  auto new_vertex = dba.InsertVertex();
-  ASSERT_TRUE(new_vertex.SetProperty(prop.second, memgraph::storage::PropertyValue(5)).HasValue());
-  auto edge = dba.InsertEdge(&v[4], &new_vertex, edge_type);
+TYPED_TEST(QueryPlanExpandWeightedShortestPath, NegativeWeight) {
+  auto new_vertex = this->dba.InsertVertex();
+  ASSERT_TRUE(new_vertex.SetProperty(this->prop.second, memgraph::storage::PropertyValue(5)).HasValue());
+  auto edge = this->dba.InsertEdge(&this->v[4], &new_vertex, this->edge_type);
   ASSERT_TRUE(edge.HasValue());
-  ASSERT_TRUE(edge->SetProperty(prop.second, memgraph::storage::PropertyValue(-10)).HasValue());  // negative weight
-  dba.AdvanceCommand();
-  EXPECT_THROW(ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true)), QueryRuntimeException);
+  ASSERT_TRUE(
+      edge->SetProperty(this->prop.second, memgraph::storage::PropertyValue(-10)).HasValue());  // negative weight
+  this->dba.AdvanceCommand();
+  EXPECT_THROW(this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true)), QueryRuntimeException);
 }
 
-TEST_F(QueryPlanExpandWeightedShortestPath, NegativeUpperBound) {
-  EXPECT_THROW(ExpandWShortest(EdgeAtom::Direction::BOTH, -1, LITERAL(true)), QueryRuntimeException);
+TYPED_TEST(QueryPlanExpandWeightedShortestPath, NegativeUpperBound) {
+  EXPECT_THROW(this->ExpandWShortest(EdgeAtom::Direction::BOTH, -1, LITERAL(true)), QueryRuntimeException);
 }
 
 #if MG_ENTERPRISE
-TEST_F(QueryPlanExpandWeightedShortestPath, FineGrainedFiltering) {
+TYPED_TEST(QueryPlanExpandWeightedShortestPath, FineGrainedFiltering) {
   // All edge_types and labels allowed
   {
     memgraph::auth::User user{"test"};
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
-    auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
     EXPECT_EQ(results[0].path.size(), 1);
-    EXPECT_EQ(GetDoubleProp(results[0].path[0]), 3);
+    EXPECT_EQ(this->GetDoubleProp(results[0].path[0]), 3);
     EXPECT_EQ(results[0].total_weight, 3);
 
     EXPECT_EQ(results[1].path.size(), 1);
-    EXPECT_EQ(GetDoubleProp(results[1].path[0]), 5);
+    EXPECT_EQ(this->GetDoubleProp(results[1].path[0]), 5);
     EXPECT_EQ(results[1].total_weight, 5);
 
     EXPECT_EQ(results[2].path.size(), 2);
-    EXPECT_EQ(GetDoubleProp(results[2].path[0]), 3);
-    EXPECT_EQ(GetDoubleProp(results[2].path[1]), 3);
+    EXPECT_EQ(this->GetDoubleProp(results[2].path[0]), 3);
+    EXPECT_EQ(this->GetDoubleProp(results[2].path[1]), 3);
     EXPECT_EQ(results[2].total_weight, 6);
 
     EXPECT_EQ(results[3].path.size(), 3);
-    EXPECT_EQ(GetDoubleProp(results[3].path[0]), 3);
-    EXPECT_EQ(GetDoubleProp(results[3].path[1]), 3);
-    EXPECT_EQ(GetDoubleProp(results[3].path[2]), 3);
+    EXPECT_EQ(this->GetDoubleProp(results[3].path[0]), 3);
+    EXPECT_EQ(this->GetDoubleProp(results[3].path[1]), 3);
+    EXPECT_EQ(this->GetDoubleProp(results[3].path[2]), 3);
     EXPECT_EQ(results[3].total_weight, 9);
   }
 
@@ -2104,7 +2165,7 @@ TEST_F(QueryPlanExpandWeightedShortestPath, FineGrainedFiltering) {
     memgraph::auth::User user{"test"};
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::NOTHING);
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
-    auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
     ASSERT_EQ(results.size(), 0);
   }
 
@@ -2114,7 +2175,7 @@ TEST_F(QueryPlanExpandWeightedShortestPath, FineGrainedFiltering) {
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().edge_type_permissions().Grant("*",
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
-    auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
     ASSERT_EQ(results.size(), 0);
   }
 
@@ -2124,7 +2185,7 @@ TEST_F(QueryPlanExpandWeightedShortestPath, FineGrainedFiltering) {
     user.fine_grained_access_handler().label_permissions().Grant("l0", memgraph::auth::FineGrainedPermission::NOTHING);
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
 
-    auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
     ASSERT_EQ(results.size(), 0);
   }
 
@@ -2138,43 +2199,44 @@ TEST_F(QueryPlanExpandWeightedShortestPath, FineGrainedFiltering) {
     user.fine_grained_access_handler().label_permissions().Grant("l4", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
 
-    auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
     ASSERT_EQ(results.size(), 4);
 
     user.fine_grained_access_handler().label_permissions().Grant("l2", memgraph::auth::FineGrainedPermission::NOTHING);
-    auto filtered_results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto filtered_results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
     ASSERT_EQ(filtered_results.size(), 3);
   }
 
   // Deny edge type (created vertex 5 and edge vertex 4 to vertex 5)
   {
-    v.push_back(dba.InsertVertex());
-    ASSERT_TRUE(v.back().SetProperty(prop.second, memgraph::storage::PropertyValue(5)).HasValue());
-    ASSERT_TRUE(v.back().AddLabel(db.NameToLabel("l5")).HasValue());
-    dba.AdvanceCommand();
-    memgraph::storage::EdgeTypeId edge_type_filter = dba.NameToEdgeType("edge_type_filter");
-    auto edge = dba.InsertEdge(&v[4], &v[5], edge_type_filter);
-    ASSERT_TRUE(edge->SetProperty(prop.second, memgraph::storage::PropertyValue(1)).HasValue());
-    e.emplace(std::make_pair(4, 5), *edge);
-    dba.AdvanceCommand();
+    this->v.push_back(this->dba.InsertVertex());
+    ASSERT_TRUE(this->v.back().SetProperty(this->prop.second, memgraph::storage::PropertyValue(5)).HasValue());
+    ASSERT_TRUE(this->v.back().AddLabel(this->db->NameToLabel("l5")).HasValue());
+    this->dba.AdvanceCommand();
+    memgraph::storage::EdgeTypeId edge_type_filter = this->dba.NameToEdgeType("edge_type_filter");
+    auto edge = this->dba.InsertEdge(&this->v[4], &this->v[5], edge_type_filter);
+    ASSERT_TRUE(edge->SetProperty(this->prop.second, memgraph::storage::PropertyValue(1)).HasValue());
+    this->e.emplace(std::make_pair(4, 5), *edge);
+    this->dba.AdvanceCommand();
 
     memgraph::auth::User user{"test"};
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
-    auto results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
     ASSERT_EQ(results.size(), 5);
 
     user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type",
                                                                      memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type_filter",
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
-    auto filtered_results = ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto filtered_results = this->ExpandWShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
     ASSERT_EQ(filtered_results.size(), 4);
   }
 }
 #endif
 
 /** A test fixture for all shortest paths expansion */
+template <typename StorageType>
 class QueryPlanExpandAllShortestPaths : public testing::Test {
  public:
   struct ResultType {
@@ -2184,10 +2246,11 @@ class QueryPlanExpandAllShortestPaths : public testing::Test {
   };
 
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
-  std::pair<std::string, memgraph::storage::PropertyId> prop = PROPERTY_PAIR("property");
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
+  std::pair<std::string, memgraph::storage::PropertyId> prop = PROPERTY_PAIR(dba, "property");
   memgraph::storage::EdgeTypeId edge_type = dba.NameToEdgeType("edge_type");
 
   // make 5 vertices because we'll need to compare against them exactly
@@ -2209,14 +2272,14 @@ class QueryPlanExpandAllShortestPaths : public testing::Test {
 
   Symbol total_weight = symbol_table.CreateSymbol("total_weight", true);
 
-  void SetUp() {
+  void SetUp() override {
     memgraph::license::global_license_checker.EnableTesting();
 
     for (int i = 0; i < 5; i++) {
       v.push_back(dba.InsertVertex());
       ASSERT_TRUE(v.back().SetProperty(prop.second, memgraph::storage::PropertyValue(i)).HasValue());
       auto label = fmt::format("l{}", i);
-      ASSERT_TRUE(v.back().AddLabel(db.NameToLabel(label)).HasValue());
+      ASSERT_TRUE(v.back().AddLabel(db->NameToLabel(label)).HasValue());
     }
 
     auto add_edge = [&](int from, int to, double weight) {
@@ -2235,6 +2298,12 @@ class QueryPlanExpandAllShortestPaths : public testing::Test {
     dba.AdvanceCommand();
   }
 
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+
   // defines and performs an all shortest paths expansion with the given
   // params returns a vector of pairs. each pair is (vector-of-edges,
   // vertex)
@@ -2246,7 +2315,7 @@ class QueryPlanExpandAllShortestPaths : public testing::Test {
     auto last_op = n.op_;
     if (node_id) {
       last_op = std::make_shared<Filter>(last_op, std::vector<std::shared_ptr<LogicalOperator>>{},
-                                         EQ(PROPERTY_LOOKUP(n.node_->identifier_, prop), LITERAL(*node_id)));
+                                         EQ(PROPERTY_LOOKUP(dba, n.node_->identifier_, prop), LITERAL(*node_id)));
     }
 
     auto ident_e = IDENT("e");
@@ -2259,7 +2328,7 @@ class QueryPlanExpandAllShortestPaths : public testing::Test {
         last_op, n.sym_, node_sym, edge_list_sym, EdgeAtom::Type::ALL_SHORTEST_PATHS, direction,
         std::vector<memgraph::storage::EdgeTypeId>{}, false, nullptr, max_depth ? LITERAL(max_depth.value()) : nullptr,
         existing_node_input != nullptr, ExpansionLambda{filter_edge, filter_node, where},
-        ExpansionLambda{weight_edge, weight_node, PROPERTY_LOOKUP(ident_e, prop)}, total_weight);
+        ExpansionLambda{weight_edge, weight_node, PROPERTY_LOOKUP(dba, ident_e, prop)}, total_weight);
 
     Frame frame(symbol_table.max_position());
     auto cursor = last_op->MakeCursor(memgraph::utils::NewDeleteResource());
@@ -2296,12 +2365,16 @@ class QueryPlanExpandAllShortestPaths : public testing::Test {
   Expression *PropNe(Symbol symbol, int value) {
     auto ident = IDENT("inner_element");
     ident->MapTo(symbol);
-    return NEQ(PROPERTY_LOOKUP(ident, prop), LITERAL(value));
+    return NEQ(PROPERTY_LOOKUP(dba, ident, prop), LITERAL(value));
   }
 };
 
-bool compareResultType(const QueryPlanExpandAllShortestPaths::ResultType &a,
-                       const QueryPlanExpandAllShortestPaths::ResultType &b) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(QueryPlanExpandAllShortestPaths, StorageTypes);
+
+template <typename StorageType>
+bool compareResultType(const typename QueryPlanExpandAllShortestPaths<StorageType>::ResultType &a,
+                       const typename QueryPlanExpandAllShortestPaths<StorageType>::ResultType &b) {
   return a.total_weight < b.total_weight;
 }
 
@@ -2317,175 +2390,176 @@ bool compareResultType(const QueryPlanExpandAllShortestPaths::ResultType &a,
 //      \->[2]->-[3]->/
 //      3      3     3
 
-TEST_F(QueryPlanExpandAllShortestPaths, Basic) {
-  auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true));
-  sort(results.begin(), results.end(), compareResultType);
+TYPED_TEST(QueryPlanExpandAllShortestPaths, Basic) {
+  auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true));
+  sort(results.begin(), results.end(), compareResultType<TypeParam>);
 
   ASSERT_EQ(results.size(), 4);
 
   // check end nodes
-  EXPECT_EQ(GetProp(results[0].vertex), 2);
-  EXPECT_EQ(GetProp(results[1].vertex), 1);
-  EXPECT_EQ(GetProp(results[2].vertex), 3);
-  EXPECT_EQ(GetProp(results[3].vertex), 4);
+  EXPECT_EQ(this->GetProp(results[0].vertex), 2);
+  EXPECT_EQ(this->GetProp(results[1].vertex), 1);
+  EXPECT_EQ(this->GetProp(results[2].vertex), 3);
+  EXPECT_EQ(this->GetProp(results[3].vertex), 4);
 
   // check paths and total weights
   EXPECT_EQ(results[0].path.size(), 1);
-  EXPECT_EQ(GetDoubleProp(results[0].path[0]), 3);
+  EXPECT_EQ(this->GetDoubleProp(results[0].path[0]), 3);
   EXPECT_EQ(results[0].total_weight, 3);
 
   EXPECT_EQ(results[1].path.size(), 1);
-  EXPECT_EQ(GetDoubleProp(results[1].path[0]), 5);
+  EXPECT_EQ(this->GetDoubleProp(results[1].path[0]), 5);
   EXPECT_EQ(results[1].total_weight, 5);
 
   EXPECT_EQ(results[2].path.size(), 2);
-  EXPECT_EQ(GetDoubleProp(results[2].path[0]), 3);
-  EXPECT_EQ(GetDoubleProp(results[2].path[1]), 3);
+  EXPECT_EQ(this->GetDoubleProp(results[2].path[0]), 3);
+  EXPECT_EQ(this->GetDoubleProp(results[2].path[1]), 3);
   EXPECT_EQ(results[2].total_weight, 6);
 
   EXPECT_EQ(results[3].path.size(), 3);
-  EXPECT_EQ(GetDoubleProp(results[3].path[0]), 3);
-  EXPECT_EQ(GetDoubleProp(results[3].path[1]), 3);
-  EXPECT_EQ(GetDoubleProp(results[3].path[2]), 3);
+  EXPECT_EQ(this->GetDoubleProp(results[3].path[0]), 3);
+  EXPECT_EQ(this->GetDoubleProp(results[3].path[1]), 3);
+  EXPECT_EQ(this->GetDoubleProp(results[3].path[2]), 3);
   EXPECT_EQ(results[3].total_weight, 9);
 }
 
-TEST_F(QueryPlanExpandAllShortestPaths, EdgeDirection) {
+TYPED_TEST(QueryPlanExpandAllShortestPaths, EdgeDirection) {
   {
-    auto results = ExpandAllShortest(EdgeAtom::Direction::OUT, 1000, LITERAL(true));
-    sort(results.begin(), results.end(), compareResultType);
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::OUT, 1000, LITERAL(true));
+    sort(results.begin(), results.end(), compareResultType<TypeParam>);
     ASSERT_EQ(results.size(), 4);
-    EXPECT_EQ(GetProp(results[0].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 2);
     EXPECT_EQ(results[0].total_weight, 3);
-    EXPECT_EQ(GetProp(results[1].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 1);
     EXPECT_EQ(results[1].total_weight, 5);
-    EXPECT_EQ(GetProp(results[2].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 3);
     EXPECT_EQ(results[2].total_weight, 6);
-    EXPECT_EQ(GetProp(results[3].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[3].vertex), 4);
     EXPECT_EQ(results[3].total_weight, 9);
   }
   {
-    auto results = ExpandAllShortest(EdgeAtom::Direction::IN, 1000, LITERAL(true));
-    sort(results.begin(), results.end(), compareResultType);
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::IN, 1000, LITERAL(true));
+    sort(results.begin(), results.end(), compareResultType<TypeParam>);
     ASSERT_EQ(results.size(), 4);
-    EXPECT_EQ(GetProp(results[0].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 4);
     EXPECT_EQ(results[0].total_weight, 12);
-    EXPECT_EQ(GetProp(results[1].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 3);
     EXPECT_EQ(results[1].total_weight, 15);
-    EXPECT_EQ(GetProp(results[2].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 1);
     EXPECT_EQ(results[2].total_weight, 17);
-    EXPECT_EQ(GetProp(results[3].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[3].vertex), 2);
     EXPECT_EQ(results[3].total_weight, 18);
   }
 }
 
-TEST_F(QueryPlanExpandAllShortestPaths, Where) {
+TYPED_TEST(QueryPlanExpandAllShortestPaths, Where) {
   {
-    auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, PropNe(filter_node, 2));
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, this->PropNe(this->filter_node, 2));
     ASSERT_EQ(results.size(), 3);
-    EXPECT_EQ(GetProp(results[0].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 1);
     EXPECT_EQ(results[0].total_weight, 5);
-    EXPECT_EQ(GetProp(results[1].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 4);
     EXPECT_EQ(results[1].total_weight, 10);
-    EXPECT_EQ(GetProp(results[2].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 3);
     EXPECT_EQ(results[2].total_weight, 13);
   }
   {
-    auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, PropNe(filter_node, 1));
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, this->PropNe(this->filter_node, 1));
     ASSERT_EQ(results.size(), 3);
-    EXPECT_EQ(GetProp(results[0].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 2);
     EXPECT_EQ(results[0].total_weight, 3);
-    EXPECT_EQ(GetProp(results[1].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 3);
     EXPECT_EQ(results[1].total_weight, 6);
-    EXPECT_EQ(GetProp(results[2].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 4);
     EXPECT_EQ(results[2].total_weight, 9);
   }
 }
 
-TEST_F(QueryPlanExpandAllShortestPaths, UpperBound) {
+TYPED_TEST(QueryPlanExpandAllShortestPaths, UpperBound) {
   {
-    auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, std::nullopt, LITERAL(true));
-    std::sort(results.begin(), results.end(), compareResultType);
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, std::nullopt, LITERAL(true));
+    std::sort(results.begin(), results.end(), compareResultType<TypeParam>);
     ASSERT_EQ(results.size(), 4);
-    EXPECT_EQ(GetProp(results[0].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 2);
     EXPECT_EQ(results[0].total_weight, 3);
-    EXPECT_EQ(GetProp(results[1].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 1);
     EXPECT_EQ(results[1].total_weight, 5);
-    EXPECT_EQ(GetProp(results[2].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 3);
     EXPECT_EQ(results[2].total_weight, 6);
-    EXPECT_EQ(GetProp(results[3].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[3].vertex), 4);
     EXPECT_EQ(results[3].total_weight, 9);
   }
   {
-    auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 2, LITERAL(true));
-    std::sort(results.begin(), results.end(), compareResultType);
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 2, LITERAL(true));
+    std::sort(results.begin(), results.end(), compareResultType<TypeParam>);
     ASSERT_EQ(results.size(), 4);
-    EXPECT_EQ(GetProp(results[0].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 2);
     EXPECT_EQ(results[0].total_weight, 3);
-    EXPECT_EQ(GetProp(results[1].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 1);
     EXPECT_EQ(results[1].total_weight, 5);
-    EXPECT_EQ(GetProp(results[2].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 3);
     EXPECT_EQ(results[2].total_weight, 6);
-    EXPECT_EQ(GetProp(results[3].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[3].vertex), 4);
     EXPECT_EQ(results[3].total_weight, 10);
   }
   {
-    auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1, LITERAL(true));
-    std::sort(results.begin(), results.end(), compareResultType);
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1, LITERAL(true));
+    std::sort(results.begin(), results.end(), compareResultType<TypeParam>);
     ASSERT_EQ(results.size(), 3);
-    EXPECT_EQ(GetProp(results[0].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 2);
     EXPECT_EQ(results[0].total_weight, 3);
-    EXPECT_EQ(GetProp(results[1].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 1);
     EXPECT_EQ(results[1].total_weight, 5);
-    EXPECT_EQ(GetProp(results[2].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 4);
     EXPECT_EQ(results[2].total_weight, 12);
   }
   {
-    auto new_vertex = dba.InsertVertex();
-    ASSERT_TRUE(new_vertex.SetProperty(prop.second, memgraph::storage::PropertyValue(5)).HasValue());
-    auto edge = dba.InsertEdge(&v[4], &new_vertex, edge_type);
+    auto new_vertex = this->dba.InsertVertex();
+    ASSERT_TRUE(new_vertex.SetProperty(this->prop.second, memgraph::storage::PropertyValue(5)).HasValue());
+    auto edge = this->dba.InsertEdge(&this->v[4], &new_vertex, this->edge_type);
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_TRUE(edge->SetProperty(prop.second, memgraph::storage::PropertyValue(2)).HasValue());
-    dba.AdvanceCommand();
+    ASSERT_TRUE(edge->SetProperty(this->prop.second, memgraph::storage::PropertyValue(2)).HasValue());
+    this->dba.AdvanceCommand();
 
-    auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 3, LITERAL(true));
-    std::sort(results.begin(), results.end(), compareResultType);
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 3, LITERAL(true));
+    std::sort(results.begin(), results.end(), compareResultType<TypeParam>);
     ASSERT_EQ(results.size(), 5);
-    EXPECT_EQ(GetProp(results[0].vertex), 2);
+    EXPECT_EQ(this->GetProp(results[0].vertex), 2);
     EXPECT_EQ(results[0].total_weight, 3);
-    EXPECT_EQ(GetProp(results[1].vertex), 1);
+    EXPECT_EQ(this->GetProp(results[1].vertex), 1);
     EXPECT_EQ(results[1].total_weight, 5);
-    EXPECT_EQ(GetProp(results[2].vertex), 3);
+    EXPECT_EQ(this->GetProp(results[2].vertex), 3);
     EXPECT_EQ(results[2].total_weight, 6);
-    EXPECT_EQ(GetProp(results[3].vertex), 4);
+    EXPECT_EQ(this->GetProp(results[3].vertex), 4);
     EXPECT_EQ(results[3].total_weight, 9);
-    EXPECT_EQ(GetProp(results[4].vertex), 5);
+    EXPECT_EQ(this->GetProp(results[4].vertex), 5);
     EXPECT_EQ(results[4].total_weight, 12);
   }
 }
 
-TEST_F(QueryPlanExpandAllShortestPaths, NonNumericWeight) {
-  auto new_vertex = dba.InsertVertex();
-  ASSERT_TRUE(new_vertex.SetProperty(prop.second, memgraph::storage::PropertyValue(5)).HasValue());
-  auto edge = dba.InsertEdge(&v[4], &new_vertex, edge_type);
+TYPED_TEST(QueryPlanExpandAllShortestPaths, NonNumericWeight) {
+  auto new_vertex = this->dba.InsertVertex();
+  ASSERT_TRUE(new_vertex.SetProperty(this->prop.second, memgraph::storage::PropertyValue(5)).HasValue());
+  auto edge = this->dba.InsertEdge(&this->v[4], &new_vertex, this->edge_type);
   ASSERT_TRUE(edge.HasValue());
-  ASSERT_TRUE(edge->SetProperty(prop.second, memgraph::storage::PropertyValue("not a number")).HasValue());
-  dba.AdvanceCommand();
-  EXPECT_THROW(ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true)), QueryRuntimeException);
+  ASSERT_TRUE(edge->SetProperty(this->prop.second, memgraph::storage::PropertyValue("not a number")).HasValue());
+  this->dba.AdvanceCommand();
+  EXPECT_THROW(this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true)), QueryRuntimeException);
 }
 
-TEST_F(QueryPlanExpandAllShortestPaths, NegativeWeight) {
-  auto new_vertex = dba.InsertVertex();
-  ASSERT_TRUE(new_vertex.SetProperty(prop.second, memgraph::storage::PropertyValue(5)).HasValue());
-  auto edge = dba.InsertEdge(&v[4], &new_vertex, edge_type);
+TYPED_TEST(QueryPlanExpandAllShortestPaths, NegativeWeight) {
+  auto new_vertex = this->dba.InsertVertex();
+  ASSERT_TRUE(new_vertex.SetProperty(this->prop.second, memgraph::storage::PropertyValue(5)).HasValue());
+  auto edge = this->dba.InsertEdge(&this->v[4], &new_vertex, this->edge_type);
   ASSERT_TRUE(edge.HasValue());
-  ASSERT_TRUE(edge->SetProperty(prop.second, memgraph::storage::PropertyValue(-10)).HasValue());  // negative weight
-  dba.AdvanceCommand();
-  EXPECT_THROW(ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true)), QueryRuntimeException);
+  ASSERT_TRUE(
+      edge->SetProperty(this->prop.second, memgraph::storage::PropertyValue(-10)).HasValue());  // negative weight
+  this->dba.AdvanceCommand();
+  EXPECT_THROW(this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true)), QueryRuntimeException);
 }
 
-TEST_F(QueryPlanExpandAllShortestPaths, NegativeUpperBound) {
-  EXPECT_THROW(ExpandAllShortest(EdgeAtom::Direction::BOTH, -1, LITERAL(true)), QueryRuntimeException);
+TYPED_TEST(QueryPlanExpandAllShortestPaths, NegativeUpperBound) {
+  EXPECT_THROW(this->ExpandAllShortest(EdgeAtom::Direction::BOTH, -1, LITERAL(true)), QueryRuntimeException);
 }
 
 // MultiplePaths testing on this graph:
@@ -2497,65 +2571,65 @@ TEST_F(QueryPlanExpandAllShortestPaths, NegativeUpperBound) {
 //  [2]-->--[3]->
 //       3       3
 
-TEST_F(QueryPlanExpandAllShortestPaths, MultiplePaths) {
-  auto new_vertex = dba.InsertVertex();
-  ASSERT_TRUE(new_vertex.SetProperty(prop.second, memgraph::storage::PropertyValue(6)).HasValue());
+TYPED_TEST(QueryPlanExpandAllShortestPaths, MultiplePaths) {
+  auto new_vertex = this->dba.InsertVertex();
+  ASSERT_TRUE(new_vertex.SetProperty(this->prop.second, memgraph::storage::PropertyValue(6)).HasValue());
 
-  auto edge = dba.InsertEdge(&v[4], &new_vertex, edge_type);
+  auto edge = this->dba.InsertEdge(&this->v[4], &new_vertex, this->edge_type);
   ASSERT_TRUE(edge.HasValue());
-  ASSERT_TRUE(edge->SetProperty(prop.second, memgraph::storage::PropertyValue(1)).HasValue());
-  dba.AdvanceCommand();
+  ASSERT_TRUE(edge->SetProperty(this->prop.second, memgraph::storage::PropertyValue(1)).HasValue());
+  this->dba.AdvanceCommand();
 
-  auto edge2 = dba.InsertEdge(&v[1], &new_vertex, edge_type);
+  auto edge2 = this->dba.InsertEdge(&this->v[1], &new_vertex, this->edge_type);
   ASSERT_TRUE(edge2.HasValue());
-  ASSERT_TRUE(edge2->SetProperty(prop.second, memgraph::storage::PropertyValue(5)).HasValue());
-  dba.AdvanceCommand();
+  ASSERT_TRUE(edge2->SetProperty(this->prop.second, memgraph::storage::PropertyValue(5)).HasValue());
+  this->dba.AdvanceCommand();
 
-  auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true));
-  std::sort(results.begin(), results.end(), compareResultType);
+  auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true));
+  std::sort(results.begin(), results.end(), compareResultType<TypeParam>);
   ASSERT_EQ(results.size(), 6);
-  EXPECT_EQ(GetProp(results[4].vertex), 6);
+  EXPECT_EQ(this->GetProp(results[4].vertex), 6);
   EXPECT_EQ(results[4].total_weight, 10);
-  EXPECT_EQ(GetProp(results[5].vertex), 6);
+  EXPECT_EQ(this->GetProp(results[5].vertex), 6);
   EXPECT_EQ(results[5].total_weight, 10);
 }
 
 // Uses graph from Basic test, with double edge 2->-3 and 3->-4
-TEST_F(QueryPlanExpandAllShortestPaths, MultiEdge) {
-  auto edge = dba.InsertEdge(&v[2], &v[3], edge_type);
+TYPED_TEST(QueryPlanExpandAllShortestPaths, MultiEdge) {
+  auto edge = this->dba.InsertEdge(&this->v[2], &this->v[3], this->edge_type);
   ASSERT_TRUE(edge.HasValue());
-  ASSERT_TRUE(edge->SetProperty(prop.second, memgraph::storage::PropertyValue(3)).HasValue());
-  dba.AdvanceCommand();
+  ASSERT_TRUE(edge->SetProperty(this->prop.second, memgraph::storage::PropertyValue(3)).HasValue());
+  this->dba.AdvanceCommand();
 
-  auto edge2 = dba.InsertEdge(&v[3], &v[4], edge_type);
+  auto edge2 = this->dba.InsertEdge(&this->v[3], &this->v[4], this->edge_type);
   ASSERT_TRUE(edge2.HasValue());
-  ASSERT_TRUE(edge2->SetProperty(prop.second, memgraph::storage::PropertyValue(3)).HasValue());
-  dba.AdvanceCommand();
+  ASSERT_TRUE(edge2->SetProperty(this->prop.second, memgraph::storage::PropertyValue(3)).HasValue());
+  this->dba.AdvanceCommand();
 
-  auto results = ExpandAllShortest(EdgeAtom::Direction::OUT, 1000, LITERAL(true));
-  std::sort(results.begin(), results.end(), compareResultType);
+  auto results = this->ExpandAllShortest(EdgeAtom::Direction::OUT, 1000, LITERAL(true));
+  std::sort(results.begin(), results.end(), compareResultType<TypeParam>);
   ASSERT_EQ(results.size(), 8);
-  EXPECT_EQ(GetProp(results[6].vertex), 4);
+  EXPECT_EQ(this->GetProp(results[6].vertex), 4);
   EXPECT_EQ(results[4].total_weight, 9);
-  EXPECT_EQ(GetProp(results[7].vertex), 4);
+  EXPECT_EQ(this->GetProp(results[7].vertex), 4);
   EXPECT_EQ(results[5].total_weight, 9);
 }
 
 #ifdef MG_ENTERPRISE
-TEST_F(QueryPlanExpandAllShortestPaths, BasicWithFineGrainedFiltering) {
+TYPED_TEST(QueryPlanExpandAllShortestPaths, BasicWithFineGrainedFiltering) {
   // All edge_types and labels allowed
   {
     memgraph::auth::User user{"test"};
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
-    auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true));
-    sort(results.begin(), results.end(), compareResultType);
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true));
+    sort(results.begin(), results.end(), compareResultType<TypeParam>);
 
     EXPECT_EQ(results[0].path.size(), 1);
     EXPECT_EQ(results[1].path.size(), 1);
     EXPECT_EQ(results[2].path.size(), 2);
 
-    EXPECT_EQ(GetDoubleProp(results[3].path[2]), 3);
+    EXPECT_EQ(this->GetDoubleProp(results[3].path[2]), 3);
   }
   // Denied all labels
   {
@@ -2563,7 +2637,7 @@ TEST_F(QueryPlanExpandAllShortestPaths, BasicWithFineGrainedFiltering) {
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().edge_type_permissions().Grant("*",
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
-    auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
     ASSERT_EQ(results.size(), 0);
   }
 
@@ -2572,7 +2646,7 @@ TEST_F(QueryPlanExpandAllShortestPaths, BasicWithFineGrainedFiltering) {
     memgraph::auth::User user{"test"};
     user.fine_grained_access_handler().label_permissions().Grant("l0", memgraph::auth::FineGrainedPermission::NOTHING);
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
-    auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
 
     ASSERT_EQ(results.size(), 0);
   }
@@ -2587,49 +2661,47 @@ TEST_F(QueryPlanExpandAllShortestPaths, BasicWithFineGrainedFiltering) {
     user.fine_grained_access_handler().label_permissions().Grant("l4", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
 
-    auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
     ASSERT_EQ(results.size(), 4);
     user.fine_grained_access_handler().label_permissions().Grant("l2", memgraph::auth::FineGrainedPermission::NOTHING);
-    auto filtered_results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto filtered_results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
 
     ASSERT_EQ(filtered_results.size(), 3);
   }
 
   // Deny edge type (created vertex 5 and edge vertex 4 to vertex 5)
   {
-    v.push_back(dba.InsertVertex());
-    ASSERT_TRUE(v.back().SetProperty(prop.second, memgraph::storage::PropertyValue(5)).HasValue());
-    ASSERT_TRUE(v.back().AddLabel(db.NameToLabel("l5")).HasValue());
-    dba.AdvanceCommand();
-    memgraph::storage::EdgeTypeId edge_type_filter = dba.NameToEdgeType("edge_type_filter");
-    auto edge = dba.InsertEdge(&v[4], &v[5], edge_type_filter);
-    ASSERT_TRUE(edge->SetProperty(prop.second, memgraph::storage::PropertyValue(1)).HasValue());
-    e.emplace(std::make_pair(4, 5), *edge);
-    dba.AdvanceCommand();
+    this->v.push_back(this->dba.InsertVertex());
+    ASSERT_TRUE(this->v.back().SetProperty(this->prop.second, memgraph::storage::PropertyValue(5)).HasValue());
+    ASSERT_TRUE(this->v.back().AddLabel(this->db->NameToLabel("l5")).HasValue());
+    this->dba.AdvanceCommand();
+    memgraph::storage::EdgeTypeId edge_type_filter = this->dba.NameToEdgeType("edge_type_filter");
+    auto edge = this->dba.InsertEdge(&this->v[4], &this->v[5], edge_type_filter);
+    ASSERT_TRUE(edge->SetProperty(this->prop.second, memgraph::storage::PropertyValue(1)).HasValue());
+    this->e.emplace(std::make_pair(4, 5), *edge);
+    this->dba.AdvanceCommand();
 
     memgraph::auth::User user{"test"};
     user.fine_grained_access_handler().label_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().edge_type_permissions().Grant("*", memgraph::auth::FineGrainedPermission::READ);
-    auto results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
     ASSERT_EQ(results.size(), 5);
 
     user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type",
                                                                      memgraph::auth::FineGrainedPermission::READ);
     user.fine_grained_access_handler().edge_type_permissions().Grant("edge_type_filter",
                                                                      memgraph::auth::FineGrainedPermission::NOTHING);
-    auto filtered_results = ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
+    auto filtered_results = this->ExpandAllShortest(EdgeAtom::Direction::BOTH, 1000, LITERAL(true), 0, nullptr, &user);
 
     ASSERT_EQ(filtered_results.size(), 4);
   }
 }
 #endif
 
-TEST(QueryPlan, ExpandOptional) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, ExpandOptional) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   // graph (v2 {p: 2})<-[:T]-(v1 {p: 1})-[:T]->(v3 {p: 2})
@@ -2646,8 +2718,8 @@ TEST(QueryPlan, ExpandOptional) {
   dba.AdvanceCommand();
 
   // MATCH (n) OPTIONAL MATCH (n)-[r]->(m)
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto r_m = MakeExpand(storage, symbol_table, nullptr, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto r_m = MakeExpand(this->storage, symbol_table, nullptr, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
                         memgraph::storage::View::OLD);
   auto optional = std::make_shared<plan::Optional>(n.op_, r_m.op_, std::vector<Symbol>{r_m.edge_sym_, r_m.node_sym_});
 
@@ -2656,7 +2728,7 @@ TEST(QueryPlan, ExpandOptional) {
   auto r_ne = NEXPR("r", IDENT("r")->MapTo(r_m.edge_sym_))->MapTo(symbol_table.CreateSymbol("r", true));
   auto m_ne = NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))->MapTo(symbol_table.CreateSymbol("m", true));
   auto produce = MakeProduce(optional, n_ne, r_ne, m_ne);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(4, results.size());
   int v1_is_n_count = 0;
@@ -2677,34 +2749,29 @@ TEST(QueryPlan, ExpandOptional) {
   EXPECT_EQ(2, v1_is_n_count);
 }
 
-TEST(QueryPlan, OptionalMatchEmptyDB) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-
-  AstStorage storage;
+TYPED_TEST(QueryPlan, OptionalMatchEmptyDB) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
 
   // OPTIONAL MATCH (n)
-  auto n = MakeScanAll(storage, symbol_table, "n");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
   // RETURN n
   auto n_ne = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("n", true));
   auto optional = std::make_shared<plan::Optional>(nullptr, n.op_, std::vector<Symbol>{n.sym_});
   auto produce = MakeProduce(optional, n_ne);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(1, results.size());
   EXPECT_EQ(results[0][0].type(), TypedValue::Type::Null);
 }
 
-TEST(QueryPlan, OptionalMatchEmptyDBExpandFromNode) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+TYPED_TEST(QueryPlan, OptionalMatchEmptyDBExpandFromNode) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
   // OPTIONAL MATCH (n)
-  auto n = MakeScanAll(storage, symbol_table, "n");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
   auto optional = std::make_shared<plan::Optional>(nullptr, n.op_, std::vector<Symbol>{n.sym_});
   // WITH n
   auto n_ne = NEXPR("n", IDENT("n")->MapTo(n.sym_));
@@ -2712,20 +2779,19 @@ TEST(QueryPlan, OptionalMatchEmptyDBExpandFromNode) {
   n_ne->MapTo(with_n_sym);
   auto with = MakeProduce(optional, n_ne);
   // MATCH (n) -[r]-> (m)
-  auto r_m = MakeExpand(storage, symbol_table, with, with_n_sym, "r", EdgeAtom::Direction::OUT, {}, "m", false,
+  auto r_m = MakeExpand(this->storage, symbol_table, with, with_n_sym, "r", EdgeAtom::Direction::OUT, {}, "m", false,
                         memgraph::storage::View::OLD);
   // RETURN m
   auto m_ne = NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))->MapTo(symbol_table.CreateSymbol("m", true));
   auto produce = MakeProduce(r_m.op_, m_ne);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(0, results.size());
 }
 
-TEST(QueryPlan, OptionalMatchThenExpandToMissingNode) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, OptionalMatchThenExpandToMissingNode) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Make a graph with 2 connected, unlabeled nodes.
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
@@ -2734,14 +2800,13 @@ TEST(QueryPlan, OptionalMatchThenExpandToMissingNode) {
   dba.AdvanceCommand();
   EXPECT_EQ(2, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
   EXPECT_EQ(1, CountEdges(&dba, memgraph::storage::View::OLD));
-  AstStorage storage;
   SymbolTable symbol_table;
   // OPTIONAL MATCH (n :missing)
-  auto n = MakeScanAll(storage, symbol_table, "n");
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
   auto label_missing = "missing";
-  n.node_->labels_.emplace_back(storage.GetLabelIx(label_missing));
+  n.node_->labels_.emplace_back(this->storage.GetLabelIx(label_missing));
 
-  auto *filter_expr = storage.Create<LabelsTest>(n.node_->identifier_, n.node_->labels_);
+  auto *filter_expr = this->storage.template Create<LabelsTest>(n.node_->identifier_, n.node_->labels_);
   auto node_filter = std::make_shared<Filter>(n.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
   auto optional = std::make_shared<plan::Optional>(nullptr, node_filter, std::vector<Symbol>{n.sym_});
   // WITH n
@@ -2750,7 +2815,7 @@ TEST(QueryPlan, OptionalMatchThenExpandToMissingNode) {
   n_ne->MapTo(with_n_sym);
   auto with = MakeProduce(optional, n_ne);
   // MATCH (m) -[r]-> (n)
-  auto m = MakeScanAll(storage, symbol_table, "m", with);
+  auto m = MakeScanAll(this->storage, symbol_table, "m", with);
   auto edge_direction = EdgeAtom::Direction::OUT;
   auto edge = EDGE("r", edge_direction);
   auto edge_sym = symbol_table.CreateSymbol("r", true);
@@ -2763,15 +2828,14 @@ TEST(QueryPlan, OptionalMatchThenExpandToMissingNode) {
   // RETURN m
   auto m_ne = NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(symbol_table.CreateSymbol("m", true));
   auto produce = MakeProduce(expand, m_ne);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(0, results.size());
 }
 
-TEST(QueryPlan, ExpandExistingNode) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, ExpandExistingNode) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // make a graph (v1)->(v2) that
   // has a recursive edge (v1)->(v1)
@@ -2782,13 +2846,12 @@ TEST(QueryPlan, ExpandExistingNode) {
   ASSERT_TRUE(dba.InsertEdge(&v1, &v2, edge_type).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   auto test_existing = [&](bool with_existing, int expected_result_count) {
-    auto n = MakeScanAll(storage, symbol_table, "n");
-    auto r_n = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "n", with_existing,
-                          memgraph::storage::View::OLD);
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
+    auto r_n = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "n",
+                          with_existing, memgraph::storage::View::OLD);
     if (with_existing)
       r_n.op_ = std::make_shared<Expand>(n.op_, n.sym_, n.sym_, r_n.edge_sym_, r_n.edge_->direction_,
                                          std::vector<memgraph::storage::EdgeTypeId>{}, with_existing,
@@ -2797,7 +2860,7 @@ TEST(QueryPlan, ExpandExistingNode) {
     // make a named expression and a produce
     auto output = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(r_n.op_, output);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     auto results = CollectProduce(*produce, &context);
     EXPECT_EQ(results.size(), expected_result_count);
   };
@@ -2806,31 +2869,28 @@ TEST(QueryPlan, ExpandExistingNode) {
   test_existing(false, 2);
 }
 
-TEST(QueryPlan, ExpandBothCycleEdgeCase) {
+TYPED_TEST(QueryPlan, ExpandBothCycleEdgeCase) {
   // we're testing that expanding on BOTH
   // does only one expansion for a cycle
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto v = dba.InsertVertex();
   ASSERT_TRUE(dba.InsertEdge(&v, &v, dba.NameToEdgeType("et")).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto r_ = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::BOTH, {}, "_", false,
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto r_ = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::BOTH, {}, "_", false,
                        memgraph::storage::View::OLD);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(1, PullAll(*r_.op_, &context));
 }
 
-TEST(QueryPlan, EdgeFilter) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, EdgeFilter) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // make an N-star expanding from (v1)
   // where only one edge will qualify
@@ -2840,7 +2900,7 @@ TEST(QueryPlan, EdgeFilter) {
   for (int j = 0; j < 2; ++j) edge_types.push_back(dba.NameToEdgeType("et" + std::to_string(j)));
   std::vector<memgraph::query::VertexAccessor> vertices;
   for (int i = 0; i < 7; ++i) vertices.push_back(dba.InsertVertex());
-  auto prop = PROPERTY_PAIR("property");
+  auto prop = PROPERTY_PAIR(dba, "property");
   std::vector<memgraph::query::EdgeAccessor> edges;
   for (int i = 0; i < 6; ++i) {
     edges.push_back(*dba.InsertEdge(&vertices[0], &vertices[i + 1], edge_types[i % 2]));
@@ -2857,27 +2917,26 @@ TEST(QueryPlan, EdgeFilter) {
   }
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   auto test_filter = [&]() {
     // define an operator tree for query
     // MATCH (n)-[r :et0 {property: 42}]->(m) RETURN m
 
-    auto n = MakeScanAll(storage, symbol_table, "n");
+    auto n = MakeScanAll(this->storage, symbol_table, "n");
     const auto &edge_type = edge_types[0];
-    auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {edge_type}, "m", false,
-                          memgraph::storage::View::OLD);
-    r_m.edge_->edge_types_.push_back(storage.GetEdgeTypeIx(dba.EdgeTypeToName(edge_type)));
-    std::get<0>(r_m.edge_->properties_)[storage.GetPropertyIx(prop.first)] = LITERAL(42);
-    auto *filter_expr = EQ(PROPERTY_LOOKUP(r_m.edge_->identifier_, prop), LITERAL(42));
+    auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {edge_type}, "m",
+                          false, memgraph::storage::View::OLD);
+    r_m.edge_->edge_types_.push_back(this->storage.GetEdgeTypeIx(dba.EdgeTypeToName(edge_type)));
+    std::get<0>(r_m.edge_->properties_)[this->storage.GetPropertyIx(prop.first)] = LITERAL(42);
+    auto *filter_expr = EQ(PROPERTY_LOOKUP(dba, r_m.edge_->identifier_, prop), LITERAL(42));
     auto edge_filter = std::make_shared<Filter>(r_m.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
 
     // make a named expression and a produce
     auto output =
         NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(edge_filter, output);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     return PullAll(*produce, &context);
   };
 
@@ -2889,10 +2948,9 @@ TEST(QueryPlan, EdgeFilter) {
   EXPECT_EQ(3, test_filter());
 }
 
-TEST(QueryPlan, EdgeFilterMultipleTypes) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, EdgeFilterMultipleTypes) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
@@ -2904,53 +2962,49 @@ TEST(QueryPlan, EdgeFilterMultipleTypes) {
   ASSERT_TRUE(dba.InsertEdge(&v1, &v2, type_3).HasValue());
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   // make a scan all
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto r_m = MakeExpand(storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {type_1, type_2}, "m",
-                        false, memgraph::storage::View::OLD);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto r_m = MakeExpand(this->storage, symbol_table, n.op_, n.sym_, "r", EdgeAtom::Direction::OUT, {type_1, type_2},
+                        "m", false, memgraph::storage::View::OLD);
 
   // make a named expression and a produce
   auto output =
       NEXPR("m", IDENT("m")->MapTo(r_m.node_sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto produce = MakeProduce(r_m.op_, output);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 2);
 }
 
-TEST(QueryPlan, Filter) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, Filter) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // add a 6 nodes with property 'prop', 2 have true as value
-  auto property = PROPERTY_PAIR("property");
+  auto property = PROPERTY_PAIR(dba, "property");
   for (int i = 0; i < 6; ++i)
     ASSERT_TRUE(
         dba.InsertVertex().SetProperty(property.second, memgraph::storage::PropertyValue(i % 3 == 0)).HasValue());
   dba.InsertVertex();  // prop not set, gives NULL
   dba.AdvanceCommand();
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto e = PROPERTY_LOOKUP(IDENT("n")->MapTo(n.sym_), property);
+  auto n = MakeScanAll(this->storage, symbol_table, "n");
+  auto e = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(n.sym_), property);
   auto f = std::make_shared<Filter>(n.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, e);
 
   auto output = NEXPR("x", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
   auto produce = MakeProduce(f, output);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(CollectProduce(*produce, &context).size(), 2);
 }
 
-TEST(QueryPlan, EdgeUniquenessFilter) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, EdgeUniquenessFilter) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   // make a graph that has (v1)->(v2) and a recursive edge (v1)->(v1)
   auto v1 = dba.InsertVertex();
@@ -2961,19 +3015,18 @@ TEST(QueryPlan, EdgeUniquenessFilter) {
   dba.AdvanceCommand();
 
   auto check_expand_results = [&](bool edge_uniqueness) {
-    AstStorage storage;
     SymbolTable symbol_table;
 
-    auto n1 = MakeScanAll(storage, symbol_table, "n1");
-    auto r1_n2 = MakeExpand(storage, symbol_table, n1.op_, n1.sym_, "r1", EdgeAtom::Direction::OUT, {}, "n2", false,
-                            memgraph::storage::View::OLD);
-    std::shared_ptr<LogicalOperator> last_op = r1_n2.op_;
-    auto r2_n3 = MakeExpand(storage, symbol_table, last_op, r1_n2.node_sym_, "r2", EdgeAtom::Direction::OUT, {}, "n3",
+    auto n1 = MakeScanAll(this->storage, symbol_table, "n1");
+    auto r1_n2 = MakeExpand(this->storage, symbol_table, n1.op_, n1.sym_, "r1", EdgeAtom::Direction::OUT, {}, "n2",
                             false, memgraph::storage::View::OLD);
+    std::shared_ptr<LogicalOperator> last_op = r1_n2.op_;
+    auto r2_n3 = MakeExpand(this->storage, symbol_table, last_op, r1_n2.node_sym_, "r2", EdgeAtom::Direction::OUT, {},
+                            "n3", false, memgraph::storage::View::OLD);
     last_op = r2_n3.op_;
     if (edge_uniqueness)
       last_op = std::make_shared<EdgeUniquenessFilter>(last_op, r2_n3.edge_sym_, std::vector<Symbol>{r1_n2.edge_sym_});
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     return PullAll(*last_op, &context);
   };
 
@@ -2981,14 +3034,12 @@ TEST(QueryPlan, EdgeUniquenessFilter) {
   EXPECT_EQ(1, check_expand_results(true));
 }
 
-TEST(QueryPlan, Distinct) {
+TYPED_TEST(QueryPlan, Distinct) {
   // test queries like
   // UNWIND [1, 2, 3, 3] AS x RETURN DISTINCT x
 
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   SymbolTable symbol_table;
 
   auto check_distinct = [&](const std::vector<TypedValue> input, const std::vector<TypedValue> output,
@@ -3005,7 +3056,7 @@ TEST(QueryPlan, Distinct) {
     auto x_ne = NEXPR("x", x_expr);
     x_ne->MapTo(symbol_table.CreateSymbol("x_ne", true));
     auto produce = MakeProduce(distinct, x_ne);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     auto results = CollectProduce(*produce, &context);
     ASSERT_EQ(output.size(), results.size());
     auto output_it = output.begin();
@@ -3028,12 +3079,11 @@ TEST(QueryPlan, Distinct) {
       {TypedValue(3), TypedValue("two"), TypedValue(), TypedValue(true), TypedValue(false), TypedValue("TWO")}, false);
 }
 
-TEST(QueryPlan, ScanAllByLabel) {
-  memgraph::storage::Storage db;
-  auto label = db.NameToLabel("label");
-  [[maybe_unused]] auto _ = db.CreateIndex(label);
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(QueryPlan, ScanAllByLabel) {
+  auto label = this->db->NameToLabel("label");
+  [[maybe_unused]] auto _ = this->db->CreateIndex(label);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Add a vertex with a label and one without.
   auto labeled_vertex = dba.InsertVertex();
   ASSERT_TRUE(labeled_vertex.AddLabel(label).HasValue());
@@ -3041,25 +3091,24 @@ TEST(QueryPlan, ScanAllByLabel) {
   dba.AdvanceCommand();
   EXPECT_EQ(2, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
   // MATCH (n :label)
-  AstStorage storage;
   SymbolTable symbol_table;
-  auto scan_all_by_label = MakeScanAllByLabel(storage, symbol_table, "n", label);
+  auto scan_all_by_label = MakeScanAllByLabel(this->storage, symbol_table, "n", label);
   // RETURN n
   auto output = NEXPR("n", IDENT("n")->MapTo(scan_all_by_label.sym_))->MapTo(symbol_table.CreateSymbol("n", true));
   auto produce = MakeProduce(scan_all_by_label.op_, output);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(results.size(), 1);
   auto result_row = results[0];
   ASSERT_EQ(result_row.size(), 1);
-  EXPECT_EQ(result_row[0].ValueVertex(), labeled_vertex);
+  auto result_vertex = result_row[0].ValueVertex();
+  EXPECT_EQ(result_vertex.Gid(), labeled_vertex.Gid());
 }
 
-TEST(QueryPlan, ScanAllByLabelProperty) {
-  memgraph::storage::Storage db;
+TYPED_TEST(QueryPlan, ScanAllByLabelProperty) {
   // Add 5 vertices with same label, but with different property values.
-  auto label = db.NameToLabel("label");
-  auto prop = db.NameToProperty("prop");
+  auto label = this->db->NameToLabel("label");
+  auto prop = this->db->NameToProperty("prop");
   // vertex property values that will be stored into the DB
   std::vector<memgraph::storage::PropertyValue> values{
       memgraph::storage::PropertyValue(true),
@@ -3080,8 +3129,8 @@ TEST(QueryPlan, ScanAllByLabelProperty) {
       memgraph::storage::PropertyValue(
           std::vector<memgraph::storage::PropertyValue>{memgraph::storage::PropertyValue(2)})};
   {
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     for (const auto &value : values) {
       auto vertex = dba.InsertVertex();
       ASSERT_TRUE(vertex.AddLabel(label).HasValue());
@@ -3089,36 +3138,49 @@ TEST(QueryPlan, ScanAllByLabelProperty) {
     }
     ASSERT_FALSE(dba.Commit().HasError());
   }
-  [[maybe_unused]] auto _ = db.CreateIndex(label, prop);
+  [[maybe_unused]] auto _ = this->db->CreateIndex(label, prop);
 
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   ASSERT_EQ(14, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
 
   auto run_scan_all = [&](const TypedValue &lower, Bound::Type lower_type, const TypedValue &upper,
                           Bound::Type upper_type) {
-    AstStorage storage;
     SymbolTable symbol_table;
     auto scan_all =
-        MakeScanAllByLabelPropertyRange(storage, symbol_table, "n", label, prop, "prop",
+        MakeScanAllByLabelPropertyRange(this->storage, symbol_table, "n", label, prop, "prop",
                                         Bound{LITERAL(lower), lower_type}, Bound{LITERAL(upper), upper_type});
     // RETURN n
     auto output = NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))->MapTo(symbol_table.CreateSymbol("n", true));
     auto produce = MakeProduce(scan_all.op_, output);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     return CollectProduce(*produce, &context);
   };
 
+  auto check_no_order = [&](auto results, auto expected) -> bool {
+    for (size_t i = 0; i < expected.size(); i++) {
+      bool local_check = false;
+      for (size_t j = 0; j < results.size(); j++) {
+        bool local_equal =
+            (TypedValue(*results[j][0].ValueVertex().GetProperty(memgraph::storage::View::OLD, prop)) == expected[i])
+                .ValueBool();
+        if (local_equal) {
+          local_check = true;
+          break;
+        }
+      }
+      if (!local_check) {
+        return false;
+      }
+    }
+    return true;
+  };
+
   auto check = [&](TypedValue lower, Bound::Type lower_type, TypedValue upper, Bound::Type upper_type,
                    const std::vector<TypedValue> &expected) {
     auto results = run_scan_all(lower, lower_type, upper, upper_type);
     ASSERT_EQ(results.size(), expected.size());
-    for (size_t i = 0; i < expected.size(); i++) {
-      TypedValue equal =
-          TypedValue(*results[i][0].ValueVertex().GetProperty(memgraph::storage::View::OLD, prop)) == expected[i];
-      ASSERT_EQ(equal.type(), TypedValue::Type::Bool);
-      EXPECT_TRUE(equal.ValueBool());
-    }
+    ASSERT_TRUE(check_no_order(results, expected));
   };
 
   // normal ranges that return something
@@ -3166,15 +3228,14 @@ TEST(QueryPlan, ScanAllByLabelProperty) {
                QueryRuntimeException);
 }
 
-TEST(QueryPlan, ScanAllByLabelPropertyEqualityNoError) {
-  memgraph::storage::Storage db;
+TYPED_TEST(QueryPlan, ScanAllByLabelPropertyEqualityNoError) {
   // Add 2 vertices with same label, but with property values that cannot be
   // compared. On the other hand, equality works fine.
-  auto label = db.NameToLabel("label");
-  auto prop = db.NameToProperty("prop");
+  auto label = this->db->NameToLabel("label");
+  auto prop = this->db->NameToProperty("prop");
   {
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto number_vertex = dba.InsertVertex();
     ASSERT_TRUE(number_vertex.AddLabel(label).HasValue());
     ASSERT_TRUE(number_vertex.SetProperty(prop, memgraph::storage::PropertyValue(42)).HasValue());
@@ -3183,19 +3244,18 @@ TEST(QueryPlan, ScanAllByLabelPropertyEqualityNoError) {
     ASSERT_TRUE(string_vertex.SetProperty(prop, memgraph::storage::PropertyValue("string")).HasValue());
     ASSERT_FALSE(dba.Commit().HasError());
   }
-  [[maybe_unused]] auto _ = db.CreateIndex(label, prop);
+  [[maybe_unused]] auto _ = this->db->CreateIndex(label, prop);
 
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   EXPECT_EQ(2, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
   // MATCH (n :label {prop: 42})
-  AstStorage storage;
   SymbolTable symbol_table;
-  auto scan_all = MakeScanAllByLabelPropertyValue(storage, symbol_table, "n", label, prop, "prop", LITERAL(42));
+  auto scan_all = MakeScanAllByLabelPropertyValue(this->storage, symbol_table, "n", label, prop, "prop", LITERAL(42));
   // RETURN n
   auto output = NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))->MapTo(symbol_table.CreateSymbol("n", true));
   auto produce = MakeProduce(scan_all.op_, output);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   ASSERT_EQ(results.size(), 1);
   const auto &row = results[0];
@@ -3206,13 +3266,12 @@ TEST(QueryPlan, ScanAllByLabelPropertyEqualityNoError) {
   EXPECT_TRUE(eq(value, TypedValue(42)));
 }
 
-TEST(QueryPlan, ScanAllByLabelPropertyValueError) {
-  memgraph::storage::Storage db;
-  auto label = db.NameToLabel("label");
-  auto prop = db.NameToProperty("prop");
+TYPED_TEST(QueryPlan, ScanAllByLabelPropertyValueError) {
+  auto label = this->db->NameToLabel("label");
+  auto prop = this->db->NameToProperty("prop");
   {
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     for (int i = 0; i < 2; ++i) {
       auto vertex = dba.InsertVertex();
       ASSERT_TRUE(vertex.AddLabel(label).HasValue());
@@ -3220,30 +3279,28 @@ TEST(QueryPlan, ScanAllByLabelPropertyValueError) {
     }
     ASSERT_FALSE(dba.Commit().HasError());
   }
-  [[maybe_unused]] auto _ = db.CreateIndex(label, prop);
+  [[maybe_unused]] auto _ = this->db->CreateIndex(label, prop);
 
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   EXPECT_EQ(2, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
   // MATCH (m), (n :label {prop: m})
-  AstStorage storage;
   SymbolTable symbol_table;
-  auto scan_all = MakeScanAll(storage, symbol_table, "m");
+  auto scan_all = MakeScanAll(this->storage, symbol_table, "m");
   auto *ident_m = IDENT("m");
   ident_m->MapTo(scan_all.sym_);
   auto scan_index =
-      MakeScanAllByLabelPropertyValue(storage, symbol_table, "n", label, prop, "prop", ident_m, scan_all.op_);
-  auto context = MakeContext(storage, symbol_table, &dba);
+      MakeScanAllByLabelPropertyValue(this->storage, symbol_table, "n", label, prop, "prop", ident_m, scan_all.op_);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_THROW(PullAll(*scan_index.op_, &context), QueryRuntimeException);
 }
 
-TEST(QueryPlan, ScanAllByLabelPropertyRangeError) {
-  memgraph::storage::Storage db;
-  auto label = db.NameToLabel("label");
-  auto prop = db.NameToProperty("prop");
+TYPED_TEST(QueryPlan, ScanAllByLabelPropertyRangeError) {
+  auto label = this->db->NameToLabel("label");
+  auto prop = this->db->NameToProperty("prop");
   {
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     for (int i = 0; i < 2; ++i) {
       auto vertex = dba.InsertVertex();
       ASSERT_TRUE(vertex.AddLabel(label).HasValue());
@@ -3251,52 +3308,51 @@ TEST(QueryPlan, ScanAllByLabelPropertyRangeError) {
     }
     ASSERT_FALSE(dba.Commit().HasError());
   }
-  [[maybe_unused]] auto _ = db.CreateIndex(label, prop);
+  [[maybe_unused]] auto _ = this->db->CreateIndex(label, prop);
 
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   EXPECT_EQ(2, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
   // MATCH (m), (n :label {prop: m})
-  AstStorage storage;
   SymbolTable symbol_table;
-  auto scan_all = MakeScanAll(storage, symbol_table, "m");
+  auto scan_all = MakeScanAll(this->storage, symbol_table, "m");
   auto *ident_m = IDENT("m");
   ident_m->MapTo(scan_all.sym_);
   {
     // Lower bound isn't property value
     auto scan_index =
-        MakeScanAllByLabelPropertyRange(storage, symbol_table, "n", label, prop, "prop",
+        MakeScanAllByLabelPropertyRange(this->storage, symbol_table, "n", label, prop, "prop",
                                         Bound{ident_m, Bound::Type::INCLUSIVE}, std::nullopt, scan_all.op_);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_THROW(PullAll(*scan_index.op_, &context), QueryRuntimeException);
   }
   {
     // Upper bound isn't property value
-    auto scan_index = MakeScanAllByLabelPropertyRange(storage, symbol_table, "n", label, prop, "prop", std::nullopt,
-                                                      Bound{ident_m, Bound::Type::INCLUSIVE}, scan_all.op_);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto scan_index =
+        MakeScanAllByLabelPropertyRange(this->storage, symbol_table, "n", label, prop, "prop", std::nullopt,
+                                        Bound{ident_m, Bound::Type::INCLUSIVE}, scan_all.op_);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_THROW(PullAll(*scan_index.op_, &context), QueryRuntimeException);
   }
   {
     // Both bounds aren't property value
-    auto scan_index = MakeScanAllByLabelPropertyRange(storage, symbol_table, "n", label, prop, "prop",
+    auto scan_index = MakeScanAllByLabelPropertyRange(this->storage, symbol_table, "n", label, prop, "prop",
                                                       Bound{ident_m, Bound::Type::INCLUSIVE},
                                                       Bound{ident_m, Bound::Type::INCLUSIVE}, scan_all.op_);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_THROW(PullAll(*scan_index.op_, &context), QueryRuntimeException);
   }
 }
 
-TEST(QueryPlan, ScanAllByLabelPropertyEqualNull) {
-  memgraph::storage::Storage db;
+TYPED_TEST(QueryPlan, ScanAllByLabelPropertyEqualNull) {
   // Add 2 vertices with the same label, but one has a property value while
   // the other does not. Checking if the value is equal to null, should
   // yield no results.
-  auto label = db.NameToLabel("label");
-  auto prop = db.NameToProperty("prop");
+  auto label = this->db->NameToLabel("label");
+  auto prop = this->db->NameToProperty("prop");
   {
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto vertex = dba.InsertVertex();
     ASSERT_TRUE(vertex.AddLabel(label).HasValue());
     auto vertex_with_prop = dba.InsertVertex();
@@ -3304,34 +3360,32 @@ TEST(QueryPlan, ScanAllByLabelPropertyEqualNull) {
     ASSERT_TRUE(vertex_with_prop.SetProperty(prop, memgraph::storage::PropertyValue(42)).HasValue());
     ASSERT_FALSE(dba.Commit().HasError());
   }
-  [[maybe_unused]] auto _ = db.CreateIndex(label, prop);
+  [[maybe_unused]] auto _ = this->db->CreateIndex(label, prop);
 
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   EXPECT_EQ(2, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
   // MATCH (n :label {prop: 42})
-  AstStorage storage;
   SymbolTable symbol_table;
   auto scan_all =
-      MakeScanAllByLabelPropertyValue(storage, symbol_table, "n", label, prop, "prop", LITERAL(TypedValue()));
+      MakeScanAllByLabelPropertyValue(this->storage, symbol_table, "n", label, prop, "prop", LITERAL(TypedValue()));
   // RETURN n
   auto output = NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))->MapTo(symbol_table.CreateSymbol("n", true));
   auto produce = MakeProduce(scan_all.op_, output);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 0);
 }
 
-TEST(QueryPlan, ScanAllByLabelPropertyRangeNull) {
-  memgraph::storage::Storage db;
+TYPED_TEST(QueryPlan, ScanAllByLabelPropertyRangeNull) {
   // Add 2 vertices with the same label, but one has a property value while
   // the other does not. Checking if the value is between nulls, should
   // yield no results.
-  auto label = db.NameToLabel("label");
-  auto prop = db.NameToProperty("prop");
+  auto label = this->db->NameToLabel("label");
+  auto prop = this->db->NameToProperty("prop");
   {
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto vertex = dba.InsertVertex();
     ASSERT_TRUE(vertex.AddLabel(label).HasValue());
     auto vertex_with_prop = dba.InsertVertex();
@@ -3339,44 +3393,41 @@ TEST(QueryPlan, ScanAllByLabelPropertyRangeNull) {
     ASSERT_TRUE(vertex_with_prop.SetProperty(prop, memgraph::storage::PropertyValue(42)).HasValue());
     ASSERT_FALSE(dba.Commit().HasError());
   }
-  [[maybe_unused]] auto _ = db.CreateIndex(label, prop);
+  [[maybe_unused]] auto _ = this->db->CreateIndex(label, prop);
 
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   EXPECT_EQ(2, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
   // MATCH (n :label) WHERE null <= n.prop < null
-  AstStorage storage;
   SymbolTable symbol_table;
-  auto scan_all = MakeScanAllByLabelPropertyRange(storage, symbol_table, "n", label, prop, "prop",
+  auto scan_all = MakeScanAllByLabelPropertyRange(this->storage, symbol_table, "n", label, prop, "prop",
                                                   Bound{LITERAL(TypedValue()), Bound::Type::INCLUSIVE},
                                                   Bound{LITERAL(TypedValue()), Bound::Type::EXCLUSIVE});
   // RETURN n
   auto output = NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))->MapTo(symbol_table.CreateSymbol("n", true));
   auto produce = MakeProduce(scan_all.op_, output);
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 0);
 }
 
-TEST(QueryPlan, ScanAllByLabelPropertyNoValueInIndexContinuation) {
-  memgraph::storage::Storage db;
-  auto label = db.NameToLabel("label");
-  auto prop = db.NameToProperty("prop");
+TYPED_TEST(QueryPlan, ScanAllByLabelPropertyNoValueInIndexContinuation) {
+  auto label = this->db->NameToLabel("label");
+  auto prop = this->db->NameToProperty("prop");
   {
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto v = dba.InsertVertex();
     ASSERT_TRUE(v.AddLabel(label).HasValue());
     ASSERT_TRUE(v.SetProperty(prop, memgraph::storage::PropertyValue(2)).HasValue());
     ASSERT_FALSE(dba.Commit().HasError());
   }
-  [[maybe_unused]] auto _ = db.CreateIndex(label, prop);
+  [[maybe_unused]] auto _ = this->db->CreateIndex(label, prop);
 
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   EXPECT_EQ(1, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
 
-  AstStorage storage;
   SymbolTable symbol_table;
 
   // UNWIND [1, 2, 3] as x
@@ -3387,24 +3438,24 @@ TEST(QueryPlan, ScanAllByLabelPropertyNoValueInIndexContinuation) {
   x_expr->MapTo(x);
 
   // MATCH (n :label {prop: x})
-  auto scan_all = MakeScanAllByLabelPropertyValue(storage, symbol_table, "n", label, prop, "prop", x_expr, unwind);
+  auto scan_all =
+      MakeScanAllByLabelPropertyValue(this->storage, symbol_table, "n", label, prop, "prop", x_expr, unwind);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, symbol_table, &dba);
   EXPECT_EQ(PullAll(*scan_all.op_, &context), 1);
 }
 
-TEST(QueryPlan, ScanAllEqualsScanAllByLabelProperty) {
-  memgraph::storage::Storage db;
-  auto label = db.NameToLabel("label");
-  auto prop = db.NameToProperty("prop");
+TYPED_TEST(QueryPlan, ScanAllEqualsScanAllByLabelProperty) {
+  auto label = this->db->NameToLabel("label");
+  auto prop = this->db->NameToProperty("prop");
 
   // Insert vertices
   const int vertex_count = 300, vertex_prop_count = 50;
   const int prop_value1 = 42, prop_value2 = 69;
 
   for (int i = 0; i < vertex_count; ++i) {
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto v = dba.InsertVertex();
     ASSERT_TRUE(v.AddLabel(label).HasValue());
     ASSERT_TRUE(v.SetProperty(prop, memgraph::storage::PropertyValue(i < vertex_prop_count ? prop_value1 : prop_value2))
@@ -3412,44 +3463,42 @@ TEST(QueryPlan, ScanAllEqualsScanAllByLabelProperty) {
     ASSERT_FALSE(dba.Commit().HasError());
   }
 
-  [[maybe_unused]] auto _ = db.CreateIndex(label, prop);
+  [[maybe_unused]] auto _ = this->db->CreateIndex(label, prop);
 
   // Make sure there are `vertex_count` vertices
   {
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     EXPECT_EQ(vertex_count, CountIterable(dba.Vertices(memgraph::storage::View::OLD)));
   }
 
   // Make sure there are `vertex_prop_count` results when using index
-  auto count_with_index = [&db, &label, &prop](int prop_value, int prop_count) {
-    AstStorage storage;
+  auto count_with_index = [this, &label, &prop](int prop_value, int prop_count) {
     SymbolTable symbol_table;
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
     auto scan_all_by_label_property_value =
-        MakeScanAllByLabelPropertyValue(storage, symbol_table, "n", label, prop, "prop", LITERAL(prop_value));
+        MakeScanAllByLabelPropertyValue(this->storage, symbol_table, "n", label, prop, "prop", LITERAL(prop_value));
     auto output = NEXPR("n", IDENT("n")->MapTo(scan_all_by_label_property_value.sym_))
                       ->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(scan_all_by_label_property_value.op_, output);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_EQ(PullAll(*produce, &context), prop_count);
   };
 
   // Make sure there are `vertex_count` results when using scan all
-  auto count_with_scan_all = [&db, &prop](int prop_value, int prop_count) {
-    AstStorage storage;
+  auto count_with_scan_all = [this, &prop](int prop_value, int prop_count) {
     SymbolTable symbol_table;
-    auto storage_dba = db.Access();
-    memgraph::query::DbAccessor dba(&storage_dba);
-    auto scan_all = MakeScanAll(storage, symbol_table, "n");
-    auto e = PROPERTY_LOOKUP(IDENT("n")->MapTo(scan_all.sym_), std::make_pair("prop", prop));
+    auto storage_dba = this->db->Access();
+    memgraph::query::DbAccessor dba(storage_dba.get());
+    auto scan_all = MakeScanAll(this->storage, symbol_table, "n");
+    auto e = PROPERTY_LOOKUP(dba, IDENT("n")->MapTo(scan_all.sym_), std::make_pair("prop", prop));
     auto filter = std::make_shared<Filter>(scan_all.op_, std::vector<std::shared_ptr<LogicalOperator>>{},
                                            EQ(e, LITERAL(prop_value)));
     auto output =
         NEXPR("n", IDENT("n")->MapTo(scan_all.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
     auto produce = MakeProduce(filter, output);
-    auto context = MakeContext(storage, symbol_table, &dba);
+    auto context = MakeContext(this->storage, symbol_table, &dba);
     EXPECT_EQ(PullAll(*produce, &context), prop_count);
   };
 
@@ -3460,24 +3509,26 @@ TEST(QueryPlan, ScanAllEqualsScanAllByLabelProperty) {
   count_with_scan_all(prop_value2, vertex_count - vertex_prop_count);
 }
 
+template <typename StorageType>
 class ExistsFixture : public testing::Test {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
   AstStorage storage;
   SymbolTable symbol_table;
 
-  std::pair<std::string, memgraph::storage::PropertyId> prop = PROPERTY_PAIR("property");
+  std::pair<std::string, memgraph::storage::PropertyId> prop = PROPERTY_PAIR(dba, "property");
 
   memgraph::query::VertexAccessor v1{dba.InsertVertex()};
   memgraph::query::VertexAccessor v2{dba.InsertVertex()};
-  memgraph::storage::EdgeTypeId edge_type{db.NameToEdgeType("Edge")};
+  memgraph::storage::EdgeTypeId edge_type{db->NameToEdgeType("Edge")};
   memgraph::query::EdgeAccessor r1{*dba.InsertEdge(&v1, &v2, edge_type)};
 
   memgraph::query::VertexAccessor v3{dba.InsertVertex()};
   memgraph::query::VertexAccessor v4{dba.InsertVertex()};
-  memgraph::storage::EdgeTypeId edge_type_unknown{db.NameToEdgeType("Other")};
+  memgraph::storage::EdgeTypeId edge_type_unknown{db->NameToEdgeType("Other")};
   memgraph::query::EdgeAccessor r2{*dba.InsertEdge(&v3, &v4, edge_type_unknown)};
 
   void SetUp() override {
@@ -3498,6 +3549,12 @@ class ExistsFixture : public testing::Test {
     dba.AdvanceCommand();
   }
 
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+
   int TestExists(std::string match_label, EdgeAtom::Direction direction,
                  std::vector<memgraph::storage::EdgeTypeId> edge_types,
                  std::optional<std::string> destination_label = std::nullopt,
@@ -3505,7 +3562,7 @@ class ExistsFixture : public testing::Test {
                  std::optional<int64_t> edge_prop = std::nullopt) {
     std::vector<std::string> edge_type_names;
     for (const auto &type : edge_types) {
-      edge_type_names.emplace_back(db.EdgeTypeToName(type));
+      edge_type_names.emplace_back(db->EdgeTypeToName(type));
     }
 
     auto *source_node = NODE("n");
@@ -3544,13 +3601,13 @@ class ExistsFixture : public testing::Test {
 
       if (destination_prop.has_value()) {
         auto prop_expr = static_cast<Expression *>(
-            EQ(PROPERTY_LOOKUP(destination_node->identifier_, prop), LITERAL(destination_prop.value())));
+            EQ(PROPERTY_LOOKUP(dba, destination_node->identifier_, prop), LITERAL(destination_prop.value())));
         filter_expr = filter_expr ? AND(filter_expr, prop_expr) : prop_expr;
       }
 
       if (edge_prop.has_value()) {
         auto prop_expr = static_cast<Expression *>(
-            EQ(PROPERTY_LOOKUP(expansion_edge->identifier_, prop), LITERAL(edge_prop.value())));
+            EQ(PROPERTY_LOOKUP(dba, expansion_edge->identifier_, prop), LITERAL(edge_prop.value())));
         filter_expr = filter_expr ? AND(filter_expr, prop_expr) : prop_expr;
       }
 
@@ -3579,12 +3636,12 @@ class ExistsFixture : public testing::Test {
                        std::vector<memgraph::storage::EdgeTypeId> second_edge_type, bool or_flag = false) {
     std::vector<std::string> first_edge_type_names;
     for (const auto &type : first_edge_type) {
-      first_edge_type_names.emplace_back(db.EdgeTypeToName(type));
+      first_edge_type_names.emplace_back(db->EdgeTypeToName(type));
     }
 
     std::vector<std::string> second_edge_type_names;
     for (const auto &type : second_edge_type) {
-      second_edge_type_names.emplace_back(db.EdgeTypeToName(type));
+      second_edge_type_names.emplace_back(db->EdgeTypeToName(type));
     }
 
     auto *source_node = NODE("n");
@@ -3645,51 +3702,56 @@ class ExistsFixture : public testing::Test {
   }
 };
 
-TEST_F(ExistsFixture, BasicExists) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(ExistsFixture, StorageTypes);
+
+TYPED_TEST(ExistsFixture, BasicExists) {
   std::vector<memgraph::storage::EdgeTypeId> known_edge_types;
-  known_edge_types.push_back(edge_type);
+  known_edge_types.push_back(this->edge_type);
   std::vector<memgraph::storage::EdgeTypeId> unknown_edge_types;
-  unknown_edge_types.push_back(edge_type_unknown);
+  unknown_edge_types.push_back(this->edge_type_unknown);
 
-  EXPECT_EQ(1, TestExists("l1", EdgeAtom::Direction::OUT, {}));
-  EXPECT_EQ(1, TestExists("l1", EdgeAtom::Direction::BOTH, {}));
-  EXPECT_EQ(0, TestExists("l1", EdgeAtom::Direction::IN, {}));
-  EXPECT_EQ(1, TestExists("l1", EdgeAtom::Direction::OUT, known_edge_types));
-  EXPECT_EQ(0, TestExists("l1", EdgeAtom::Direction::OUT, unknown_edge_types));
+  EXPECT_EQ(1, this->TestExists("l1", EdgeAtom::Direction::OUT, {}));
+  EXPECT_EQ(1, this->TestExists("l1", EdgeAtom::Direction::BOTH, {}));
+  EXPECT_EQ(0, this->TestExists("l1", EdgeAtom::Direction::IN, {}));
+  EXPECT_EQ(1, this->TestExists("l1", EdgeAtom::Direction::OUT, known_edge_types));
+  EXPECT_EQ(0, this->TestExists("l1", EdgeAtom::Direction::OUT, unknown_edge_types));
 }
 
-TEST_F(ExistsFixture, ExistsWithFiltering) {
-  EXPECT_EQ(1, TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2"));
-  EXPECT_EQ(0, TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l3"));
+TYPED_TEST(ExistsFixture, ExistsWithFiltering) {
+  EXPECT_EQ(1, this->TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2"));
+  EXPECT_EQ(0, this->TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l3"));
 
-  EXPECT_EQ(1, TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2", 2));
-  EXPECT_EQ(0, TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2", 1));
+  EXPECT_EQ(1, this->TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2", 2));
+  EXPECT_EQ(0, this->TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2", 1));
 
-  EXPECT_EQ(1, TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2", std::nullopt, 1));
-  EXPECT_EQ(0, TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2", std::nullopt, 2));
+  EXPECT_EQ(1, this->TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2", std::nullopt, 1));
+  EXPECT_EQ(0, this->TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2", std::nullopt, 2));
 
-  EXPECT_EQ(1, TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2", 2, 1));
-  EXPECT_EQ(0, TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2", 1, 1));
+  EXPECT_EQ(1, this->TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2", 2, 1));
+  EXPECT_EQ(0, this->TestExists("l1", EdgeAtom::Direction::BOTH, {}, "l2", 1, 1));
 }
 
-TEST_F(ExistsFixture, DoubleFilters) {
-  EXPECT_EQ(1, TestDoubleExists("l1", EdgeAtom::Direction::BOTH, {}, {}, true));
-  EXPECT_EQ(1, TestDoubleExists("l1", EdgeAtom::Direction::BOTH, {}, {}, false));
+TYPED_TEST(ExistsFixture, DoubleFilters) {
+  EXPECT_EQ(1, this->TestDoubleExists("l1", EdgeAtom::Direction::BOTH, {}, {}, true));
+  EXPECT_EQ(1, this->TestDoubleExists("l1", EdgeAtom::Direction::BOTH, {}, {}, false));
 }
 
+template <typename StorageType>
 class SubqueriesFeature : public testing::Test {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
   AstStorage storage;
   SymbolTable symbol_table;
 
-  std::pair<std::string, memgraph::storage::PropertyId> prop = PROPERTY_PAIR("property");
+  std::pair<std::string, memgraph::storage::PropertyId> prop = PROPERTY_PAIR(dba, "property");
 
   memgraph::query::VertexAccessor v1{dba.InsertVertex()};
   memgraph::query::VertexAccessor v2{dba.InsertVertex()};
-  memgraph::storage::EdgeTypeId edge_type{db.NameToEdgeType("Edge")};
+  memgraph::storage::EdgeTypeId edge_type{db->NameToEdgeType("Edge")};
   memgraph::query::EdgeAccessor r1{*dba.InsertEdge(&v1, &v2, edge_type)};
 
   void SetUp() override {
@@ -3705,107 +3767,127 @@ class SubqueriesFeature : public testing::Test {
 
     dba.AdvanceCommand();
   }
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
 };
 
-TEST_F(SubqueriesFeature, BasicCartesian) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(SubqueriesFeature, StorageTypes);
+
+TYPED_TEST(SubqueriesFeature, BasicCartesian) {
   // MATCH (n) CALL { MATCH (m) RETURN m } RETURN n, m
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+  auto n = MakeScanAll(this->storage, this->symbol_table, "n");
+  auto return_n =
+      NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
 
-  auto m = MakeScanAll(storage, symbol_table, "m");
-  auto return_m = NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_2", true));
+  auto m = MakeScanAll(this->storage, this->symbol_table, "m");
+  auto return_m =
+      NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_2", true));
   auto produce_subquery = MakeProduce(m.op_, return_m);
 
   auto apply = std::make_shared<Apply>(n.op_, produce_subquery, true);
 
   auto produce = MakeProduce(apply, return_n, return_m);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 4);
 }
 
-TEST_F(SubqueriesFeature, BasicCartesianWithFilter) {
+TYPED_TEST(SubqueriesFeature, BasicCartesianWithFilter) {
   // MATCH (n) WHERE n.prop = 2 CALL { MATCH (m) RETURN m } RETURN n, m
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto *filter_expr = AND(storage.Create<LabelsTest>(n.node_->identifier_, n.node_->labels_),
-                          EQ(PROPERTY_LOOKUP(n.node_->identifier_, prop), LITERAL(2)));
+  auto n = MakeScanAll(this->storage, this->symbol_table, "n");
+  auto *filter_expr = AND(this->storage.template Create<LabelsTest>(n.node_->identifier_, n.node_->labels_),
+                          EQ(PROPERTY_LOOKUP(this->dba, n.node_->identifier_, this->prop), LITERAL(2)));
   auto filter = std::make_shared<Filter>(n.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
 
-  auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+  auto return_n =
+      NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
 
-  auto m = MakeScanAll(storage, symbol_table, "m");
-  auto return_m = NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_2", true));
+  auto m = MakeScanAll(this->storage, this->symbol_table, "m");
+  auto return_m =
+      NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_2", true));
   auto produce_subquery = MakeProduce(m.op_, return_m);
 
   auto apply = std::make_shared<Apply>(filter, produce_subquery, true);
 
   auto produce = MakeProduce(apply, return_n, return_m);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 2);
 }
 
-TEST_F(SubqueriesFeature, BasicCartesianWithFilterInsideSubquery) {
+TYPED_TEST(SubqueriesFeature, BasicCartesianWithFilterInsideSubquery) {
   // MATCH (n) CALL { MATCH (m) WHERE m.prop = 2 RETURN m } RETURN n, m
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+  auto n = MakeScanAll(this->storage, this->symbol_table, "n");
+  auto return_n =
+      NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
 
-  auto m = MakeScanAll(storage, symbol_table, "m");
-  auto *filter_expr = AND(storage.Create<LabelsTest>(n.node_->identifier_, n.node_->labels_),
-                          EQ(PROPERTY_LOOKUP(n.node_->identifier_, prop), LITERAL(2)));
+  auto m = MakeScanAll(this->storage, this->symbol_table, "m");
+  auto *filter_expr = AND(this->storage.template Create<LabelsTest>(n.node_->identifier_, n.node_->labels_),
+                          EQ(PROPERTY_LOOKUP(this->dba, n.node_->identifier_, this->prop), LITERAL(2)));
   auto filter = std::make_shared<Filter>(m.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
 
-  auto return_m = NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_2", true));
+  auto return_m =
+      NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_2", true));
   auto produce_subquery = MakeProduce(filter, return_m);
 
   auto apply = std::make_shared<Apply>(n.op_, produce_subquery, true);
 
   auto produce = MakeProduce(apply, return_n, return_m);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 2);
 }
 
-TEST_F(SubqueriesFeature, BasicCartesianWithFilterNoResults) {
+TYPED_TEST(SubqueriesFeature, BasicCartesianWithFilterNoResults) {
   // MATCH (n) WHERE n.prop = 3 CALL { MATCH (m) RETURN m } RETURN n, m
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto *filter_expr = AND(storage.Create<LabelsTest>(n.node_->identifier_, n.node_->labels_),
-                          EQ(PROPERTY_LOOKUP(n.node_->identifier_, prop), LITERAL(3)));
+  auto n = MakeScanAll(this->storage, this->symbol_table, "n");
+  auto *filter_expr = AND(this->storage.template Create<LabelsTest>(n.node_->identifier_, n.node_->labels_),
+                          EQ(PROPERTY_LOOKUP(this->dba, n.node_->identifier_, this->prop), LITERAL(3)));
   auto filter = std::make_shared<Filter>(n.op_, std::vector<std::shared_ptr<LogicalOperator>>{}, filter_expr);
 
-  auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+  auto return_n =
+      NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
 
-  auto m = MakeScanAll(storage, symbol_table, "m");
-  auto return_m = NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_2", true));
+  auto m = MakeScanAll(this->storage, this->symbol_table, "m");
+  auto return_m =
+      NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_2", true));
   auto produce_subquery = MakeProduce(m.op_, return_m);
 
   auto apply = std::make_shared<Apply>(filter, produce_subquery, true);
 
   auto produce = MakeProduce(apply, return_n, return_m);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 0);
 }
 
-TEST_F(SubqueriesFeature, SubqueryInsideSubqueryCartesian) {
+TYPED_TEST(SubqueriesFeature, SubqueryInsideSubqueryCartesian) {
   // MATCH (n) CALL { MATCH (m) CALL { MATCH (o) RETURN o} RETURN m, o } RETURN n, m, o
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+  auto n = MakeScanAll(this->storage, this->symbol_table, "n");
+  auto return_n =
+      NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
 
-  auto m = MakeScanAll(storage, symbol_table, "m");
-  auto return_m = NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_2", true));
+  auto m = MakeScanAll(this->storage, this->symbol_table, "m");
+  auto return_m =
+      NEXPR("m", IDENT("m")->MapTo(m.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_2", true));
 
-  auto o = MakeScanAll(storage, symbol_table, "o");
-  auto return_o = NEXPR("o", IDENT("o")->MapTo(o.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_3", true));
+  auto o = MakeScanAll(this->storage, this->symbol_table, "o");
+  auto return_o =
+      NEXPR("o", IDENT("o")->MapTo(o.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_3", true));
   auto produce_nested_subquery = MakeProduce(o.op_, return_o);
 
   auto inner_apply = std::make_shared<Apply>(m.op_, produce_nested_subquery, true);
@@ -3814,126 +3896,132 @@ TEST_F(SubqueriesFeature, SubqueryInsideSubqueryCartesian) {
   auto outer_apply = std::make_shared<Apply>(n.op_, produce_subquery, true);
   auto produce = MakeProduce(outer_apply, return_n, return_m, return_o);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
   auto results = CollectProduce(*produce, &context);
 
   EXPECT_EQ(results.size(), 8);
 }
 
-TEST_F(SubqueriesFeature, UnitSubquery) {
+TYPED_TEST(SubqueriesFeature, UnitSubquery) {
   // CALL { MATCH (m) RETURN m } RETURN m
 
   auto once = std::make_shared<Once>();
 
-  auto o = MakeScanAll(storage, symbol_table, "o");
-  auto return_o = NEXPR("o", IDENT("o")->MapTo(o.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_3", true));
+  auto o = MakeScanAll(this->storage, this->symbol_table, "o");
+  auto return_o =
+      NEXPR("o", IDENT("o")->MapTo(o.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_3", true));
   auto produce_subquery = MakeProduce(o.op_, return_o);
 
   auto apply = std::make_shared<Apply>(once, produce_subquery, true);
   auto produce = MakeProduce(apply, return_o);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
   auto results = CollectProduce(*produce, &context);
 
   EXPECT_EQ(results.size(), 2);
 }
 
-TEST_F(SubqueriesFeature, SubqueryWithBoundedSymbol) {
+TYPED_TEST(SubqueriesFeature, SubqueryWithBoundedSymbol) {
   // MATCH (n) CALL { WITH n MATCH (n)-[]->(m) RETURN m } RETURN n, m
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+  auto n = MakeScanAll(this->storage, this->symbol_table, "n");
+  auto return_n =
+      NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
 
   auto once = std::make_shared<Once>();
   auto produce_with = MakeProduce(once, return_n);
-  auto expand = MakeExpand(storage, symbol_table, produce_with, n.sym_, "r", EdgeAtom::Direction::OUT, {}, "m", false,
-                           memgraph::storage::View::OLD);
-  auto return_m =
-      NEXPR("m", IDENT("m")->MapTo(expand.node_sym_))->MapTo(symbol_table.CreateSymbol("named_expression_3", true));
+  auto expand = MakeExpand(this->storage, this->symbol_table, produce_with, n.sym_, "r", EdgeAtom::Direction::OUT, {},
+                           "m", false, memgraph::storage::View::OLD);
+  auto return_m = NEXPR("m", IDENT("m")->MapTo(expand.node_sym_))
+                      ->MapTo(this->symbol_table.CreateSymbol("named_expression_3", true));
   auto produce_subquery = MakeProduce(expand.op_, return_m);
 
   auto apply = std::make_shared<Apply>(n.op_, produce_subquery, true);
   auto produce = MakeProduce(apply, return_n, return_m);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
   auto results = CollectProduce(*produce, &context);
 
   EXPECT_EQ(results.size(), 1);
 }
 
-TEST_F(SubqueriesFeature, SubqueryWithUnionAll) {
+TYPED_TEST(SubqueriesFeature, SubqueryWithUnionAll) {
   // MATCH (n) CALL { MATCH (m) RETURN m UNION ALL MATCH (m) RETURN m } RETURN n, m
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+  auto n = MakeScanAll(this->storage, this->symbol_table, "n");
+  auto return_n =
+      NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
 
-  auto m1 = MakeScanAll(storage, symbol_table, "m");
-  auto return_m = NEXPR("m", IDENT("m")->MapTo(m1.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_2", true));
+  auto m1 = MakeScanAll(this->storage, this->symbol_table, "m");
+  auto return_m =
+      NEXPR("m", IDENT("m")->MapTo(m1.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_2", true));
   auto produce_left_union_subquery = MakeProduce(m1.op_, return_m);
 
-  auto m2 = MakeScanAll(storage, symbol_table, "m");
+  auto m2 = MakeScanAll(this->storage, this->symbol_table, "m");
   auto produce_right_union_subquery = MakeProduce(m2.op_, return_m);
 
   auto union_operator =
       std::make_shared<Union>(produce_left_union_subquery, produce_right_union_subquery, std::vector<Symbol>{m1.sym_},
-                              produce_left_union_subquery->OutputSymbols(symbol_table),
-                              produce_right_union_subquery->OutputSymbols(symbol_table));
+                              produce_left_union_subquery->OutputSymbols(this->symbol_table),
+                              produce_right_union_subquery->OutputSymbols(this->symbol_table));
 
   auto apply = std::make_shared<Apply>(n.op_, union_operator, true);
 
   auto produce = MakeProduce(apply, return_n, return_m);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 8);
 }
 
-TEST_F(SubqueriesFeature, SubqueryWithUnion) {
+TYPED_TEST(SubqueriesFeature, SubqueryWithUnion) {
   // MATCH (n) CALL { MATCH (m) RETURN m UNION MATCH (m) RETURN m } RETURN n, m
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+  auto n = MakeScanAll(this->storage, this->symbol_table, "n");
+  auto return_n =
+      NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
 
-  auto m1 = MakeScanAll(storage, symbol_table, "m");
+  auto m1 = MakeScanAll(this->storage, this->symbol_table, "m");
 
-  auto subquery_return_symbol = symbol_table.CreateSymbol("named_expression_2", true);
+  auto subquery_return_symbol = this->symbol_table.CreateSymbol("named_expression_2", true);
   auto return_m = NEXPR("m", IDENT("m")->MapTo(m1.sym_))->MapTo(subquery_return_symbol);
 
   auto produce_left_union_subquery = MakeProduce(m1.op_, return_m);
 
-  auto m2 = MakeScanAll(storage, symbol_table, "m");
+  auto m2 = MakeScanAll(this->storage, this->symbol_table, "m");
   auto produce_right_union_subquery = MakeProduce(m2.op_, return_m);
 
   auto union_operator = std::make_shared<Union>(produce_left_union_subquery, produce_right_union_subquery,
                                                 std::vector<Symbol>{subquery_return_symbol},
-                                                produce_left_union_subquery->OutputSymbols(symbol_table),
-                                                produce_right_union_subquery->OutputSymbols(symbol_table));
+                                                produce_left_union_subquery->OutputSymbols(this->symbol_table),
+                                                produce_right_union_subquery->OutputSymbols(this->symbol_table));
 
-  auto union_output_symbols = union_operator->OutputSymbols(symbol_table);
+  auto union_output_symbols = union_operator->OutputSymbols(this->symbol_table);
   auto distinct = std::make_shared<Distinct>(union_operator, std::vector<Symbol>{union_output_symbols});
 
   auto apply = std::make_shared<Apply>(n.op_, distinct, true);
 
   auto produce = MakeProduce(apply, return_n);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 4);
 }
 
-TEST_F(SubqueriesFeature, SubqueriesWithForeach) {
+TYPED_TEST(SubqueriesFeature, SubqueriesWithForeach) {
   // MATCH (n) CALL { FOREACH (i in range(1, 5) | CREATE (n)) } RETURN n
 
-  auto n = MakeScanAll(storage, symbol_table, "n");
-  auto return_n = NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(symbol_table.CreateSymbol("named_expression_1", true));
+  auto n = MakeScanAll(this->storage, this->symbol_table, "n");
+  auto return_n =
+      NEXPR("n", IDENT("n")->MapTo(n.sym_))->MapTo(this->symbol_table.CreateSymbol("named_expression_1", true));
 
   auto once_create = std::make_shared<Once>();
   NodeCreationInfo node_creation_info;
-  node_creation_info.symbol = symbol_table.CreateSymbol("n", true);
+  node_creation_info.symbol = this->symbol_table.CreateSymbol("n", true);
   auto create = std::make_shared<plan::CreateNode>(once_create, node_creation_info);
 
   auto once_foreach = std::make_shared<Once>();
-  auto iteration_symbol = symbol_table.CreateSymbol("i", true);
+  auto iteration_symbol = this->symbol_table.CreateSymbol("i", true);
   auto iterating_list = LIST(LITERAL(1), LITERAL(2), LITERAL(3), LITERAL(4), LITERAL(5));
   auto foreach = std::make_shared<plan::Foreach>(once_foreach, create, iterating_list, iteration_symbol);
   auto empty_result = std::make_shared<EmptyResult>(foreach);
@@ -3942,7 +4030,7 @@ TEST_F(SubqueriesFeature, SubqueriesWithForeach) {
 
   auto produce = MakeProduce(apply, return_n);
 
-  auto context = MakeContext(storage, symbol_table, &dba);
+  auto context = MakeContext(this->storage, this->symbol_table, &this->dba);
   auto results = CollectProduce(*produce, &context);
   EXPECT_EQ(results.size(), 2);
 }
diff --git a/tests/unit/query_plan_read_write_typecheck.cpp b/tests/unit/query_plan_read_write_typecheck.cpp
index 1b8ebbadd..dba2d5f57 100644
--- a/tests/unit/query_plan_read_write_typecheck.cpp
+++ b/tests/unit/query_plan_read_write_typecheck.cpp
@@ -11,6 +11,12 @@
 
 #include <gtest/gtest.h>
 
+#include <memory>
+
+#include "disk_test_utils.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
+
 #include "query/frontend/semantic/symbol_table.hpp"
 #include "query/plan/operator.hpp"
 #include "query/plan/read_write_type_checker.hpp"
@@ -21,15 +27,23 @@ using namespace memgraph::query;
 using namespace memgraph::query::plan;
 using RWType = ReadWriteTypeChecker::RWType;
 
+template <typename StorageType>
 class ReadWriteTypeCheckTest : public ::testing::Test {
  protected:
-  ReadWriteTypeCheckTest() : db(), dba(db.Access()) {}
-
+  const std::string testSuite = "query_plan_read_write_typecheck";
   AstStorage storage;
   SymbolTable symbol_table;
 
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor dba;
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  std::unique_ptr<memgraph::storage::Storage::Accessor> dba_storage{db->Access()};
+  memgraph::query::DbAccessor dba{dba_storage.get()};
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
 
   const Symbol &GetSymbol(std::string name) { return symbol_table.CreateSymbol(name, true); }
 
@@ -40,101 +54,106 @@ class ReadWriteTypeCheckTest : public ::testing::Test {
   }
 };
 
-TEST_F(ReadWriteTypeCheckTest, NONEOps) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(ReadWriteTypeCheckTest, StorageTypes);
+
+TYPED_TEST(ReadWriteTypeCheckTest, NONEOps) {
   std::shared_ptr<LogicalOperator> once = std::make_shared<Once>();
   std::shared_ptr<LogicalOperator> produce =
       std::make_shared<Produce>(once, std::vector<NamedExpression *>{NEXPR("n", IDENT("n"))});
-  CheckPlanType(produce.get(), RWType::NONE);
+  this->CheckPlanType(produce.get(), RWType::NONE);
 }
 
-TEST_F(ReadWriteTypeCheckTest, CreateNode) {
+TYPED_TEST(ReadWriteTypeCheckTest, CreateNode) {
   std::shared_ptr<LogicalOperator> once = std::make_shared<Once>();
   std::shared_ptr<LogicalOperator> create_node = std::make_shared<CreateNode>(once, NodeCreationInfo());
 
-  CheckPlanType(create_node.get(), RWType::W);
+  this->CheckPlanType(create_node.get(), RWType::W);
 }
 
-TEST_F(ReadWriteTypeCheckTest, Filter) {
-  std::shared_ptr<LogicalOperator> scan_all = std::make_shared<ScanAll>(nullptr, GetSymbol("node1"));
+TYPED_TEST(ReadWriteTypeCheckTest, Filter) {
+  std::shared_ptr<LogicalOperator> scan_all = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node1"));
   std::shared_ptr<LogicalOperator> filter =
       std::make_shared<Filter>(scan_all, std::vector<std::shared_ptr<LogicalOperator>>{},
-                               EQ(PROPERTY_LOOKUP("node1", dba.NameToProperty("prop")), LITERAL(0)));
+                               EQ(PROPERTY_LOOKUP(this->dba, "node1", this->dba.NameToProperty("prop")), LITERAL(0)));
 
-  CheckPlanType(filter.get(), RWType::R);
+  this->CheckPlanType(filter.get(), RWType::R);
 }
 
-TEST_F(ReadWriteTypeCheckTest, ScanAllBy) {
+TYPED_TEST(ReadWriteTypeCheckTest, ScanAllBy) {
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAllByLabelPropertyRange>(
-      nullptr, GetSymbol("node"), dba.NameToLabel("Label"), dba.NameToProperty("prop"), "prop",
+      nullptr, this->GetSymbol("node"), this->dba.NameToLabel("Label"), this->dba.NameToProperty("prop"), "prop",
       memgraph::utils::MakeBoundInclusive<Expression *>(LITERAL(1)),
       memgraph::utils::MakeBoundExclusive<Expression *>(LITERAL(20)));
-  last_op =
-      std::make_shared<ScanAllByLabelPropertyValue>(last_op, GetSymbol("node"), dba.NameToLabel("Label"),
-                                                    dba.NameToProperty("prop"), "prop", ADD(LITERAL(21), LITERAL(21)));
+  last_op = std::make_shared<ScanAllByLabelPropertyValue>(
+      last_op, this->GetSymbol("node"), this->dba.NameToLabel("Label"), this->dba.NameToProperty("prop"), "prop",
+      ADD(LITERAL(21), LITERAL(21)));
 
-  CheckPlanType(last_op.get(), RWType::R);
+  this->CheckPlanType(last_op.get(), RWType::R);
 }
 
-TEST_F(ReadWriteTypeCheckTest, OrderByAndLimit) {
+TYPED_TEST(ReadWriteTypeCheckTest, OrderByAndLimit) {
   // We build an operator tree that would result from e.g.
   // MATCH (node:label)
   // WHERE n.property = 5
   // RETURN n
   // ORDER BY n.property
   // LIMIT 10
-  Symbol node_sym = GetSymbol("node");
-  memgraph::storage::LabelId label = dba.NameToLabel("label");
-  memgraph::storage::PropertyId prop = dba.NameToProperty("property");
+  Symbol node_sym = this->GetSymbol("node");
+  memgraph::storage::LabelId label = this->dba.NameToLabel("label");
+  memgraph::storage::PropertyId prop = this->dba.NameToProperty("property");
 
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<Once>();
   last_op = std::make_shared<ScanAllByLabel>(last_op, node_sym, label);
   last_op = std::make_shared<Filter>(last_op, std::vector<std::shared_ptr<LogicalOperator>>{},
-                                     EQ(PROPERTY_LOOKUP("node", prop), LITERAL(5)));
+                                     EQ(PROPERTY_LOOKUP(this->dba, "node", prop), LITERAL(5)));
   last_op = std::make_shared<Produce>(last_op, std::vector<NamedExpression *>{NEXPR("n", IDENT("n"))});
-  last_op = std::make_shared<OrderBy>(last_op, std::vector<SortItem>{{Ordering::DESC, PROPERTY_LOOKUP("node", prop)}},
+  last_op = std::make_shared<OrderBy>(last_op,
+                                      std::vector<SortItem>{{Ordering::DESC, PROPERTY_LOOKUP(this->dba, "node", prop)}},
                                       std::vector<Symbol>{node_sym});
   last_op = std::make_shared<Limit>(last_op, LITERAL(10));
 
-  CheckPlanType(last_op.get(), RWType::R);
+  this->CheckPlanType(last_op.get(), RWType::R);
 }
 
-TEST_F(ReadWriteTypeCheckTest, Delete) {
-  auto node_sym = GetSymbol("node1");
+TYPED_TEST(ReadWriteTypeCheckTest, Delete) {
+  auto node_sym = this->GetSymbol("node1");
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node_sym);
 
-  last_op =
-      std::make_shared<Expand>(last_op, node_sym, GetSymbol("node2"), GetSymbol("edge"), EdgeAtom::Direction::BOTH,
-                               std::vector<memgraph::storage::EdgeTypeId>{}, false, memgraph::storage::View::OLD);
+  last_op = std::make_shared<Expand>(last_op, node_sym, this->GetSymbol("node2"), this->GetSymbol("edge"),
+                                     EdgeAtom::Direction::BOTH, std::vector<memgraph::storage::EdgeTypeId>{}, false,
+                                     memgraph::storage::View::OLD);
   last_op = std::make_shared<plan::Delete>(last_op, std::vector<Expression *>{IDENT("node2")}, true);
 
-  CheckPlanType(last_op.get(), RWType::RW);
+  this->CheckPlanType(last_op.get(), RWType::RW);
 }
 
-TEST_F(ReadWriteTypeCheckTest, ExpandVariable) {
-  auto node1_sym = GetSymbol("node1");
+TYPED_TEST(ReadWriteTypeCheckTest, ExpandVariable) {
+  auto node1_sym = this->GetSymbol("node1");
 
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node1_sym);
 
   last_op = std::make_shared<ExpandVariable>(
-      last_op, node1_sym, GetSymbol("node2"), GetSymbol("edge"), EdgeAtom::Type::BREADTH_FIRST,
+      last_op, node1_sym, this->GetSymbol("node2"), this->GetSymbol("edge"), EdgeAtom::Type::BREADTH_FIRST,
       EdgeAtom::Direction::OUT,
-      std::vector<memgraph::storage::EdgeTypeId>{dba.NameToEdgeType("EdgeType1"), dba.NameToEdgeType("EdgeType2")},
+      std::vector<memgraph::storage::EdgeTypeId>{this->dba.NameToEdgeType("EdgeType1"),
+                                                 this->dba.NameToEdgeType("EdgeType2")},
       false, LITERAL(2), LITERAL(5), false,
-      ExpansionLambda{GetSymbol("inner_node"), GetSymbol("inner_edge"),
-                      PROPERTY_LOOKUP("inner_node", dba.NameToProperty("unblocked"))},
+      ExpansionLambda{this->GetSymbol("inner_node"), this->GetSymbol("inner_edge"),
+                      PROPERTY_LOOKUP(this->dba, "inner_node", this->dba.NameToProperty("unblocked"))},
       std::nullopt, std::nullopt);
 
-  CheckPlanType(last_op.get(), RWType::R);
+  this->CheckPlanType(last_op.get(), RWType::R);
 }
 
-TEST_F(ReadWriteTypeCheckTest, EdgeUniquenessFilter) {
-  auto node1_sym = GetSymbol("node1");
-  auto node2_sym = GetSymbol("node2");
-  auto node3_sym = GetSymbol("node3");
-  auto node4_sym = GetSymbol("node4");
+TYPED_TEST(ReadWriteTypeCheckTest, EdgeUniquenessFilter) {
+  auto node1_sym = this->GetSymbol("node1");
+  auto node2_sym = this->GetSymbol("node2");
+  auto node3_sym = this->GetSymbol("node3");
+  auto node4_sym = this->GetSymbol("node4");
 
-  auto edge1_sym = GetSymbol("edge1");
-  auto edge2_sym = GetSymbol("edge2");
+  auto edge1_sym = this->GetSymbol("edge1");
+  auto edge2_sym = this->GetSymbol("edge2");
 
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node1_sym);
   last_op = std::make_shared<Expand>(last_op, node1_sym, node2_sym, edge1_sym, EdgeAtom::Direction::IN,
@@ -144,100 +163,102 @@ TEST_F(ReadWriteTypeCheckTest, EdgeUniquenessFilter) {
                                      std::vector<memgraph::storage::EdgeTypeId>{}, false, memgraph::storage::View::OLD);
   last_op = std::make_shared<EdgeUniquenessFilter>(last_op, edge2_sym, std::vector<Symbol>{edge1_sym});
 
-  CheckPlanType(last_op.get(), RWType::R);
+  this->CheckPlanType(last_op.get(), RWType::R);
 }
 
-TEST_F(ReadWriteTypeCheckTest, SetRemovePropertiesLabels) {
-  auto node_sym = GetSymbol("node");
-  memgraph::storage::PropertyId prop = dba.NameToProperty("prop");
+TYPED_TEST(ReadWriteTypeCheckTest, SetRemovePropertiesLabels) {
+  auto node_sym = this->GetSymbol("node");
+  memgraph::storage::PropertyId prop = this->dba.NameToProperty("prop");
 
-  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, GetSymbol("node"));
-  last_op = std::make_shared<plan::SetProperty>(last_op, prop, PROPERTY_LOOKUP("node", prop),
-                                                ADD(PROPERTY_LOOKUP("node", prop), LITERAL(1)));
-  last_op = std::make_shared<plan::RemoveProperty>(last_op, dba.NameToProperty("prop"),
-                                                   PROPERTY_LOOKUP("node", dba.NameToProperty("prop")));
-  last_op =
-      std::make_shared<plan::SetProperties>(last_op, node_sym,
-                                            MAP({{storage.GetPropertyIx("prop1"), LITERAL(1)},
-                                                 {storage.GetPropertyIx("prop2"), LITERAL("this is a property")}}),
-                                            plan::SetProperties::Op::REPLACE);
+  std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, this->GetSymbol("node"));
+  last_op = std::make_shared<plan::SetProperty>(last_op, prop, PROPERTY_LOOKUP(this->dba, "node", prop),
+                                                ADD(PROPERTY_LOOKUP(this->dba, "node", prop), LITERAL(1)));
+  last_op = std::make_shared<plan::RemoveProperty>(
+      last_op, this->dba.NameToProperty("prop"), PROPERTY_LOOKUP(this->dba, "node", this->dba.NameToProperty("prop")));
+  last_op = std::make_shared<plan::SetProperties>(
+      last_op, node_sym,
+      MAP({{this->storage.GetPropertyIx("prop1"), LITERAL(1)},
+           {this->storage.GetPropertyIx("prop2"), LITERAL("this is a property")}}),
+      plan::SetProperties::Op::REPLACE);
   last_op = std::make_shared<plan::SetLabels>(
-      last_op, node_sym, std::vector<memgraph::storage::LabelId>{dba.NameToLabel("label1"), dba.NameToLabel("label2")});
+      last_op, node_sym,
+      std::vector<memgraph::storage::LabelId>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
   last_op = std::make_shared<plan::RemoveLabels>(
-      last_op, node_sym, std::vector<memgraph::storage::LabelId>{dba.NameToLabel("label1"), dba.NameToLabel("label2")});
+      last_op, node_sym,
+      std::vector<memgraph::storage::LabelId>{this->dba.NameToLabel("label1"), this->dba.NameToLabel("label2")});
 
-  CheckPlanType(last_op.get(), RWType::RW);
+  this->CheckPlanType(last_op.get(), RWType::RW);
 }
 
-TEST_F(ReadWriteTypeCheckTest, Cartesian) {
-  Symbol x = GetSymbol("x");
+TYPED_TEST(ReadWriteTypeCheckTest, Cartesian) {
+  Symbol x = this->GetSymbol("x");
   std::shared_ptr<LogicalOperator> lhs =
       std::make_shared<plan::Unwind>(nullptr, LIST(LITERAL(1), LITERAL(2), LITERAL(3)), x);
-  Symbol node = GetSymbol("node");
+  Symbol node = this->GetSymbol("node");
   std::shared_ptr<LogicalOperator> rhs = std::make_shared<ScanAll>(nullptr, node);
   std::shared_ptr<LogicalOperator> cartesian =
       std::make_shared<Cartesian>(lhs, std::vector<Symbol>{x}, rhs, std::vector<Symbol>{node});
 
-  CheckPlanType(cartesian.get(), RWType::R);
+  this->CheckPlanType(cartesian.get(), RWType::R);
 }
 
-TEST_F(ReadWriteTypeCheckTest, Union) {
-  Symbol x = GetSymbol("x");
+TYPED_TEST(ReadWriteTypeCheckTest, Union) {
+  Symbol x = this->GetSymbol("x");
   std::shared_ptr<LogicalOperator> lhs =
       std::make_shared<plan::Unwind>(nullptr, LIST(LITERAL(2), LITERAL(3), LITERAL(2)), x);
-  Symbol node = GetSymbol("x");
+  Symbol node = this->GetSymbol("x");
   std::shared_ptr<LogicalOperator> rhs = std::make_shared<ScanAll>(nullptr, node);
   std::shared_ptr<LogicalOperator> union_op = std::make_shared<Union>(
-      lhs, rhs, std::vector<Symbol>{GetSymbol("x")}, std::vector<Symbol>{x}, std::vector<Symbol>{node});
+      lhs, rhs, std::vector<Symbol>{this->GetSymbol("x")}, std::vector<Symbol>{x}, std::vector<Symbol>{node});
 
-  CheckPlanType(union_op.get(), RWType::R);
+  this->CheckPlanType(union_op.get(), RWType::R);
 }
 
-TEST_F(ReadWriteTypeCheckTest, CallReadProcedure) {
+TYPED_TEST(ReadWriteTypeCheckTest, CallReadProcedure) {
   plan::CallProcedure call_op;
   call_op.input_ = std::make_shared<Once>();
   call_op.procedure_name_ = "mg.reload";
   call_op.arguments_ = {LITERAL("example")};
   call_op.result_fields_ = {"name", "signature"};
   call_op.is_write_ = false;
-  call_op.result_symbols_ = {GetSymbol("name_alias"), GetSymbol("signature_alias")};
+  call_op.result_symbols_ = {this->GetSymbol("name_alias"), this->GetSymbol("signature_alias")};
 
-  CheckPlanType(&call_op, RWType::R);
+  this->CheckPlanType(&call_op, RWType::R);
 }
 
-TEST_F(ReadWriteTypeCheckTest, CallWriteProcedure) {
+TYPED_TEST(ReadWriteTypeCheckTest, CallWriteProcedure) {
   plan::CallProcedure call_op;
   call_op.input_ = std::make_shared<Once>();
   call_op.procedure_name_ = "mg.reload";
   call_op.arguments_ = {LITERAL("example")};
   call_op.result_fields_ = {"name", "signature"};
   call_op.is_write_ = true;
-  call_op.result_symbols_ = {GetSymbol("name_alias"), GetSymbol("signature_alias")};
+  call_op.result_symbols_ = {this->GetSymbol("name_alias"), this->GetSymbol("signature_alias")};
 
-  CheckPlanType(&call_op, RWType::RW);
+  this->CheckPlanType(&call_op, RWType::RW);
 }
 
-TEST_F(ReadWriteTypeCheckTest, CallReadProcedureBeforeUpdate) {
+TYPED_TEST(ReadWriteTypeCheckTest, CallReadProcedureBeforeUpdate) {
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<Once>();
   last_op = std::make_shared<CreateNode>(last_op, NodeCreationInfo());
 
   std::string procedure_name{"mg.reload"};
   std::vector<Expression *> arguments{LITERAL("example")};
   std::vector<std::string> result_fields{"name", "signature"};
-  std::vector<Symbol> result_symbols{GetSymbol("name_alias"), GetSymbol("signature_alias")};
+  std::vector<Symbol> result_symbols{this->GetSymbol("name_alias"), this->GetSymbol("signature_alias")};
 
   last_op = std::make_shared<plan::CallProcedure>(last_op, procedure_name, arguments, result_fields, result_symbols,
                                                   nullptr, 0, false);
 
-  CheckPlanType(last_op.get(), RWType::RW);
+  this->CheckPlanType(last_op.get(), RWType::RW);
 }
 
-TEST_F(ReadWriteTypeCheckTest, ConstructNamedPath) {
-  auto node1_sym = GetSymbol("node1");
-  auto edge1_sym = GetSymbol("edge1");
-  auto node2_sym = GetSymbol("node2");
-  auto edge2_sym = GetSymbol("edge2");
-  auto node3_sym = GetSymbol("node3");
+TYPED_TEST(ReadWriteTypeCheckTest, ConstructNamedPath) {
+  auto node1_sym = this->GetSymbol("node1");
+  auto edge1_sym = this->GetSymbol("edge1");
+  auto node2_sym = this->GetSymbol("node2");
+  auto edge2_sym = this->GetSymbol("edge2");
+  auto node3_sym = this->GetSymbol("node3");
 
   std::shared_ptr<LogicalOperator> last_op = std::make_shared<ScanAll>(nullptr, node1_sym);
   last_op = std::make_shared<Expand>(last_op, node1_sym, node2_sym, edge1_sym, EdgeAtom::Direction::OUT,
@@ -245,18 +266,18 @@ TEST_F(ReadWriteTypeCheckTest, ConstructNamedPath) {
   last_op = std::make_shared<Expand>(last_op, node2_sym, node3_sym, edge2_sym, EdgeAtom::Direction::OUT,
                                      std::vector<memgraph::storage::EdgeTypeId>{}, false, memgraph::storage::View::OLD);
   last_op = std::make_shared<ConstructNamedPath>(
-      last_op, GetSymbol("path"), std::vector<Symbol>{node1_sym, edge1_sym, node2_sym, edge2_sym, node3_sym});
+      last_op, this->GetSymbol("path"), std::vector<Symbol>{node1_sym, edge1_sym, node2_sym, edge2_sym, node3_sym});
 
-  CheckPlanType(last_op.get(), RWType::R);
+  this->CheckPlanType(last_op.get(), RWType::R);
 }
 
-TEST_F(ReadWriteTypeCheckTest, Foreach) {
-  Symbol x = GetSymbol("x");
+TYPED_TEST(ReadWriteTypeCheckTest, Foreach) {
+  Symbol x = this->GetSymbol("x");
   std::shared_ptr<LogicalOperator> foreach = std::make_shared<plan::Foreach>(nullptr, nullptr, nullptr, x);
-  CheckPlanType(foreach.get(), RWType::RW);
+  this->CheckPlanType(foreach.get(), RWType::RW);
 }
 
-TEST_F(ReadWriteTypeCheckTest, CheckUpdateType) {
+TYPED_TEST(ReadWriteTypeCheckTest, CheckUpdateType) {
   std::array<std::array<RWType, 3>, 16> scenarios = {{
       {RWType::NONE, RWType::NONE, RWType::NONE},
       {RWType::NONE, RWType::R, RWType::R},
diff --git a/tests/unit/query_plan_v2_create_set_remove_delete.cpp b/tests/unit/query_plan_v2_create_set_remove_delete.cpp
index 106aaf700..93aa6c601 100644
--- a/tests/unit/query_plan_v2_create_set_remove_delete.cpp
+++ b/tests/unit/query_plan_v2_create_set_remove_delete.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -9,17 +9,35 @@
 // by the Apache License, Version 2.0, included in the file
 // licenses/APL.txt.
 
+#include "disk_test_utils.hpp"
 #include "query_plan_common.hpp"
 
 #include <gtest/gtest.h>
 
 #include "query/frontend/semantic/symbol_table.hpp"
 #include "query/plan/operator.hpp"
-#include "storage/v2/storage.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
-TEST(QueryPlan, CreateNodeWithAttributes) {
-  memgraph::storage::Storage db;
-  auto dba = db.Access();
+template <typename StorageType>
+class QueryPlan : public testing::Test {
+ public:
+  const std::string testSuite = "query_plan_v2_create_set_remove_delete";
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db = std::make_unique<StorageType>(config);
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+};
+
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(QueryPlan, StorageTypes);
+
+TYPED_TEST(QueryPlan, CreateNodeWithAttributes) {
+  auto dba = this->db->Access();
 
   auto label = memgraph::storage::LabelId::FromInt(42);
   auto property = memgraph::storage::PropertyId::FromInt(1);
@@ -34,7 +52,7 @@ TEST(QueryPlan, CreateNodeWithAttributes) {
       .emplace_back(property, ast.Create<PrimitiveLiteral>(42));
 
   memgraph::query::plan::CreateNode create_node(nullptr, node);
-  DbAccessor execution_dba(&dba);
+  DbAccessor execution_dba(dba.get());
   auto context = MakeContext(ast, symbol_table, &execution_dba);
   Frame frame(context.symbol_table.max_position());
   auto cursor = create_node.MakeCursor(memgraph::utils::NewDeleteResource());
@@ -46,6 +64,8 @@ TEST(QueryPlan, CreateNodeWithAttributes) {
     const auto &v = node_value.ValueVertex();
     EXPECT_TRUE(*v.HasLabel(memgraph::storage::View::NEW, label));
     EXPECT_EQ(v.GetProperty(memgraph::storage::View::NEW, property)->ValueInt(), 42);
+    dba->PrefetchInEdges(v.impl_);
+    dba->PrefetchOutEdges(v.impl_);
     EXPECT_EQ(CountIterable(*v.InEdges(memgraph::storage::View::NEW)), 0);
     EXPECT_EQ(CountIterable(*v.OutEdges(memgraph::storage::View::NEW)), 0);
     // Invokes LOG(FATAL) instead of erroring out.
@@ -54,12 +74,11 @@ TEST(QueryPlan, CreateNodeWithAttributes) {
   EXPECT_EQ(count, 1);
 }
 
-TEST(QueryPlan, ScanAllEmpty) {
+TYPED_TEST(QueryPlan, ScanAllEmpty) {
   memgraph::query::AstStorage ast;
   memgraph::query::SymbolTable symbol_table;
-  memgraph::storage::Storage db;
-  auto dba = db.Access();
-  DbAccessor execution_dba(&dba);
+  auto dba = this->db->Access();
+  DbAccessor execution_dba(dba.get());
   auto node_symbol = symbol_table.CreateSymbol("n", true);
   {
     memgraph::query::plan::ScanAll scan_all(nullptr, node_symbol, memgraph::storage::View::OLD);
@@ -81,17 +100,16 @@ TEST(QueryPlan, ScanAllEmpty) {
   }
 }
 
-TEST(QueryPlan, ScanAll) {
-  memgraph::storage::Storage db;
+TYPED_TEST(QueryPlan, ScanAll) {
   {
-    auto dba = db.Access();
-    for (int i = 0; i < 42; ++i) dba.CreateVertex();
-    EXPECT_FALSE(dba.Commit().HasError());
+    auto dba = this->db->Access();
+    for (int i = 0; i < 42; ++i) dba->CreateVertex();
+    EXPECT_FALSE(dba->Commit().HasError());
   }
   memgraph::query::AstStorage ast;
   memgraph::query::SymbolTable symbol_table;
-  auto dba = db.Access();
-  DbAccessor execution_dba(&dba);
+  auto dba = this->db->Access();
+  DbAccessor execution_dba(dba.get());
   auto node_symbol = symbol_table.CreateSymbol("n", true);
   memgraph::query::plan::ScanAll scan_all(nullptr, node_symbol);
   auto context = MakeContext(ast, symbol_table, &execution_dba);
@@ -102,26 +120,25 @@ TEST(QueryPlan, ScanAll) {
   EXPECT_EQ(count, 42);
 }
 
-TEST(QueryPlan, ScanAllByLabel) {
-  memgraph::storage::Storage db;
-  auto label = db.NameToLabel("label");
-  ASSERT_FALSE(db.CreateIndex(label).HasError());
+TYPED_TEST(QueryPlan, ScanAllByLabel) {
+  auto label = this->db->NameToLabel("label");
+  ASSERT_FALSE(this->db->CreateIndex(label).HasError());
   {
-    auto dba = db.Access();
+    auto dba = this->db->Access();
     // Add some unlabeled vertices
-    for (int i = 0; i < 12; ++i) dba.CreateVertex();
+    for (int i = 0; i < 12; ++i) dba->CreateVertex();
     // Add labeled vertices
     for (int i = 0; i < 42; ++i) {
-      auto v = dba.CreateVertex();
+      auto v = dba->CreateVertex();
       ASSERT_TRUE(v.AddLabel(label).HasValue());
     }
-    EXPECT_FALSE(dba.Commit().HasError());
+    EXPECT_FALSE(dba->Commit().HasError());
   }
-  auto dba = db.Access();
+  auto dba = this->db->Access();
   memgraph::query::AstStorage ast;
   memgraph::query::SymbolTable symbol_table;
   auto node_symbol = symbol_table.CreateSymbol("n", true);
-  DbAccessor execution_dba(&dba);
+  DbAccessor execution_dba(dba.get());
   memgraph::query::plan::ScanAllByLabel scan_all(nullptr, node_symbol, label);
   auto context = MakeContext(ast, symbol_table, &execution_dba);
   Frame frame(context.symbol_table.max_position());
diff --git a/tests/unit/query_pretty_print.cpp b/tests/unit/query_pretty_print.cpp
index 1f5893253..1b19f8aa8 100644
--- a/tests/unit/query_pretty_print.cpp
+++ b/tests/unit/query_pretty_print.cpp
@@ -15,9 +15,12 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "disk_test_utils.hpp"
 #include "query/frontend/ast/ast.hpp"
 #include "query/frontend/ast/pretty_print.hpp"
 #include "query_common.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "utils/string.hpp"
 
 using namespace memgraph::query;
@@ -27,14 +30,27 @@ using testing::UnorderedElementsAre;
 
 namespace {
 
-struct ExpressionPrettyPrinterTest : public ::testing::Test {
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
+template <typename StorageType>
+class ExpressionPrettyPrinterTest : public ::testing::Test {
+ public:
+  const std::string testSuite = "query_pretty_print";
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
   AstStorage storage;
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
 };
 
-TEST_F(ExpressionPrettyPrinterTest, Literals) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(ExpressionPrettyPrinterTest, StorageTypes);
+
+TYPED_TEST(ExpressionPrettyPrinterTest, Literals) {
   // 1
   EXPECT_EQ(ToString(LITERAL(1)), "1");
 
@@ -74,16 +90,17 @@ TEST_F(ExpressionPrettyPrinterTest, Literals) {
 
   // map {literalEntry: 10, variableSelector: a, .map, .*}
   auto elements = std::unordered_map<memgraph::query::PropertyIx, memgraph::query::Expression *>{
-      {storage.GetPropertyIx("literalEntry"), LITERAL(10)},
-      {storage.GetPropertyIx("variableSelector"), IDENT("a")},
-      {storage.GetPropertyIx("propertySelector"), PROPERTY_LOOKUP("map", PROPERTY_PAIR("hello"))},
-      {storage.GetPropertyIx("allPropertiesSelector"), ALL_PROPERTIES_LOOKUP("map")}};
+      {this->storage.GetPropertyIx("literalEntry"), LITERAL(10)},
+      {this->storage.GetPropertyIx("variableSelector"), IDENT("a")},
+      {this->storage.GetPropertyIx("propertySelector"),
+       PROPERTY_LOOKUP(this->dba, "map", PROPERTY_PAIR(this->dba, "hello"))},
+      {this->storage.GetPropertyIx("allPropertiesSelector"), ALL_PROPERTIES_LOOKUP("map")}};
   EXPECT_EQ(ToString(MAP_PROJECTION(IDENT("map"), elements)),
             "(Identifier \"map\"){\"allPropertiesSelector\": .*, \"literalEntry\": 10, \"propertySelector\": "
             "(PropertyLookup (Identifier \"map\") \"hello\"), \"variableSelector\": (Identifier \"a\")}");
 }
 
-TEST_F(ExpressionPrettyPrinterTest, Identifiers) {
+TYPED_TEST(ExpressionPrettyPrinterTest, Identifiers) {
   // x
   EXPECT_EQ(ToString(IDENT("x")), "(Identifier \"x\")");
 
@@ -91,11 +108,11 @@ TEST_F(ExpressionPrettyPrinterTest, Identifiers) {
   EXPECT_EQ(ToString(IDENT("hello_there")), "(Identifier \"hello_there\")");
 }
 
-TEST_F(ExpressionPrettyPrinterTest, Reducing) {
+TYPED_TEST(ExpressionPrettyPrinterTest, Reducing) {
   // all(x in list where x.prop = 42)
-  auto prop = dba.NameToProperty("prop");
+  auto prop = this->dba.NameToProperty("prop");
   EXPECT_EQ(ToString(ALL("x", LITERAL(std::vector<memgraph::storage::PropertyValue>{}),
-                         WHERE(EQ(PROPERTY_LOOKUP("x", prop), LITERAL(42))))),
+                         WHERE(EQ(PROPERTY_LOOKUP(this->dba, "x", prop), LITERAL(42))))),
             "(All (Identifier \"x\") [] (== (PropertyLookup "
             "(Identifier \"x\") \"prop\") 42))");
 
@@ -106,7 +123,7 @@ TEST_F(ExpressionPrettyPrinterTest, Reducing) {
             "\"expression\"))");
 }
 
-TEST_F(ExpressionPrettyPrinterTest, UnaryOperators) {
+TYPED_TEST(ExpressionPrettyPrinterTest, UnaryOperators) {
   // not(false)
   EXPECT_EQ(ToString(NOT(LITERAL(false))), "(Not false)");
 
@@ -120,23 +137,24 @@ TEST_F(ExpressionPrettyPrinterTest, UnaryOperators) {
   EXPECT_EQ(ToString(IS_NULL(LITERAL(TypedValue()))), "(IsNull null)");
 }
 
-TEST_F(ExpressionPrettyPrinterTest, BinaryOperators) {
+TYPED_TEST(ExpressionPrettyPrinterTest, BinaryOperators) {
   // and(null, 5)
   EXPECT_EQ(ToString(AND(LITERAL(TypedValue()), LITERAL(5))), "(And null 5)");
 
   // or(5, {hello: "there"}["hello"])
-  EXPECT_EQ(
-      ToString(OR(LITERAL(5),
-                  PROPERTY_LOOKUP(MAP(std::make_pair(storage.GetPropertyIx("hello"), LITERAL("there"))), "hello"))),
-      "(Or 5 (PropertyLookup {\"hello\": \"there\"} \"hello\"))");
+  EXPECT_EQ(ToString(OR(
+                LITERAL(5),
+                PROPERTY_LOOKUP(this->dba, MAP(std::make_pair(this->storage.GetPropertyIx("hello"), LITERAL("there"))),
+                                "hello"))),
+            "(Or 5 (PropertyLookup {\"hello\": \"there\"} \"hello\"))");
 
   // and(coalesce(null, 1), {hello: "there"})
   EXPECT_EQ(ToString(AND(COALESCE(LITERAL(TypedValue()), LITERAL(1)),
-                         MAP(std::make_pair(storage.GetPropertyIx("hello"), LITERAL("there"))))),
+                         MAP(std::make_pair(this->storage.GetPropertyIx("hello"), LITERAL("there"))))),
             "(And (Coalesce [null, 1]) {\"hello\": \"there\"})");
 }
 
-TEST_F(ExpressionPrettyPrinterTest, Coalesce) {
+TYPED_TEST(ExpressionPrettyPrinterTest, Coalesce) {
   // coalesce()
   EXPECT_EQ(ToString(COALESCE()), "(Coalesce [])");
 
@@ -159,18 +177,19 @@ TEST_F(ExpressionPrettyPrinterTest, Coalesce) {
             "(Coalesce [[null, null]])");
 }
 
-TEST_F(ExpressionPrettyPrinterTest, ParameterLookup) {
+TYPED_TEST(ExpressionPrettyPrinterTest, ParameterLookup) {
   // and($hello, $there)
   EXPECT_EQ(ToString(AND(PARAMETER_LOOKUP(1), PARAMETER_LOOKUP(2))), "(And (ParameterLookup 1) (ParameterLookup 2))");
 }
 
-TEST_F(ExpressionPrettyPrinterTest, PropertyLookup) {
+TYPED_TEST(ExpressionPrettyPrinterTest, PropertyLookup) {
   // {hello: "there"}["hello"]
-  EXPECT_EQ(ToString(PROPERTY_LOOKUP(MAP(std::make_pair(storage.GetPropertyIx("hello"), LITERAL("there"))), "hello")),
+  EXPECT_EQ(ToString(PROPERTY_LOOKUP(
+                this->dba, MAP(std::make_pair(this->storage.GetPropertyIx("hello"), LITERAL("there"))), "hello")),
             "(PropertyLookup {\"hello\": \"there\"} \"hello\")");
 }
 
-TEST_F(ExpressionPrettyPrinterTest, NamedExpression) {
+TYPED_TEST(ExpressionPrettyPrinterTest, NamedExpression) {
   // n AS 1
   EXPECT_EQ(ToString(NEXPR("n", LITERAL(1))), "(NamedExpression \"n\" 1)");
 }
diff --git a/tests/unit/query_procedure_mgp_type.cpp b/tests/unit/query_procedure_mgp_type.cpp
index c9a2800b7..3ebdcbec2 100644
--- a/tests/unit/query_procedure_mgp_type.cpp
+++ b/tests/unit/query_procedure_mgp_type.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -17,10 +17,30 @@
 
 #include "query/procedure/cypher_types.hpp"
 #include "query/procedure/mg_procedure_impl.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
+#include "disk_test_utils.hpp"
 #include "test_utils.hpp"
 
-TEST(CypherType, PresentableNameSimpleTypes) {
+template <typename StorageType>
+class CypherType : public testing::Test {
+ public:
+  const std::string testSuite = "query_procedure_mgp_type";
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+};
+
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(CypherType, StorageTypes);
+
+TYPED_TEST(CypherType, PresentableNameSimpleTypes) {
   EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any)->impl->GetPresentableName(), "ANY");
   EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_bool)->impl->GetPresentableName(), "BOOLEAN");
   EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_string)->impl->GetPresentableName(), "STRING");
@@ -33,7 +53,7 @@ TEST(CypherType, PresentableNameSimpleTypes) {
   EXPECT_EQ(EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_path)->impl->GetPresentableName(), "PATH");
 }
 
-TEST(CypherType, PresentableNameCompositeTypes) {
+TYPED_TEST(CypherType, PresentableNameCompositeTypes) {
   mgp_type *any_type = EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_any);
   {
     auto *nullable_any = EXPECT_MGP_NO_ERROR(mgp_type *, mgp_type_nullable, any_type);
@@ -82,7 +102,7 @@ TEST(CypherType, PresentableNameCompositeTypes) {
   }
 }
 
-TEST(CypherType, NullSatisfiesType) {
+TYPED_TEST(CypherType, NullSatisfiesType) {
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   {
     auto *mgp_null = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory);
@@ -134,7 +154,7 @@ static void CheckNotSatisfiesTypesAndListAndNullable(const mgp_value *mgp_val, c
   }
 }
 
-TEST(CypherType, BoolSatisfiesType) {
+TYPED_TEST(CypherType, BoolSatisfiesType) {
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   auto *mgp_bool = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_bool, 1, &memory);
   const memgraph::query::TypedValue tv_bool(true);
@@ -150,7 +170,7 @@ TEST(CypherType, BoolSatisfiesType) {
   mgp_value_destroy(mgp_bool);
 }
 
-TEST(CypherType, IntSatisfiesType) {
+TYPED_TEST(CypherType, IntSatisfiesType) {
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   auto *mgp_int = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, 42, &memory);
   const memgraph::query::TypedValue tv_int(42);
@@ -167,7 +187,7 @@ TEST(CypherType, IntSatisfiesType) {
   mgp_value_destroy(mgp_int);
 }
 
-TEST(CypherType, DoubleSatisfiesType) {
+TYPED_TEST(CypherType, DoubleSatisfiesType) {
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   auto *mgp_double = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_double, 42, &memory);
   const memgraph::query::TypedValue tv_double(42.0);
@@ -184,7 +204,7 @@ TEST(CypherType, DoubleSatisfiesType) {
   mgp_value_destroy(mgp_double);
 }
 
-TEST(CypherType, StringSatisfiesType) {
+TYPED_TEST(CypherType, StringSatisfiesType) {
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   auto *mgp_string = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_string, "text", &memory);
   const memgraph::query::TypedValue tv_string("text");
@@ -200,7 +220,7 @@ TEST(CypherType, StringSatisfiesType) {
   mgp_value_destroy(mgp_string);
 }
 
-TEST(CypherType, MapSatisfiesType) {
+TYPED_TEST(CypherType, MapSatisfiesType) {
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   auto *map = EXPECT_MGP_NO_ERROR(mgp_map *, mgp_map_make_empty, &memory);
   EXPECT_EQ(
@@ -223,10 +243,9 @@ TEST(CypherType, MapSatisfiesType) {
   mgp_value_destroy(mgp_map_v);
 }
 
-TEST(CypherType, VertexSatisfiesType) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(CypherType, VertexSatisfiesType) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto vertex = dba.InsertVertex();
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   memgraph::utils::Allocator<mgp_vertex> alloc(memory.impl);
@@ -247,10 +266,9 @@ TEST(CypherType, VertexSatisfiesType) {
   mgp_value_destroy(mgp_vertex_v);
 }
 
-TEST(CypherType, EdgeSatisfiesType) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(CypherType, EdgeSatisfiesType) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
   auto edge = *dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("edge_type"));
@@ -272,10 +290,9 @@ TEST(CypherType, EdgeSatisfiesType) {
   mgp_value_destroy(mgp_edge_v);
 }
 
-TEST(CypherType, PathSatisfiesType) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(CypherType, PathSatisfiesType) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
   auto edge = *dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("edge_type"));
@@ -314,7 +331,7 @@ static std::vector<mgp_type *> MakeListTypes(const std::vector<mgp_type *> &elem
   return list_types;
 }
 
-TEST(CypherType, EmptyListSatisfiesType) {
+TYPED_TEST(CypherType, EmptyListSatisfiesType) {
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   auto *list = EXPECT_MGP_NO_ERROR(mgp_list *, mgp_list_make_empty, 0, &memory);
   auto *mgp_list_v = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_list, list);
@@ -332,7 +349,7 @@ TEST(CypherType, EmptyListSatisfiesType) {
   mgp_value_destroy(mgp_list_v);
 }
 
-TEST(CypherType, ListOfIntSatisfiesType) {
+TYPED_TEST(CypherType, ListOfIntSatisfiesType) {
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   static constexpr int64_t elem_count = 3;
   auto *list = EXPECT_MGP_NO_ERROR(mgp_list *, mgp_list_make_empty, elem_count, &memory);
@@ -360,7 +377,7 @@ TEST(CypherType, ListOfIntSatisfiesType) {
   mgp_value_destroy(mgp_list_v);
 }
 
-TEST(CypherType, ListOfIntAndBoolSatisfiesType) {
+TYPED_TEST(CypherType, ListOfIntAndBoolSatisfiesType) {
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   static constexpr int64_t elem_count = 2;
   auto *list = EXPECT_MGP_NO_ERROR(mgp_list *, mgp_list_make_empty, elem_count, &memory);
@@ -394,7 +411,7 @@ TEST(CypherType, ListOfIntAndBoolSatisfiesType) {
   mgp_value_destroy(mgp_list_v);
 }
 
-TEST(CypherType, ListOfNullSatisfiesType) {
+TYPED_TEST(CypherType, ListOfNullSatisfiesType) {
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   auto *list = EXPECT_MGP_NO_ERROR(mgp_list *, mgp_list_make_empty, 1, &memory);
   auto *mgp_list_v = EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_list, list);
diff --git a/tests/unit/query_procedure_py_module.cpp b/tests/unit/query_procedure_py_module.cpp
index 883489643..abe8b7f27 100644
--- a/tests/unit/query_procedure_py_module.cpp
+++ b/tests/unit/query_procedure_py_module.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -14,11 +14,31 @@
 #include <filesystem>
 #include <string>
 
+#include "disk_test_utils.hpp"
 #include "query/procedure/mg_procedure_impl.hpp"
 #include "query/procedure/py_module.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "test_utils.hpp"
 
-TEST(PyModule, MgpValueToPyObject) {
+template <typename StorageType>
+class PyModule : public testing::Test {
+ public:
+  const std::string testSuite = "query_procedure_py_module";
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+};
+
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(PyModule, StorageTypes);
+
+TYPED_TEST(PyModule, MgpValueToPyObject) {
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   auto *list = EXPECT_MGP_NO_ERROR(mgp_list *, mgp_list_make_empty, 42, &memory);
   {
@@ -93,25 +113,24 @@ static void AssertPickleAndCopyAreNotSupported(PyObject *py_obj) {
   ASSERT_TRUE(memgraph::py::FetchError());
 }
 
-TEST(PyModule, PyVertex) {
+TYPED_TEST(PyModule, PyVertex) {
   // Initialize the database with 2 vertices and 1 edge.
-  memgraph::storage::Storage db;
   {
-    auto dba = db.Access();
-    auto v1 = dba.CreateVertex();
-    auto v2 = dba.CreateVertex();
+    auto dba = this->db->Access();
+    auto v1 = dba->CreateVertex();
+    auto v2 = dba->CreateVertex();
 
-    ASSERT_TRUE(v1.SetProperty(dba.NameToProperty("key1"), memgraph::storage::PropertyValue("value1")).HasValue());
-    ASSERT_TRUE(v1.SetProperty(dba.NameToProperty("key2"), memgraph::storage::PropertyValue(1337)).HasValue());
+    ASSERT_TRUE(v1.SetProperty(dba->NameToProperty("key1"), memgraph::storage::PropertyValue("value1")).HasValue());
+    ASSERT_TRUE(v1.SetProperty(dba->NameToProperty("key2"), memgraph::storage::PropertyValue(1337)).HasValue());
 
-    auto e = dba.CreateEdge(&v1, &v2, dba.NameToEdgeType("type"));
+    auto e = dba->CreateEdge(&v1, &v2, dba->NameToEdgeType("type"));
     ASSERT_TRUE(e.HasValue());
 
-    ASSERT_FALSE(dba.Commit().HasError());
+    ASSERT_FALSE(dba->Commit().HasError());
   }
   // Get the first vertex as an mgp_value.
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   mgp_graph graph{&dba, memgraph::storage::View::OLD};
   auto *vertex = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{0}, &memory);
@@ -143,24 +162,25 @@ TEST(PyModule, PyVertex) {
   ASSERT_FALSE(dba.Commit().HasError());
 }
 
-TEST(PyModule, PyEdge) {
+TYPED_TEST(PyModule, PyEdge) {
   // Initialize the database with 2 vertices and 1 edge.
-  memgraph::storage::Storage db;
   {
-    auto dba = db.Access();
-    auto v1 = dba.CreateVertex();
-    auto v2 = dba.CreateVertex();
+    auto dba = this->db->Access();
+    auto v1 = dba->CreateVertex();
+    auto v2 = dba->CreateVertex();
 
-    auto e = dba.CreateEdge(&v1, &v2, dba.NameToEdgeType("type"));
+    auto e = dba->CreateEdge(&v1, &v2, dba->NameToEdgeType("type"));
     ASSERT_TRUE(e.HasValue());
 
-    ASSERT_TRUE(e->SetProperty(dba.NameToProperty("key1"), memgraph::storage::PropertyValue("value1")).HasValue());
-    ASSERT_TRUE(e->SetProperty(dba.NameToProperty("key2"), memgraph::storage::PropertyValue(1337)).HasValue());
-    ASSERT_FALSE(dba.Commit().HasError());
+    ASSERT_TRUE(
+        e.GetValue().SetProperty(dba->NameToProperty("key1"), memgraph::storage::PropertyValue("value1")).HasValue());
+    ASSERT_TRUE(
+        e.GetValue().SetProperty(dba->NameToProperty("key2"), memgraph::storage::PropertyValue(1337)).HasValue());
+    ASSERT_FALSE(dba->Commit().HasError());
   }
   // Get the edge as an mgp_value.
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   mgp_graph graph{&dba, memgraph::storage::View::OLD};
   auto *start_v = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{0}, &memory);
@@ -197,17 +217,16 @@ TEST(PyModule, PyEdge) {
   ASSERT_FALSE(dba.Commit().HasError());
 }
 
-TEST(PyModule, PyPath) {
-  memgraph::storage::Storage db;
+TYPED_TEST(PyModule, PyPath) {
   {
-    auto dba = db.Access();
-    auto v1 = dba.CreateVertex();
-    auto v2 = dba.CreateVertex();
-    ASSERT_TRUE(dba.CreateEdge(&v1, &v2, dba.NameToEdgeType("type")).HasValue());
-    ASSERT_FALSE(dba.Commit().HasError());
+    auto dba = this->db->Access();
+    auto v1 = dba->CreateVertex();
+    auto v2 = dba->CreateVertex();
+    ASSERT_TRUE(dba->CreateEdge(&v1, &v2, dba->NameToEdgeType("type")).HasValue());
+    ASSERT_FALSE(dba->Commit().HasError());
   }
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   mgp_graph graph{&dba, memgraph::storage::View::OLD};
   auto *start_v = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{0}, &memory);
@@ -245,7 +264,7 @@ TEST(PyModule, PyPath) {
   ASSERT_FALSE(dba.Commit().HasError());
 }
 
-TEST(PyModule, PyObjectToMgpValue) {
+TYPED_TEST(PyModule, PyObjectToMgpValue) {
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
   auto gil = memgraph::py::EnsureGIL();
   memgraph::py::Object py_value{
diff --git a/tests/unit/query_procedures_mgp_graph.cpp b/tests/unit/query_procedures_mgp_graph.cpp
index b2742e8f1..1b8918f9d 100644
--- a/tests/unit/query_procedures_mgp_graph.cpp
+++ b/tests/unit/query_procedures_mgp_graph.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -18,13 +18,15 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include "disk_test_utils.hpp"
 #include "mg_procedure.h"
 #include "query/db_accessor.hpp"
 #include "query/plan/operator.hpp"
 #include "query/procedure/mg_procedure_impl.hpp"
+#include "storage/v2/disk/storage.hpp"
 #include "storage/v2/id_types.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/property_value.hpp"
-#include "storage/v2/storage.hpp"
 #include "storage/v2/vertex_accessor.hpp"
 #include "storage/v2/view.hpp"
 #include "storage_test_utils.hpp"
@@ -94,6 +96,18 @@ size_t CountMaybeIterables(TMaybeIterable &&maybe_iterable) {
 ;
 
 void CheckEdgeCountBetween(const MgpVertexPtr &from, const MgpVertexPtr &to, const size_t number_of_edges_between) {
+  if (auto dbAccessor = std::get_if<memgraph::query::DbAccessor *>(&from->graph->impl)) {
+    (*dbAccessor)->PrefetchOutEdges(std::get<memgraph::query::VertexAccessor>(from->impl));
+    (*dbAccessor)->PrefetchInEdges(std::get<memgraph::query::VertexAccessor>(from->impl));
+    (*dbAccessor)->PrefetchOutEdges(std::get<memgraph::query::VertexAccessor>(to->impl));
+    (*dbAccessor)->PrefetchInEdges(std::get<memgraph::query::VertexAccessor>(to->impl));
+  } else if (auto dbAccessor = std::get<memgraph::query::SubgraphDbAccessor *>(from->graph->impl)) {
+    dbAccessor->PrefetchOutEdges(std::get<memgraph::query::SubgraphVertexAccessor>(from->impl));
+    dbAccessor->PrefetchInEdges(std::get<memgraph::query::SubgraphVertexAccessor>(from->impl));
+    dbAccessor->PrefetchOutEdges(std::get<memgraph::query::SubgraphVertexAccessor>(to->impl));
+    dbAccessor->PrefetchInEdges(std::get<memgraph::query::SubgraphVertexAccessor>(to->impl));
+  }
+
   EXPECT_EQ(
       CountMaybeIterables(std::visit([](auto impl) { return impl.InEdges(memgraph::storage::View::NEW); }, from->impl)),
       0);
@@ -109,7 +123,9 @@ void CheckEdgeCountBetween(const MgpVertexPtr &from, const MgpVertexPtr &to, con
 }
 }  // namespace
 
-struct MgpGraphTest : public ::testing::Test {
+template <typename StorageType>
+class MgpGraphTest : public ::testing::Test {
+ public:
   mgp_graph CreateGraph(const memgraph::storage::View view = memgraph::storage::View::NEW) {
     // the execution context can be null as it shouldn't be used in these tests
     return mgp_graph{&CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION), view, ctx_.get()};
@@ -132,118 +148,147 @@ struct MgpGraphTest : public ::testing::Test {
 
   void GetFirstOutEdge(mgp_graph &graph, memgraph::storage::Gid vertex_id, MgpEdgePtr &edge) {
     MgpVertexPtr from{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
-                                          mgp_vertex_id{vertex_id.AsInt()}, &memory)};
+                                          mgp_vertex_id{vertex_id.AsInt()}, &this->memory)};
     ASSERT_NE(from, nullptr);
 
-    MgpEdgesIteratorPtr it{EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, from.get(), &memory)};
+    MgpEdgesIteratorPtr it{
+        EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, from.get(), &this->memory)};
     ASSERT_NE(it, nullptr);
     auto *edge_from_it = EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edges_iterator_get, it.get());
     ASSERT_NE(edge_from_it, nullptr);
     // Copy is used to get a non const pointer because mgp_edges_iterator_get_mutable doesn't work with immutable graph
-    edge.reset(EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edge_copy, edge_from_it, &memory));
+    edge.reset(EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edge_copy, edge_from_it, &this->memory));
     ASSERT_NE(edge, nullptr);
   }
 
   memgraph::query::DbAccessor &CreateDbAccessor(const memgraph::storage::IsolationLevel isolationLevel) {
-    accessors_.push_back(storage.Access(isolationLevel));
-    db_accessors_.emplace_back(&accessors_.back());
+    accessors_.push_back(storage->Access(isolationLevel));
+    db_accessors_.emplace_back(accessors_.back().get());
     return db_accessors_.back();
   }
 
-  memgraph::storage::Storage storage;
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+
+  const std::string testSuite = "query_procedures_mgp_graph";
+
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> storage{new StorageType(config)};
   mgp_memory memory{memgraph::utils::NewDeleteResource()};
 
  private:
-  std::list<memgraph::storage::Storage::Accessor> accessors_;
+  std::list<std::unique_ptr<memgraph::storage::Storage::Accessor>> accessors_;
   std::list<memgraph::query::DbAccessor> db_accessors_;
   std::unique_ptr<memgraph::query::ExecutionContext> ctx_ = std::make_unique<memgraph::query::ExecutionContext>();
 };
 
-TEST_F(MgpGraphTest, IsMutable) {
-  mgp_graph immutable_graph = CreateGraph(memgraph::storage::View::OLD);
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(MgpGraphTest, StorageTypes);
+
+TYPED_TEST(MgpGraphTest, IsMutable) {
+  mgp_graph immutable_graph = this->CreateGraph(memgraph::storage::View::OLD);
   EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_graph_is_mutable, &immutable_graph), 0);
-  mgp_graph mutable_graph = CreateGraph(memgraph::storage::View::NEW);
+  mgp_graph mutable_graph = this->CreateGraph(memgraph::storage::View::NEW);
   EXPECT_NE(EXPECT_MGP_NO_ERROR(int, mgp_graph_is_mutable, &mutable_graph), 0);
 }
 
-TEST_F(MgpGraphTest, CreateVertex) {
-  mgp_graph graph = CreateGraph();
-  auto read_uncommited_accessor = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
-  EXPECT_EQ(CountVertices(read_uncommited_accessor, memgraph::storage::View::NEW), 0);
-  MgpVertexPtr vertex{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_create_vertex, &graph, &memory)};
+TYPED_TEST(MgpGraphTest, CreateVertex) {
+  if (std::is_same<TypeParam, memgraph::storage::DiskStorage>::value) {
+    // DiskStorage doesn't support READ_UNCOMMITTED isolation level
+    return;
+  }
+  mgp_graph graph = this->CreateGraph();
+  auto read_uncommited_accessor = this->storage->Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
+  EXPECT_EQ(CountVertices(*read_uncommited_accessor, memgraph::storage::View::NEW), 0);
+  MgpVertexPtr vertex{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_create_vertex, &graph, &this->memory)};
   EXPECT_NE(vertex, nullptr);
-  EXPECT_EQ(CountVertices(read_uncommited_accessor, memgraph::storage::View::NEW), 1);
+  EXPECT_EQ(CountVertices(*read_uncommited_accessor, memgraph::storage::View::NEW), 1);
   const auto vertex_id = EXPECT_MGP_NO_ERROR(mgp_vertex_id, mgp_vertex_get_id, vertex.get());
-  EXPECT_TRUE(read_uncommited_accessor
-                  .FindVertex(memgraph::storage::Gid::FromInt(vertex_id.as_int), memgraph::storage::View::NEW)
-                  .has_value());
+  EXPECT_TRUE(read_uncommited_accessor->FindVertex(memgraph::storage::Gid::FromInt(vertex_id.as_int),
+                                                   memgraph::storage::View::NEW));
 }
 
-TEST_F(MgpGraphTest, DeleteVertex) {
+TYPED_TEST(MgpGraphTest, DeleteVertex) {
+  if (std::is_same<TypeParam, memgraph::storage::DiskStorage>::value) {
+    // DiskStorage doesn't support READ_UNCOMMITTED isolation level
+    return;
+  }
   memgraph::storage::Gid vertex_id{};
   {
-    auto accessor = CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
+    auto accessor = this->CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
     const auto vertex = accessor.InsertVertex();
     vertex_id = vertex.Gid();
     ASSERT_FALSE(accessor.Commit().HasError());
   }
-  mgp_graph graph = CreateGraph();
-  auto read_uncommited_accessor = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
-  EXPECT_EQ(CountVertices(read_uncommited_accessor, memgraph::storage::View::NEW), 1);
-  MgpVertexPtr vertex{
-      EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{vertex_id.AsInt()}, &memory)};
+  mgp_graph graph = this->CreateGraph();
+  auto read_uncommited_accessor = this->storage->Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
+  EXPECT_EQ(CountVertices(*read_uncommited_accessor, memgraph::storage::View::NEW), 1);
+  MgpVertexPtr vertex{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
+                                          mgp_vertex_id{vertex_id.AsInt()}, &this->memory)};
   EXPECT_NE(vertex, nullptr);
   EXPECT_SUCCESS(mgp_graph_delete_vertex(&graph, vertex.get()));
-  EXPECT_EQ(CountVertices(read_uncommited_accessor, memgraph::storage::View::NEW), 0);
+  EXPECT_EQ(CountVertices(*read_uncommited_accessor, memgraph::storage::View::NEW), 0);
 }
 
-TEST_F(MgpGraphTest, DetachDeleteVertex) {
-  const auto vertex_ids = CreateEdge();
-  auto graph = CreateGraph();
-  auto read_uncommited_accessor = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
-  EXPECT_EQ(CountVertices(read_uncommited_accessor, memgraph::storage::View::NEW), 2);
+TYPED_TEST(MgpGraphTest, DetachDeleteVertex) {
+  if (std::is_same<TypeParam, memgraph::storage::DiskStorage>::value) {
+    // DiskStorage doesn't support READ_UNCOMMITTED isolation level
+    return;
+  }
+  const auto vertex_ids = this->CreateEdge();
+  auto graph = this->CreateGraph();
+  auto read_uncommited_accessor = this->storage->Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
+  EXPECT_EQ(CountVertices(*read_uncommited_accessor, memgraph::storage::View::NEW), 2);
   MgpVertexPtr vertex{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
-                                          mgp_vertex_id{vertex_ids.front().AsInt()}, &memory)};
+                                          mgp_vertex_id{vertex_ids.front().AsInt()}, &this->memory)};
   EXPECT_EQ(mgp_graph_delete_vertex(&graph, vertex.get()), mgp_error::MGP_ERROR_LOGIC_ERROR);
-  EXPECT_EQ(CountVertices(read_uncommited_accessor, memgraph::storage::View::NEW), 2);
+  EXPECT_EQ(CountVertices(*read_uncommited_accessor, memgraph::storage::View::NEW), 2);
   EXPECT_SUCCESS(mgp_graph_detach_delete_vertex(&graph, vertex.get()));
-  EXPECT_EQ(CountVertices(read_uncommited_accessor, memgraph::storage::View::NEW), 1);
+  EXPECT_EQ(CountVertices(*read_uncommited_accessor, memgraph::storage::View::NEW), 1);
 }
 
-TEST_F(MgpGraphTest, CreateDeleteWithImmutableGraph) {
+TYPED_TEST(MgpGraphTest, CreateDeleteWithImmutableGraph) {
+  if (std::is_same<TypeParam, memgraph::storage::DiskStorage>::value) {
+    // DiskStorage doesn't support READ_UNCOMMITTED isolation level
+    return;
+  }
   memgraph::storage::Gid vertex_id{};
   {
-    auto accessor = CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
+    auto accessor = this->CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
     const auto vertex = accessor.InsertVertex();
     vertex_id = vertex.Gid();
     ASSERT_FALSE(accessor.Commit().HasError());
   }
-  auto read_uncommited_accessor = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
-  EXPECT_EQ(CountVertices(read_uncommited_accessor, memgraph::storage::View::NEW), 1);
+  auto read_uncommited_accessor = this->storage->Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
+  EXPECT_EQ(CountVertices(*read_uncommited_accessor, memgraph::storage::View::NEW), 1);
 
-  mgp_graph immutable_graph = CreateGraph(memgraph::storage::View::OLD);
+  mgp_graph immutable_graph = this->CreateGraph(memgraph::storage::View::OLD);
   mgp_vertex *raw_vertex{nullptr};
-  EXPECT_EQ(mgp_graph_create_vertex(&immutable_graph, &memory, &raw_vertex), mgp_error::MGP_ERROR_IMMUTABLE_OBJECT);
+  EXPECT_EQ(mgp_graph_create_vertex(&immutable_graph, &this->memory, &raw_vertex),
+            mgp_error::MGP_ERROR_IMMUTABLE_OBJECT);
   MgpVertexPtr created_vertex{raw_vertex};
   EXPECT_EQ(created_vertex, nullptr);
-  EXPECT_EQ(CountVertices(read_uncommited_accessor, memgraph::storage::View::NEW), 1);
+  EXPECT_EQ(CountVertices(*read_uncommited_accessor, memgraph::storage::View::NEW), 1);
   MgpVertexPtr vertex_to_delete{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &immutable_graph,
-                                                    mgp_vertex_id{vertex_id.AsInt()}, &memory)};
+                                                    mgp_vertex_id{vertex_id.AsInt()}, &this->memory)};
   ASSERT_NE(vertex_to_delete, nullptr);
   EXPECT_EQ(mgp_graph_delete_vertex(&immutable_graph, vertex_to_delete.get()), mgp_error::MGP_ERROR_IMMUTABLE_OBJECT);
-  EXPECT_EQ(CountVertices(read_uncommited_accessor, memgraph::storage::View::NEW), 1);
+  EXPECT_EQ(CountVertices(*read_uncommited_accessor, memgraph::storage::View::NEW), 1);
 }
 
-TEST_F(MgpGraphTest, VerticesIterator) {
+TYPED_TEST(MgpGraphTest, VerticesIterator) {
   {
-    auto accessor = CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
+    auto accessor = this->CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
     accessor.InsertVertex();
     ASSERT_FALSE(accessor.Commit().HasError());
   }
   auto check_vertices_iterator = [this](const memgraph::storage::View view) {
-    mgp_graph graph = CreateGraph(view);
+    mgp_graph graph = this->CreateGraph(view);
     MgpVerticesIteratorPtr vertices_iter{
-        EXPECT_MGP_NO_ERROR(mgp_vertices_iterator *, mgp_graph_iter_vertices, &graph, &memory)};
+        EXPECT_MGP_NO_ERROR(mgp_vertices_iterator *, mgp_graph_iter_vertices, &graph, &this->memory)};
     ASSERT_NE(vertices_iter, nullptr);
     EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_vertices_iterator_get, vertices_iter.get());
     if (view == memgraph::storage::View::NEW) {
@@ -262,21 +307,25 @@ TEST_F(MgpGraphTest, VerticesIterator) {
   }
 }
 
-TEST_F(MgpGraphTest, VertexIsMutable) {
-  auto graph = CreateGraph(memgraph::storage::View::NEW);
-  MgpVertexPtr vertex{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_create_vertex, &graph, &memory)};
+TYPED_TEST(MgpGraphTest, VertexIsMutable) {
+  auto graph = this->CreateGraph(memgraph::storage::View::NEW);
+  MgpVertexPtr vertex{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_create_vertex, &graph, &this->memory)};
   ASSERT_NE(vertex.get(), nullptr);
   EXPECT_NE(EXPECT_MGP_NO_ERROR(int, mgp_vertex_underlying_graph_is_mutable, vertex.get()), 0);
   graph.view = memgraph::storage::View::OLD;
   EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_vertex_underlying_graph_is_mutable, vertex.get()), 0);
 }
 
-TEST_F(MgpGraphTest, VertexSetProperty) {
+TYPED_TEST(MgpGraphTest, VertexSetProperty) {
+  if (std::is_same<TypeParam, memgraph::storage::DiskStorage>::value) {
+    // DiskStorage doesn't support READ_UNCOMMITTED isolation level
+    return;
+  }
   static constexpr std::string_view property_to_update{"to_update"};
   static constexpr std::string_view property_to_set{"to_set"};
   memgraph::storage::Gid vertex_id{};
   {
-    auto accessor = CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
+    auto accessor = this->CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
     auto vertex = accessor.InsertVertex();
     vertex_id = vertex.Gid();
     const auto result =
@@ -284,23 +333,23 @@ TEST_F(MgpGraphTest, VertexSetProperty) {
     ASSERT_TRUE(result.HasValue());
     ASSERT_FALSE(accessor.Commit().HasError());
   }
-  auto read_uncommited_accessor = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
-  EXPECT_EQ(CountVertices(read_uncommited_accessor, memgraph::storage::View::NEW), 1);
+  auto read_uncommited_accessor = this->storage->Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
+  EXPECT_EQ(CountVertices(*read_uncommited_accessor, memgraph::storage::View::NEW), 1);
 
-  mgp_graph graph = CreateGraph(memgraph::storage::View::NEW);
-  MgpVertexPtr vertex{
-      EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{vertex_id.AsInt()}, &memory)};
+  mgp_graph graph = this->CreateGraph(memgraph::storage::View::NEW);
+  MgpVertexPtr vertex{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
+                                          mgp_vertex_id{vertex_id.AsInt()}, &this->memory)};
   ASSERT_NE(vertex, nullptr);
 
-  auto vertex_acc = read_uncommited_accessor.FindVertex(vertex_id, memgraph::storage::View::NEW);
-  ASSERT_TRUE(vertex_acc.has_value());
-  const auto property_id_to_update = read_uncommited_accessor.NameToProperty(property_to_update);
+  auto vertex_acc = read_uncommited_accessor->FindVertex(vertex_id, memgraph::storage::View::NEW);
+  ASSERT_TRUE(vertex_acc);
+  const auto property_id_to_update = read_uncommited_accessor->NameToProperty(property_to_update);
 
   {
     SCOPED_TRACE("Update the property");
     static constexpr int64_t numerical_value_to_update_to{69};
     MgpValuePtr value_to_update_to{
-        EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, numerical_value_to_update_to, &memory)};
+        EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, numerical_value_to_update_to, &this->memory)};
     ASSERT_NE(value_to_update_to, nullptr);
     EXPECT_SUCCESS(mgp_vertex_set_property(vertex.get(), property_to_update.data(), value_to_update_to.get()));
 
@@ -310,7 +359,7 @@ TEST_F(MgpGraphTest, VertexSetProperty) {
   }
   {
     SCOPED_TRACE("Remove the property");
-    MgpValuePtr null_value{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)};
+    MgpValuePtr null_value{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &this->memory)};
     ASSERT_NE(null_value, nullptr);
     EXPECT_SUCCESS(mgp_vertex_set_property(vertex.get(), property_to_update.data(), null_value.get()));
 
@@ -321,51 +370,60 @@ TEST_F(MgpGraphTest, VertexSetProperty) {
   {
     SCOPED_TRACE("Add a property");
     static constexpr double numerical_value_to_set{3.5};
-    MgpValuePtr value_to_set{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_double, numerical_value_to_set, &memory)};
+    MgpValuePtr value_to_set{
+        EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_double, numerical_value_to_set, &this->memory)};
     ASSERT_NE(value_to_set, nullptr);
     EXPECT_SUCCESS(mgp_vertex_set_property(vertex.get(), property_to_set.data(), value_to_set.get()));
-    const auto maybe_prop =
-        vertex_acc->GetProperty(read_uncommited_accessor.NameToProperty(property_to_set), memgraph::storage::View::NEW);
+    const auto maybe_prop = vertex_acc->GetProperty(read_uncommited_accessor->NameToProperty(property_to_set),
+                                                    memgraph::storage::View::NEW);
     ASSERT_TRUE(maybe_prop.HasValue());
     EXPECT_EQ(*maybe_prop, memgraph::storage::PropertyValue{numerical_value_to_set});
   }
 }
 
-TEST_F(MgpGraphTest, VertexAddLabel) {
+TYPED_TEST(MgpGraphTest, VertexAddLabel) {
+  if (std::is_same<TypeParam, memgraph::storage::DiskStorage>::value) {
+    // DiskStorage doesn't support READ_UNCOMMITTED isolation level
+    return;
+  }
   static constexpr std::string_view label = "test_label";
   memgraph::storage::Gid vertex_id{};
   {
-    auto accessor = CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
+    auto accessor = this->CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
     const auto vertex = accessor.InsertVertex();
     vertex_id = vertex.Gid();
     ASSERT_FALSE(accessor.Commit().HasError());
   }
 
-  mgp_graph graph = CreateGraph(memgraph::storage::View::NEW);
-  MgpVertexPtr vertex{
-      EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{vertex_id.AsInt()}, &memory)};
+  mgp_graph graph = this->CreateGraph(memgraph::storage::View::NEW);
+  MgpVertexPtr vertex{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
+                                          mgp_vertex_id{vertex_id.AsInt()}, &this->memory)};
   EXPECT_SUCCESS(mgp_vertex_add_label(vertex.get(), mgp_label{label.data()}));
 
   auto check_label = [&]() {
     EXPECT_NE(EXPECT_MGP_NO_ERROR(int, mgp_vertex_has_label_named, vertex.get(), label.data()), 0);
 
-    auto read_uncommited_accessor = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
-    const auto maybe_vertex = read_uncommited_accessor.FindVertex(vertex_id, memgraph::storage::View::NEW);
-    ASSERT_TRUE(maybe_vertex.has_value());
+    auto read_uncommited_accessor = this->storage->Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
+    const auto maybe_vertex = read_uncommited_accessor->FindVertex(vertex_id, memgraph::storage::View::NEW);
+    ASSERT_TRUE(maybe_vertex);
     const auto label_ids = maybe_vertex->Labels(memgraph::storage::View::NEW);
     ASSERT_TRUE(label_ids.HasValue());
-    EXPECT_THAT(*label_ids, ::testing::ContainerEq(std::vector{read_uncommited_accessor.NameToLabel(label)}));
+    EXPECT_THAT(*label_ids, ::testing::ContainerEq(std::vector{read_uncommited_accessor->NameToLabel(label)}));
   };
   ASSERT_NO_FATAL_FAILURE(check_label());
   EXPECT_SUCCESS(mgp_vertex_add_label(vertex.get(), mgp_label{label.data()}));
   ASSERT_NO_FATAL_FAILURE(check_label());
 }
 
-TEST_F(MgpGraphTest, VertexRemoveLabel) {
+TYPED_TEST(MgpGraphTest, VertexRemoveLabel) {
+  if (std::is_same<TypeParam, memgraph::storage::DiskStorage>::value) {
+    // DiskStorage doesn't support READ_UNCOMMITTED isolation level
+    return;
+  }
   static constexpr std::string_view label = "test_label";
   memgraph::storage::Gid vertex_id{};
   {
-    auto accessor = CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
+    auto accessor = this->CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
     auto vertex = accessor.InsertVertex();
     const auto result = vertex.AddLabel(accessor.NameToLabel(label));
     ASSERT_TRUE(result.HasValue());
@@ -374,17 +432,17 @@ TEST_F(MgpGraphTest, VertexRemoveLabel) {
     ASSERT_FALSE(accessor.Commit().HasError());
   }
 
-  mgp_graph graph = CreateGraph(memgraph::storage::View::NEW);
-  MgpVertexPtr vertex{
-      EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{vertex_id.AsInt()}, &memory)};
+  mgp_graph graph = this->CreateGraph(memgraph::storage::View::NEW);
+  MgpVertexPtr vertex{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
+                                          mgp_vertex_id{vertex_id.AsInt()}, &this->memory)};
   EXPECT_SUCCESS(mgp_vertex_remove_label(vertex.get(), mgp_label{label.data()}));
 
   auto check_label = [&]() {
     EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_vertex_has_label_named, vertex.get(), label.data()), 0);
 
-    auto read_uncommited_accessor = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
-    const auto maybe_vertex = read_uncommited_accessor.FindVertex(vertex_id, memgraph::storage::View::NEW);
-    ASSERT_TRUE(maybe_vertex.has_value());
+    auto read_uncommited_accessor = this->storage->Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
+    const auto maybe_vertex = read_uncommited_accessor->FindVertex(vertex_id, memgraph::storage::View::NEW);
+    ASSERT_TRUE(maybe_vertex);
     const auto label_ids = maybe_vertex->Labels(memgraph::storage::View::NEW);
     ASSERT_TRUE(label_ids.HasValue());
     EXPECT_EQ(label_ids->size(), 0);
@@ -394,58 +452,58 @@ TEST_F(MgpGraphTest, VertexRemoveLabel) {
   ASSERT_NO_FATAL_FAILURE(check_label());
 }
 
-TEST_F(MgpGraphTest, ModifyImmutableVertex) {
+TYPED_TEST(MgpGraphTest, ModifyImmutableVertex) {
   static constexpr std::string_view label_to_remove{"label_to_remove"};
   memgraph::storage::Gid vertex_id{};
   {
-    auto accessor = CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
+    auto accessor = this->CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
     auto vertex = accessor.InsertVertex();
     vertex_id = vertex.Gid();
     ASSERT_TRUE(vertex.AddLabel(accessor.NameToLabel(label_to_remove)).HasValue());
     ASSERT_FALSE(accessor.Commit().HasError());
   }
-  auto graph = CreateGraph(memgraph::storage::View::OLD);
-  MgpVertexPtr vertex{
-      EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{vertex_id.AsInt()}, &memory)};
+  auto graph = this->CreateGraph(memgraph::storage::View::OLD);
+  MgpVertexPtr vertex{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
+                                          mgp_vertex_id{vertex_id.AsInt()}, &this->memory)};
   EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_vertex_underlying_graph_is_mutable, vertex.get()), 0);
 
   EXPECT_EQ(mgp_vertex_add_label(vertex.get(), mgp_label{"label"}), mgp_error::MGP_ERROR_IMMUTABLE_OBJECT);
   EXPECT_EQ(mgp_vertex_remove_label(vertex.get(), mgp_label{label_to_remove.data()}),
             mgp_error::MGP_ERROR_IMMUTABLE_OBJECT);
-  MgpValuePtr value{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, 4, &memory)};
+  MgpValuePtr value{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, 4, &this->memory)};
   EXPECT_EQ(mgp_vertex_set_property(vertex.get(), "property", value.get()), mgp_error::MGP_ERROR_IMMUTABLE_OBJECT);
 }
 
-TEST_F(MgpGraphTest, CreateDeleteEdge) {
+TYPED_TEST(MgpGraphTest, CreateDeleteEdge) {
   std::array<memgraph::storage::Gid, 2> vertex_ids{};
   {
-    auto accessor = CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
+    auto accessor = this->CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
     for (auto i = 0; i < 2; ++i) {
       vertex_ids[i] = accessor.InsertVertex().Gid();
     }
     ASSERT_FALSE(accessor.Commit().HasError());
   }
-  auto graph = CreateGraph();
+  auto graph = this->CreateGraph();
   MgpVertexPtr from{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
-                                        mgp_vertex_id{vertex_ids[0].AsInt()}, &memory)};
+                                        mgp_vertex_id{vertex_ids[0].AsInt()}, &this->memory)};
   MgpVertexPtr to{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
-                                      mgp_vertex_id{vertex_ids[1].AsInt()}, &memory)};
+                                      mgp_vertex_id{vertex_ids[1].AsInt()}, &this->memory)};
   ASSERT_NE(from, nullptr);
   ASSERT_NE(to, nullptr);
   CheckEdgeCountBetween(from, to, 0);
   MgpEdgePtr edge{EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_graph_create_edge, &graph, from.get(), to.get(),
-                                      mgp_edge_type{"EDGE"}, &memory)};
+                                      mgp_edge_type{"EDGE"}, &this->memory)};
   CheckEdgeCountBetween(from, to, 1);
   ASSERT_NE(edge, nullptr);
   EXPECT_SUCCESS(mgp_graph_delete_edge(&graph, edge.get()));
   CheckEdgeCountBetween(from, to, 0);
 }
 
-TEST_F(MgpGraphTest, CreateDeleteEdgeWithImmutableGraph) {
+TYPED_TEST(MgpGraphTest, CreateDeleteEdgeWithImmutableGraph) {
   memgraph::storage::Gid from_id;
   memgraph::storage::Gid to_id;
   {
-    auto accessor = CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
+    auto accessor = this->CreateDbAccessor(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
     auto from = accessor.InsertVertex();
     auto to = accessor.InsertVertex();
     from_id = from.Gid();
@@ -453,51 +511,51 @@ TEST_F(MgpGraphTest, CreateDeleteEdgeWithImmutableGraph) {
     ASSERT_TRUE(accessor.InsertEdge(&from, &to, accessor.NameToEdgeType("EDGE_TYPE_TO_REMOVE")).HasValue());
     ASSERT_FALSE(accessor.Commit().HasError());
   }
-  auto graph = CreateGraph(memgraph::storage::View::OLD);
-  MgpVertexPtr from{
-      EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{from_id.AsInt()}, &memory)};
-  MgpVertexPtr to{
-      EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{to_id.AsInt()}, &memory)};
+  auto graph = this->CreateGraph(memgraph::storage::View::OLD);
+  MgpVertexPtr from{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
+                                        mgp_vertex_id{from_id.AsInt()}, &this->memory)};
+  MgpVertexPtr to{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph, mgp_vertex_id{to_id.AsInt()},
+                                      &this->memory)};
   ASSERT_NE(from, nullptr);
   ASSERT_NE(to, nullptr);
   CheckEdgeCountBetween(from, to, 1);
   mgp_edge *edge{nullptr};
-  EXPECT_EQ(
-      mgp_graph_create_edge(&graph, from.get(), to.get(), mgp_edge_type{"NEWLY_CREATED_EDGE_TYPE"}, &memory, &edge),
-      mgp_error::MGP_ERROR_IMMUTABLE_OBJECT);
+  EXPECT_EQ(mgp_graph_create_edge(&graph, from.get(), to.get(), mgp_edge_type{"NEWLY_CREATED_EDGE_TYPE"}, &this->memory,
+                                  &edge),
+            mgp_error::MGP_ERROR_IMMUTABLE_OBJECT);
   CheckEdgeCountBetween(from, to, 1);
 
   MgpEdgesIteratorPtr edges_it{
-      EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, from.get(), &memory)};
+      EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, from.get(), &this->memory)};
   auto *edge_from_it = EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edges_iterator_get, edges_it.get());
   ASSERT_NE(edge_from_it, nullptr);
   EXPECT_EQ(mgp_graph_delete_edge(&graph, edge_from_it), mgp_error::MGP_ERROR_IMMUTABLE_OBJECT);
-  MgpEdgePtr edge_copy_of_immutable{EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edge_copy, edge_from_it, &memory)};
+  MgpEdgePtr edge_copy_of_immutable{EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edge_copy, edge_from_it, &this->memory)};
   EXPECT_EQ(mgp_graph_delete_edge(&graph, edge_copy_of_immutable.get()), mgp_error::MGP_ERROR_IMMUTABLE_OBJECT);
   CheckEdgeCountBetween(from, to, 1);
 }
 
-TEST_F(MgpGraphTest, EdgeIsMutable) {
-  const auto vertex_ids = CreateEdge();
-  auto graph = CreateGraph();
+TYPED_TEST(MgpGraphTest, EdgeIsMutable) {
+  const auto vertex_ids = this->CreateEdge();
+  auto graph = this->CreateGraph();
   MgpEdgePtr edge{};
-  ASSERT_NO_FATAL_FAILURE(GetFirstOutEdge(graph, vertex_ids[0], edge));
+  ASSERT_NO_FATAL_FAILURE(this->GetFirstOutEdge(graph, vertex_ids[0], edge));
   EXPECT_NE(EXPECT_MGP_NO_ERROR(int, mgp_edge_underlying_graph_is_mutable, edge.get()), 0);
   graph.view = memgraph::storage::View::OLD;
   EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_edge_underlying_graph_is_mutable, edge.get()), 0);
 }
 
-TEST_F(MgpGraphTest, MutableFromTo) {
+TYPED_TEST(MgpGraphTest, MutableFromTo) {
   memgraph::storage::Gid from_vertex_id{};
   {
-    const auto vertex_ids = CreateEdge();
+    const auto vertex_ids = this->CreateEdge();
     from_vertex_id = vertex_ids[0];
   }
   auto check_edges_iterator = [this, from_vertex_id](const memgraph::storage::View view) {
-    mgp_graph graph = CreateGraph(view);
+    mgp_graph graph = this->CreateGraph(view);
 
     MgpEdgePtr edge{};
-    ASSERT_NO_FATAL_FAILURE(GetFirstOutEdge(graph, from_vertex_id, edge));
+    ASSERT_NO_FATAL_FAILURE(this->GetFirstOutEdge(graph, from_vertex_id, edge));
     auto *from = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_edge_get_from, edge.get());
     auto *to = EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_edge_get_from, edge.get());
     auto check_is_mutable = [&edge, from, to](bool is_mutable) {
@@ -521,18 +579,19 @@ TEST_F(MgpGraphTest, MutableFromTo) {
   }
 }
 
-TEST_F(MgpGraphTest, EdgesIterator) {
+TYPED_TEST(MgpGraphTest, EdgesIterator) {
   memgraph::storage::Gid from_vertex_id{};
   {
-    const auto vertex_ids = CreateEdge();
+    const auto vertex_ids = this->CreateEdge();
     from_vertex_id = vertex_ids[0];
   }
   auto check_edges_iterator = [this, from_vertex_id](const memgraph::storage::View view) {
-    mgp_graph graph = CreateGraph(view);
+    mgp_graph graph = this->CreateGraph(view);
 
     MgpVertexPtr from{EXPECT_MGP_NO_ERROR(mgp_vertex *, mgp_graph_get_vertex_by_id, &graph,
-                                          mgp_vertex_id{from_vertex_id.AsInt()}, &memory)};
-    MgpEdgesIteratorPtr iter{EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, from.get(), &memory)};
+                                          mgp_vertex_id{from_vertex_id.AsInt()}, &this->memory)};
+    MgpEdgesIteratorPtr iter{
+        EXPECT_MGP_NO_ERROR(mgp_edges_iterator *, mgp_vertex_iter_out_edges, from.get(), &this->memory)};
     auto *edge = EXPECT_MGP_NO_ERROR(mgp_edge *, mgp_edges_iterator_get, iter.get());
     auto check_is_mutable = [&edge, &iter](bool is_mutable) {
       EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_edges_iterator_underlying_graph_is_mutable, iter.get()) != 0, is_mutable);
@@ -554,39 +613,43 @@ TEST_F(MgpGraphTest, EdgesIterator) {
   }
 }
 
-TEST_F(MgpGraphTest, EdgeSetProperty) {
+TYPED_TEST(MgpGraphTest, EdgeSetProperty) {
+  if (std::is_same<TypeParam, memgraph::storage::DiskStorage>::value) {
+    // DiskStorage doesn't support READ_UNCOMMITTED isolation level
+    return;
+  }
   static constexpr std::string_view property_to_update{"to_update"};
   static constexpr std::string_view property_to_set{"to_set"};
 
   memgraph::storage::Gid from_vertex_id{};
-  auto get_edge = [&from_vertex_id](memgraph::storage::Storage::Accessor &accessor) -> memgraph::storage::EdgeAccessor {
-    auto from = accessor.FindVertex(from_vertex_id, memgraph::storage::View::NEW);
-    return from->OutEdges(memgraph::storage::View::NEW).GetValue().front();
+  auto get_edge = [&from_vertex_id](memgraph::storage::Storage::Accessor *accessor) -> memgraph::storage::EdgeAccessor {
+    auto from = accessor->FindVertex(from_vertex_id, memgraph::storage::View::NEW);
+    return std::move(from->OutEdges(memgraph::storage::View::NEW).GetValue().front());
   };
   {
-    const auto vertex_ids = CreateEdge();
+    const auto vertex_ids = this->CreateEdge();
     from_vertex_id = vertex_ids[0];
-    auto accessor = storage.Access(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
-    auto edge = get_edge(accessor);
+    auto accessor = this->storage->Access(memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION);
+    auto edge = get_edge(accessor.get());
     const auto result =
-        edge.SetProperty(accessor.NameToProperty(property_to_update), memgraph::storage::PropertyValue(42));
+        edge.SetProperty(accessor->NameToProperty(property_to_update), memgraph::storage::PropertyValue(42));
     ASSERT_TRUE(result.HasValue());
-    ASSERT_FALSE(accessor.Commit().HasError());
+    ASSERT_FALSE(accessor->Commit().HasError());
   }
-  auto read_uncommited_accessor = storage.Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
+  auto read_uncommited_accessor = this->storage->Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
 
-  mgp_graph graph = CreateGraph(memgraph::storage::View::NEW);
+  mgp_graph graph = this->CreateGraph(memgraph::storage::View::NEW);
   MgpEdgePtr edge;
-  ASSERT_NO_FATAL_FAILURE(GetFirstOutEdge(graph, from_vertex_id, edge));
-  const auto edge_acc = get_edge(read_uncommited_accessor);
+  ASSERT_NO_FATAL_FAILURE(this->GetFirstOutEdge(graph, from_vertex_id, edge));
+  const auto edge_acc = get_edge(read_uncommited_accessor.get());
 
-  const auto property_id_to_update = read_uncommited_accessor.NameToProperty(property_to_update);
+  const auto property_id_to_update = read_uncommited_accessor->NameToProperty(property_to_update);
 
   {
     SCOPED_TRACE("Update the property");
     static constexpr int64_t numerical_value_to_update_to{69};
     MgpValuePtr value_to_update_to{
-        EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, numerical_value_to_update_to, &memory)};
+        EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, numerical_value_to_update_to, &this->memory)};
     ASSERT_NE(value_to_update_to, nullptr);
     EXPECT_SUCCESS(mgp_edge_set_property(edge.get(), property_to_update.data(), value_to_update_to.get()));
 
@@ -596,7 +659,7 @@ TEST_F(MgpGraphTest, EdgeSetProperty) {
   }
   {
     SCOPED_TRACE("Remove the property");
-    MgpValuePtr null_value{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &memory)};
+    MgpValuePtr null_value{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_null, &this->memory)};
     ASSERT_NE(null_value, nullptr);
     EXPECT_SUCCESS(mgp_edge_set_property(edge.get(), property_to_update.data(), null_value.get()));
 
@@ -607,26 +670,27 @@ TEST_F(MgpGraphTest, EdgeSetProperty) {
   {
     SCOPED_TRACE("Add a property");
     static constexpr double numerical_value_to_set{3.5};
-    MgpValuePtr value_to_set{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_double, numerical_value_to_set, &memory)};
+    MgpValuePtr value_to_set{
+        EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_double, numerical_value_to_set, &this->memory)};
     ASSERT_NE(value_to_set, nullptr);
     EXPECT_SUCCESS(mgp_edge_set_property(edge.get(), property_to_set.data(), value_to_set.get()));
     const auto maybe_prop =
-        edge_acc.GetProperty(read_uncommited_accessor.NameToProperty(property_to_set), memgraph::storage::View::NEW);
+        edge_acc.GetProperty(read_uncommited_accessor->NameToProperty(property_to_set), memgraph::storage::View::NEW);
     ASSERT_TRUE(maybe_prop.HasValue());
     EXPECT_EQ(*maybe_prop, memgraph::storage::PropertyValue{numerical_value_to_set});
   }
 }
 
-TEST_F(MgpGraphTest, EdgeSetPropertyWithImmutableGraph) {
+TYPED_TEST(MgpGraphTest, EdgeSetPropertyWithImmutableGraph) {
   memgraph::storage::Gid from_vertex_id{};
   {
-    const auto vertex_ids = CreateEdge();
+    const auto vertex_ids = this->CreateEdge();
     from_vertex_id = vertex_ids[0];
   }
-  auto graph = CreateGraph(memgraph::storage::View::OLD);
+  auto graph = this->CreateGraph(memgraph::storage::View::OLD);
   MgpEdgePtr edge;
-  ASSERT_NO_FATAL_FAILURE(GetFirstOutEdge(graph, from_vertex_id, edge));
-  MgpValuePtr value{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, 65, &memory)};
+  ASSERT_NO_FATAL_FAILURE(this->GetFirstOutEdge(graph, from_vertex_id, edge));
+  MgpValuePtr value{EXPECT_MGP_NO_ERROR(mgp_value *, mgp_value_make_int, 65, &this->memory)};
   EXPECT_EQ(EXPECT_MGP_NO_ERROR(int, mgp_edge_underlying_graph_is_mutable, edge.get()), 0);
   EXPECT_EQ(mgp_edge_set_property(edge.get(), "property", value.get()), mgp_error::MGP_ERROR_IMMUTABLE_OBJECT);
 }
diff --git a/tests/unit/query_required_privileges.cpp b/tests/unit/query_required_privileges.cpp
index 55ab02cb9..4bdef60dc 100644
--- a/tests/unit/query_required_privileges.cpp
+++ b/tests/unit/query_required_privileges.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -70,7 +70,7 @@ TEST_F(TestPrivilegeExtractor, MatchNodeSetLabels) {
 
 TEST_F(TestPrivilegeExtractor, MatchNodeSetProperty) {
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))),
-                                   SET(PROPERTY_LOOKUP(storage.Create<Identifier>("n"), PROP_0), LITERAL(42))));
+                                   SET(PROPERTY_LOOKUP(dba, storage.Create<Identifier>("n"), PROP_0), LITERAL(42))));
   EXPECT_THAT(GetRequiredPrivileges(query),
               UnorderedElementsAre(AuthQuery::Privilege::MATCH, AuthQuery::Privilege::SET));
 }
@@ -88,8 +88,8 @@ TEST_F(TestPrivilegeExtractor, MatchNodeRemoveLabels) {
 }
 
 TEST_F(TestPrivilegeExtractor, MatchNodeRemoveProperty) {
-  auto *query =
-      QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), REMOVE(PROPERTY_LOOKUP(storage.Create<Identifier>("n"), PROP_0))));
+  auto *query = QUERY(
+      SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), REMOVE(PROPERTY_LOOKUP(dba, storage.Create<Identifier>("n"), PROP_0))));
   EXPECT_THAT(GetRequiredPrivileges(query),
               UnorderedElementsAre(AuthQuery::Privilege::MATCH, AuthQuery::Privilege::REMOVE));
 }
diff --git a/tests/unit/query_semantic.cpp b/tests/unit/query_semantic.cpp
index ee18380b2..0c75e7027 100644
--- a/tests/unit/query_semantic.cpp
+++ b/tests/unit/query_semantic.cpp
@@ -13,6 +13,7 @@
 #include <sstream>
 #include <variant>
 
+#include "disk_test_utils.hpp"
 #include "gtest/gtest.h"
 
 #include "query/exceptions.hpp"
@@ -20,20 +21,34 @@
 #include "query/frontend/semantic/symbol_generator.hpp"
 #include "query/frontend/semantic/symbol_table.hpp"
 #include "query/plan/preprocess.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 #include "query_common.hpp"
 
 using namespace memgraph::query;
 
+template <typename StorageType>
 class TestSymbolGenerator : public ::testing::Test {
  protected:
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
+  const std::string testSuite = "query_semantic";
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
   AstStorage storage;
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
 };
 
-TEST_F(TestSymbolGenerator, MatchNodeReturn) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(TestSymbolGenerator, StorageTypes);
+
+TYPED_TEST(TestSymbolGenerator, MatchNodeReturn) {
   // MATCH (node_atom_1) RETURN node_atom_1
   auto query_ast = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("node_atom_1"))), RETURN("node_atom_1")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query_ast);
@@ -57,7 +72,7 @@ TEST_F(TestSymbolGenerator, MatchNodeReturn) {
   EXPECT_EQ(node_sym, ret_sym);
 }
 
-TEST_F(TestSymbolGenerator, MatchNamedPattern) {
+TYPED_TEST(TestSymbolGenerator, MatchNamedPattern) {
   // MATCH p = (node_atom_1) RETURN node_atom_1
   auto query_ast = QUERY(SINGLE_QUERY(MATCH(NAMED_PATTERN("p", NODE("node_atom_1"))), RETURN("p")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query_ast);
@@ -71,7 +86,7 @@ TEST_F(TestSymbolGenerator, MatchNamedPattern) {
   EXPECT_TRUE(pattern_sym.user_declared());
 }
 
-TEST_F(TestSymbolGenerator, MatchUnboundMultiReturn) {
+TYPED_TEST(TestSymbolGenerator, MatchUnboundMultiReturn) {
   // AST using variable in return bound by naming the previous return
   // expression. This is treated as an unbound variable.
   // MATCH (node_atom_1) RETURN node_atom_1 AS n, n
@@ -79,21 +94,21 @@ TEST_F(TestSymbolGenerator, MatchUnboundMultiReturn) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query_ast), UnboundVariableError);
 }
 
-TEST_F(TestSymbolGenerator, MatchNodeUnboundReturn) {
+TYPED_TEST(TestSymbolGenerator, MatchNodeUnboundReturn) {
   // AST with unbound variable in return: MATCH (n) RETURN x
   auto query_ast = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), RETURN("x")));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query_ast), UnboundVariableError);
 }
 
-TEST_F(TestSymbolGenerator, CreatePropertyUnbound) {
+TYPED_TEST(TestSymbolGenerator, CreatePropertyUnbound) {
   // AST with unbound variable in create: CREATE ({prop: x})
   auto node = NODE("anon");
-  std::get<0>(node->properties_)[storage.GetPropertyIx("prop")] = IDENT("x");
+  std::get<0>(node->properties_)[this->storage.GetPropertyIx("prop")] = IDENT("x");
   auto *query_ast = QUERY(SINGLE_QUERY(CREATE(PATTERN(node))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query_ast), UnboundVariableError);
 }
 
-TEST_F(TestSymbolGenerator, CreateNodeReturn) {
+TYPED_TEST(TestSymbolGenerator, CreateNodeReturn) {
   // Simple AST returning a created node: CREATE (n) RETURN n
   auto query_ast = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"))), RETURN("n")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query_ast);
@@ -114,26 +129,26 @@ TEST_F(TestSymbolGenerator, CreateNodeReturn) {
   EXPECT_EQ(node_sym, ret_sym);
 }
 
-TEST_F(TestSymbolGenerator, CreateRedeclareNode) {
+TYPED_TEST(TestSymbolGenerator, CreateRedeclareNode) {
   // AST with redeclaring a variable when creating nodes: CREATE (n), (n)
   auto query_ast = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n")), PATTERN(NODE("n")))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query_ast), RedeclareVariableError);
 }
 
-TEST_F(TestSymbolGenerator, MultiCreateRedeclareNode) {
+TYPED_TEST(TestSymbolGenerator, MultiCreateRedeclareNode) {
   // AST with redeclaring a variable when creating nodes with multiple creates:
   // CREATE (n) CREATE (n)
   auto query_ast = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"))), CREATE(PATTERN(NODE("n")))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query_ast), RedeclareVariableError);
 }
 
-TEST_F(TestSymbolGenerator, MatchCreateRedeclareNode) {
+TYPED_TEST(TestSymbolGenerator, MatchCreateRedeclareNode) {
   // AST with redeclaring a match node variable in create: MATCH (n) CREATE (n)
   auto query_ast = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), CREATE(PATTERN(NODE("n")))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query_ast), RedeclareVariableError);
 }
 
-TEST_F(TestSymbolGenerator, MatchCreateRedeclareEdge) {
+TYPED_TEST(TestSymbolGenerator, MatchCreateRedeclareEdge) {
   // AST with redeclaring a match edge variable in create:
   // MATCH (n) -[r]- (m) CREATE (n) -[r :relationship]-> (l)
   auto relationship = "relationship";
@@ -143,14 +158,14 @@ TEST_F(TestSymbolGenerator, MatchCreateRedeclareEdge) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), RedeclareVariableError);
 }
 
-TEST_F(TestSymbolGenerator, MatchTypeMismatch) {
+TYPED_TEST(TestSymbolGenerator, MatchTypeMismatch) {
   // Using an edge variable as a node causes a type mismatch.
   // MATCH (n) -[r]-> (r)
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("r")))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), TypeMismatchError);
 }
 
-TEST_F(TestSymbolGenerator, MatchCreateTypeMismatch) {
+TYPED_TEST(TestSymbolGenerator, MatchCreateTypeMismatch) {
   // Using an edge variable as a node causes a type mismatch.
   // MATCH (n1) -[r1]- (n2) CREATE (r1) -[r2]-> (n2)
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n1"), EDGE("r1"), NODE("n2"))),
@@ -158,18 +173,18 @@ TEST_F(TestSymbolGenerator, MatchCreateTypeMismatch) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), TypeMismatchError);
 }
 
-TEST_F(TestSymbolGenerator, CreateMultipleEdgeType) {
+TYPED_TEST(TestSymbolGenerator, CreateMultipleEdgeType) {
   // Multiple edge relationship are not allowed when creating edges.
   // CREATE (n) -[r :rel1 | :rel2]-> (m)
   auto rel1 = "rel1";
   auto rel2 = "rel2";
   auto edge = EDGE("r", EdgeAtom::Direction::OUT, {rel1});
-  edge->edge_types_.emplace_back(storage.GetEdgeTypeIx(rel2));
+  edge->edge_types_.emplace_back(this->storage.GetEdgeTypeIx(rel2));
   auto query = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"), edge, NODE("m")))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, CreateBidirectionalEdge) {
+TYPED_TEST(TestSymbolGenerator, CreateBidirectionalEdge) {
   // Bidirectional relationships are not allowed when creating edges.
   // CREATE (n) -[r :rel1]- (m)
   auto rel1 = "rel1";
@@ -177,13 +192,13 @@ TEST_F(TestSymbolGenerator, CreateBidirectionalEdge) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, MatchWhereUnbound) {
+TYPED_TEST(TestSymbolGenerator, MatchWhereUnbound) {
   // Test MATCH (n) WHERE missing < 42 RETURN n
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), WHERE(LESS(IDENT("missing"), LITERAL(42))), RETURN("n")));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), UnboundVariableError);
 }
 
-TEST_F(TestSymbolGenerator, CreateDelete) {
+TYPED_TEST(TestSymbolGenerator, CreateDelete) {
   // Test CREATE (n) DELETE n
   auto node = NODE("n");
   auto ident = IDENT("n");
@@ -197,13 +212,13 @@ TEST_F(TestSymbolGenerator, CreateDelete) {
   EXPECT_EQ(node_symbol, ident_symbol);
 }
 
-TEST_F(TestSymbolGenerator, CreateDeleteUnbound) {
+TYPED_TEST(TestSymbolGenerator, CreateDeleteUnbound) {
   // Test CREATE (n) DELETE missing
   auto query = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"))), DELETE(IDENT("missing"))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), UnboundVariableError);
 }
 
-TEST_F(TestSymbolGenerator, MatchWithReturn) {
+TYPED_TEST(TestSymbolGenerator, MatchWithReturn) {
   // Test MATCH (old) WITH old AS n RETURN n AS n
   auto node = NODE("old");
   auto old_ident = IDENT("old");
@@ -225,19 +240,19 @@ TEST_F(TestSymbolGenerator, MatchWithReturn) {
   EXPECT_NE(n, ret_n);
 }
 
-TEST_F(TestSymbolGenerator, MatchWithReturnUnbound) {
+TYPED_TEST(TestSymbolGenerator, MatchWithReturnUnbound) {
   // Test MATCH (old) WITH old AS n RETURN old
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("old"))), WITH("old", AS("n")), RETURN("old")));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), UnboundVariableError);
 }
 
-TEST_F(TestSymbolGenerator, MatchWithWhere) {
+TYPED_TEST(TestSymbolGenerator, MatchWithWhere) {
   // Test MATCH (old) WITH old AS n WHERE n.prop < 42
-  auto prop = dba.NameToProperty("prop");
+  auto prop = this->dba.NameToProperty("prop");
   auto node = NODE("old");
   auto old_ident = IDENT("old");
   auto with_as_n = AS("n");
-  auto n_prop = PROPERTY_LOOKUP("n", prop);
+  auto n_prop = PROPERTY_LOOKUP(this->dba, "n", prop);
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node)), WITH(old_ident, with_as_n), WHERE(LESS(n_prop, LITERAL(42)))));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   // symbols for pattern, `old` and `n`
@@ -251,15 +266,15 @@ TEST_F(TestSymbolGenerator, MatchWithWhere) {
   EXPECT_EQ(n, with_n);
 }
 
-TEST_F(TestSymbolGenerator, MatchWithWhereUnbound) {
+TYPED_TEST(TestSymbolGenerator, MatchWithWhereUnbound) {
   // Test MATCH (old) WITH COUNT(old) AS c WHERE old.prop < 42
-  auto prop = dba.NameToProperty("prop");
+  auto prop = this->dba.NameToProperty("prop");
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("old"))), WITH(COUNT(IDENT("old"), false), AS("c")),
-                                  WHERE(LESS(PROPERTY_LOOKUP("old", prop), LITERAL(42)))));
+                                  WHERE(LESS(PROPERTY_LOOKUP(this->dba, "old", prop), LITERAL(42)))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), UnboundVariableError);
 }
 
-TEST_F(TestSymbolGenerator, CreateMultiExpand) {
+TYPED_TEST(TestSymbolGenerator, CreateMultiExpand) {
   // Test CREATE (n) -[r :r]-> (m), (n) - [p :p]-> (l)
   auto r_type = "r";
   auto p_type = "p";
@@ -291,7 +306,7 @@ TEST_F(TestSymbolGenerator, CreateMultiExpand) {
   EXPECT_NE(r, p);
 }
 
-TEST_F(TestSymbolGenerator, MatchCreateExpandLabel) {
+TYPED_TEST(TestSymbolGenerator, MatchCreateExpandLabel) {
   // Test MATCH (n) CREATE (m) -[r :r]-> (n:label)
   auto r_type = "r";
   auto label = "label";
@@ -301,20 +316,20 @@ TEST_F(TestSymbolGenerator, MatchCreateExpandLabel) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, CreateExpandProperty) {
+TYPED_TEST(TestSymbolGenerator, CreateExpandProperty) {
   // Test CREATE (n) -[r :r]-> (n {prop: 42})
   auto r_type = "r";
   auto n_prop = NODE("n");
-  std::get<0>(n_prop->properties_)[storage.GetPropertyIx("prop")] = LITERAL(42);
+  std::get<0>(n_prop->properties_)[this->storage.GetPropertyIx("prop")] = LITERAL(42);
   auto query = QUERY(SINGLE_QUERY(CREATE(PATTERN(NODE("n"), EDGE("r", EdgeAtom::Direction::OUT, {r_type}), n_prop))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, MatchReturnSum) {
+TYPED_TEST(TestSymbolGenerator, MatchReturnSum) {
   // Test MATCH (n) RETURN SUM(n.prop) + 42 AS result
-  auto prop = dba.NameToProperty("prop");
+  auto prop = this->dba.NameToProperty("prop");
   auto node = NODE("n");
-  auto sum = SUM(PROPERTY_LOOKUP("n", prop), false);
+  auto sum = SUM(PROPERTY_LOOKUP(this->dba, "n", prop), false);
   auto as_result = AS("result");
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node)), RETURN(ADD(sum, LITERAL(42)), as_result)));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
@@ -328,30 +343,30 @@ TEST_F(TestSymbolGenerator, MatchReturnSum) {
   EXPECT_NE(result_symbol, sum_symbol);
 }
 
-TEST_F(TestSymbolGenerator, NestedAggregation) {
+TYPED_TEST(TestSymbolGenerator, NestedAggregation) {
   // Test MATCH (n) RETURN SUM(42 + SUM(n.prop)) AS s
-  auto prop = dba.NameToProperty("prop");
-  auto query =
-      QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))),
-                         RETURN(SUM(ADD(LITERAL(42), SUM(PROPERTY_LOOKUP("n", prop), false)), false), AS("s"))));
+  auto prop = this->dba.NameToProperty("prop");
+  auto query = QUERY(
+      SINGLE_QUERY(MATCH(PATTERN(NODE("n"))),
+                   RETURN(SUM(ADD(LITERAL(42), SUM(PROPERTY_LOOKUP(this->dba, "n", prop), false)), false), AS("s"))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, WrongAggregationContext) {
+TYPED_TEST(TestSymbolGenerator, WrongAggregationContext) {
   // Test MATCH (n) WITH n.prop AS prop WHERE SUM(prop) < 42
-  auto prop = dba.NameToProperty("prop");
-  auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), WITH(PROPERTY_LOOKUP("n", prop), AS("prop")),
+  auto prop = this->dba.NameToProperty("prop");
+  auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), WITH(PROPERTY_LOOKUP(this->dba, "n", prop), AS("prop")),
                                   WHERE(LESS(SUM(IDENT("prop"), false), LITERAL(42)))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, MatchPropCreateNodeProp) {
+TYPED_TEST(TestSymbolGenerator, MatchPropCreateNodeProp) {
   // Test MATCH (n) CREATE (m {prop: n.prop})
-  auto prop = PROPERTY_PAIR("prop");
+  auto prop = PROPERTY_PAIR(this->dba, "prop");
   auto node_n = NODE("n");
   auto node_m = NODE("m");
-  auto n_prop = PROPERTY_LOOKUP("n", prop.second);
-  std::get<0>(node_m->properties_)[storage.GetPropertyIx(prop.first)] = n_prop;
+  auto n_prop = PROPERTY_LOOKUP(this->dba, "n", prop.second);
+  std::get<0>(node_m->properties_)[this->storage.GetPropertyIx(prop.first)] = n_prop;
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n)), CREATE(PATTERN(node_m))));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   // symbols: pattern * 2, `node_n`, `node_m`
@@ -362,7 +377,7 @@ TEST_F(TestSymbolGenerator, MatchPropCreateNodeProp) {
   EXPECT_NE(n, m);
 }
 
-TEST_F(TestSymbolGenerator, CreateNodeEdge) {
+TYPED_TEST(TestSymbolGenerator, CreateNodeEdge) {
   // Test CREATE (n), (n) -[r :r]-> (n)
   auto r_type = "r";
   auto node_1 = NODE("n");
@@ -379,7 +394,7 @@ TEST_F(TestSymbolGenerator, CreateNodeEdge) {
   EXPECT_NE(n, symbol_table.at(*edge->identifier_));
 }
 
-TEST_F(TestSymbolGenerator, MatchWithCreate) {
+TYPED_TEST(TestSymbolGenerator, MatchWithCreate) {
   // Test MATCH (n) WITH n AS m CREATE (m) -[r :r]-> (m)
   auto r_type = "r";
   auto node_1 = NODE("n");
@@ -399,51 +414,51 @@ TEST_F(TestSymbolGenerator, MatchWithCreate) {
   EXPECT_EQ(m, symbol_table.at(*node_3->identifier_));
 }
 
-TEST_F(TestSymbolGenerator, SameResultsWith) {
+TYPED_TEST(TestSymbolGenerator, SameResultsWith) {
   // Test MATCH (n) WITH n AS m, n AS m
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), WITH("n", AS("m"), "n", AS("m"))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, SameResults) {
+TYPED_TEST(TestSymbolGenerator, SameResults) {
   // Test MATCH (n) RETURN n, n
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), RETURN("n", "n")));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, SkipUsingIdentifier) {
+TYPED_TEST(TestSymbolGenerator, SkipUsingIdentifier) {
   // Test MATCH (old) WITH old AS new SKIP old
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("old"))), WITH("old", AS("new"), SKIP(IDENT("old")))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, SkipUsingIdentifierAlias) {
+TYPED_TEST(TestSymbolGenerator, SkipUsingIdentifierAlias) {
   // Test MATCH (old) WITH old AS new SKIP new
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("old"))), WITH("old", AS("new"), SKIP(IDENT("new")))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, LimitUsingIdentifier) {
+TYPED_TEST(TestSymbolGenerator, LimitUsingIdentifier) {
   // Test MATCH (n) RETURN n AS n LIMIT n
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), RETURN("n", LIMIT(IDENT("n")))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, OrderByAggregation) {
+TYPED_TEST(TestSymbolGenerator, OrderByAggregation) {
   // Test MATCH (old) RETURN old AS new ORDER BY COUNT(1)
   auto query =
       QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("old"))), RETURN("old", AS("new"), ORDER_BY(COUNT(LITERAL(1), false)))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, OrderByUnboundVariable) {
+TYPED_TEST(TestSymbolGenerator, OrderByUnboundVariable) {
   // Test MATCH (old) RETURN COUNT(old) AS new ORDER BY old
   auto query = QUERY(
       SINGLE_QUERY(MATCH(PATTERN(NODE("old"))), RETURN(COUNT(IDENT("old"), false), AS("new"), ORDER_BY(IDENT("old")))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), UnboundVariableError);
 }
 
-TEST_F(TestSymbolGenerator, AggregationOrderBy) {
+TYPED_TEST(TestSymbolGenerator, AggregationOrderBy) {
   // Test MATCH (old) RETURN COUNT(old) AS new ORDER BY new
   auto node = NODE("old");
   auto ident_old = IDENT("old");
@@ -460,7 +475,7 @@ TEST_F(TestSymbolGenerator, AggregationOrderBy) {
   EXPECT_EQ(new_sym, symbol_table.at(*ident_new));
 }
 
-TEST_F(TestSymbolGenerator, OrderByOldVariable) {
+TYPED_TEST(TestSymbolGenerator, OrderByOldVariable) {
   // Test MATCH (old) RETURN old AS new ORDER BY old
   auto node = NODE("old");
   auto ident_old = IDENT("old");
@@ -477,13 +492,13 @@ TEST_F(TestSymbolGenerator, OrderByOldVariable) {
   EXPECT_NE(old, new_sym);
 }
 
-TEST_F(TestSymbolGenerator, MergeVariableError) {
+TYPED_TEST(TestSymbolGenerator, MergeVariableError) {
   // Test MATCH (n) MERGE (n)
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), MERGE(PATTERN(NODE("n")))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), RedeclareVariableError);
 }
 
-TEST_F(TestSymbolGenerator, MergeVariableErrorEdge) {
+TYPED_TEST(TestSymbolGenerator, MergeVariableErrorEdge) {
   // Test MATCH (n) -[r]- (m) MERGE (a) -[r :rel]- (b)
   auto rel = "rel";
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r"), NODE("m"))),
@@ -491,24 +506,24 @@ TEST_F(TestSymbolGenerator, MergeVariableErrorEdge) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), RedeclareVariableError);
 }
 
-TEST_F(TestSymbolGenerator, MergeEdgeWithoutType) {
+TYPED_TEST(TestSymbolGenerator, MergeEdgeWithoutType) {
   // Test MERGE (a) -[r]- (b)
   auto query = QUERY(SINGLE_QUERY(MERGE(PATTERN(NODE("a"), EDGE("r"), NODE("b")))));
   // Edge must have a type, since it doesn't we raise.
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, MergeOnMatchOnCreate) {
+TYPED_TEST(TestSymbolGenerator, MergeOnMatchOnCreate) {
   // Test MATCH (n) MERGE (n) -[r :rel]- (m) ON MATCH SET n.prop = 42
   //      ON CREATE SET m.prop = 42 RETURN r AS r
   auto rel = "rel";
-  auto prop = dba.NameToProperty("prop");
+  auto prop = this->dba.NameToProperty("prop");
   auto match_n = NODE("n");
   auto merge_n = NODE("n");
   auto edge_r = EDGE("r", EdgeAtom::Direction::BOTH, {rel});
   auto node_m = NODE("m");
-  auto n_prop = PROPERTY_LOOKUP("n", prop);
-  auto m_prop = PROPERTY_LOOKUP("m", prop);
+  auto n_prop = PROPERTY_LOOKUP(this->dba, "n", prop);
+  auto m_prop = PROPERTY_LOOKUP(this->dba, "m", prop);
   auto ident_r = IDENT("r");
   auto as_r = AS("r");
   auto query = QUERY(SINGLE_QUERY(
@@ -532,14 +547,14 @@ TEST_F(TestSymbolGenerator, MergeOnMatchOnCreate) {
   EXPECT_EQ(m, symbol_table.at(*dynamic_cast<Identifier *>(m_prop->expression_)));
 }
 
-TEST_F(TestSymbolGenerator, WithUnwindRedeclareReturn) {
+TYPED_TEST(TestSymbolGenerator, WithUnwindRedeclareReturn) {
   // Test WITH [1, 2] AS list UNWIND list AS list RETURN list
   auto query = QUERY(
       SINGLE_QUERY(WITH(LIST(LITERAL(1), LITERAL(2)), AS("list")), UNWIND(IDENT("list"), AS("list")), RETURN("list")));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), RedeclareVariableError);
 }
 
-TEST_F(TestSymbolGenerator, WithUnwindReturn) {
+TYPED_TEST(TestSymbolGenerator, WithUnwindReturn) {
   // WITH [1, 2] AS list UNWIND list AS elem RETURN list AS list, elem AS elem
   auto with_as_list = AS("list");
   auto unwind = UNWIND(IDENT("list"), AS("elem"));
@@ -562,15 +577,15 @@ TEST_F(TestSymbolGenerator, WithUnwindReturn) {
   EXPECT_NE(elem, symbol_table.at(*ret_as_elem));
 }
 
-TEST_F(TestSymbolGenerator, MatchCrossReferenceVariable) {
+TYPED_TEST(TestSymbolGenerator, MatchCrossReferenceVariable) {
   // MATCH (n {prop: m.prop}), (m {prop: n.prop}) RETURN n
-  auto prop = PROPERTY_PAIR("prop");
+  auto prop = PROPERTY_PAIR(this->dba, "prop");
   auto node_n = NODE("n");
-  auto m_prop = PROPERTY_LOOKUP("m", prop.second);
-  std::get<0>(node_n->properties_)[storage.GetPropertyIx(prop.first)] = m_prop;
+  auto m_prop = PROPERTY_LOOKUP(this->dba, "m", prop.second);
+  std::get<0>(node_n->properties_)[this->storage.GetPropertyIx(prop.first)] = m_prop;
   auto node_m = NODE("m");
-  auto n_prop = PROPERTY_LOOKUP("n", prop.second);
-  std::get<0>(node_m->properties_)[storage.GetPropertyIx(prop.first)] = n_prop;
+  auto n_prop = PROPERTY_LOOKUP(this->dba, "n", prop.second);
+  std::get<0>(node_m->properties_)[this->storage.GetPropertyIx(prop.first)] = n_prop;
   auto ident_n = IDENT("n");
   auto as_n = AS("n");
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n), PATTERN(node_m)), RETURN(ident_n, as_n)));
@@ -587,16 +602,16 @@ TEST_F(TestSymbolGenerator, MatchCrossReferenceVariable) {
   EXPECT_NE(m, symbol_table.at(*as_n));
 }
 
-TEST_F(TestSymbolGenerator, MatchWithAsteriskReturnAsterisk) {
+TYPED_TEST(TestSymbolGenerator, MatchWithAsteriskReturnAsterisk) {
   // MATCH (n) -[e]- (m) WITH * RETURN *, n.prop
-  auto prop = dba.NameToProperty("prop");
-  auto n_prop = PROPERTY_LOOKUP("n", prop);
+  auto prop = this->dba.NameToProperty("prop");
+  auto n_prop = PROPERTY_LOOKUP(this->dba, "n", prop);
   auto ret = RETURN(n_prop, AS("n.prop"));
   ret->body_.all_identifiers = true;
   auto node_n = NODE("n");
   auto edge = EDGE("e");
   auto node_m = NODE("m");
-  auto with = storage.Create<With>();
+  auto with = this->storage.template Create<With>();
   with->body_.all_identifiers = true;
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n, edge, node_m)), with, ret));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
@@ -606,7 +621,7 @@ TEST_F(TestSymbolGenerator, MatchWithAsteriskReturnAsterisk) {
   EXPECT_EQ(n, symbol_table.at(*dynamic_cast<Identifier *>(n_prop->expression_)));
 }
 
-TEST_F(TestSymbolGenerator, MatchReturnAsteriskSameResult) {
+TYPED_TEST(TestSymbolGenerator, MatchReturnAsteriskSameResult) {
   // MATCH (n) RETURN *, n
   auto ret = RETURN("n");
   ret->body_.all_identifiers = true;
@@ -614,17 +629,17 @@ TEST_F(TestSymbolGenerator, MatchReturnAsteriskSameResult) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, MatchReturnAsteriskNoUserVariables) {
+TYPED_TEST(TestSymbolGenerator, MatchReturnAsteriskNoUserVariables) {
   // MATCH () RETURN *
-  auto ret = storage.Create<Return>();
+  auto ret = this->storage.template Create<Return>();
   ret->body_.all_identifiers = true;
-  auto ident_n = storage.Create<Identifier>("anon", false);
-  auto node = storage.Create<NodeAtom>(ident_n);
+  auto ident_n = this->storage.template Create<Identifier>("anon", false);
+  auto node = this->storage.template Create<NodeAtom>(ident_n);
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node)), ret));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, MatchMergeExpandLabel) {
+TYPED_TEST(TestSymbolGenerator, MatchMergeExpandLabel) {
   // Test MATCH (n) MERGE (m) -[r :r]-> (n:label)
   auto r_type = "r";
   auto label = "label";
@@ -634,12 +649,12 @@ TEST_F(TestSymbolGenerator, MatchMergeExpandLabel) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, MatchEdgeWithIdentifierInProperty) {
+TYPED_TEST(TestSymbolGenerator, MatchEdgeWithIdentifierInProperty) {
   // Test MATCH (n) -[r {prop: n.prop}]- (m) RETURN r
-  auto prop = PROPERTY_PAIR("prop");
+  auto prop = PROPERTY_PAIR(this->dba, "prop");
   auto edge = EDGE("r");
-  auto n_prop = PROPERTY_LOOKUP("n", prop.second);
-  std::get<0>(edge->properties_)[storage.GetPropertyIx(prop.first)] = n_prop;
+  auto n_prop = PROPERTY_LOOKUP(this->dba, "n", prop.second);
+  std::get<0>(edge->properties_)[this->storage.GetPropertyIx(prop.first)] = n_prop;
   auto node_n = NODE("n");
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n, edge, NODE("m"))), RETURN("r")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
@@ -649,11 +664,11 @@ TEST_F(TestSymbolGenerator, MatchEdgeWithIdentifierInProperty) {
   EXPECT_EQ(n, symbol_table.at(*dynamic_cast<Identifier *>(n_prop->expression_)));
 }
 
-TEST_F(TestSymbolGenerator, MatchVariablePathUsingIdentifier) {
+TYPED_TEST(TestSymbolGenerator, MatchVariablePathUsingIdentifier) {
   // Test MATCH (n) -[r *..l.prop]- (m), (l) RETURN r
-  auto prop = dba.NameToProperty("prop");
+  auto prop = this->dba.NameToProperty("prop");
   auto edge = EDGE_VARIABLE("r");
-  auto l_prop = PROPERTY_LOOKUP("l", prop);
+  auto l_prop = PROPERTY_LOOKUP(this->dba, "l", prop);
   edge->upper_bound_ = l_prop;
   auto node_l = NODE("l");
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m")), PATTERN(node_l)), RETURN("r")));
@@ -667,18 +682,18 @@ TEST_F(TestSymbolGenerator, MatchVariablePathUsingIdentifier) {
   EXPECT_EQ(r.type(), Symbol::Type::EDGE_LIST);
 }
 
-TEST_F(TestSymbolGenerator, MatchVariablePathUsingUnboundIdentifier) {
+TYPED_TEST(TestSymbolGenerator, MatchVariablePathUsingUnboundIdentifier) {
   // Test MATCH (n) -[r *..l.prop]- (m) MATCH (l) RETURN r
-  auto prop = dba.NameToProperty("prop");
+  auto prop = this->dba.NameToProperty("prop");
   auto edge = EDGE_VARIABLE("r");
-  auto l_prop = PROPERTY_LOOKUP("l", prop);
+  auto l_prop = PROPERTY_LOOKUP(this->dba, "l", prop);
   edge->upper_bound_ = l_prop;
   auto node_l = NODE("l");
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), MATCH(PATTERN(node_l)), RETURN("r")));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, CreateVariablePath) {
+TYPED_TEST(TestSymbolGenerator, CreateVariablePath) {
   // Test CREATE (n) -[r *]-> (m) raises a SemanticException, since variable
   // paths cannot be created.
   auto edge = EDGE_VARIABLE("r", EdgeAtom::Type::DEPTH_FIRST, EdgeAtom::Direction::OUT);
@@ -686,7 +701,7 @@ TEST_F(TestSymbolGenerator, CreateVariablePath) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, MergeVariablePath) {
+TYPED_TEST(TestSymbolGenerator, MergeVariablePath) {
   // Test MERGE (n) -[r *]-> (m) raises a SemanticException, since variable
   // paths cannot be created.
   auto edge = EDGE_VARIABLE("r", EdgeAtom::Type::DEPTH_FIRST, EdgeAtom::Direction::OUT);
@@ -694,7 +709,7 @@ TEST_F(TestSymbolGenerator, MergeVariablePath) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, RedeclareVariablePath) {
+TYPED_TEST(TestSymbolGenerator, RedeclareVariablePath) {
   // Test MATCH (n) -[n*]-> (m) RETURN n raises RedeclareVariableError.
   // This is just a temporary solution, before we add the support for using
   // variable paths with already declared symbols. In the future, this test
@@ -704,32 +719,32 @@ TEST_F(TestSymbolGenerator, RedeclareVariablePath) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), RedeclareVariableError);
 }
 
-TEST_F(TestSymbolGenerator, VariablePathSameIdentifier) {
+TYPED_TEST(TestSymbolGenerator, VariablePathSameIdentifier) {
   // Test MATCH (n) -[r *r.prop..]-> (m) RETURN r raises UnboundVariableError.
   // `r` cannot be used inside the range expression, since it is bound by the
   // variable expansion itself.
-  auto prop = dba.NameToProperty("prop");
+  auto prop = this->dba.NameToProperty("prop");
   auto edge = EDGE_VARIABLE("r", EdgeAtom::Type::DEPTH_FIRST, EdgeAtom::Direction::OUT);
-  edge->lower_bound_ = PROPERTY_LOOKUP("r", prop);
+  edge->lower_bound_ = PROPERTY_LOOKUP(this->dba, "r", prop);
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), RETURN("r")));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), UnboundVariableError);
 }
 
-TEST_F(TestSymbolGenerator, MatchPropertySameIdentifier) {
+TYPED_TEST(TestSymbolGenerator, MatchPropertySameIdentifier) {
   // Test MATCH (n {prop: n.prop}) RETURN n
   // Using `n.prop` needs to work, because filters are run after the value for
   // matched symbol is obtained.
-  auto prop = PROPERTY_PAIR("prop");
+  auto prop = PROPERTY_PAIR(this->dba, "prop");
   auto node_n = NODE("n");
-  auto n_prop = PROPERTY_LOOKUP("n", prop.second);
-  std::get<0>(node_n->properties_)[storage.GetPropertyIx(prop.first)] = n_prop;
+  auto n_prop = PROPERTY_LOOKUP(this->dba, "n", prop.second);
+  std::get<0>(node_n->properties_)[this->storage.GetPropertyIx(prop.first)] = n_prop;
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n)), RETURN("n")));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   auto n = symbol_table.at(*node_n->identifier_);
   EXPECT_EQ(n, symbol_table.at(*dynamic_cast<Identifier *>(n_prop->expression_)));
 }
 
-TEST_F(TestSymbolGenerator, WithReturnAll) {
+TYPED_TEST(TestSymbolGenerator, WithReturnAll) {
   // Test WITH 42 AS x RETURN all(x IN [x] WHERE x = 2) AS x, x AS y
   auto *with_as_x = AS("x");
   auto *list_x = IDENT("x");
@@ -751,7 +766,7 @@ TEST_F(TestSymbolGenerator, WithReturnAll) {
   EXPECT_NE(symbol_table.at(*all->identifier_), symbol_table.at(*ret_as_x));
 }
 
-TEST_F(TestSymbolGenerator, WithReturnSingle) {
+TYPED_TEST(TestSymbolGenerator, WithReturnSingle) {
   // Test WITH 42 AS x RETURN single(x IN [x] WHERE x = 2) AS x, x AS y
   auto *with_as_x = AS("x");
   auto *list_x = IDENT("x");
@@ -773,7 +788,7 @@ TEST_F(TestSymbolGenerator, WithReturnSingle) {
   EXPECT_NE(symbol_table.at(*single->identifier_), symbol_table.at(*ret_as_x));
 }
 
-TEST_F(TestSymbolGenerator, WithReturnReduce) {
+TYPED_TEST(TestSymbolGenerator, WithReturnReduce) {
   // Test WITH 42 AS x RETURN reduce(y = 0, x IN [x] y + x) AS x, x AS y
   auto *with_as_x = AS("x");
   auto *list_x = IDENT("x");
@@ -800,7 +815,7 @@ TEST_F(TestSymbolGenerator, WithReturnReduce) {
   EXPECT_NE(symbol_table.at(*reduce->accumulator_), symbol_table.at(*ret_as_y));
 }
 
-TEST_F(TestSymbolGenerator, WithReturnExtract) {
+TYPED_TEST(TestSymbolGenerator, WithReturnExtract) {
   // Test WITH [1, 2, 3] AS x RETURN extract(x IN x | x + 1) AS x, x AS y
   auto *with_as_x = AS("x");
   auto *list_x = IDENT("x");
@@ -825,14 +840,14 @@ TEST_F(TestSymbolGenerator, WithReturnExtract) {
   EXPECT_NE(symbol_table.at(*extract->identifier_), symbol_table.at(*ret_as_x));
 }
 
-TEST_F(TestSymbolGenerator, MatchBfsReturn) {
+TYPED_TEST(TestSymbolGenerator, MatchBfsReturn) {
   // Test MATCH (n) -[r *bfs..n.prop] (r, n | r.prop)]-> (m) RETURN r AS r
-  auto prop = dba.NameToProperty("prop");
+  auto prop = this->dba.NameToProperty("prop");
   auto *node_n = NODE("n");
-  auto *r_prop = PROPERTY_LOOKUP("r", prop);
-  auto *n_prop = PROPERTY_LOOKUP("n", prop);
-  auto *bfs = storage.Create<EdgeAtom>(IDENT("r"), EdgeAtom::Type::BREADTH_FIRST, EdgeAtom::Direction::OUT,
-                                       std::vector<EdgeTypeIx>{});
+  auto *r_prop = PROPERTY_LOOKUP(this->dba, "r", prop);
+  auto *n_prop = PROPERTY_LOOKUP(this->dba, "n", prop);
+  auto *bfs = this->storage.template Create<EdgeAtom>(IDENT("r"), EdgeAtom::Type::BREADTH_FIRST,
+                                                      EdgeAtom::Direction::OUT, std::vector<EdgeTypeIx>{});
   bfs->filter_lambda_.inner_edge = IDENT("r");
   bfs->filter_lambda_.inner_node = IDENT("n");
   bfs->filter_lambda_.expression = r_prop;
@@ -852,9 +867,10 @@ TEST_F(TestSymbolGenerator, MatchBfsReturn) {
   EXPECT_EQ(symbol_table.at(*node_n->identifier_), symbol_table.at(*dynamic_cast<Identifier *>(n_prop->expression_)));
 }
 
-TEST_F(TestSymbolGenerator, MatchBfsUsesEdgeSymbolError) {
+TYPED_TEST(TestSymbolGenerator, MatchBfsUsesEdgeSymbolError) {
   // Test MATCH (n) -[r *bfs..10 (e, n | r)]-> (m) RETURN r
-  auto *bfs = storage.Create<EdgeAtom>(IDENT("r"), EdgeAtom::Type::BREADTH_FIRST, EdgeAtom::Direction::OUT);
+  auto *bfs =
+      this->storage.template Create<EdgeAtom>(IDENT("r"), EdgeAtom::Type::BREADTH_FIRST, EdgeAtom::Direction::OUT);
   bfs->filter_lambda_.inner_edge = IDENT("e");
   bfs->filter_lambda_.inner_node = IDENT("n");
   bfs->filter_lambda_.expression = IDENT("r");
@@ -863,10 +879,11 @@ TEST_F(TestSymbolGenerator, MatchBfsUsesEdgeSymbolError) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), UnboundVariableError);
 }
 
-TEST_F(TestSymbolGenerator, MatchBfsUsesPreviousOuterSymbol) {
+TYPED_TEST(TestSymbolGenerator, MatchBfsUsesPreviousOuterSymbol) {
   // Test MATCH (a) -[r *bfs..10 (e, n | a)]-> (m) RETURN r
   auto *node_a = NODE("a");
-  auto *bfs = storage.Create<EdgeAtom>(IDENT("r"), EdgeAtom::Type::BREADTH_FIRST, EdgeAtom::Direction::OUT);
+  auto *bfs =
+      this->storage.template Create<EdgeAtom>(IDENT("r"), EdgeAtom::Type::BREADTH_FIRST, EdgeAtom::Direction::OUT);
   bfs->filter_lambda_.inner_edge = IDENT("e");
   bfs->filter_lambda_.inner_node = IDENT("n");
   bfs->filter_lambda_.expression = IDENT("a");
@@ -877,9 +894,10 @@ TEST_F(TestSymbolGenerator, MatchBfsUsesPreviousOuterSymbol) {
             symbol_table.at(*dynamic_cast<Identifier *>(bfs->filter_lambda_.expression)));
 }
 
-TEST_F(TestSymbolGenerator, MatchBfsUsesLaterSymbolError) {
+TYPED_TEST(TestSymbolGenerator, MatchBfsUsesLaterSymbolError) {
   // Test MATCH (n) -[r *bfs..10 (e, n | m)]-> (m) RETURN r
-  auto *bfs = storage.Create<EdgeAtom>(IDENT("r"), EdgeAtom::Type::BREADTH_FIRST, EdgeAtom::Direction::OUT);
+  auto *bfs =
+      this->storage.template Create<EdgeAtom>(IDENT("r"), EdgeAtom::Type::BREADTH_FIRST, EdgeAtom::Direction::OUT);
   bfs->filter_lambda_.inner_edge = IDENT("e");
   bfs->filter_lambda_.inner_node = IDENT("n");
   bfs->filter_lambda_.expression = IDENT("m");
@@ -888,15 +906,15 @@ TEST_F(TestSymbolGenerator, MatchBfsUsesLaterSymbolError) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), UnboundVariableError);
 }
 
-TEST_F(TestSymbolGenerator, MatchVariableLambdaSymbols) {
+TYPED_TEST(TestSymbolGenerator, MatchVariableLambdaSymbols) {
   // MATCH ()-[*]-() RETURN 42 AS res
-  auto ident_n = storage.Create<Identifier>("anon_n", false);
-  auto node = storage.Create<NodeAtom>(ident_n);
-  auto edge = storage.Create<EdgeAtom>(storage.Create<Identifier>("anon_r", false), EdgeAtom::Type::DEPTH_FIRST,
-                                       EdgeAtom::Direction::BOTH);
-  edge->filter_lambda_.inner_edge = storage.Create<Identifier>("anon_inner_e", false);
-  edge->filter_lambda_.inner_node = storage.Create<Identifier>("anon_inner_n", false);
-  auto end_node = storage.Create<NodeAtom>(storage.Create<Identifier>("anon_end", false));
+  auto ident_n = this->storage.template Create<Identifier>("anon_n", false);
+  auto node = this->storage.template Create<NodeAtom>(ident_n);
+  auto edge = this->storage.template Create<EdgeAtom>(this->storage.template Create<Identifier>("anon_r", false),
+                                                      EdgeAtom::Type::DEPTH_FIRST, EdgeAtom::Direction::BOTH);
+  edge->filter_lambda_.inner_edge = this->storage.template Create<Identifier>("anon_inner_e", false);
+  edge->filter_lambda_.inner_node = this->storage.template Create<Identifier>("anon_inner_n", false);
+  auto end_node = this->storage.template Create<NodeAtom>(this->storage.template Create<Identifier>("anon_end", false));
   auto query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node, edge, end_node)), RETURN(LITERAL(42), AS("res"))));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   // Symbols for `anon_n`, `anon_r`, `anon_inner_e`, `anon_inner_n`, `anon_end`
@@ -912,16 +930,16 @@ TEST_F(TestSymbolGenerator, MatchVariableLambdaSymbols) {
   }
 }
 
-TEST_F(TestSymbolGenerator, MatchWShortestReturn) {
+TYPED_TEST(TestSymbolGenerator, MatchWShortestReturn) {
   // Test MATCH (n) -[r *wShortest (r, n | r.weight) (r, n | r.filter)]-> (m)
   // RETURN r AS r
-  auto weight = dba.NameToProperty("weight");
-  auto filter = dba.NameToProperty("filter");
+  auto weight = this->dba.NameToProperty("weight");
+  auto filter = this->dba.NameToProperty("filter");
   auto *node_n = NODE("n");
-  auto *r_weight = PROPERTY_LOOKUP("r", weight);
-  auto *r_filter = PROPERTY_LOOKUP("r", filter);
-  auto *shortest = storage.Create<EdgeAtom>(IDENT("r"), EdgeAtom::Type::WEIGHTED_SHORTEST_PATH,
-                                            EdgeAtom::Direction::OUT, std::vector<EdgeTypeIx>{});
+  auto *r_weight = PROPERTY_LOOKUP(this->dba, "r", weight);
+  auto *r_filter = PROPERTY_LOOKUP(this->dba, "r", filter);
+  auto *shortest = this->storage.template Create<EdgeAtom>(IDENT("r"), EdgeAtom::Type::WEIGHTED_SHORTEST_PATH,
+                                                           EdgeAtom::Direction::OUT, std::vector<EdgeTypeIx>{});
   {
     shortest->weight_lambda_.inner_edge = IDENT("r");
     shortest->weight_lambda_.inner_node = IDENT("n");
@@ -954,14 +972,14 @@ TEST_F(TestSymbolGenerator, MatchWShortestReturn) {
   EXPECT_TRUE(symbol_table.at(*shortest->filter_lambda_.inner_node).user_declared());
 }
 
-TEST_F(TestSymbolGenerator, MatchUnionSymbols) {
+TYPED_TEST(TestSymbolGenerator, MatchUnionSymbols) {
   // RETURN 5 as X UNION RETURN 6 AS x
   auto query = QUERY(SINGLE_QUERY(RETURN(LITERAL(5), AS("X"))), UNION(SINGLE_QUERY(RETURN(LITERAL(6), AS("X")))));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   EXPECT_EQ(symbol_table.max_position(), 3);
 }
 
-TEST_F(TestSymbolGenerator, MatchUnionMultipleSymbols) {
+TYPED_TEST(TestSymbolGenerator, MatchUnionMultipleSymbols) {
   // RETURN 5 as X, 6 AS Y UNION RETURN 5 AS Y, 6 AS x
   auto query = QUERY(SINGLE_QUERY(RETURN(LITERAL(5), AS("X"), LITERAL(6), AS("Y"))),
                      UNION(SINGLE_QUERY(RETURN(LITERAL(5), AS("Y"), LITERAL(6), AS("X")))));
@@ -969,14 +987,14 @@ TEST_F(TestSymbolGenerator, MatchUnionMultipleSymbols) {
   EXPECT_EQ(symbol_table.max_position(), 6);
 }
 
-TEST_F(TestSymbolGenerator, MatchUnionAllSymbols) {
+TYPED_TEST(TestSymbolGenerator, MatchUnionAllSymbols) {
   // RETURN 5 as X UNION ALL RETURN 6 AS x
   auto query = QUERY(SINGLE_QUERY(RETURN(LITERAL(5), AS("X"))), UNION_ALL(SINGLE_QUERY(RETURN(LITERAL(6), AS("X")))));
   auto symbol_table = memgraph::query::MakeSymbolTable(query);
   EXPECT_EQ(symbol_table.max_position(), 3);
 }
 
-TEST_F(TestSymbolGenerator, MatchUnionAllMultipleSymbols) {
+TYPED_TEST(TestSymbolGenerator, MatchUnionAllMultipleSymbols) {
   // RETURN 5 as X, 6 AS Y UNION ALL RETURN 5 AS Y, 6 AS x
   auto query = QUERY(SINGLE_QUERY(RETURN(LITERAL(5), AS("X"), LITERAL(6), AS("Y"))),
                      UNION_ALL(SINGLE_QUERY(RETURN(LITERAL(5), AS("Y"), LITERAL(6), AS("X")))));
@@ -984,9 +1002,9 @@ TEST_F(TestSymbolGenerator, MatchUnionAllMultipleSymbols) {
   EXPECT_EQ(symbol_table.max_position(), 6);
 }
 
-TEST_F(TestSymbolGenerator, MatchUnionReturnAllSymbols) {
+TYPED_TEST(TestSymbolGenerator, MatchUnionReturnAllSymbols) {
   // WITH 1 as X, 2 AS Y RETURN * UNION RETURN 3 AS X, 4 AS Y
-  auto ret = storage.Create<Return>();
+  auto ret = this->storage.template Create<Return>();
   ret->body_.all_identifiers = true;
   auto query = QUERY(SINGLE_QUERY(WITH(LITERAL(1), AS("X"), LITERAL(2), AS("Y")), ret),
                      UNION(SINGLE_QUERY(RETURN(LITERAL(3), AS("X"), LITERAL(4), AS("Y")))));
@@ -994,7 +1012,7 @@ TEST_F(TestSymbolGenerator, MatchUnionReturnAllSymbols) {
   EXPECT_EQ(symbol_table.max_position(), 6);
 }
 
-TEST_F(TestSymbolGenerator, MatchUnionReturnSymbols) {
+TYPED_TEST(TestSymbolGenerator, MatchUnionReturnSymbols) {
   // WITH 1 as X, 2 AS Y RETURN Y, X UNION RETURN 3 AS X, 4 AS Y
   auto query = QUERY(SINGLE_QUERY(WITH(LITERAL(1), AS("X"), LITERAL(2), AS("Y")), RETURN("Y", "X")),
                      UNION(SINGLE_QUERY(RETURN(LITERAL(3), AS("X"), LITERAL(4), AS("Y")))));
@@ -1002,27 +1020,27 @@ TEST_F(TestSymbolGenerator, MatchUnionReturnSymbols) {
   EXPECT_EQ(symbol_table.max_position(), 8);
 }
 
-TEST_F(TestSymbolGenerator, MatchUnionParameterNameThrowSemanticExpcetion) {
+TYPED_TEST(TestSymbolGenerator, MatchUnionParameterNameThrowSemanticExpcetion) {
   // WITH 1 as X, 2 AS Y RETURN * UNION RETURN 3 AS Z, 4 AS Y
-  auto ret = storage.Create<Return>();
+  auto ret = this->storage.template Create<Return>();
   ret->body_.all_identifiers = true;
   auto query = QUERY(SINGLE_QUERY(WITH(LITERAL(1), AS("X"), LITERAL(2), AS("Y")), ret),
                      UNION(SINGLE_QUERY(RETURN(LITERAL(3), AS("Z"), LITERAL(4), AS("Y")))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, MatchUnionParameterNumberThrowSemanticExpcetion) {
+TYPED_TEST(TestSymbolGenerator, MatchUnionParameterNumberThrowSemanticExpcetion) {
   // WITH 1 as X, 2 AS Y RETURN * UNION RETURN 4 AS Y
-  auto ret = storage.Create<Return>();
+  auto ret = this->storage.template Create<Return>();
   ret->body_.all_identifiers = true;
   auto query = QUERY(SINGLE_QUERY(WITH(LITERAL(1), AS("X"), LITERAL(2), AS("Y")), ret),
                      UNION(SINGLE_QUERY(RETURN(LITERAL(4), AS("Y")))));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, MatchUnion) {
+TYPED_TEST(TestSymbolGenerator, MatchUnion) {
   // WITH 5 AS X, 3 AS Y RETURN * UNION WITH 9 AS Y, 4 AS X RETURN Y, X
-  auto ret = storage.Create<Return>();
+  auto ret = this->storage.template Create<Return>();
   ret->body_.all_identifiers = true;
   auto query = QUERY(SINGLE_QUERY(WITH(LITERAL(5), AS("X"), LITERAL(3), AS("Y")), ret),
                      UNION(SINGLE_QUERY(WITH(LITERAL(9), AS("Y"), LITERAL(4), AS("X")), RETURN("Y", "X"))));
@@ -1030,9 +1048,9 @@ TEST_F(TestSymbolGenerator, MatchUnion) {
   EXPECT_EQ(symbol_table.max_position(), 8);
 }
 
-TEST_F(TestSymbolGenerator, CallProcedureYield) {
+TYPED_TEST(TestSymbolGenerator, CallProcedureYield) {
   // WITH 1 AS x CALL proc(x) YIELD x AS y RETURN x, y
-  auto call = storage.Create<CallProcedure>();
+  auto call = this->storage.template Create<CallProcedure>();
   call->procedure_name_ = "proc";
   auto *arg_x = IDENT("x");
   call->arguments_.push_back(arg_x);
@@ -1056,9 +1074,9 @@ TEST_F(TestSymbolGenerator, CallProcedureYield) {
   EXPECT_NE(symbol_table.at(*ret->body_.named_expressions[1]), sym_y);
 }
 
-TEST_F(TestSymbolGenerator, CallProcedureShadowingYield) {
+TYPED_TEST(TestSymbolGenerator, CallProcedureShadowingYield) {
   // WITH 1 AS x CALL proc() YIELD x RETURN 42 AS res
-  auto call = storage.Create<CallProcedure>();
+  auto call = this->storage.template Create<CallProcedure>();
   call->procedure_name_ = "proc";
   call->result_fields_.emplace_back("x");
   call->result_identifiers_.push_back(IDENT("x"));
@@ -1066,9 +1084,9 @@ TEST_F(TestSymbolGenerator, CallProcedureShadowingYield) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, CallProcedureShadowingYieldAlias) {
+TYPED_TEST(TestSymbolGenerator, CallProcedureShadowingYieldAlias) {
   // WITH 1 AS x CALL proc() YIELD y AS x RETURN 42 AS res
-  auto call = storage.Create<CallProcedure>();
+  auto call = this->storage.template Create<CallProcedure>();
   call->procedure_name_ = "proc";
   call->result_fields_.emplace_back("y");
   call->result_identifiers_.push_back(IDENT("x"));
@@ -1076,20 +1094,20 @@ TEST_F(TestSymbolGenerator, CallProcedureShadowingYieldAlias) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, CallProcedureUnboundArgument) {
+TYPED_TEST(TestSymbolGenerator, CallProcedureUnboundArgument) {
   // CALL proc(unbound)
-  auto call = storage.Create<CallProcedure>();
+  auto call = this->storage.template Create<CallProcedure>();
   call->procedure_name_ = "proc";
   call->arguments_.push_back(IDENT("unbound"));
   auto query = QUERY(SINGLE_QUERY(call));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, CallWithoutFieldsReturnAsterisk) {
+TYPED_TEST(TestSymbolGenerator, CallWithoutFieldsReturnAsterisk) {
   // CALL proc() RETURN *
-  auto call = storage.Create<CallProcedure>();
+  auto call = this->storage.template Create<CallProcedure>();
   call->procedure_name_ = "proc";
-  auto ret = storage.Create<Return>();
+  auto ret = this->storage.template Create<Return>();
   ret->body_.all_identifiers = true;
   auto query = QUERY(SINGLE_QUERY(call, ret));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), SemanticException);
@@ -1110,7 +1128,7 @@ TEST(TestSymbolTable, CreateAnonymousSymbolWithExistingUserSymbolCalledAnon) {
   ASSERT_EQ(anon2.name_, "anon2");
 }
 
-TEST_F(TestSymbolGenerator, PredefinedIdentifiers) {
+TYPED_TEST(TestSymbolGenerator, PredefinedIdentifiers) {
   auto *first_op = IDENT("first_op", false);
   auto *second_op = IDENT("second_op", false);
   // RETURN first_op + second_op AS result
@@ -1155,13 +1173,13 @@ TEST_F(TestSymbolGenerator, PredefinedIdentifiers) {
   // UNWIND first_op as u CREATE(first_op {prop: u})
   auto unwind = UNWIND(first_op, AS("u"));
   auto node = NODE("first_op");
-  std::get<0>(node->properties_)[storage.GetPropertyIx("prop")] =
+  std::get<0>(node->properties_)[this->storage.GetPropertyIx("prop")] =
       dynamic_cast<Identifier *>(unwind->named_expression_->expression_);
   query = QUERY(SINGLE_QUERY(unwind, CREATE(PATTERN(node))));
   ASSERT_THROW(memgraph::query::MakeSymbolTable(query, {first_op}), SemanticException);
 }
 
-TEST_F(TestSymbolGenerator, Foreach) {
+TYPED_TEST(TestSymbolGenerator, Foreach) {
   auto *i = NEXPR("i", IDENT("i"));
   auto query = QUERY(SINGLE_QUERY(FOREACH(i, {CREATE(PATTERN(NODE("n")))}), RETURN("n")));
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), UnboundVariableError);
@@ -1181,7 +1199,7 @@ TEST_F(TestSymbolGenerator, Foreach) {
   EXPECT_THROW(memgraph::query::MakeSymbolTable(query), UnboundVariableError);
 }
 
-TEST_F(TestSymbolGenerator, Exists) {
+TYPED_TEST(TestSymbolGenerator, Exists) {
   auto query = QUERY(SINGLE_QUERY(
       MATCH(PATTERN(NODE("n"))),
       WHERE(EXISTS(PATTERN(NODE("n"), EDGE("", EdgeAtom::Direction::BOTH, {}, false), NODE("m")))), RETURN("n")));
@@ -1215,7 +1233,7 @@ TEST_F(TestSymbolGenerator, Exists) {
   ASSERT_EQ(symbol.name_, "n");
 }
 
-TEST_F(TestSymbolGenerator, Subqueries) {
+TYPED_TEST(TestSymbolGenerator, Subqueries) {
   // MATCH (n) CALL { MATCH (n) RETURN n } RETURN n
   // Yields exception because n in subquery is referenced in outer scope
   auto subquery = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), RETURN("n")));
diff --git a/tests/unit/query_streams.cpp b/tests/unit/query_streams.cpp
index 3d080f8f0..3c78b5590 100644
--- a/tests/unit/query_streams.cpp
+++ b/tests/unit/query_streams.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -16,13 +16,15 @@
 
 #include <gtest/gtest.h>
 
+#include "disk_test_utils.hpp"
 #include "integrations/constants.hpp"
 #include "integrations/kafka/exceptions.hpp"
 #include "kafka_mock.hpp"
 #include "query/config.hpp"
 #include "query/interpreter.hpp"
 #include "query/stream/streams.hpp"
-#include "storage/v2/storage.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "test_utils.hpp"
 
 using Streams = memgraph::query::stream::Streams;
@@ -49,12 +51,22 @@ std::filesystem::path GetCleanDataDirectory() {
 }
 }  // namespace
 
-class StreamsTest : public ::testing::Test {
+// We need this proxy class because we can't make template class a friend in different file.
+class StreamsTest {
  public:
-  StreamsTest() { ResetStreamsObject(); }
+  std::optional<Streams> streams_;
+  using KafkaStream = memgraph::query::stream::Streams::StreamData<memgraph::query::stream::KafkaStream>;
+
+  auto ReadLock() { return streams_->streams_.ReadLock(); }
+};
+
+template <typename StorageType>
+class StreamsTestFixture : public ::testing::Test {
+ public:
+  StreamsTestFixture() { ResetStreamsObject(); }
 
  protected:
-  memgraph::storage::Storage db_;
+  const std::string testSuite = "query_streams";
   std::filesystem::path data_directory_{GetCleanDataDirectory()};
   KafkaClusterMock mock_cluster_{std::vector<std::string>{kTopicName}};
   // Though there is a Streams object in interpreter context, it makes more sense to use a separate object to test,
@@ -62,15 +74,26 @@ class StreamsTest : public ::testing::Test {
   // Streams constructor.
   // InterpreterContext::auth_checker_ is used in the Streams object, but only in the message processing part. Because
   // these tests don't send any messages, the auth_checker_ pointer can be left as nullptr.
-  memgraph::query::InterpreterContext interpreter_context_{&db_, memgraph::query::InterpreterConfig{}, data_directory_};
+  memgraph::query::InterpreterContext interpreter_context_{
+      std::make_unique<StorageType>(disk_test_utils::GenerateOnDiskConfig(testSuite)),
+      memgraph::query::InterpreterConfig{}, data_directory_};
   std::filesystem::path streams_data_directory_{data_directory_ / "separate-dir-for-test"};
-  std::optional<Streams> streams_;
+  std::optional<StreamsTest> proxyStreams_;
 
-  void ResetStreamsObject() { streams_.emplace(&interpreter_context_, streams_data_directory_); }
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+
+  void ResetStreamsObject() {
+    proxyStreams_.emplace();
+    proxyStreams_->streams_.emplace(&interpreter_context_, streams_data_directory_);
+  }
 
   void CheckStreamStatus(const StreamCheckData &check_data) {
     SCOPED_TRACE(fmt::format("Checking status of '{}'", check_data.name));
-    const auto &stream_statuses = streams_->GetStreamInfo();
+    const auto &stream_statuses = proxyStreams_->streams_->GetStreamInfo();
     auto it = std::find_if(stream_statuses.begin(), stream_statuses.end(),
                            [&check_data](const auto &stream_status) { return stream_status.name == check_data.name; });
     ASSERT_NE(it, stream_statuses.end());
@@ -82,10 +105,9 @@ class StreamsTest : public ::testing::Test {
   }
 
   void CheckConfigAndCredentials(const StreamCheckData &check_data) {
-    const auto locked_streams = streams_->streams_.ReadLock();
+    const auto locked_streams = proxyStreams_->ReadLock();
     const auto &stream = locked_streams->at(check_data.name);
-    const auto *stream_data =
-        std::get_if<memgraph::query::stream::Streams::StreamData<memgraph::query::stream::KafkaStream>>(&stream);
+    const auto *stream_data = std::get_if<StreamsTest::KafkaStream>(&stream);
     ASSERT_NE(stream_data, nullptr);
     const auto stream_info =
         stream_data->stream_source->ReadLock()->Info(check_data.info.common_info.transformation_name);
@@ -94,12 +116,12 @@ class StreamsTest : public ::testing::Test {
   }
 
   void StartStream(StreamCheckData &check_data) {
-    streams_->Start(check_data.name);
+    proxyStreams_->streams_->Start(check_data.name);
     check_data.is_running = true;
   }
 
   void StopStream(StreamCheckData &check_data) {
-    streams_->Stop(check_data.name);
+    proxyStreams_->streams_->Stop(check_data.name);
     check_data.is_running = false;
   }
 
@@ -124,64 +146,71 @@ class StreamsTest : public ::testing::Test {
   }
 };
 
-TEST_F(StreamsTest, SimpleStreamManagement) {
-  auto check_data = CreateDefaultStreamCheckData();
-  streams_->Create<memgraph::query::stream::KafkaStream>(check_data.name, check_data.info, check_data.owner);
-  EXPECT_NO_FATAL_FAILURE(CheckStreamStatus(check_data));
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(StreamsTestFixture, StorageTypes);
 
-  EXPECT_NO_THROW(streams_->Start(check_data.name));
+TYPED_TEST(StreamsTestFixture, SimpleStreamManagement) {
+  auto check_data = this->CreateDefaultStreamCheckData();
+  this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(check_data.name, check_data.info,
+                                                                                       check_data.owner);
+  EXPECT_NO_FATAL_FAILURE(this->CheckStreamStatus(check_data));
+
+  EXPECT_NO_THROW(this->proxyStreams_->streams_->Start(check_data.name));
   check_data.is_running = true;
-  EXPECT_NO_FATAL_FAILURE(CheckStreamStatus(check_data));
+  EXPECT_NO_FATAL_FAILURE(this->CheckStreamStatus(check_data));
 
-  EXPECT_NO_THROW(streams_->StopAll());
+  EXPECT_NO_THROW(this->proxyStreams_->streams_->StopAll());
   check_data.is_running = false;
-  EXPECT_NO_FATAL_FAILURE(CheckStreamStatus(check_data));
+  EXPECT_NO_FATAL_FAILURE(this->CheckStreamStatus(check_data));
 
-  EXPECT_NO_THROW(streams_->StartAll());
+  EXPECT_NO_THROW(this->proxyStreams_->streams_->StartAll());
   check_data.is_running = true;
-  EXPECT_NO_FATAL_FAILURE(CheckStreamStatus(check_data));
+  EXPECT_NO_FATAL_FAILURE(this->CheckStreamStatus(check_data));
 
-  EXPECT_NO_THROW(streams_->Stop(check_data.name));
+  EXPECT_NO_THROW(this->proxyStreams_->streams_->Stop(check_data.name));
   check_data.is_running = false;
-  EXPECT_NO_FATAL_FAILURE(CheckStreamStatus(check_data));
+  EXPECT_NO_FATAL_FAILURE(this->CheckStreamStatus(check_data));
 
-  EXPECT_NO_THROW(streams_->Drop(check_data.name));
-  EXPECT_TRUE(streams_->GetStreamInfo().empty());
+  EXPECT_NO_THROW(this->proxyStreams_->streams_->Drop(check_data.name));
+  EXPECT_TRUE(this->proxyStreams_->streams_->GetStreamInfo().empty());
 }
 
-TEST_F(StreamsTest, CreateAlreadyExisting) {
-  auto stream_info = CreateDefaultStreamInfo();
+TYPED_TEST(StreamsTestFixture, CreateAlreadyExisting) {
+  auto stream_info = this->CreateDefaultStreamInfo();
   auto stream_name = GetDefaultStreamName();
-  streams_->Create<memgraph::query::stream::KafkaStream>(stream_name, stream_info, std::nullopt);
+  this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(stream_name, stream_info,
+                                                                                       std::nullopt);
 
   try {
-    streams_->Create<memgraph::query::stream::KafkaStream>(stream_name, stream_info, std::nullopt);
+    this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(stream_name, stream_info,
+                                                                                         std::nullopt);
     FAIL() << "Creating already existing stream should throw\n";
   } catch (memgraph::query::stream::StreamsException &exception) {
     EXPECT_EQ(exception.what(), fmt::format("Stream already exists with name '{}'", stream_name));
   }
 }
 
-TEST_F(StreamsTest, DropNotExistingStream) {
-  const auto stream_info = CreateDefaultStreamInfo();
+TYPED_TEST(StreamsTestFixture, DropNotExistingStream) {
+  const auto stream_info = this->CreateDefaultStreamInfo();
   const auto stream_name = GetDefaultStreamName();
   const std::string not_existing_stream_name{"ThisDoesn'tExists"};
-  streams_->Create<memgraph::query::stream::KafkaStream>(stream_name, stream_info, std::nullopt);
+  this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(stream_name, stream_info,
+                                                                                       std::nullopt);
 
   try {
-    streams_->Drop(not_existing_stream_name);
+    this->proxyStreams_->streams_->Drop(not_existing_stream_name);
     FAIL() << "Dropping not existing stream should throw\n";
   } catch (memgraph::query::stream::StreamsException &exception) {
     EXPECT_EQ(exception.what(), fmt::format("Couldn't find stream '{}'", not_existing_stream_name));
   }
 }
 
-TEST_F(StreamsTest, RestoreStreams) {
+TYPED_TEST(StreamsTestFixture, RestoreStreams) {
   std::array stream_check_datas{
-      CreateDefaultStreamCheckData(),
-      CreateDefaultStreamCheckData(),
-      CreateDefaultStreamCheckData(),
-      CreateDefaultStreamCheckData(),
+      this->CreateDefaultStreamCheckData(),
+      this->CreateDefaultStreamCheckData(),
+      this->CreateDefaultStreamCheckData(),
+      this->CreateDefaultStreamCheckData(),
   };
 
   // make the stream infos unique
@@ -212,28 +241,29 @@ TEST_F(StreamsTest, RestoreStreams) {
       }
     }
 
-    mock_cluster_.CreateTopic(stream_info.topics[0]);
+    this->mock_cluster_.CreateTopic(stream_info.topics[0]);
   }
 
   stream_check_datas[3].owner = {};
 
   const auto check_restore_logic = [&stream_check_datas, this]() {
     // Reset the Streams object to trigger reloading
-    ResetStreamsObject();
-    EXPECT_TRUE(streams_->GetStreamInfo().empty());
-    streams_->RestoreStreams();
-    EXPECT_EQ(stream_check_datas.size(), streams_->GetStreamInfo().size());
+    this->ResetStreamsObject();
+    EXPECT_TRUE(this->proxyStreams_->streams_->GetStreamInfo().empty());
+    this->proxyStreams_->streams_->RestoreStreams();
+    EXPECT_EQ(stream_check_datas.size(), this->proxyStreams_->streams_->GetStreamInfo().size());
     for (const auto &check_data : stream_check_datas) {
-      ASSERT_NO_FATAL_FAILURE(CheckStreamStatus(check_data));
-      ASSERT_NO_FATAL_FAILURE(CheckConfigAndCredentials(check_data));
+      ASSERT_NO_FATAL_FAILURE(this->CheckStreamStatus(check_data));
+      ASSERT_NO_FATAL_FAILURE(this->CheckConfigAndCredentials(check_data));
     }
   };
 
-  streams_->RestoreStreams();
-  EXPECT_TRUE(streams_->GetStreamInfo().empty());
+  this->proxyStreams_->streams_->RestoreStreams();
+  EXPECT_TRUE(this->proxyStreams_->streams_->GetStreamInfo().empty());
 
   for (auto &check_data : stream_check_datas) {
-    streams_->Create<memgraph::query::stream::KafkaStream>(check_data.name, check_data.info, check_data.owner);
+    this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
+        check_data.name, check_data.info, check_data.owner);
   }
   {
     SCOPED_TRACE("After streams are created");
@@ -241,7 +271,7 @@ TEST_F(StreamsTest, RestoreStreams) {
   }
 
   for (auto &check_data : stream_check_datas) {
-    StartStream(check_data);
+    this->StartStream(check_data);
   }
   {
     SCOPED_TRACE("After starting streams");
@@ -249,16 +279,16 @@ TEST_F(StreamsTest, RestoreStreams) {
   }
 
   // Stop two of the streams
-  StopStream(stream_check_datas[1]);
-  StopStream(stream_check_datas[3]);
+  this->StopStream(stream_check_datas[1]);
+  this->StopStream(stream_check_datas[3]);
   {
     SCOPED_TRACE("After stopping two streams");
     check_restore_logic();
   }
 
   // Stop the rest of the streams
-  StopStream(stream_check_datas[0]);
-  StopStream(stream_check_datas[2]);
+  this->StopStream(stream_check_datas[0]);
+  this->StopStream(stream_check_datas[2]);
   check_restore_logic();
   {
     SCOPED_TRACE("After stopping all streams");
@@ -266,15 +296,16 @@ TEST_F(StreamsTest, RestoreStreams) {
   }
 }
 
-TEST_F(StreamsTest, CheckWithTimeout) {
-  const auto stream_info = CreateDefaultStreamInfo();
+TYPED_TEST(StreamsTestFixture, CheckWithTimeout) {
+  const auto stream_info = this->CreateDefaultStreamInfo();
   const auto stream_name = GetDefaultStreamName();
-  streams_->Create<memgraph::query::stream::KafkaStream>(stream_name, stream_info, std::nullopt);
+  this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(stream_name, stream_info,
+                                                                                       std::nullopt);
 
   std::chrono::milliseconds timeout{3000};
 
   const auto start = std::chrono::steady_clock::now();
-  EXPECT_THROW(streams_->Check(stream_name, timeout, std::nullopt),
+  EXPECT_THROW(this->proxyStreams_->streams_->Check(stream_name, timeout, std::nullopt),
                memgraph::integrations::kafka::ConsumerCheckFailedException);
   const auto end = std::chrono::steady_clock::now();
 
@@ -283,8 +314,8 @@ TEST_F(StreamsTest, CheckWithTimeout) {
   EXPECT_LE(elapsed, timeout * 1.2);
 }
 
-TEST_F(StreamsTest, CheckInvalidConfig) {
-  auto stream_info = CreateDefaultStreamInfo();
+TYPED_TEST(StreamsTestFixture, CheckInvalidConfig) {
+  auto stream_info = this->CreateDefaultStreamInfo();
   const auto stream_name = GetDefaultStreamName();
   static constexpr auto kInvalidConfigName = "doesnt.exist";
   static constexpr auto kConfigValue = "myprecious";
@@ -293,12 +324,13 @@ TEST_F(StreamsTest, CheckInvalidConfig) {
     EXPECT_TRUE(message.find(kInvalidConfigName) != std::string::npos) << message;
     EXPECT_TRUE(message.find(kConfigValue) != std::string::npos) << message;
   };
-  EXPECT_THROW_WITH_MSG(streams_->Create<memgraph::query::stream::KafkaStream>(stream_name, stream_info, std::nullopt),
+  EXPECT_THROW_WITH_MSG(this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
+                            stream_name, stream_info, std::nullopt),
                         memgraph::integrations::kafka::SettingCustomConfigFailed, checker);
 }
 
-TEST_F(StreamsTest, CheckInvalidCredentials) {
-  auto stream_info = CreateDefaultStreamInfo();
+TYPED_TEST(StreamsTestFixture, CheckInvalidCredentials) {
+  auto stream_info = this->CreateDefaultStreamInfo();
   const auto stream_name = GetDefaultStreamName();
   static constexpr auto kInvalidCredentialName = "doesnt.exist";
   static constexpr auto kCredentialValue = "myprecious";
@@ -308,6 +340,7 @@ TEST_F(StreamsTest, CheckInvalidCredentials) {
     EXPECT_TRUE(message.find(memgraph::integrations::kReducted) != std::string::npos) << message;
     EXPECT_TRUE(message.find(kCredentialValue) == std::string::npos) << message;
   };
-  EXPECT_THROW_WITH_MSG(streams_->Create<memgraph::query::stream::KafkaStream>(stream_name, stream_info, std::nullopt),
+  EXPECT_THROW_WITH_MSG(this->proxyStreams_->streams_->template Create<memgraph::query::stream::KafkaStream>(
+                            stream_name, stream_info, std::nullopt),
                         memgraph::integrations::kafka::SettingCustomConfigFailed, checker);
 }
diff --git a/tests/unit/query_trigger.cpp b/tests/unit/query_trigger.cpp
index 8a20d1294..af73fe32c 100644
--- a/tests/unit/query_trigger.cpp
+++ b/tests/unit/query_trigger.cpp
@@ -14,6 +14,7 @@
 #include <filesystem>
 
 #include <fmt/format.h>
+#include "disk_test_utils.hpp"
 #include "glue/auth_checker.hpp"
 #include "query/auth_checker.hpp"
 #include "query/config.hpp"
@@ -22,7 +23,9 @@
 #include "query/interpreter.hpp"
 #include "query/trigger.hpp"
 #include "query/typed_value.hpp"
+#include "storage/v2/config.hpp"
 #include "storage/v2/id_types.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "utils/exceptions.hpp"
 #include "utils/memory.hpp"
 
@@ -47,25 +50,35 @@ class MockAuthChecker : public memgraph::query::AuthChecker {
 };
 }  // namespace
 
+const std::string testSuite = "query_trigger";
+
+template <typename StorageType>
 class TriggerContextTest : public ::testing::Test {
  public:
-  void SetUp() override { db.emplace(); }
+  void SetUp() override { db = std::make_unique<StorageType>(disk_test_utils::GenerateOnDiskConfig(testSuite)); }
 
   void TearDown() override {
     accessors.clear();
     db.reset();
+
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
   }
 
-  memgraph::storage::Storage::Accessor &StartTransaction() {
-    accessors.push_back(db->Access());
-    return accessors.back();
+  memgraph::storage::Storage::Accessor *StartTransaction() {
+    accessors.emplace_back(db->Access());
+    return accessors.back().get();
   }
 
  protected:
-  std::optional<memgraph::storage::Storage> db;
-  std::list<memgraph::storage::Storage::Accessor> accessors;
+  std::unique_ptr<memgraph::storage::Storage> db;
+  std::list<std::unique_ptr<memgraph::storage::Storage::Accessor>> accessors;
 };
 
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(TriggerContextTest, StorageTypes);
+
 namespace {
 void CheckTypedValueSize(const memgraph::query::TriggerContext &trigger_context,
                          const memgraph::query::TriggerIdentifierTag tag, const size_t expected_size,
@@ -101,14 +114,14 @@ void CheckLabelList(const memgraph::query::TriggerContext &trigger_context,
 // Ensure that TriggerContext returns only valid objects.
 // Returned TypedValue should always contain only objects
 // that exist (unless its explicitly created for the deleted object)
-TEST_F(TriggerContextTest, ValidObjectsTest) {
+TYPED_TEST(TriggerContextTest, ValidObjectsTest) {
   memgraph::query::TriggerContext trigger_context;
   memgraph::query::TriggerContextCollector trigger_context_collector{kAllEventTypes};
 
   size_t vertex_count = 0;
   size_t edge_count = 0;
   {
-    memgraph::query::DbAccessor dba{&StartTransaction()};
+    memgraph::query::DbAccessor dba{this->StartTransaction()};
 
     auto create_vertex = [&] {
       auto created_vertex = dba.InsertVertex();
@@ -162,7 +175,7 @@ TEST_F(TriggerContextTest, ValidObjectsTest) {
   }
 
   {
-    memgraph::query::DbAccessor dba{&StartTransaction()};
+    memgraph::query::DbAccessor dba{this->StartTransaction()};
     trigger_context.AdaptForAccessor(&dba);
 
     // Should have one less created object for vertex and edge
@@ -175,7 +188,7 @@ TEST_F(TriggerContextTest, ValidObjectsTest) {
   size_t deleted_vertex_count = 0;
   size_t deleted_edge_count = 0;
   {
-    memgraph::query::DbAccessor dba{&StartTransaction()};
+    memgraph::query::DbAccessor dba{this->StartTransaction()};
 
     // register each type of change for each object
     {
@@ -189,6 +202,7 @@ TEST_F(TriggerContextTest, ValidObjectsTest) {
         trigger_context_collector.RegisterSetVertexLabel(vertex, dba.NameToLabel("LABEL1"));
         trigger_context_collector.RegisterRemovedVertexLabel(vertex, dba.NameToLabel("LABEL2"));
 
+        dba.PrefetchOutEdges(vertex);
         auto out_edges = vertex.OutEdges(memgraph::storage::View::OLD);
         ASSERT_TRUE(out_edges.HasValue());
 
@@ -259,7 +273,7 @@ TEST_F(TriggerContextTest, ValidObjectsTest) {
   // for each update event.
   // TypedValue of the deleted objects stay the same as they're bound to the transaction which deleted them.
   {
-    memgraph::query::DbAccessor dba{&StartTransaction()};
+    memgraph::query::DbAccessor dba{this->StartTransaction()};
     trigger_context.AdaptForAccessor(&dba);
 
     auto vertices = dba.Vertices(memgraph::storage::View::OLD);
@@ -274,7 +288,7 @@ TEST_F(TriggerContextTest, ValidObjectsTest) {
   }
 
   {
-    memgraph::query::DbAccessor dba{&StartTransaction()};
+    memgraph::query::DbAccessor dba{this->StartTransaction()};
     trigger_context.AdaptForAccessor(&dba);
 
     CheckTypedValueSize(trigger_context, memgraph::query::TriggerIdentifierTag::SET_VERTEX_PROPERTIES, vertex_count,
@@ -306,10 +320,10 @@ TEST_F(TriggerContextTest, ValidObjectsTest) {
 // If the trigger context registered a created object, each future event on the same object will be ignored.
 // Binding the trigger context to transaction will mean that creating and updating an object in the same transaction
 // will return only the CREATE event.
-TEST_F(TriggerContextTest, ReturnCreateOnlyEvent) {
+TYPED_TEST(TriggerContextTest, ReturnCreateOnlyEvent) {
   memgraph::query::TriggerContextCollector trigger_context_collector{kAllEventTypes};
 
-  memgraph::query::DbAccessor dba{&StartTransaction()};
+  memgraph::query::DbAccessor dba{this->StartTransaction()};
 
   auto create_vertex = [&] {
     auto vertex = dba.InsertVertex();
@@ -370,8 +384,8 @@ void EXPECT_PROP_EQ(const memgraph::query::TypedValue &a, const memgraph::query:
 // During a transaction, same property for the same object can change multiple times. TriggerContext should ensure
 // that only the change on the global value is returned (value before the transaction + latest value after the
 // transaction) everything inbetween should be ignored.
-TEST_F(TriggerContextTest, GlobalPropertyChange) {
-  memgraph::query::DbAccessor dba{&StartTransaction()};
+TYPED_TEST(TriggerContextTest, GlobalPropertyChange) {
+  memgraph::query::DbAccessor dba{this->StartTransaction()};
   const std::unordered_set<memgraph::query::TriggerEventType> event_types{
       memgraph::query::TriggerEventType::VERTEX_UPDATE};
 
@@ -565,8 +579,8 @@ TEST_F(TriggerContextTest, GlobalPropertyChange) {
 }
 
 // Same as above, but for label changes
-TEST_F(TriggerContextTest, GlobalLabelChange) {
-  memgraph::query::DbAccessor dba{&StartTransaction()};
+TYPED_TEST(TriggerContextTest, GlobalLabelChange) {
+  memgraph::query::DbAccessor dba{this->StartTransaction()};
   const std::unordered_set<memgraph::query::TriggerEventType> event_types{
       memgraph::query::TriggerEventType::VERTEX_UPDATE};
 
@@ -796,87 +810,88 @@ void CheckFilters(const std::unordered_set<memgraph::query::TriggerEventType> &e
 }
 }  // namespace
 
-TEST_F(TriggerContextTest, Filtering) {
+TYPED_TEST(TriggerContextTest, Filtering) {
   using TET = memgraph::query::TriggerEventType;
   // Check all event type individually
   {
     SCOPED_TRACE("TET::ANY");
     CheckFilters({TET::ANY}, ShouldRegisterExpectation{true, true, true}, ShouldRegisterExpectation{true, true, true},
-                 &StartTransaction());
+                 this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::VERTEX_CREATE");
     CheckFilters({TET::VERTEX_CREATE}, ShouldRegisterExpectation{true, false, false},
-                 ShouldRegisterExpectation{false, false, false}, &StartTransaction());
+                 ShouldRegisterExpectation{false, false, false}, this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::EDGE_CREATE");
     CheckFilters({TET::EDGE_CREATE}, ShouldRegisterExpectation{false, false, false},
-                 ShouldRegisterExpectation{true, false, false}, &StartTransaction());
+                 ShouldRegisterExpectation{true, false, false}, this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::CREATE");
     CheckFilters({TET::CREATE}, ShouldRegisterExpectation{true, false, false},
-                 ShouldRegisterExpectation{true, false, false}, &StartTransaction());
+                 ShouldRegisterExpectation{true, false, false}, this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::VERTEX_DELETE");
     CheckFilters({TET::VERTEX_DELETE}, ShouldRegisterExpectation{true, true, false},
-                 ShouldRegisterExpectation{false, false, false}, &StartTransaction());
+                 ShouldRegisterExpectation{false, false, false}, this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::EDGE_DELETE");
     CheckFilters({TET::EDGE_DELETE}, ShouldRegisterExpectation{false, false, false},
-                 ShouldRegisterExpectation{true, true, false}, &StartTransaction());
+                 ShouldRegisterExpectation{true, true, false}, this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::DELETE");
     CheckFilters({TET::DELETE}, ShouldRegisterExpectation{true, true, false},
-                 ShouldRegisterExpectation{true, true, false}, &StartTransaction());
+                 ShouldRegisterExpectation{true, true, false}, this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::VERTEX_UPDATE");
     CheckFilters({TET::VERTEX_UPDATE}, ShouldRegisterExpectation{true, false, true},
-                 ShouldRegisterExpectation{false, false, false}, &StartTransaction());
+                 ShouldRegisterExpectation{false, false, false}, this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::EDGE_UPDATE");
     CheckFilters({TET::EDGE_UPDATE}, ShouldRegisterExpectation{false, false, false},
-                 ShouldRegisterExpectation{true, false, true}, &StartTransaction());
+                 ShouldRegisterExpectation{true, false, true}, this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::UPDATE");
     CheckFilters({TET::UPDATE}, ShouldRegisterExpectation{true, false, true},
-                 ShouldRegisterExpectation{true, false, true}, &StartTransaction());
+                 ShouldRegisterExpectation{true, false, true}, this->StartTransaction());
   }
   // Some combined versions
   {
     SCOPED_TRACE("TET::VERTEX_UPDATE, TET::EDGE_UPDATE");
     CheckFilters({TET::VERTEX_UPDATE, TET::EDGE_UPDATE}, ShouldRegisterExpectation{true, false, true},
-                 ShouldRegisterExpectation{true, false, true}, &StartTransaction());
+                 ShouldRegisterExpectation{true, false, true}, this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::VERTEX_UPDATE, TET::EDGE_UPDATE, TET::DELETE");
     CheckFilters({TET::VERTEX_UPDATE, TET::EDGE_UPDATE, TET::DELETE}, ShouldRegisterExpectation{true, true, true},
-                 ShouldRegisterExpectation{true, true, true}, &StartTransaction());
+                 ShouldRegisterExpectation{true, true, true}, this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::UPDATE, TET::VERTEX_DELETE, TET::EDGE_DELETE");
     CheckFilters({TET::UPDATE, TET::VERTEX_DELETE, TET::EDGE_DELETE}, ShouldRegisterExpectation{true, true, true},
-                 ShouldRegisterExpectation{true, true, true}, &StartTransaction());
+                 ShouldRegisterExpectation{true, true, true}, this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::VERTEX_CREATE, TET::VERTEX_UPDATE");
     CheckFilters({TET::VERTEX_CREATE, TET::VERTEX_UPDATE}, ShouldRegisterExpectation{true, false, true},
-                 ShouldRegisterExpectation{false, false, false}, &StartTransaction());
+                 ShouldRegisterExpectation{false, false, false}, this->StartTransaction());
   }
   {
     SCOPED_TRACE("TET::EDGE_CREATE, TET::EDGE_UPDATE");
     CheckFilters({TET::EDGE_CREATE, TET::EDGE_UPDATE}, ShouldRegisterExpectation{false, false, false},
-                 ShouldRegisterExpectation{true, false, true}, &StartTransaction());
+                 ShouldRegisterExpectation{true, false, true}, this->StartTransaction());
   }
 }
 
+template <typename StorageType>
 class TriggerStoreTest : public ::testing::Test {
  protected:
   const std::filesystem::path testing_directory{std::filesystem::temp_directory_path() / "MG_test_unit_query_trigger"};
@@ -884,8 +899,10 @@ class TriggerStoreTest : public ::testing::Test {
   void SetUp() override {
     Clear();
 
-    storage_accessor.emplace(storage.Access());
-    dba.emplace(&*storage_accessor);
+    config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+    storage = std::make_unique<StorageType>(config);
+    storage_accessor = storage->Access();
+    dba.emplace(storage_accessor.get());
   }
 
   void TearDown() override {
@@ -893,6 +910,11 @@ class TriggerStoreTest : public ::testing::Test {
 
     dba.reset();
     storage_accessor.reset();
+    storage.reset();
+
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
   }
 
   std::optional<memgraph::query::DbAccessor> dba;
@@ -906,16 +928,20 @@ class TriggerStoreTest : public ::testing::Test {
     std::filesystem::remove_all(testing_directory);
   }
 
-  memgraph::storage::Storage storage;
-  std::optional<memgraph::storage::Storage::Accessor> storage_accessor;
+  memgraph::storage::Config config;
+  std::unique_ptr<memgraph::storage::Storage> storage;
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_accessor;
 };
 
-TEST_F(TriggerStoreTest, Restore) {
+TYPED_TEST_CASE(TriggerStoreTest, StorageTypes);
+
+TYPED_TEST(TriggerStoreTest, Restore) {
   std::optional<memgraph::query::TriggerStore> store;
 
   const auto reset_store = [&] {
-    store.emplace(testing_directory);
-    store->RestoreTriggers(&ast_cache, &*dba, memgraph::query::InterpreterConfig::Query{}, &auth_checker);
+    store.emplace(this->testing_directory);
+    store->RestoreTriggers(&this->ast_cache, &*this->dba, memgraph::query::InterpreterConfig::Query{},
+                           &this->auth_checker);
   };
 
   reset_store();
@@ -936,13 +962,13 @@ TEST_F(TriggerStoreTest, Restore) {
   store->AddTrigger(
       trigger_name_before, trigger_statement,
       std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{1}}},
-      event_type, memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
-      memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
+      event_type, memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
+      memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker);
   store->AddTrigger(
       trigger_name_after, trigger_statement,
       std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{"value"}}},
-      event_type, memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba,
-      memgraph::query::InterpreterConfig::Query{}, {owner}, &auth_checker);
+      event_type, memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
+      memgraph::query::InterpreterConfig::Query{}, {owner}, &this->auth_checker);
 
   const auto check_triggers = [&] {
     ASSERT_EQ(store->GetTriggerInfo().size(), 2);
@@ -987,38 +1013,38 @@ TEST_F(TriggerStoreTest, Restore) {
   check_empty();
 }
 
-TEST_F(TriggerStoreTest, AddTrigger) {
-  memgraph::query::TriggerStore store{testing_directory};
+TYPED_TEST(TriggerStoreTest, AddTrigger) {
+  memgraph::query::TriggerStore store{this->testing_directory};
 
   // Invalid query in statements
   ASSERT_THROW(store.AddTrigger("trigger", "RETUR 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
-                                memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
-                                memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
+                                memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
+                                memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker),
                memgraph::utils::BasicException);
   ASSERT_THROW(store.AddTrigger("trigger", "RETURN createdEdges", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
-                                memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
-                                memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
+                                memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
+                                memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker),
                memgraph::utils::BasicException);
 
   ASSERT_THROW(store.AddTrigger("trigger", "RETURN $parameter", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
-                                memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
-                                memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
+                                memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
+                                memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker),
                memgraph::utils::BasicException);
 
   ASSERT_NO_THROW(store.AddTrigger(
       "trigger", "RETURN $parameter",
       std::map<std::string, memgraph::storage::PropertyValue>{{"parameter", memgraph::storage::PropertyValue{1}}},
-      memgraph::query::TriggerEventType::VERTEX_CREATE, memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
-      memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker));
+      memgraph::query::TriggerEventType::VERTEX_CREATE, memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache,
+      &*this->dba, memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker));
 
   // Inserting with the same name
   ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
-                                memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
-                                memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
+                                memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
+                                memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker),
                memgraph::utils::BasicException);
   ASSERT_THROW(store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
-                                memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba,
-                                memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker),
+                                memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
+                                memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker),
                memgraph::utils::BasicException);
 
   ASSERT_EQ(store.GetTriggerInfo().size(), 1);
@@ -1026,28 +1052,28 @@ TEST_F(TriggerStoreTest, AddTrigger) {
   ASSERT_EQ(store.AfterCommitTriggers().size(), 0);
 }
 
-TEST_F(TriggerStoreTest, DropTrigger) {
-  memgraph::query::TriggerStore store{testing_directory};
+TYPED_TEST(TriggerStoreTest, DropTrigger) {
+  memgraph::query::TriggerStore store{this->testing_directory};
 
   ASSERT_THROW(store.DropTrigger("Unknown"), memgraph::utils::BasicException);
 
   const auto *trigger_name = "trigger";
   store.AddTrigger(trigger_name, "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
-                   memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
-                   memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
+                   memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
+                   memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker);
 
   ASSERT_THROW(store.DropTrigger("Unknown"), memgraph::utils::BasicException);
   ASSERT_NO_THROW(store.DropTrigger(trigger_name));
   ASSERT_EQ(store.GetTriggerInfo().size(), 0);
 }
 
-TEST_F(TriggerStoreTest, TriggerInfo) {
-  memgraph::query::TriggerStore store{testing_directory};
+TYPED_TEST(TriggerStoreTest, TriggerInfo) {
+  memgraph::query::TriggerStore store{this->testing_directory};
 
   std::vector<memgraph::query::TriggerStore::TriggerInfo> expected_info;
   store.AddTrigger("trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::VERTEX_CREATE,
-                   memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
-                   memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
+                   memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
+                   memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker);
   expected_info.push_back({"trigger", "RETURN 1", memgraph::query::TriggerEventType::VERTEX_CREATE,
                            memgraph::query::TriggerPhase::BEFORE_COMMIT});
 
@@ -1066,8 +1092,8 @@ TEST_F(TriggerStoreTest, TriggerInfo) {
   check_trigger_info();
 
   store.AddTrigger("edge_update_trigger", "RETURN 1", {}, memgraph::query::TriggerEventType::EDGE_UPDATE,
-                   memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba,
-                   memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker);
+                   memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
+                   memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker);
   expected_info.push_back({"edge_update_trigger", "RETURN 1", memgraph::query::TriggerEventType::EDGE_UPDATE,
                            memgraph::query::TriggerPhase::AFTER_COMMIT});
 
@@ -1088,8 +1114,8 @@ TEST_F(TriggerStoreTest, TriggerInfo) {
   check_trigger_info();
 }
 
-TEST_F(TriggerStoreTest, AnyTriggerAllKeywords) {
-  memgraph::query::TriggerStore store{testing_directory};
+TYPED_TEST(TriggerStoreTest, AnyTriggerAllKeywords) {
+  memgraph::query::TriggerStore store{this->testing_directory};
 
   using namespace std::literals;
 
@@ -1180,19 +1206,19 @@ TEST_F(TriggerStoreTest, AnyTriggerAllKeywords) {
     for (const auto keyword : keywords) {
       SCOPED_TRACE(keyword);
       EXPECT_NO_THROW(store.AddTrigger(trigger_name, fmt::format("RETURN {}", keyword), {}, event_type,
-                                       memgraph::query::TriggerPhase::BEFORE_COMMIT, &ast_cache, &*dba,
-                                       memgraph::query::InterpreterConfig::Query{}, std::nullopt, &auth_checker));
+                                       memgraph::query::TriggerPhase::BEFORE_COMMIT, &this->ast_cache, &*this->dba,
+                                       memgraph::query::InterpreterConfig::Query{}, std::nullopt, &this->auth_checker));
       store.DropTrigger(trigger_name);
     }
   }
 }
 
-TEST_F(TriggerStoreTest, AuthCheckerUsage) {
+TYPED_TEST(TriggerStoreTest, AuthCheckerUsage) {
   using Privilege = memgraph::query::AuthQuery::Privilege;
   using ::testing::_;
   using ::testing::ElementsAre;
   using ::testing::Return;
-  std::optional<memgraph::query::TriggerStore> store{testing_directory};
+  std::optional<memgraph::query::TriggerStore> store{this->testing_directory};
   const std::optional<std::string> owner{"testing_owner"};
   MockAuthChecker mock_checker;
 
@@ -1205,32 +1231,32 @@ TEST_F(TriggerStoreTest, AuthCheckerUsage) {
 
   ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_1", "CREATE (n:VERTEX) RETURN n", {},
                                     memgraph::query::TriggerEventType::EDGE_UPDATE,
-                                    memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba,
+                                    memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
                                     memgraph::query::InterpreterConfig::Query{}, std::nullopt, &mock_checker));
 
   ASSERT_NO_THROW(store->AddTrigger("successfull_trigger_2", "CREATE (n:VERTEX) RETURN n", {},
                                     memgraph::query::TriggerEventType::EDGE_UPDATE,
-                                    memgraph::query::TriggerPhase::AFTER_COMMIT, &ast_cache, &*dba,
+                                    memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
                                     memgraph::query::InterpreterConfig::Query{}, owner, &mock_checker));
 
   EXPECT_CALL(mock_checker, IsUserAuthorized(std::optional<std::string>{}, ElementsAre(Privilege::MATCH)))
       .Times(1)
       .WillOnce(Return(false));
 
-  ASSERT_THROW(
-      store->AddTrigger("unprivileged_trigger", "MATCH (n:VERTEX) RETURN n", {},
-                        memgraph::query::TriggerEventType::EDGE_UPDATE, memgraph::query::TriggerPhase::AFTER_COMMIT,
-                        &ast_cache, &*dba, memgraph::query::InterpreterConfig::Query{}, std::nullopt, &mock_checker);
-      , memgraph::utils::BasicException);
+  ASSERT_THROW(store->AddTrigger("unprivileged_trigger", "MATCH (n:VERTEX) RETURN n", {},
+                                 memgraph::query::TriggerEventType::EDGE_UPDATE,
+                                 memgraph::query::TriggerPhase::AFTER_COMMIT, &this->ast_cache, &*this->dba,
+                                 memgraph::query::InterpreterConfig::Query{}, std::nullopt, &mock_checker);
+               , memgraph::utils::BasicException);
 
-  store.emplace(testing_directory);
+  store.emplace(this->testing_directory);
   EXPECT_CALL(mock_checker, IsUserAuthorized(std::optional<std::string>{}, ElementsAre(Privilege::CREATE)))
       .Times(1)
       .WillOnce(Return(false));
   EXPECT_CALL(mock_checker, IsUserAuthorized(owner, ElementsAre(Privilege::CREATE))).Times(1).WillOnce(Return(true));
 
-  ASSERT_NO_THROW(
-      store->RestoreTriggers(&ast_cache, &*dba, memgraph::query::InterpreterConfig::Query{}, &mock_checker));
+  ASSERT_NO_THROW(store->RestoreTriggers(&this->ast_cache, &*this->dba, memgraph::query::InterpreterConfig::Query{},
+                                         &mock_checker));
 
   const auto triggers = store->GetTriggerInfo();
   ASSERT_EQ(triggers.size(), 1);
diff --git a/tests/unit/query_variable_start_planner.cpp b/tests/unit/query_variable_start_planner.cpp
index 00f794ffc..615f10b87 100644
--- a/tests/unit/query_variable_start_planner.cpp
+++ b/tests/unit/query_variable_start_planner.cpp
@@ -12,11 +12,14 @@
 #include <algorithm>
 #include <variant>
 
+#include "disk_test_utils.hpp"
 #include "gtest/gtest.h"
 
 #include "query/frontend/semantic/symbol_generator.hpp"
 #include "query/frontend/semantic/symbol_table.hpp"
 #include "query/plan/planner.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "utils/algorithm.hpp"
 
 #include "query_plan_common.hpp"
@@ -86,29 +89,44 @@ void CheckPlansProduce(size_t expected_plan_count, memgraph::query::CypherQuery
   }
 }
 
-TEST(TestVariableStartPlanner, MatchReturn) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+template <typename StorageType>
+class TestVariableStartPlanner : public testing::Test {
+ public:
+  const std::string testSuite = "query_variable_start_planner";
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config)};
+  AstStorage storage;
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+};
+
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(TestVariableStartPlanner, StorageTypes);
+
+TYPED_TEST(TestVariableStartPlanner, MatchReturn) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Make a graph (v1) -[:r]-> (v2)
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
   ASSERT_TRUE(dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("r")).HasValue());
   dba.AdvanceCommand();
   // Test MATCH (n) -[r]-> (m) RETURN n
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r", Direction::OUT), NODE("m"))), RETURN("n")));
   // We have 2 nodes `n` and `m` from which we could start, so expect 2 plans.
-  CheckPlansProduce(2, query, storage, &dba, [&](const auto &results) {
+  CheckPlansProduce(2, query, this->storage, &dba, [&](const auto &results) {
     // We expect to produce only a single (v1) node.
     AssertRows(results, {{TypedValue(memgraph::query::VertexAccessor(v1))}}, dba);
   });
 }
 
-TEST(TestVariableStartPlanner, MatchTripletPatternReturn) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(TestVariableStartPlanner, MatchTripletPatternReturn) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Make a graph (v1) -[:r]-> (v2) -[:r]-> (v3)
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
@@ -118,32 +136,29 @@ TEST(TestVariableStartPlanner, MatchTripletPatternReturn) {
   dba.AdvanceCommand();
   {
     // Test `MATCH (n) -[r]-> (m) -[e]-> (l) RETURN n`
-    AstStorage storage;
     auto *query = QUERY(SINGLE_QUERY(
         MATCH(PATTERN(NODE("n"), EDGE("r", Direction::OUT), NODE("m"), EDGE("e", Direction::OUT), NODE("l"))),
         RETURN("n")));
     // We have 3 nodes: `n`, `m` and `l` from which we could start.
-    CheckPlansProduce(3, query, storage, &dba, [&](const auto &results) {
+    CheckPlansProduce(3, query, this->storage, &dba, [&](const auto &results) {
       // We expect to produce only a single (v1) node.
       AssertRows(results, {{TypedValue(memgraph::query::VertexAccessor(v1))}}, dba);
     });
   }
   {
     // Equivalent to `MATCH (n) -[r]-> (m), (m) -[e]-> (l) RETURN n`.
-    AstStorage storage;
     auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r", Direction::OUT), NODE("m")),
                                            PATTERN(NODE("m"), EDGE("e", Direction::OUT), NODE("l"))),
                                      RETURN("n")));
-    CheckPlansProduce(3, query, storage, &dba, [&](const auto &results) {
+    CheckPlansProduce(3, query, this->storage, &dba, [&](const auto &results) {
       AssertRows(results, {{TypedValue(memgraph::query::VertexAccessor(v1))}}, dba);
     });
   }
 }
 
-TEST(TestVariableStartPlanner, MatchOptionalMatchReturn) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(TestVariableStartPlanner, MatchOptionalMatchReturn) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Make a graph (v1) -[:r]-> (v2) -[:r]-> (v3)
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
@@ -152,13 +167,12 @@ TEST(TestVariableStartPlanner, MatchOptionalMatchReturn) {
   ASSERT_TRUE(dba.InsertEdge(&v2, &v3, dba.NameToEdgeType("r")).HasValue());
   dba.AdvanceCommand();
   // Test MATCH (n) -[r]-> (m) OPTIONAL MATCH (m) -[e]-> (l) RETURN n, l
-  AstStorage storage;
   auto *query =
       QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r", Direction::OUT), NODE("m"))),
                          OPTIONAL_MATCH(PATTERN(NODE("m"), EDGE("e", Direction::OUT), NODE("l"))), RETURN("n", "l")));
   // We have 2 nodes `n` and `m` from which we could start the MATCH, and 2
   // nodes for OPTIONAL MATCH. This should produce 2 * 2 plans.
-  CheckPlansProduce(4, query, storage, &dba, [&](const auto &results) {
+  CheckPlansProduce(4, query, this->storage, &dba, [&](const auto &results) {
     // We expect to produce 2 rows:
     //   * (v1), (v3)
     //   * (v2), null
@@ -169,10 +183,9 @@ TEST(TestVariableStartPlanner, MatchOptionalMatchReturn) {
   });
 }
 
-TEST(TestVariableStartPlanner, MatchOptionalMatchMergeReturn) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(TestVariableStartPlanner, MatchOptionalMatchMergeReturn) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Graph (v1) -[:r]-> (v2)
   memgraph::query::VertexAccessor v1(dba.InsertVertex());
   memgraph::query::VertexAccessor v2(dba.InsertVertex());
@@ -182,45 +195,41 @@ TEST(TestVariableStartPlanner, MatchOptionalMatchMergeReturn) {
   dba.AdvanceCommand();
   // Test MATCH (n) -[r]-> (m) OPTIONAL MATCH (m) -[e]-> (l)
   //      MERGE (u) -[q:r]-> (v) RETURN n, m, l, u, v
-  AstStorage storage;
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r", Direction::OUT), NODE("m"))),
                                    OPTIONAL_MATCH(PATTERN(NODE("m"), EDGE("e", Direction::OUT), NODE("l"))),
                                    MERGE(PATTERN(NODE("u"), EDGE("q", Direction::OUT, {r_type_name}), NODE("v"))),
                                    RETURN("n", "m", "l", "u", "v")));
   // Since MATCH, OPTIONAL MATCH and MERGE each have 2 nodes from which we can
   // start, we generate 2 * 2 * 2 plans.
-  CheckPlansProduce(8, query, storage, &dba, [&](const auto &results) {
+  CheckPlansProduce(8, query, this->storage, &dba, [&](const auto &results) {
     // We expect to produce a single row: (v1), (v2), null, (v1), (v2)
     AssertRows(results, {{TypedValue(v1), TypedValue(v2), TypedValue(), TypedValue(v1), TypedValue(v2)}}, dba);
   });
 }
 
-TEST(TestVariableStartPlanner, MatchWithMatchReturn) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(TestVariableStartPlanner, MatchWithMatchReturn) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Graph (v1) -[:r]-> (v2)
   memgraph::query::VertexAccessor v1(dba.InsertVertex());
   memgraph::query::VertexAccessor v2(dba.InsertVertex());
   ASSERT_TRUE(dba.InsertEdge(&v1, &v2, dba.NameToEdgeType("r")).HasValue());
   dba.AdvanceCommand();
   // Test MATCH (n) -[r]-> (m) WITH n MATCH (m) -[r]-> (l) RETURN n, m, l
-  AstStorage storage;
   auto *query =
       QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), EDGE("r", Direction::OUT), NODE("m"))), WITH("n"),
                          MATCH(PATTERN(NODE("m"), EDGE("r", Direction::OUT), NODE("l"))), RETURN("n", "m", "l")));
   // We can start from 2 nodes in each match. Since WITH separates query parts,
   // we expect to get 2 plans for each, which totals 2 * 2.
-  CheckPlansProduce(4, query, storage, &dba, [&](const auto &results) {
+  CheckPlansProduce(4, query, this->storage, &dba, [&](const auto &results) {
     // We expect to produce a single row: (v1), (v1), (v2)
     AssertRows(results, {{TypedValue(v1), TypedValue(v1), TypedValue(v2)}}, dba);
   });
 }
 
-TEST(TestVariableStartPlanner, MatchVariableExpand) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(TestVariableStartPlanner, MatchVariableExpand) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   // Graph (v1) -[:r1]-> (v2) -[:r2]-> (v3)
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
@@ -229,7 +238,6 @@ TEST(TestVariableStartPlanner, MatchVariableExpand) {
   auto r2 = *dba.InsertEdge(&v2, &v3, dba.NameToEdgeType("r2"));
   dba.AdvanceCommand();
   // Test MATCH (n) -[r*]-> (m) RETURN r
-  AstStorage storage;
   auto edge = EDGE_VARIABLE("r", Type::DEPTH_FIRST, Direction::OUT);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), RETURN("r")));
   // We expect to get a single column with the following rows:
@@ -237,15 +245,14 @@ TEST(TestVariableStartPlanner, MatchVariableExpand) {
   TypedValue r2_list(std::vector<TypedValue>{TypedValue(r2)});  // [r2]
   // [r1, r2]
   TypedValue r1_r2_list(std::vector<TypedValue>{TypedValue(r1), TypedValue(r2)});
-  CheckPlansProduce(2, query, storage, &dba, [&](const auto &results) {
+  CheckPlansProduce(2, query, this->storage, &dba, [&](const auto &results) {
     AssertRows(results, {{r1_list}, {r2_list}, {r1_r2_list}}, dba);
   });
 }
 
-TEST(TestVariableStartPlanner, MatchVariableExpandReferenceNode) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(TestVariableStartPlanner, MatchVariableExpandReferenceNode) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto id = dba.NameToProperty("id");
   // Graph (v1 {id:1}) -[:r1]-> (v2 {id: 2}) -[:r2]-> (v3 {id: 3})
   auto v1 = dba.InsertVertex();
@@ -258,24 +265,22 @@ TEST(TestVariableStartPlanner, MatchVariableExpandReferenceNode) {
   auto r2 = *dba.InsertEdge(&v2, &v3, dba.NameToEdgeType("r2"));
   dba.AdvanceCommand();
   // Test MATCH (n) -[r*..n.id]-> (m) RETURN r
-  AstStorage storage;
   auto edge = EDGE_VARIABLE("r", Type::DEPTH_FIRST, Direction::OUT);
-  edge->upper_bound_ = PROPERTY_LOOKUP("n", id);
+  edge->upper_bound_ = PROPERTY_LOOKUP(dba, "n", id);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), edge, NODE("m"))), RETURN("r")));
   // We expect to get a single column with the following rows:
   // [r1] (v1 -[*..1]-> v2)
   TypedValue r1_list(std::vector<TypedValue>{TypedValue(r1)});
   // [r2] (v2 -[*..2]-> v3)
   TypedValue r2_list(std::vector<TypedValue>{TypedValue(r2)});
-  CheckPlansProduce(2, query, storage, &dba, [&](const auto &results) {
+  CheckPlansProduce(2, query, this->storage, &dba, [&](const auto &results) {
     AssertRows(results, {{r1_list}, {r2_list}}, dba);
   });
 }
 
-TEST(TestVariableStartPlanner, MatchVariableExpandBoth) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(TestVariableStartPlanner, MatchVariableExpandBoth) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto id = dba.NameToProperty("id");
   // Graph (v1 {id:1}) -[:r1]-> (v2) -[:r2]-> (v3)
   auto v1 = dba.InsertVertex();
@@ -286,24 +291,22 @@ TEST(TestVariableStartPlanner, MatchVariableExpandBoth) {
   auto r2 = *dba.InsertEdge(&v2, &v3, dba.NameToEdgeType("r2"));
   dba.AdvanceCommand();
   // Test MATCH (n {id:1}) -[r*]- (m) RETURN r
-  AstStorage storage;
   auto edge = EDGE_VARIABLE("r", Type::DEPTH_FIRST, Direction::BOTH);
   auto node_n = NODE("n");
-  std::get<0>(node_n->properties_)[storage.GetPropertyIx("id")] = LITERAL(1);
+  std::get<0>(node_n->properties_)[this->storage.GetPropertyIx("id")] = LITERAL(1);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(node_n, edge, NODE("m"))), RETURN("r")));
   // We expect to get a single column with the following rows:
   TypedValue r1_list(std::vector<TypedValue>{TypedValue(r1)});  // [r1]
   // [r1, r2]
   TypedValue r1_r2_list(std::vector<TypedValue>{TypedValue(r1), TypedValue(r2)});
-  CheckPlansProduce(2, query, storage, &dba, [&](const auto &results) {
+  CheckPlansProduce(2, query, this->storage, &dba, [&](const auto &results) {
     AssertRows(results, {{r1_list}, {r1_r2_list}}, dba);
   });
 }
 
-TEST(TestVariableStartPlanner, MatchBfs) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
+TYPED_TEST(TestVariableStartPlanner, MatchBfs) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto id = dba.NameToProperty("id");
   // Graph (v1 {id:1}) -[:r1]-> (v2 {id: 2}) -[:r2]-> (v3 {id: 3})
   auto v1 = dba.InsertVertex();
@@ -316,24 +319,21 @@ TEST(TestVariableStartPlanner, MatchBfs) {
   ASSERT_TRUE(dba.InsertEdge(&v2, &v3, dba.NameToEdgeType("r2")).HasValue());
   dba.AdvanceCommand();
   // Test MATCH (n) -[r *bfs..10](r, n | n.id <> 3)]-> (m) RETURN r
-  AstStorage storage;
-  auto *bfs = storage.Create<memgraph::query::EdgeAtom>(IDENT("r"), EdgeAtom::Type::BREADTH_FIRST, Direction::OUT,
-                                                        std::vector<memgraph::query::EdgeTypeIx>{});
+  auto *bfs = this->storage.template Create<memgraph::query::EdgeAtom>(
+      IDENT("r"), EdgeAtom::Type::BREADTH_FIRST, Direction::OUT, std::vector<memgraph::query::EdgeTypeIx>{});
   bfs->filter_lambda_.inner_edge = IDENT("r");
   bfs->filter_lambda_.inner_node = IDENT("n");
-  bfs->filter_lambda_.expression = NEQ(PROPERTY_LOOKUP("n", id), LITERAL(3));
+  bfs->filter_lambda_.expression = NEQ(PROPERTY_LOOKUP(dba, "n", id), LITERAL(3));
   bfs->upper_bound_ = LITERAL(10);
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"), bfs, NODE("m"))), RETURN("r")));
   // We expect to get a single column with the following rows:
   TypedValue r1_list(std::vector<TypedValue>{TypedValue(r1)});  // [r1]
-  CheckPlansProduce(2, query, storage, &dba, [&](const auto &results) { AssertRows(results, {{r1_list}}, dba); });
+  CheckPlansProduce(2, query, this->storage, &dba, [&](const auto &results) { AssertRows(results, {{r1_list}}, dba); });
 }
 
-TEST(TestVariableStartPlanner, TestBasicSubquery) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+TYPED_TEST(TestVariableStartPlanner, TestBasicSubquery) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
@@ -343,7 +343,7 @@ TEST(TestVariableStartPlanner, TestBasicSubquery) {
 
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("n"))), CALL_SUBQUERY(subquery), RETURN("n", "m")));
 
-  CheckPlansProduce(1, query, storage, &dba, [&](const auto &results) {
+  CheckPlansProduce(1, query, this->storage, &dba, [&](const auto &results) {
     AssertRows(results,
                {{TypedValue(v1), TypedValue(v1)},
                 {TypedValue(v1), TypedValue(v2)},
@@ -353,11 +353,9 @@ TEST(TestVariableStartPlanner, TestBasicSubquery) {
   });
 }
 
-TEST(TestVariableStartPlanner, TestBasicSubqueryWithMatching) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+TYPED_TEST(TestVariableStartPlanner, TestBasicSubqueryWithMatching) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
 
   auto v1 = dba.InsertVertex();
   auto v2 = dba.InsertVertex();
@@ -371,16 +369,14 @@ TEST(TestVariableStartPlanner, TestBasicSubqueryWithMatching) {
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("m1"), EDGE("r1", EdgeAtom::Direction::OUT), NODE("n1"))),
                                    CALL_SUBQUERY(subquery), RETURN("m1", "m2")));
 
-  CheckPlansProduce(4, query, storage, &dba, [&](const auto &results) {
+  CheckPlansProduce(4, query, this->storage, &dba, [&](const auto &results) {
     AssertRows(results, {{TypedValue(v1), TypedValue(v1)}}, dba);
   });
 }
 
-TEST(TestVariableStartPlanner, TestSubqueryWithUnion) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+TYPED_TEST(TestVariableStartPlanner, TestSubqueryWithUnion) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto id = dba.NameToProperty("id");
 
   auto v1 = dba.InsertVertex();
@@ -401,16 +397,14 @@ TEST(TestVariableStartPlanner, TestSubqueryWithUnion) {
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("m1"), EDGE("r1", EdgeAtom::Direction::OUT), NODE("n1"))),
                                    CALL_SUBQUERY(subquery), RETURN("m1", "n2")));
 
-  CheckPlansProduce(8, query, storage, &dba, [&](const auto &results) {
+  CheckPlansProduce(8, query, this->storage, &dba, [&](const auto &results) {
     AssertRows(results, {{TypedValue(v1), TypedValue(v2)}, {TypedValue(v1), TypedValue(v2)}}, dba);
   });
 }
 
-TEST(TestVariableStartPlanner, TestSubqueryWithTripleUnion) {
-  memgraph::storage::Storage db;
-  auto storage_dba = db.Access();
-  memgraph::query::DbAccessor dba(&storage_dba);
-  AstStorage storage;
+TYPED_TEST(TestVariableStartPlanner, TestSubqueryWithTripleUnion) {
+  auto storage_dba = this->db->Access();
+  memgraph::query::DbAccessor dba(storage_dba.get());
   auto id = dba.NameToProperty("id");
 
   auto v1 = dba.InsertVertex();
@@ -433,7 +427,7 @@ TEST(TestVariableStartPlanner, TestSubqueryWithTripleUnion) {
   auto *query = QUERY(SINGLE_QUERY(MATCH(PATTERN(NODE("m1"), EDGE("r1", EdgeAtom::Direction::OUT), NODE("n1"))),
                                    CALL_SUBQUERY(subquery), RETURN("m1", "n2")));
 
-  CheckPlansProduce(16, query, storage, &dba, [&](const auto &results) {
+  CheckPlansProduce(16, query, this->storage, &dba, [&](const auto &results) {
     AssertRows(results,
                {{TypedValue(v1), TypedValue(v2)}, {TypedValue(v1), TypedValue(v2)}, {TypedValue(v1), TypedValue(v2)}},
                dba);
diff --git a/tests/unit/storage_rocks.cpp b/tests/unit/storage_rocks.cpp
new file mode 100644
index 000000000..30320a529
--- /dev/null
+++ b/tests/unit/storage_rocks.cpp
@@ -0,0 +1,119 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+// #include <gmock/gmock.h>
+// #include <gtest/gtest.h>
+// #include <cassert>
+// #include <exception>
+// #include <string>
+// #include <unordered_set>
+
+// #include "query/common.hpp"
+// #include "query/db_accessor.hpp"
+// #include "storage/v2/delta.hpp"
+// #include "storage/v2/disk/storage.hpp"
+// #include "storage/v2/id_types.hpp"
+// #include "storage/v2/isolation_level.hpp"
+// #include "storage/v2/property_value.hpp"
+// #include "storage/v2/storage.hpp"
+// #include "storage/v2/vertex_accessor.hpp"
+// #include "storage/v2/view.hpp"
+// #include "utils/rocksdb_serialization.hpp"
+
+// class RocksDBStorageTest : public ::testing::TestWithParam<bool> {
+//  public:
+//   RocksDBStorageTest() { storage = std::unique_ptr<memgraph::storage::Storage>(new memgraph::storage::DiskStorage());
+//   }
+
+//   ~RocksDBStorageTest() override {}
+
+//  protected:
+//   std::unique_ptr<memgraph::storage::Storage> storage;
+// };
+
+// /// Serialize vertex which contains only GID.
+// TEST_F(RocksDBStorageTest, SerializeVertexGID) {
+//   auto acc = storage->Access();
+//   auto vertex = acc->CreateVertex();
+//   auto gid = vertex.Gid();
+// ASSERT_EQ(memgraph::utils::SerializeVertex(*vertex.vertex_), "|" + memgraph::utils::SerializeIdType(gid));
+// }
+
+/// Serialize vertex with gid and its single label.
+// TEST_F(RocksDBStorageTest, SerializeVertexGIDLabels) {
+//   auto acc = storage->Access();
+//   auto vertex = acc->CreateVertex();
+//   auto ser_player_label = acc->NameToLabel("Player");
+//   auto player_result = vertex.AddLabel(ser_player_label);
+//   auto gid = vertex.Gid();
+//   ASSERT_EQ(memgraph::utils::SerializeVertex(*vertex.vertex_),
+//             std::to_string(ser_player_label.AsInt()) + "|" + memgraph::utils::SerializeIdType(gid));
+// }
+
+// /// Serialize vertex with gid and its multiple labels.
+// TEST_F(RocksDBStorageTest, SerializeVertexGIDMultipleLabels) {
+//   auto acc = storage->Access();
+//   auto vertex = acc->CreateVertex();
+//   auto ser_player_label = acc->NameToLabel("Player");
+//   auto ser_person_label = acc->NameToLabel("Person");
+//   auto ser_ball_label = acc->NameToLabel("Ball");
+//   // NOLINTNEXTLINE
+//   auto player_res = vertex.AddLabel(ser_player_label);
+//   auto person_res = vertex.AddLabel(ser_person_label);
+//   auto ball_res = vertex.AddLabel(ser_ball_label);
+//   auto gid = vertex.Gid();
+//   ASSERT_EQ(memgraph::utils::SerializeVertex(*vertex.vertex_),
+//             std::to_string(ser_player_label.AsInt()) + "," + std::to_string(ser_person_label.AsInt()) + "," +
+//                 std::to_string(ser_ball_label.AsInt()) + "|" + memgraph::utils::SerializeIdType(gid));
+// }
+
+// /// Serialize edge.
+// TEST_F(RocksDBStorageTest, SerializeEdge) {
+//   auto acc = storage->Access();
+//   auto vertex1 = acc->CreateVertex();
+//   auto vertex2 = acc->CreateVertex();
+//   auto edge = acc->CreateEdge(&vertex1, &vertex2, acc->NameToEdgeType("KNOWS"));
+//   auto gid = edge->Gid();
+//   auto ser_result = memgraph::utils::SerializeEdge(vertex1.Gid(), vertex2.Gid(), edge->EdgeType(), edge->edge_.ptr);
+//   ASSERT_EQ(ser_result.first,
+//             memgraph::utils::SerializeIdType(vertex1.Gid()) + "|" + memgraph::utils::SerializeIdType(vertex2.Gid()) +
+//                 "|0|" + std::to_string(edge->EdgeType().AsInt()) + "|" + memgraph::utils::SerializeIdType(gid));
+//   ASSERT_EQ(ser_result.second,
+//             memgraph::utils::SerializeIdType(vertex2.Gid()) + "|" + memgraph::utils::SerializeIdType(vertex1.Gid()) +
+//                 "|1|" + std::to_string(edge->EdgeType().AsInt()) + "|" + memgraph::utils::SerializeIdType(gid));
+// }
+
+// TEST_F(RocksDBStorageTest, DeserializeVertex) {
+// NOTE: This test would fail in the case of snaphsot isolation because of the way in which RocksDB
+// serializes commit timestamp.
+// const char *serialized_vertex = "1|1";
+// auto acc = storage->Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
+// auto *acc_ptr = static_cast<memgraph::storage::DiskStorage::DiskAccessor *>(acc.get());
+// const char *value = "garbage";
+// auto vertex = acc_ptr->DeserializeVertex(serialized_vertex, value);
+// ASSERT_EQ(vertex->Gid().AsInt(), 1);
+// ASSERT_EQ(*vertex->HasLabel(memgraph::storage::LabelId::FromUint(1), memgraph::storage::View::OLD), true);
+// }
+
+// TEST_F(RocksDBStorageTest, DeserializeEdge) {
+// NOTE: This test would fail in the case of snaphsot isolation because of the way in which RocksDB
+// serializes commit timestamp.
+// auto acc = storage->Access(memgraph::storage::IsolationLevel::READ_UNCOMMITTED);
+// auto vertex1 = acc->CreateVertex();
+// auto vertex2 = acc->CreateVertex();
+// auto serialized_edge = fmt::format("{}|{}|0|1|2", vertex1.Gid().AsInt(), vertex2.Gid().AsInt());
+// auto acc_ptr = static_cast<memgraph::storage::DiskStorage::DiskAccessor *>(acc.get());
+// auto edge = acc_ptr->DeserializeEdge(serialized_edge, "garbage");
+// ASSERT_EQ(edge->Gid().AsInt(), 2);
+// ASSERT_EQ(edge->EdgeType().AsInt(), 1);
+// ASSERT_EQ(edge->from_vertex_->gid.AsInt(), vertex1.Gid().AsInt());
+// ASSERT_EQ(edge->to_vertex_->gid.AsInt(), vertex2.Gid().AsInt());
+// }
diff --git a/tests/unit/storage_test_utils.cpp b/tests/unit/storage_test_utils.cpp
index 8efcfa0b5..2620666d0 100644
--- a/tests/unit/storage_test_utils.cpp
+++ b/tests/unit/storage_test_utils.cpp
@@ -18,3 +18,14 @@ size_t CountVertices(memgraph::storage::Storage::Accessor &storage_accessor, mem
     ;
   return count;
 }
+
+std::string_view StorageModeToString(memgraph::storage::StorageMode storage_mode) {
+  switch (storage_mode) {
+    case memgraph::storage::StorageMode::IN_MEMORY_ANALYTICAL:
+      return "IN_MEMORY_ANALYTICAL";
+    case memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL:
+      return "IN_MEMORY_TRANSACTIONAL";
+    case memgraph::storage::StorageMode::ON_DISK_TRANSACTIONAL:
+      return "ON_DISK_TRANSACTIONAL";
+  }
+}
diff --git a/tests/unit/storage_v2.cpp b/tests/unit/storage_v2.cpp
index b268ca8f8..258fe6d37 100644
--- a/tests/unit/storage_v2.cpp
+++ b/tests/unit/storage_v2.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -12,593 +12,629 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <filesystem>
 #include <limits>
 
+#include "disk_test_utils.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/property_value.hpp"
 #include "storage/v2/storage.hpp"
 #include "storage/v2/vertex_accessor.hpp"
 #include "storage_test_utils.hpp"
 
+using testing::Types;
 using testing::UnorderedElementsAre;
 
+template <typename StorageType>
+class StorageV2Test : public testing::Test {
+ public:
+  StorageV2Test() {
+    config_ = disk_test_utils::GenerateOnDiskConfig(testSuite);
+    store = std::make_unique<StorageType>(config_);
+  }
+
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+    store.reset(nullptr);
+  }
+
+  const std::string testSuite = "storage_v2";
+  std::unique_ptr<memgraph::storage::Storage> store;
+  memgraph::storage::Config config_;
+};
+
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(StorageV2Test, StorageTypes);
+
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, Commit) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, Commit) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_FALSE(acc.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    ASSERT_TRUE(acc.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 1U);
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    ASSERT_TRUE(acc->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 1U);
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    ASSERT_TRUE(acc.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 1U);
-    ASSERT_TRUE(acc.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 1U);
-    acc.Abort();
+    auto acc = this->store->Access();
+    ASSERT_TRUE(acc->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 1U);
+    ASSERT_TRUE(acc->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 1U);
+    acc->Abort();
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::NEW);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto res = acc.DeleteVertex(&*vertex);
+    auto res = acc->DeleteVertex(&*vertex);
     ASSERT_FALSE(res.HasError());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 1U);
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 0U);
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 1U);
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 0U);
 
-    acc.AdvanceCommand();
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 0U);
+    acc->AdvanceCommand();
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 0U);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    ASSERT_FALSE(acc.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    ASSERT_FALSE(acc.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 0U);
-    acc.Abort();
+    auto acc = this->store->Access();
+    ASSERT_FALSE(acc->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    ASSERT_FALSE(acc->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 0U);
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, Abort) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, Abort) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_FALSE(acc.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    ASSERT_TRUE(acc.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 1U);
-    acc.Abort();
+    ASSERT_FALSE(acc->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    ASSERT_TRUE(acc->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 1U);
+    acc->Abort();
   }
   {
-    auto acc = store.Access();
-    ASSERT_FALSE(acc.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    ASSERT_FALSE(acc.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 0U);
-    acc.Abort();
+    auto acc = this->store->Access();
+    ASSERT_FALSE(acc->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    ASSERT_FALSE(acc->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 0U);
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, AdvanceCommandCommit) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, AdvanceCommandCommit) {
   memgraph::storage::Gid gid1 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid2 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   {
-    auto acc = store.Access();
+    auto acc = this->store->Access();
 
-    auto vertex1 = acc.CreateVertex();
+    auto vertex1 = acc->CreateVertex();
     gid1 = vertex1.Gid();
-    ASSERT_FALSE(acc.FindVertex(gid1, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    ASSERT_TRUE(acc.FindVertex(gid1, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 1U);
+    ASSERT_FALSE(acc->FindVertex(gid1, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    ASSERT_TRUE(acc->FindVertex(gid1, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 1U);
 
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
-    auto vertex2 = acc.CreateVertex();
+    auto vertex2 = acc->CreateVertex();
     gid2 = vertex2.Gid();
-    ASSERT_FALSE(acc.FindVertex(gid2, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 1U);
-    ASSERT_TRUE(acc.FindVertex(gid2, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 2U);
+    ASSERT_FALSE(acc->FindVertex(gid2, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 1U);
+    ASSERT_TRUE(acc->FindVertex(gid2, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 2U);
 
-    ASSERT_TRUE(acc.FindVertex(gid1, memgraph::storage::View::OLD).has_value());
-    ASSERT_TRUE(acc.FindVertex(gid1, memgraph::storage::View::NEW).has_value());
+    ASSERT_TRUE(acc->FindVertex(gid1, memgraph::storage::View::OLD).has_value());
+    ASSERT_TRUE(acc->FindVertex(gid1, memgraph::storage::View::NEW).has_value());
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    ASSERT_TRUE(acc.FindVertex(gid1, memgraph::storage::View::OLD).has_value());
-    ASSERT_TRUE(acc.FindVertex(gid1, memgraph::storage::View::NEW).has_value());
-    ASSERT_TRUE(acc.FindVertex(gid2, memgraph::storage::View::OLD).has_value());
-    ASSERT_TRUE(acc.FindVertex(gid2, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 2U);
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 2U);
-    acc.Abort();
+    auto acc = this->store->Access();
+    ASSERT_TRUE(acc->FindVertex(gid1, memgraph::storage::View::OLD).has_value());
+    ASSERT_TRUE(acc->FindVertex(gid1, memgraph::storage::View::NEW).has_value());
+    ASSERT_TRUE(acc->FindVertex(gid2, memgraph::storage::View::OLD).has_value());
+    ASSERT_TRUE(acc->FindVertex(gid2, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 2U);
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 2U);
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, AdvanceCommandAbort) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, AdvanceCommandAbort) {
   memgraph::storage::Gid gid1 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid2 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   {
-    auto acc = store.Access();
+    auto acc = this->store->Access();
 
-    auto vertex1 = acc.CreateVertex();
+    auto vertex1 = acc->CreateVertex();
     gid1 = vertex1.Gid();
-    ASSERT_FALSE(acc.FindVertex(gid1, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    ASSERT_TRUE(acc.FindVertex(gid1, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 1U);
+    ASSERT_FALSE(acc->FindVertex(gid1, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    ASSERT_TRUE(acc->FindVertex(gid1, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 1U);
 
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
-    auto vertex2 = acc.CreateVertex();
+    auto vertex2 = acc->CreateVertex();
     gid2 = vertex2.Gid();
-    ASSERT_FALSE(acc.FindVertex(gid2, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 1U);
-    ASSERT_TRUE(acc.FindVertex(gid2, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 2U);
+    ASSERT_FALSE(acc->FindVertex(gid2, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 1U);
+    ASSERT_TRUE(acc->FindVertex(gid2, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 2U);
 
-    ASSERT_TRUE(acc.FindVertex(gid1, memgraph::storage::View::OLD).has_value());
-    ASSERT_TRUE(acc.FindVertex(gid1, memgraph::storage::View::NEW).has_value());
+    ASSERT_TRUE(acc->FindVertex(gid1, memgraph::storage::View::OLD).has_value());
+    ASSERT_TRUE(acc->FindVertex(gid1, memgraph::storage::View::NEW).has_value());
 
-    acc.Abort();
+    acc->Abort();
   }
   {
-    auto acc = store.Access();
-    ASSERT_FALSE(acc.FindVertex(gid1, memgraph::storage::View::OLD).has_value());
-    ASSERT_FALSE(acc.FindVertex(gid1, memgraph::storage::View::NEW).has_value());
-    ASSERT_FALSE(acc.FindVertex(gid2, memgraph::storage::View::OLD).has_value());
-    ASSERT_FALSE(acc.FindVertex(gid2, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 0U);
-    acc.Abort();
+    auto acc = this->store->Access();
+    ASSERT_FALSE(acc->FindVertex(gid1, memgraph::storage::View::OLD).has_value());
+    ASSERT_FALSE(acc->FindVertex(gid1, memgraph::storage::View::NEW).has_value());
+    ASSERT_FALSE(acc->FindVertex(gid2, memgraph::storage::View::OLD).has_value());
+    ASSERT_FALSE(acc->FindVertex(gid2, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 0U);
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, SnapshotIsolation) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, SnapshotIsolation) {
+  auto acc1 = this->store->Access();
+  auto acc2 = this->store->Access();
 
-  auto acc1 = store.Access();
-  auto acc2 = store.Access();
-
-  auto vertex = acc1.CreateVertex();
+  auto vertex = acc1->CreateVertex();
   auto gid = vertex.Gid();
 
-  ASSERT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::OLD), 0U);
-  EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::OLD), 0U);
-  ASSERT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::NEW), 1U);
-  EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::NEW), 0U);
+  ASSERT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::OLD), 0U);
+  EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::OLD), 0U);
+  ASSERT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::NEW), 1U);
+  EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::NEW), 0U);
 
-  ASSERT_FALSE(acc1.Commit().HasError());
+  ASSERT_FALSE(acc1->Commit().HasError());
 
-  ASSERT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::OLD), 0U);
-  ASSERT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::NEW), 0U);
+  ASSERT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::OLD), 0U);
+  ASSERT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::NEW), 0U);
 
-  acc2.Abort();
+  acc2->Abort();
 
-  auto acc3 = store.Access();
-  ASSERT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc3, memgraph::storage::View::OLD), 1U);
-  ASSERT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc3, memgraph::storage::View::NEW), 1U);
-  acc3.Abort();
+  auto acc3 = this->store->Access();
+  ASSERT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc3, memgraph::storage::View::OLD), 1U);
+  ASSERT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc3, memgraph::storage::View::NEW), 1U);
+  acc3->Abort();
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, AccessorMove) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, AccessorMove) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
 
-    ASSERT_FALSE(acc.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    ASSERT_TRUE(acc.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 1U);
+    ASSERT_FALSE(acc->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    ASSERT_TRUE(acc->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 1U);
 
-    memgraph::storage::Storage::Accessor moved(std::move(acc));
+    auto moved(std::move(acc));
 
-    ASSERT_FALSE(moved.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(moved, memgraph::storage::View::OLD), 0U);
-    ASSERT_TRUE(moved.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(moved, memgraph::storage::View::NEW), 1U);
+    ASSERT_FALSE(moved->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*moved, memgraph::storage::View::OLD), 0U);
+    ASSERT_TRUE(moved->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*moved, memgraph::storage::View::NEW), 1U);
 
-    ASSERT_FALSE(moved.Commit().HasError());
+    ASSERT_FALSE(moved->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    ASSERT_TRUE(acc.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 1U);
-    ASSERT_TRUE(acc.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 1U);
-    acc.Abort();
+    auto acc = this->store->Access();
+    ASSERT_TRUE(acc->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 1U);
+    ASSERT_TRUE(acc->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 1U);
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexDeleteCommit) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexDeleteCommit) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
-
-  auto acc1 = store.Access();  // read transaction
-  auto acc2 = store.Access();  // write transaction
+  auto acc1 = this->store->Access();  // read transaction
+  auto acc2 = this->store->Access();  // write transaction
 
   // Create the vertex in transaction 2
   {
-    auto vertex = acc2.CreateVertex();
+    auto vertex = acc2->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::OLD), 0U);
-    ASSERT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::NEW), 1U);
-    ASSERT_FALSE(acc2.Commit().HasError());
+    ASSERT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::OLD), 0U);
+    ASSERT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::NEW), 1U);
+    ASSERT_FALSE(acc2->Commit().HasError());
   }
 
-  auto acc3 = store.Access();  // read transaction
-  auto acc4 = store.Access();  // write transaction
+  auto acc3 = this->store->Access();  // read transaction
+  auto acc4 = this->store->Access();  // write transaction
 
   // Check whether the vertex exists in transaction 1
-  ASSERT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::OLD), 0U);
-  ASSERT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::NEW), 0U);
+  ASSERT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::OLD), 0U);
+  ASSERT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::NEW), 0U);
 
   // Check whether the vertex exists in transaction 3
-  ASSERT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc3, memgraph::storage::View::OLD), 1U);
-  ASSERT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc3, memgraph::storage::View::NEW), 1U);
+  ASSERT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc3, memgraph::storage::View::OLD), 1U);
+  ASSERT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc3, memgraph::storage::View::NEW), 1U);
 
   // Delete the vertex in transaction 4
   {
-    auto vertex = acc4.FindVertex(gid, memgraph::storage::View::NEW);
+    auto vertex = acc4->FindVertex(gid, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
-    EXPECT_EQ(CountVertices(acc4, memgraph::storage::View::OLD), 1U);
-    EXPECT_EQ(CountVertices(acc4, memgraph::storage::View::NEW), 1U);
+    EXPECT_EQ(CountVertices(*acc4, memgraph::storage::View::OLD), 1U);
+    EXPECT_EQ(CountVertices(*acc4, memgraph::storage::View::NEW), 1U);
 
-    auto res = acc4.DeleteVertex(&*vertex);
+    auto res = acc4->DeleteVertex(&*vertex);
     ASSERT_TRUE(res.HasValue());
-    EXPECT_EQ(CountVertices(acc4, memgraph::storage::View::OLD), 1U);
-    EXPECT_EQ(CountVertices(acc4, memgraph::storage::View::NEW), 0U);
+    EXPECT_EQ(CountVertices(*acc4, memgraph::storage::View::OLD), 1U);
+    EXPECT_EQ(CountVertices(*acc4, memgraph::storage::View::NEW), 0U);
 
-    acc4.AdvanceCommand();
-    EXPECT_EQ(CountVertices(acc4, memgraph::storage::View::OLD), 0U);
-    EXPECT_EQ(CountVertices(acc4, memgraph::storage::View::NEW), 0U);
+    acc4->AdvanceCommand();
+    EXPECT_EQ(CountVertices(*acc4, memgraph::storage::View::OLD), 0U);
+    EXPECT_EQ(CountVertices(*acc4, memgraph::storage::View::NEW), 0U);
 
-    ASSERT_FALSE(acc4.Commit().HasError());
+    ASSERT_FALSE(acc4->Commit().HasError());
   }
 
-  auto acc5 = store.Access();  // read transaction
+  auto acc5 = this->store->Access();  // read transaction
 
   // Check whether the vertex exists in transaction 1
-  ASSERT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::OLD), 0U);
-  ASSERT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::NEW), 0U);
+  ASSERT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::OLD), 0U);
+  ASSERT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::NEW), 0U);
 
   // Check whether the vertex exists in transaction 3
-  ASSERT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc3, memgraph::storage::View::OLD), 1U);
-  ASSERT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc3, memgraph::storage::View::NEW), 1U);
+  ASSERT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc3, memgraph::storage::View::OLD), 1U);
+  ASSERT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc3, memgraph::storage::View::NEW), 1U);
 
   // Check whether the vertex exists in transaction 5
-  ASSERT_FALSE(acc5.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc5, memgraph::storage::View::OLD), 0U);
-  ASSERT_FALSE(acc5.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc5, memgraph::storage::View::NEW), 0U);
+  ASSERT_FALSE(acc5->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc5, memgraph::storage::View::OLD), 0U);
+  ASSERT_FALSE(acc5->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc5, memgraph::storage::View::NEW), 0U);
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexDeleteAbort) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexDeleteAbort) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
-  auto acc1 = store.Access();  // read transaction
-  auto acc2 = store.Access();  // write transaction
+  auto acc1 = this->store->Access();  // read transaction
+  auto acc2 = this->store->Access();  // write transaction
 
   // Create the vertex in transaction 2
   {
-    auto vertex = acc2.CreateVertex();
+    auto vertex = acc2->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::OLD), 0U);
-    ASSERT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::NEW), 1U);
-    ASSERT_FALSE(acc2.Commit().HasError());
+    ASSERT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::OLD), 0U);
+    ASSERT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::NEW), 1U);
+    ASSERT_FALSE(acc2->Commit().HasError());
   }
 
-  auto acc3 = store.Access();  // read transaction
-  auto acc4 = store.Access();  // write transaction (aborted)
+  auto acc3 = this->store->Access();  // read transaction
+  auto acc4 = this->store->Access();  // write transaction (aborted)
 
   // Check whether the vertex exists in transaction 1
-  ASSERT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::OLD), 0U);
-  ASSERT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::NEW), 0U);
+  ASSERT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::OLD), 0U);
+  ASSERT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::NEW), 0U);
 
   // Check whether the vertex exists in transaction 3
-  ASSERT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc3, memgraph::storage::View::OLD), 1U);
-  ASSERT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc3, memgraph::storage::View::NEW), 1U);
+  ASSERT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc3, memgraph::storage::View::OLD), 1U);
+  ASSERT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc3, memgraph::storage::View::NEW), 1U);
 
   // Delete the vertex in transaction 4, but abort the transaction
   {
-    auto vertex = acc4.FindVertex(gid, memgraph::storage::View::NEW);
+    auto vertex = acc4->FindVertex(gid, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
-    EXPECT_EQ(CountVertices(acc4, memgraph::storage::View::OLD), 1U);
-    EXPECT_EQ(CountVertices(acc4, memgraph::storage::View::NEW), 1U);
+    EXPECT_EQ(CountVertices(*acc4, memgraph::storage::View::OLD), 1U);
+    EXPECT_EQ(CountVertices(*acc4, memgraph::storage::View::NEW), 1U);
 
-    auto res = acc4.DeleteVertex(&*vertex);
+    auto res = acc4->DeleteVertex(&*vertex);
     ASSERT_TRUE(res.HasValue());
-    EXPECT_EQ(CountVertices(acc4, memgraph::storage::View::OLD), 1U);
-    EXPECT_EQ(CountVertices(acc4, memgraph::storage::View::NEW), 0U);
+    EXPECT_EQ(CountVertices(*acc4, memgraph::storage::View::OLD), 1U);
+    EXPECT_EQ(CountVertices(*acc4, memgraph::storage::View::NEW), 0U);
 
-    acc4.AdvanceCommand();
-    EXPECT_EQ(CountVertices(acc4, memgraph::storage::View::OLD), 0U);
-    EXPECT_EQ(CountVertices(acc4, memgraph::storage::View::NEW), 0U);
+    acc4->AdvanceCommand();
+    EXPECT_EQ(CountVertices(*acc4, memgraph::storage::View::OLD), 0U);
+    EXPECT_EQ(CountVertices(*acc4, memgraph::storage::View::NEW), 0U);
 
-    acc4.Abort();
+    acc4->Abort();
   }
 
-  auto acc5 = store.Access();  // read transaction
-  auto acc6 = store.Access();  // write transaction
+  auto acc5 = this->store->Access();  // read transaction
+  auto acc6 = this->store->Access();  // write transaction
 
   // Check whether the vertex exists in transaction 1
-  ASSERT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::OLD), 0U);
-  ASSERT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::NEW), 0U);
+  ASSERT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::OLD), 0U);
+  ASSERT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::NEW), 0U);
 
   // Check whether the vertex exists in transaction 3
-  ASSERT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc3, memgraph::storage::View::OLD), 1U);
-  ASSERT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc3, memgraph::storage::View::NEW), 1U);
+  ASSERT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc3, memgraph::storage::View::OLD), 1U);
+  ASSERT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc3, memgraph::storage::View::NEW), 1U);
 
   // Check whether the vertex exists in transaction 5
-  ASSERT_TRUE(acc5.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc5, memgraph::storage::View::OLD), 1U);
-  ASSERT_TRUE(acc5.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc5, memgraph::storage::View::NEW), 1U);
+  ASSERT_TRUE(acc5->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc5, memgraph::storage::View::OLD), 1U);
+  ASSERT_TRUE(acc5->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc5, memgraph::storage::View::NEW), 1U);
 
   // Delete the vertex in transaction 6
   {
-    auto vertex = acc6.FindVertex(gid, memgraph::storage::View::NEW);
+    auto vertex = acc6->FindVertex(gid, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
-    EXPECT_EQ(CountVertices(acc6, memgraph::storage::View::OLD), 1U);
-    EXPECT_EQ(CountVertices(acc6, memgraph::storage::View::NEW), 1U);
+    EXPECT_EQ(CountVertices(*acc6, memgraph::storage::View::OLD), 1U);
+    EXPECT_EQ(CountVertices(*acc6, memgraph::storage::View::NEW), 1U);
 
-    auto res = acc6.DeleteVertex(&*vertex);
+    auto res = acc6->DeleteVertex(&*vertex);
     ASSERT_TRUE(res.HasValue());
-    EXPECT_EQ(CountVertices(acc6, memgraph::storage::View::OLD), 1U);
-    EXPECT_EQ(CountVertices(acc6, memgraph::storage::View::NEW), 0U);
+    EXPECT_EQ(CountVertices(*acc6, memgraph::storage::View::OLD), 1U);
+    EXPECT_EQ(CountVertices(*acc6, memgraph::storage::View::NEW), 0U);
 
-    acc6.AdvanceCommand();
-    EXPECT_EQ(CountVertices(acc6, memgraph::storage::View::OLD), 0U);
-    EXPECT_EQ(CountVertices(acc6, memgraph::storage::View::NEW), 0U);
+    acc6->AdvanceCommand();
+    EXPECT_EQ(CountVertices(*acc6, memgraph::storage::View::OLD), 0U);
+    EXPECT_EQ(CountVertices(*acc6, memgraph::storage::View::NEW), 0U);
 
-    ASSERT_FALSE(acc6.Commit().HasError());
+    ASSERT_FALSE(acc6->Commit().HasError());
   }
 
-  auto acc7 = store.Access();  // read transaction
+  auto acc7 = this->store->Access();  // read transaction
 
   // Check whether the vertex exists in transaction 1
-  ASSERT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::OLD), 0U);
-  ASSERT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::NEW), 0U);
+  ASSERT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::OLD), 0U);
+  ASSERT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::NEW), 0U);
 
   // Check whether the vertex exists in transaction 3
-  ASSERT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc3, memgraph::storage::View::OLD), 1U);
-  ASSERT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc3, memgraph::storage::View::NEW), 1U);
+  ASSERT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc3, memgraph::storage::View::OLD), 1U);
+  ASSERT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc3, memgraph::storage::View::NEW), 1U);
 
   // Check whether the vertex exists in transaction 5
-  ASSERT_TRUE(acc5.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc5, memgraph::storage::View::OLD), 1U);
-  ASSERT_TRUE(acc5.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc5, memgraph::storage::View::NEW), 1U);
+  ASSERT_TRUE(acc5->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc5, memgraph::storage::View::OLD), 1U);
+  ASSERT_TRUE(acc5->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc5, memgraph::storage::View::NEW), 1U);
 
   // Check whether the vertex exists in transaction 7
-  ASSERT_FALSE(acc7.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-  EXPECT_EQ(CountVertices(acc7, memgraph::storage::View::OLD), 0U);
-  ASSERT_FALSE(acc7.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-  EXPECT_EQ(CountVertices(acc7, memgraph::storage::View::NEW), 0U);
+  ASSERT_FALSE(acc7->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+  EXPECT_EQ(CountVertices(*acc7, memgraph::storage::View::OLD), 0U);
+  ASSERT_FALSE(acc7->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+  EXPECT_EQ(CountVertices(*acc7, memgraph::storage::View::NEW), 0U);
 
   // Commit all accessors
-  ASSERT_FALSE(acc1.Commit().HasError());
-  ASSERT_FALSE(acc3.Commit().HasError());
-  ASSERT_FALSE(acc5.Commit().HasError());
-  ASSERT_FALSE(acc7.Commit().HasError());
+  ASSERT_FALSE(acc1->Commit().HasError());
+  ASSERT_FALSE(acc3->Commit().HasError());
+  ASSERT_FALSE(acc5->Commit().HasError());
+  ASSERT_FALSE(acc7->Commit().HasError());
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexDeleteSerializationError) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexDeleteSerializationError) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertex
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
-  auto acc1 = store.Access();
-  auto acc2 = store.Access();
+  auto acc1 = this->store->Access();
+  auto acc2 = this->store->Access();
 
   // Delete vertex in accessor 1
   {
-    auto vertex = acc1.FindVertex(gid, memgraph::storage::View::OLD);
+    auto vertex = acc1->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
-    EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::OLD), 1U);
-    EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::NEW), 1U);
+    EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::OLD), 1U);
+    EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::NEW), 1U);
 
     {
-      auto res = acc1.DeleteVertex(&*vertex);
+      auto res = acc1->DeleteVertex(&*vertex);
       ASSERT_TRUE(res.HasValue());
       ASSERT_TRUE(res.GetValue());
-      EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::OLD), 1U);
-      EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::NEW), 0U);
+      EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::OLD), 1U);
+      EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::NEW), 0U);
     }
 
     {
-      auto res = acc1.DeleteVertex(&*vertex);
+      auto res = acc1->DeleteVertex(&*vertex);
       ASSERT_TRUE(res.HasValue());
       ASSERT_FALSE(res.GetValue());
-      EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::OLD), 1U);
-      EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::NEW), 0U);
+      EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::OLD), 1U);
+      EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::NEW), 0U);
     }
 
-    acc1.AdvanceCommand();
-    EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::OLD), 0U);
-    EXPECT_EQ(CountVertices(acc1, memgraph::storage::View::NEW), 0U);
+    acc1->AdvanceCommand();
+    EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::OLD), 0U);
+    EXPECT_EQ(CountVertices(*acc1, memgraph::storage::View::NEW), 0U);
   }
 
   // Delete vertex in accessor 2
   {
-    auto vertex = acc2.FindVertex(gid, memgraph::storage::View::OLD);
+    auto vertex = acc2->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
-    EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::OLD), 1U);
-    EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::NEW), 1U);
-    auto res = acc2.DeleteVertex(&*vertex);
-    ASSERT_TRUE(res.HasError());
-    ASSERT_EQ(res.GetError(), memgraph::storage::Error::SERIALIZATION_ERROR);
-    EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::OLD), 1U);
-    EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::NEW), 1U);
-    acc2.AdvanceCommand();
-    EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::OLD), 1U);
-    EXPECT_EQ(CountVertices(acc2, memgraph::storage::View::NEW), 1U);
+    EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::OLD), 1U);
+    EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::NEW), 1U);
+    auto res = acc2->DeleteVertex(&*vertex);
+    if (std::is_same<TypeParam, memgraph::storage::InMemoryStorage>::value) {
+      // Serialization error for disk will be on commit
+      ASSERT_TRUE(res.HasError());
+      ASSERT_EQ(res.GetError(), memgraph::storage::Error::SERIALIZATION_ERROR);
+    }
+
+    EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::OLD), 1U);
+    if (std::is_same<TypeParam, memgraph::storage::InMemoryStorage>::value) {
+      // Beucase of pessimistic Serialization error happened on DeleteVertex() function
+      EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::NEW), 1U);
+    } else {
+      EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::NEW), 0U);
+    }
+
+    acc2->AdvanceCommand();
+    if (std::is_same<TypeParam, memgraph::storage::InMemoryStorage>::value) {
+      EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::OLD), 1U);
+      EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::NEW), 1U);
+    } else {
+      EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::OLD), 0U);
+      EXPECT_EQ(CountVertices(*acc2, memgraph::storage::View::NEW), 0U);
+    }
   }
 
   // Finalize both accessors
-  ASSERT_FALSE(acc1.Commit().HasError());
-  acc2.Abort();
+  ASSERT_FALSE(acc1->Commit().HasError());
+  if (std::is_same<TypeParam, memgraph::storage::InMemoryStorage>::value) {
+    acc2->Abort();
+  } else {
+    auto res = acc2->Commit();
+    ASSERT_TRUE(res.HasError());
+    ASSERT_EQ(std::get<memgraph::storage::SerializationError>(res.GetError()), memgraph::storage::SerializationError());
+  }
 
   // Check whether the vertex exists
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_FALSE(vertex);
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 0U);
-    ASSERT_FALSE(acc.Commit().HasError());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 0U);
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexDeleteSpecialCases) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexDeleteSpecialCases) {
   memgraph::storage::Gid gid1 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid2 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertex and delete it in the same transaction, but abort the
   // transaction
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid1 = vertex.Gid();
-    ASSERT_FALSE(acc.FindVertex(gid1, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    ASSERT_TRUE(acc.FindVertex(gid1, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 1U);
-    auto res = acc.DeleteVertex(&vertex);
+    ASSERT_FALSE(acc->FindVertex(gid1, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    ASSERT_TRUE(acc->FindVertex(gid1, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 1U);
+    auto res = acc->DeleteVertex(&vertex);
     ASSERT_TRUE(res.HasValue());
     ASSERT_TRUE(res.GetValue());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 0U);
-    acc.AdvanceCommand();
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 0U);
-    acc.Abort();
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 0U);
+    acc->AdvanceCommand();
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 0U);
+    acc->Abort();
   }
 
   // Create vertex and delete it in the same transaction
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid2 = vertex.Gid();
-    ASSERT_FALSE(acc.FindVertex(gid2, memgraph::storage::View::OLD).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    ASSERT_TRUE(acc.FindVertex(gid2, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 1U);
-    auto res = acc.DeleteVertex(&vertex);
+    ASSERT_FALSE(acc->FindVertex(gid2, memgraph::storage::View::OLD).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    ASSERT_TRUE(acc->FindVertex(gid2, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 1U);
+    auto res = acc->DeleteVertex(&vertex);
     ASSERT_TRUE(res.HasValue());
     ASSERT_TRUE(res.GetValue());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 0U);
-    acc.AdvanceCommand();
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 0U);
-    ASSERT_FALSE(acc.Commit().HasError());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 0U);
+    acc->AdvanceCommand();
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 0U);
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the vertices exist
   {
-    auto acc = store.Access();
-    ASSERT_FALSE(acc.FindVertex(gid1, memgraph::storage::View::OLD).has_value());
-    ASSERT_FALSE(acc.FindVertex(gid1, memgraph::storage::View::NEW).has_value());
-    ASSERT_FALSE(acc.FindVertex(gid2, memgraph::storage::View::OLD).has_value());
-    ASSERT_FALSE(acc.FindVertex(gid2, memgraph::storage::View::NEW).has_value());
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::OLD), 0U);
-    EXPECT_EQ(CountVertices(acc, memgraph::storage::View::NEW), 0U);
-    acc.Abort();
+    auto acc = this->store->Access();
+    ASSERT_FALSE(acc->FindVertex(gid1, memgraph::storage::View::OLD).has_value());
+    ASSERT_FALSE(acc->FindVertex(gid1, memgraph::storage::View::NEW).has_value());
+    ASSERT_FALSE(acc->FindVertex(gid2, memgraph::storage::View::OLD).has_value());
+    ASSERT_FALSE(acc->FindVertex(gid2, memgraph::storage::View::NEW).has_value());
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::OLD), 0U);
+    EXPECT_EQ(CountVertices(*acc, memgraph::storage::View::NEW), 0U);
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexDeleteLabel) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexDeleteLabel) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create the vertex
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_FALSE(acc.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-    ASSERT_TRUE(acc.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+    ASSERT_TRUE(acc->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Add label, delete the vertex and check the label API (same command)
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::NEW);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     // Check whether label 5 exists
     ASSERT_FALSE(vertex->HasLabel(label, memgraph::storage::View::OLD).GetValue());
@@ -620,7 +656,7 @@ TEST(StorageV2, VertexDeleteLabel) {
     }
 
     // Delete the vertex
-    ASSERT_TRUE(acc.DeleteVertex(&*vertex).GetValue());
+    ASSERT_TRUE(acc->DeleteVertex(&*vertex).GetValue());
 
     // Check whether label 5 exists
     ASSERT_FALSE(vertex->HasLabel(label, memgraph::storage::View::OLD).GetValue());
@@ -643,16 +679,16 @@ TEST(StorageV2, VertexDeleteLabel) {
       ASSERT_EQ(ret.GetError(), memgraph::storage::Error::DELETED_OBJECT);
     }
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Add label, delete the vertex and check the label API (different command)
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::NEW);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     // Check whether label 5 exists
     ASSERT_FALSE(vertex->HasLabel(label, memgraph::storage::View::OLD).GetValue());
@@ -674,7 +710,7 @@ TEST(StorageV2, VertexDeleteLabel) {
     }
 
     // Advance command
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
     // Check whether label 5 exists
     ASSERT_TRUE(vertex->HasLabel(label, memgraph::storage::View::OLD).GetValue());
@@ -691,7 +727,7 @@ TEST(StorageV2, VertexDeleteLabel) {
     }
 
     // Delete the vertex
-    ASSERT_TRUE(acc.DeleteVertex(&*vertex).GetValue());
+    ASSERT_TRUE(acc->DeleteVertex(&*vertex).GetValue());
 
     // Check whether label 5 exists
     ASSERT_TRUE(vertex->HasLabel(label, memgraph::storage::View::OLD).GetValue());
@@ -705,7 +741,7 @@ TEST(StorageV2, VertexDeleteLabel) {
     ASSERT_EQ(vertex->Labels(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
 
     // Advance command
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
     // Check whether label 5 exists
     ASSERT_EQ(vertex->HasLabel(label, memgraph::storage::View::OLD).GetError(),
@@ -729,32 +765,31 @@ TEST(StorageV2, VertexDeleteLabel) {
       ASSERT_EQ(ret.GetError(), memgraph::storage::Error::DELETED_OBJECT);
     }
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexDeleteProperty) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexDeleteProperty) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create the vertex
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_FALSE(acc.FindVertex(gid, memgraph::storage::View::OLD).has_value());
-    ASSERT_TRUE(acc.FindVertex(gid, memgraph::storage::View::NEW).has_value());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->FindVertex(gid, memgraph::storage::View::OLD).has_value());
+    ASSERT_TRUE(acc->FindVertex(gid, memgraph::storage::View::NEW).has_value());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Set property, delete the vertex and check the property API (same command)
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::NEW);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     // Check whether property 5 exists
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::OLD)->IsNull());
@@ -776,7 +811,7 @@ TEST(StorageV2, VertexDeleteProperty) {
     }
 
     // Delete the vertex
-    ASSERT_TRUE(acc.DeleteVertex(&*vertex).GetValue());
+    ASSERT_TRUE(acc->DeleteVertex(&*vertex).GetValue());
 
     // Check whether label 5 exists
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::OLD)->IsNull());
@@ -792,17 +827,17 @@ TEST(StorageV2, VertexDeleteProperty) {
       ASSERT_EQ(ret.GetError(), memgraph::storage::Error::DELETED_OBJECT);
     }
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Set property, delete the vertex and check the property API (different
   // command)
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::NEW);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     // Check whether property 5 exists
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::OLD)->IsNull());
@@ -824,7 +859,7 @@ TEST(StorageV2, VertexDeleteProperty) {
     }
 
     // Advance command
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
     // Check whether property 5 exists
     ASSERT_EQ(vertex->GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
@@ -841,7 +876,7 @@ TEST(StorageV2, VertexDeleteProperty) {
     }
 
     // Delete the vertex
-    ASSERT_TRUE(acc.DeleteVertex(&*vertex).GetValue());
+    ASSERT_TRUE(acc->DeleteVertex(&*vertex).GetValue());
 
     // Check whether property 5 exists
     ASSERT_EQ(vertex->GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
@@ -855,7 +890,7 @@ TEST(StorageV2, VertexDeleteProperty) {
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
 
     // Advance command
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
     // Check whether property 5 exists
     ASSERT_EQ(vertex->GetProperty(property, memgraph::storage::View::OLD).GetError(),
@@ -872,20 +907,20 @@ TEST(StorageV2, VertexDeleteProperty) {
       ASSERT_EQ(ret.GetError(), memgraph::storage::Error::DELETED_OBJECT);
     }
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexLabelCommit) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexLabelCommit) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     ASSERT_FALSE(vertex.HasLabel(label, memgraph::storage::View::NEW).GetValue());
     ASSERT_EQ(vertex.Labels(memgraph::storage::View::NEW)->size(), 0);
@@ -909,14 +944,15 @@ TEST(StorageV2, VertexLabelCommit) {
       ASSERT_FALSE(res.GetValue());
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
+    spdlog::debug("Commit done");
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     ASSERT_TRUE(vertex->HasLabel(label, memgraph::storage::View::OLD).GetValue());
     {
@@ -932,19 +968,20 @@ TEST(StorageV2, VertexLabelCommit) {
       ASSERT_EQ(labels[0], label);
     }
 
-    auto other_label = acc.NameToLabel("other");
+    auto other_label = acc->NameToLabel("other");
 
     ASSERT_FALSE(vertex->HasLabel(other_label, memgraph::storage::View::OLD).GetValue());
     ASSERT_FALSE(vertex->HasLabel(other_label, memgraph::storage::View::NEW).GetValue());
 
-    acc.Abort();
+    acc->Abort();
+    spdlog::debug("Abort done");
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     {
       auto res = vertex->RemoveLabel(label);
@@ -968,49 +1005,50 @@ TEST(StorageV2, VertexLabelCommit) {
       ASSERT_FALSE(res.GetValue());
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
+    spdlog::debug("Commit done");
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     ASSERT_FALSE(vertex->HasLabel(label, memgraph::storage::View::OLD).GetValue());
     ASSERT_FALSE(vertex->HasLabel(label, memgraph::storage::View::NEW).GetValue());
     ASSERT_EQ(vertex->Labels(memgraph::storage::View::OLD)->size(), 0);
     ASSERT_EQ(vertex->Labels(memgraph::storage::View::NEW)->size(), 0);
 
-    auto other_label = acc.NameToLabel("other");
+    auto other_label = acc->NameToLabel("other");
 
     ASSERT_FALSE(vertex->HasLabel(other_label, memgraph::storage::View::OLD).GetValue());
     ASSERT_FALSE(vertex->HasLabel(other_label, memgraph::storage::View::NEW).GetValue());
 
-    acc.Abort();
+    acc->Abort();
+    spdlog::debug("Abort done");
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexLabelAbort) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexLabelAbort) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create the vertex.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Add label 5, but abort the transaction.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     ASSERT_FALSE(vertex->HasLabel(label, memgraph::storage::View::NEW).GetValue());
     ASSERT_EQ(vertex->Labels(memgraph::storage::View::NEW)->size(), 0);
@@ -1034,37 +1072,37 @@ TEST(StorageV2, VertexLabelAbort) {
       ASSERT_FALSE(res.GetValue());
     }
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check that label 5 doesn't exist.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     ASSERT_FALSE(vertex->HasLabel(label, memgraph::storage::View::OLD).GetValue());
     ASSERT_FALSE(vertex->HasLabel(label, memgraph::storage::View::NEW).GetValue());
     ASSERT_EQ(vertex->Labels(memgraph::storage::View::OLD)->size(), 0);
     ASSERT_EQ(vertex->Labels(memgraph::storage::View::NEW)->size(), 0);
 
-    auto other_label = acc.NameToLabel("other");
+    auto other_label = acc->NameToLabel("other");
 
     ASSERT_FALSE(vertex->HasLabel(other_label, memgraph::storage::View::OLD).GetValue());
     ASSERT_FALSE(vertex->HasLabel(other_label, memgraph::storage::View::NEW).GetValue());
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Add label 5.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     ASSERT_FALSE(vertex->HasLabel(label, memgraph::storage::View::NEW).GetValue());
     ASSERT_EQ(vertex->Labels(memgraph::storage::View::NEW)->size(), 0);
@@ -1088,16 +1126,16 @@ TEST(StorageV2, VertexLabelAbort) {
       ASSERT_FALSE(res.GetValue());
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check that label 5 exists.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     ASSERT_TRUE(vertex->HasLabel(label, memgraph::storage::View::OLD).GetValue());
     {
@@ -1113,21 +1151,21 @@ TEST(StorageV2, VertexLabelAbort) {
       ASSERT_EQ(labels[0], label);
     }
 
-    auto other_label = acc.NameToLabel("other");
+    auto other_label = acc->NameToLabel("other");
 
     ASSERT_FALSE(vertex->HasLabel(other_label, memgraph::storage::View::OLD).GetValue());
     ASSERT_FALSE(vertex->HasLabel(other_label, memgraph::storage::View::NEW).GetValue());
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Remove label 5, but abort the transaction.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     {
       auto res = vertex->RemoveLabel(label);
@@ -1151,16 +1189,16 @@ TEST(StorageV2, VertexLabelAbort) {
       ASSERT_FALSE(res.GetValue());
     }
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check that label 5 exists.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     ASSERT_TRUE(vertex->HasLabel(label, memgraph::storage::View::OLD).GetValue());
     {
@@ -1176,21 +1214,21 @@ TEST(StorageV2, VertexLabelAbort) {
       ASSERT_EQ(labels[0], label);
     }
 
-    auto other_label = acc.NameToLabel("other");
+    auto other_label = acc->NameToLabel("other");
 
     ASSERT_FALSE(vertex->HasLabel(other_label, memgraph::storage::View::OLD).GetValue());
     ASSERT_FALSE(vertex->HasLabel(other_label, memgraph::storage::View::NEW).GetValue());
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Remove label 5.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     {
       auto res = vertex->RemoveLabel(label);
@@ -1214,52 +1252,51 @@ TEST(StorageV2, VertexLabelAbort) {
       ASSERT_FALSE(res.GetValue());
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check that label 5 doesn't exist.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label = acc.NameToLabel("label5");
+    auto label = acc->NameToLabel("label5");
 
     ASSERT_FALSE(vertex->HasLabel(label, memgraph::storage::View::OLD).GetValue());
     ASSERT_FALSE(vertex->HasLabel(label, memgraph::storage::View::NEW).GetValue());
     ASSERT_EQ(vertex->Labels(memgraph::storage::View::OLD)->size(), 0);
     ASSERT_EQ(vertex->Labels(memgraph::storage::View::NEW)->size(), 0);
 
-    auto other_label = acc.NameToLabel("other");
+    auto other_label = acc->NameToLabel("other");
 
     ASSERT_FALSE(vertex->HasLabel(other_label, memgraph::storage::View::OLD).GetValue());
     ASSERT_FALSE(vertex->HasLabel(other_label, memgraph::storage::View::NEW).GetValue());
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexLabelSerializationError) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexLabelSerializationError) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
-  auto acc1 = store.Access();
-  auto acc2 = store.Access();
+  auto acc1 = this->store->Access();
+  auto acc2 = this->store->Access();
 
   // Add label 1 in accessor 1.
   {
-    auto vertex = acc1.FindVertex(gid, memgraph::storage::View::OLD);
+    auto vertex = acc1->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label1 = acc1.NameToLabel("label1");
-    auto label2 = acc1.NameToLabel("label2");
+    auto label1 = acc1->NameToLabel("label1");
+    auto label2 = acc1->NameToLabel("label2");
 
     ASSERT_FALSE(vertex->HasLabel(label1, memgraph::storage::View::OLD).GetValue());
     ASSERT_FALSE(vertex->HasLabel(label1, memgraph::storage::View::NEW).GetValue());
@@ -1294,11 +1331,11 @@ TEST(StorageV2, VertexLabelSerializationError) {
 
   // Add label 2 in accessor 2.
   {
-    auto vertex = acc2.FindVertex(gid, memgraph::storage::View::OLD);
+    auto vertex = acc2->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label1 = acc2.NameToLabel("label1");
-    auto label2 = acc2.NameToLabel("label2");
+    auto label1 = acc2->NameToLabel("label1");
+    auto label2 = acc2->NameToLabel("label2");
 
     ASSERT_FALSE(vertex->HasLabel(label1, memgraph::storage::View::OLD).GetValue());
     ASSERT_FALSE(vertex->HasLabel(label1, memgraph::storage::View::NEW).GetValue());
@@ -1308,24 +1345,38 @@ TEST(StorageV2, VertexLabelSerializationError) {
     ASSERT_EQ(vertex->Labels(memgraph::storage::View::NEW)->size(), 0);
 
     {
-      auto res = vertex->AddLabel(label1);
-      ASSERT_TRUE(res.HasError());
-      ASSERT_EQ(res.GetError(), memgraph::storage::Error::SERIALIZATION_ERROR);
+      auto res = vertex->AddLabel(label2);
+      if (std::is_same<TypeParam, memgraph::storage::InMemoryStorage>::value) {
+        // InMemoryStorage works with pessimistic transactions.
+        ASSERT_TRUE(res.HasError());
+        ASSERT_EQ(res.GetError(), memgraph::storage::Error::SERIALIZATION_ERROR);
+      } else {
+        // Disk storage works with optimistic transactions.
+        ASSERT_TRUE(res.HasValue());
+        ASSERT_TRUE(res.GetValue());
+      }
     }
   }
 
   // Finalize both accessors.
-  ASSERT_FALSE(acc1.Commit().HasError());
-  acc2.Abort();
+  ASSERT_FALSE(acc1->Commit().HasError());
+  if (std::is_same<TypeParam, memgraph::storage::InMemoryStorage>::value) {
+    acc2->Abort();
+  } else {
+    // Disk storage works with optimistic transactions. So on write conflict, transaction fails on commit.
+    auto res = acc2->Commit();
+    ASSERT_TRUE(res.HasError());
+    ASSERT_EQ(std::get<memgraph::storage::SerializationError>(res.GetError()), memgraph::storage::SerializationError());
+  }
 
   // Check which labels exist.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto label1 = acc.NameToLabel("label1");
-    auto label2 = acc.NameToLabel("label2");
+    auto label1 = acc->NameToLabel("label1");
+    auto label2 = acc->NameToLabel("label2");
 
     ASSERT_TRUE(vertex->HasLabel(label1, memgraph::storage::View::OLD).GetValue());
     ASSERT_FALSE(vertex->HasLabel(label2, memgraph::storage::View::OLD).GetValue());
@@ -1343,20 +1394,19 @@ TEST(StorageV2, VertexLabelSerializationError) {
       ASSERT_EQ(labels[0], label1);
     }
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexPropertyCommit) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexPropertyCommit) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(vertex.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(vertex.Properties(memgraph::storage::View::NEW)->size(), 0);
@@ -1387,14 +1437,14 @@ TEST(StorageV2, VertexPropertyCommit) {
       ASSERT_EQ(properties[property].ValueString(), "nandare");
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_EQ(vertex->GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
     {
@@ -1410,19 +1460,19 @@ TEST(StorageV2, VertexPropertyCommit) {
       ASSERT_EQ(properties[property].ValueString(), "nandare");
     }
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(vertex->GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(vertex->GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     {
       auto old_value = vertex->SetProperty(property, memgraph::storage::PropertyValue());
@@ -1446,49 +1496,48 @@ TEST(StorageV2, VertexPropertyCommit) {
       ASSERT_TRUE(old_value->IsNull());
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::OLD)->size(), 0);
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::NEW)->size(), 0);
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(vertex->GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(vertex->GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexPropertyAbort) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexPropertyAbort) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create the vertex.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Set property 5 to "nandare", but abort the transaction.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::NEW)->size(), 0);
@@ -1519,37 +1568,37 @@ TEST(StorageV2, VertexPropertyAbort) {
       ASSERT_EQ(properties[property].ValueString(), "nandare");
     }
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check that property 5 is null.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::OLD)->size(), 0);
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::NEW)->size(), 0);
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(vertex->GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(vertex->GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Set property 5 to "nandare".
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::NEW)->size(), 0);
@@ -1580,16 +1629,16 @@ TEST(StorageV2, VertexPropertyAbort) {
       ASSERT_EQ(properties[property].ValueString(), "nandare");
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check that property 5 is "nandare".
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_EQ(vertex->GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
     {
@@ -1605,21 +1654,21 @@ TEST(StorageV2, VertexPropertyAbort) {
       ASSERT_EQ(properties[property].ValueString(), "nandare");
     }
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(vertex->GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(vertex->GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Set property 5 to null, but abort the transaction.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_EQ(vertex->GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
     {
@@ -1651,16 +1700,16 @@ TEST(StorageV2, VertexPropertyAbort) {
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::NEW)->size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check that property 5 is "nandare".
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_EQ(vertex->GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
     {
@@ -1676,21 +1725,21 @@ TEST(StorageV2, VertexPropertyAbort) {
       ASSERT_EQ(properties[property].ValueString(), "nandare");
     }
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(vertex->GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(vertex->GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Set property 5 to null.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_EQ(vertex->GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
     {
@@ -1722,52 +1771,51 @@ TEST(StorageV2, VertexPropertyAbort) {
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::NEW)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check that property 5 is null.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(vertex->GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::OLD)->size(), 0);
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::NEW)->size(), 0);
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(vertex->GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(vertex->GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexPropertySerializationError) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexPropertySerializationError) {
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
-  auto acc1 = store.Access();
-  auto acc2 = store.Access();
+  auto acc1 = this->store->Access();
+  auto acc2 = this->store->Access();
 
   // Set property 1 to 123 in accessor 1.
   {
-    auto vertex = acc1.FindVertex(gid, memgraph::storage::View::OLD);
+    auto vertex = acc1->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property1 = acc1.NameToProperty("property1");
-    auto property2 = acc1.NameToProperty("property2");
+    auto property1 = acc1->NameToProperty("property1");
+    auto property2 = acc1->NameToProperty("property2");
 
     ASSERT_TRUE(vertex->GetProperty(property1, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(vertex->GetProperty(property1, memgraph::storage::View::NEW)->IsNull());
@@ -1796,11 +1844,11 @@ TEST(StorageV2, VertexPropertySerializationError) {
 
   // Set property 2 to "nandare" in accessor 2.
   {
-    auto vertex = acc2.FindVertex(gid, memgraph::storage::View::OLD);
+    auto vertex = acc2->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property1 = acc2.NameToProperty("property1");
-    auto property2 = acc2.NameToProperty("property2");
+    auto property1 = acc2->NameToProperty("property1");
+    auto property2 = acc2->NameToProperty("property2");
 
     ASSERT_TRUE(vertex->GetProperty(property1, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(vertex->GetProperty(property1, memgraph::storage::View::NEW)->IsNull());
@@ -1811,23 +1859,37 @@ TEST(StorageV2, VertexPropertySerializationError) {
 
     {
       auto res = vertex->SetProperty(property2, memgraph::storage::PropertyValue("nandare"));
-      ASSERT_TRUE(res.HasError());
-      ASSERT_EQ(res.GetError(), memgraph::storage::Error::SERIALIZATION_ERROR);
+      if (std::is_same<TypeParam, memgraph::storage::InMemoryStorage>::value) {
+        // InMemoryStorage works with pessimistic transactions.
+        ASSERT_TRUE(res.HasError());
+        ASSERT_EQ(res.GetError(), memgraph::storage::Error::SERIALIZATION_ERROR);
+      } else {
+        // Disk storage works with optimistic transactions.
+        ASSERT_TRUE(res.HasValue());
+        ASSERT_TRUE(res->IsNull());
+      }
     }
   }
 
   // Finalize both accessors.
-  ASSERT_FALSE(acc1.Commit().HasError());
-  acc2.Abort();
+  ASSERT_FALSE(acc1->Commit().HasError());
+  if (std::is_same<TypeParam, memgraph::storage::InMemoryStorage>::value) {
+    acc2->Abort();
+  } else {
+    // Disk storage works with optimistic transactions. So on write conflict, transaction fails on commit.
+    auto res = acc2->Commit();
+    ASSERT_TRUE(res.HasError());
+    ASSERT_EQ(std::get<memgraph::storage::SerializationError>(res.GetError()), memgraph::storage::SerializationError());
+  }
 
   // Check which properties exist.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    auto property1 = acc.NameToProperty("property1");
-    auto property2 = acc.NameToProperty("property2");
+    auto property1 = acc->NameToProperty("property1");
+    auto property2 = acc->NameToProperty("property2");
 
     ASSERT_EQ(vertex->GetProperty(property1, memgraph::storage::View::OLD)->ValueInt(), 123);
     ASSERT_TRUE(vertex->GetProperty(property2, memgraph::storage::View::OLD)->IsNull());
@@ -1845,18 +1907,17 @@ TEST(StorageV2, VertexPropertySerializationError) {
       ASSERT_EQ(properties[property1].ValueInt(), 123);
     }
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, VertexLabelPropertyMixed) {
-  memgraph::storage::Storage store;
-  auto acc = store.Access();
-  auto vertex = acc.CreateVertex();
+TYPED_TEST(StorageV2Test, VertexLabelPropertyMixed) {
+  auto acc = this->store->Access();
+  auto vertex = acc->CreateVertex();
 
-  auto label = acc.NameToLabel("label5");
-  auto property = acc.NameToProperty("property5");
+  auto label = acc->NameToLabel("label5");
+  auto property = acc->NameToProperty("property5");
 
   // Check whether label 5 and property 5 exist
   ASSERT_FALSE(vertex.HasLabel(label, memgraph::storage::View::NEW).GetValue());
@@ -1878,7 +1939,7 @@ TEST(StorageV2, VertexLabelPropertyMixed) {
   ASSERT_EQ(vertex.Properties(memgraph::storage::View::NEW)->size(), 0);
 
   // Advance command
-  acc.AdvanceCommand();
+  acc->AdvanceCommand();
 
   // Check whether label 5 and property 5 exist
   ASSERT_TRUE(vertex.HasLabel(label, memgraph::storage::View::OLD).GetValue());
@@ -1924,7 +1985,7 @@ TEST(StorageV2, VertexLabelPropertyMixed) {
   }
 
   // Advance command
-  acc.AdvanceCommand();
+  acc->AdvanceCommand();
 
   // Check whether label 5 and property 5 exist
   ASSERT_TRUE(vertex.HasLabel(label, memgraph::storage::View::OLD).GetValue());
@@ -1982,7 +2043,7 @@ TEST(StorageV2, VertexLabelPropertyMixed) {
   }
 
   // Advance command
-  acc.AdvanceCommand();
+  acc->AdvanceCommand();
 
   // Check whether label 5 and property 5 exist
   ASSERT_TRUE(vertex.HasLabel(label, memgraph::storage::View::OLD).GetValue());
@@ -2036,7 +2097,7 @@ TEST(StorageV2, VertexLabelPropertyMixed) {
   }
 
   // Advance command
-  acc.AdvanceCommand();
+  acc->AdvanceCommand();
 
   // Check whether label 5 and property 5 exist
   ASSERT_FALSE(vertex.HasLabel(label, memgraph::storage::View::OLD).GetValue());
@@ -2074,7 +2135,7 @@ TEST(StorageV2, VertexLabelPropertyMixed) {
   ASSERT_EQ(vertex.Properties(memgraph::storage::View::NEW)->size(), 0);
 
   // Advance command
-  acc.AdvanceCommand();
+  acc->AdvanceCommand();
 
   // Check whether label 5 and property 5 exist
   ASSERT_FALSE(vertex.HasLabel(label, memgraph::storage::View::OLD).GetValue());
@@ -2086,28 +2147,27 @@ TEST(StorageV2, VertexLabelPropertyMixed) {
   ASSERT_EQ(vertex.Properties(memgraph::storage::View::OLD)->size(), 0);
   ASSERT_EQ(vertex.Properties(memgraph::storage::View::NEW)->size(), 0);
 
-  ASSERT_FALSE(acc.Commit().HasError());
+  ASSERT_FALSE(acc->Commit().HasError());
 }
 
-TEST(StorageV2, VertexPropertyClear) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexPropertyClear) {
   memgraph::storage::Gid gid;
-  auto property1 = store.NameToProperty("property1");
-  auto property2 = store.NameToProperty("property2");
+  auto property1 = this->store->NameToProperty("property1");
+  auto property2 = this->store->NameToProperty("property2");
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
 
     auto old_value = vertex.SetProperty(property1, memgraph::storage::PropertyValue("value"));
     ASSERT_TRUE(old_value.HasValue());
     ASSERT_TRUE(old_value->IsNull());
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
     ASSERT_EQ(vertex->GetProperty(property1, memgraph::storage::View::OLD)->ValueString(), "value");
@@ -2135,22 +2195,22 @@ TEST(StorageV2, VertexPropertyClear) {
     ASSERT_TRUE(vertex->GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::NEW).GetValue().size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
     auto old_value = vertex->SetProperty(property2, memgraph::storage::PropertyValue(42));
     ASSERT_TRUE(old_value.HasValue());
     ASSERT_TRUE(old_value->IsNull());
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
     ASSERT_EQ(vertex->GetProperty(property1, memgraph::storage::View::OLD)->ValueString(), "value");
@@ -2179,29 +2239,27 @@ TEST(StorageV2, VertexPropertyClear) {
     ASSERT_TRUE(vertex->GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::NEW).GetValue().size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = this->store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
     ASSERT_TRUE(vertex->GetProperty(property1, memgraph::storage::View::NEW)->IsNull());
     ASSERT_TRUE(vertex->GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(vertex->Properties(memgraph::storage::View::NEW).GetValue().size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
-TEST(StorageV2, VertexNonexistentLabelPropertyEdgeAPI) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexNonexistentLabelPropertyEdgeAPI) {
+  auto label = this->store->NameToLabel("label");
+  auto property = this->store->NameToProperty("property");
 
-  auto label = store.NameToLabel("label");
-  auto property = store.NameToProperty("property");
-
-  auto acc = store.Access();
-  auto vertex = acc.CreateVertex();
+  auto acc = this->store->Access();
+  auto vertex = acc->CreateVertex();
 
   // Check state before (OLD view).
   ASSERT_EQ(vertex.Labels(memgraph::storage::View::OLD).GetError(), memgraph::storage::Error::NONEXISTENT_OBJECT);
@@ -2228,7 +2286,7 @@ TEST(StorageV2, VertexNonexistentLabelPropertyEdgeAPI) {
   // Modify vertex.
   ASSERT_TRUE(vertex.AddLabel(label).HasValue());
   ASSERT_TRUE(vertex.SetProperty(property, memgraph::storage::PropertyValue("value")).HasValue());
-  ASSERT_TRUE(acc.CreateEdge(&vertex, &vertex, acc.NameToEdgeType("edge")).HasValue());
+  ASSERT_TRUE(acc->CreateEdge(&vertex, &vertex, acc->NameToEdgeType("edge")).HasValue());
 
   // Check state after (OLD view).
   ASSERT_EQ(vertex.Labels(memgraph::storage::View::OLD).GetError(), memgraph::storage::Error::NONEXISTENT_OBJECT);
@@ -2252,325 +2310,320 @@ TEST(StorageV2, VertexNonexistentLabelPropertyEdgeAPI) {
   ASSERT_EQ(*vertex.InDegree(memgraph::storage::View::NEW), 1);
   ASSERT_EQ(*vertex.OutDegree(memgraph::storage::View::NEW), 1);
 
-  ASSERT_FALSE(acc.Commit().HasError());
+  ASSERT_FALSE(acc->Commit().HasError());
 }
 
-TEST(StorageV2, VertexVisibilitySingleTransaction) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexVisibilitySingleTransaction) {
+  auto acc1 = this->store->Access();
+  auto acc2 = this->store->Access();
 
-  auto acc1 = store.Access();
-  auto acc2 = store.Access();
-
-  auto vertex = acc1.CreateVertex();
+  auto vertex = acc1->CreateVertex();
   auto gid = vertex.Gid();
 
-  EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-  EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
 
-  ASSERT_TRUE(vertex.AddLabel(acc1.NameToLabel("label")).HasValue());
+  ASSERT_TRUE(vertex.AddLabel(acc1->NameToLabel("label")).HasValue());
 
-  EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-  EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
 
-  ASSERT_TRUE(vertex.SetProperty(acc1.NameToProperty("meaning"), memgraph::storage::PropertyValue(42)).HasValue());
+  ASSERT_TRUE(vertex.SetProperty(acc1->NameToProperty("meaning"), memgraph::storage::PropertyValue(42)).HasValue());
 
-  auto acc3 = store.Access();
+  auto acc3 = this->store->Access();
 
-  EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-  EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-  EXPECT_FALSE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_FALSE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_FALSE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-  ASSERT_TRUE(acc1.DeleteVertex(&vertex).HasValue());
+  ASSERT_TRUE(acc1->DeleteVertex(&vertex).HasValue());
 
-  EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-  EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-  EXPECT_FALSE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_FALSE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_FALSE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-  acc1.AdvanceCommand();
-  acc3.AdvanceCommand();
+  acc1->AdvanceCommand();
+  acc3->AdvanceCommand();
 
-  EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-  EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-  EXPECT_FALSE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-  EXPECT_FALSE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+  EXPECT_FALSE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+  EXPECT_FALSE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-  acc1.Abort();
-  acc2.Abort();
-  acc3.Abort();
+  acc1->Abort();
+  acc2->Abort();
+  acc3->Abort();
 }
 
-TEST(StorageV2, VertexVisibilityMultipleTransactions) {
-  memgraph::storage::Storage store;
+TYPED_TEST(StorageV2Test, VertexVisibilityMultipleTransactions) {
   memgraph::storage::Gid gid;
 
   {
-    auto acc1 = store.Access();
-    auto acc2 = store.Access();
+    auto acc1 = this->store->Access();
+    auto acc2 = this->store->Access();
 
-    auto vertex = acc1.CreateVertex();
+    auto vertex = acc1->CreateVertex();
     gid = vertex.Gid();
 
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc2.AdvanceCommand();
+    acc2->AdvanceCommand();
 
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc1.AdvanceCommand();
+    acc1->AdvanceCommand();
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
 
-    ASSERT_FALSE(acc1.Commit().HasError());
-    ASSERT_FALSE(acc2.Commit().HasError());
+    ASSERT_FALSE(acc1->Commit().HasError());
+    ASSERT_FALSE(acc2->Commit().HasError());
   }
 
   {
-    auto acc1 = store.Access();
-    auto acc2 = store.Access();
+    auto acc1 = this->store->Access();
+    auto acc2 = this->store->Access();
 
-    auto vertex = acc1.FindVertex(gid, memgraph::storage::View::OLD);
+    auto vertex = acc1->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
 
-    ASSERT_TRUE(vertex->AddLabel(acc1.NameToLabel("label")).HasValue());
+    ASSERT_TRUE(vertex->AddLabel(acc1->NameToLabel("label")).HasValue());
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc1.AdvanceCommand();
+    acc1->AdvanceCommand();
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc2.AdvanceCommand();
+    acc2->AdvanceCommand();
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
 
-    ASSERT_TRUE(vertex->SetProperty(acc1.NameToProperty("meaning"), memgraph::storage::PropertyValue(42)).HasValue());
+    ASSERT_TRUE(vertex->SetProperty(acc1->NameToProperty("meaning"), memgraph::storage::PropertyValue(42)).HasValue());
 
-    auto acc3 = store.Access();
+    auto acc3 = this->store->Access();
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc1.AdvanceCommand();
+    acc1->AdvanceCommand();
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc2.AdvanceCommand();
+    acc2->AdvanceCommand();
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc3.AdvanceCommand();
+    acc3->AdvanceCommand();
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-    ASSERT_FALSE(acc1.Commit().HasError());
-    ASSERT_FALSE(acc2.Commit().HasError());
-    ASSERT_FALSE(acc3.Commit().HasError());
+    ASSERT_FALSE(acc1->Commit().HasError());
+    ASSERT_FALSE(acc2->Commit().HasError());
+    ASSERT_FALSE(acc3->Commit().HasError());
   }
 
   {
-    auto acc1 = store.Access();
-    auto acc2 = store.Access();
+    auto acc1 = this->store->Access();
+    auto acc2 = this->store->Access();
 
-    auto vertex = acc1.FindVertex(gid, memgraph::storage::View::OLD);
+    auto vertex = acc1->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    ASSERT_TRUE(acc1.DeleteVertex(&*vertex).HasValue());
+    ASSERT_TRUE(acc1->DeleteVertex(&*vertex).HasValue());
 
-    auto acc3 = store.Access();
+    auto acc3 = this->store->Access();
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc2.AdvanceCommand();
+    acc2->AdvanceCommand();
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc1.AdvanceCommand();
+    acc1->AdvanceCommand();
 
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc3.AdvanceCommand();
+    acc3->AdvanceCommand();
 
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc1.Abort();
-    acc2.Abort();
-    acc3.Abort();
+    acc1->Abort();
+    acc2->Abort();
+    acc3->Abort();
   }
 
   {
-    auto acc = store.Access();
+    auto acc = this->store->Access();
 
-    EXPECT_TRUE(acc.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
-    EXPECT_TRUE(acc.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc.Abort();
+    acc->Abort();
   }
 
   {
-    auto acc1 = store.Access();
-    auto acc2 = store.Access();
+    auto acc1 = this->store->Access();
+    auto acc2 = this->store->Access();
 
-    auto vertex = acc1.FindVertex(gid, memgraph::storage::View::OLD);
+    auto vertex = acc1->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
 
-    ASSERT_TRUE(acc1.DeleteVertex(&*vertex).HasValue());
+    ASSERT_TRUE(acc1->DeleteVertex(&*vertex).HasValue());
 
-    auto acc3 = store.Access();
+    auto acc3 = this->store->Access();
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc2.AdvanceCommand();
+    acc2->AdvanceCommand();
 
-    EXPECT_TRUE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc1.AdvanceCommand();
+    acc1->AdvanceCommand();
 
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc3.AdvanceCommand();
+    acc3->AdvanceCommand();
 
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc1.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc2.FindVertex(gid, memgraph::storage::View::NEW));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_TRUE(acc3.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc1->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc2->FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_TRUE(acc3->FindVertex(gid, memgraph::storage::View::NEW));
 
-    ASSERT_FALSE(acc1.Commit().HasError());
-    ASSERT_FALSE(acc2.Commit().HasError());
-    ASSERT_FALSE(acc3.Commit().HasError());
+    ASSERT_FALSE(acc1->Commit().HasError());
+    ASSERT_FALSE(acc2->Commit().HasError());
+    ASSERT_FALSE(acc3->Commit().HasError());
   }
 
   {
-    auto acc = store.Access();
+    auto acc = this->store->Access();
 
-    EXPECT_FALSE(acc.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_FALSE(acc->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
-    EXPECT_FALSE(acc.FindVertex(gid, memgraph::storage::View::OLD));
-    EXPECT_FALSE(acc.FindVertex(gid, memgraph::storage::View::NEW));
+    EXPECT_FALSE(acc->FindVertex(gid, memgraph::storage::View::OLD));
+    EXPECT_FALSE(acc->FindVertex(gid, memgraph::storage::View::NEW));
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST(StorageV2, DeletedVertexAccessor) {
-  memgraph::storage::Storage store;
-
-  const auto property = store.NameToProperty("property");
+TYPED_TEST(StorageV2Test, DeletedVertexAccessor) {
+  const auto property = this->store->NameToProperty("property");
   const memgraph::storage::PropertyValue property_value{"property_value"};
 
   std::optional<memgraph::storage::Gid> gid;
   // Create the vertex
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
     ASSERT_FALSE(vertex.SetProperty(property, property_value).HasError());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
-  auto acc = store.Access();
-  auto vertex = acc.FindVertex(*gid, memgraph::storage::View::OLD);
+  auto acc = this->store->Access();
+  auto vertex = acc->FindVertex(*gid, memgraph::storage::View::OLD);
   ASSERT_TRUE(vertex);
-  auto maybe_deleted_vertex = acc.DeleteVertex(&*vertex);
+  auto maybe_deleted_vertex = acc->DeleteVertex(&*vertex);
   ASSERT_FALSE(maybe_deleted_vertex.HasError());
 
   auto deleted_vertex = maybe_deleted_vertex.GetValue();
@@ -2582,7 +2635,7 @@ TEST(StorageV2, DeletedVertexAccessor) {
   const auto maybe_property = deleted_vertex->GetProperty(property, memgraph::storage::View::OLD);
   ASSERT_FALSE(maybe_property.HasError());
   ASSERT_EQ(property_value, *maybe_property);
-  ASSERT_FALSE(acc.Commit().HasError());
+  ASSERT_FALSE(acc->Commit().HasError());
 
   {
     // you can call read only methods and get valid results even after the
diff --git a/tests/unit/storage_v2_constraints.cpp b/tests/unit/storage_v2_constraints.cpp
index aadc98ab0..8094c4ab4 100644
--- a/tests/unit/storage_v2_constraints.cpp
+++ b/tests/unit/storage_v2_constraints.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -11,338 +11,379 @@
 
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
+#include <filesystem>
 #include <variant>
 
-#include "storage/v2/storage.hpp"
+#include "storage/v2/constraints/constraints.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/disk/unique_constraints.hpp"
+#include "storage/v2/inmemory/storage.hpp"
+
+#include "disk_test_utils.hpp"
 
 // NOLINTNEXTLINE(google-build-using-namespace)
 using namespace memgraph::storage;
 
+using testing::Types;
 using testing::UnorderedElementsAre;
 
 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
 #define ASSERT_NO_ERROR(result) ASSERT_FALSE((result).HasError())
 
+template <typename StorageType>
 class ConstraintsTest : public testing::Test {
- protected:
-  ConstraintsTest()
-      : prop1(storage.NameToProperty("prop1")),
-        prop2(storage.NameToProperty("prop2")),
-        label1(storage.NameToLabel("label1")),
-        label2(storage.NameToLabel("label2")) {}
+ public:
+  const std::string testSuite = "storage_v2_constraints";
 
-  Storage storage;
+  ConstraintsTest() {
+    /// TODO: andi How to make this better? Because currentlly for every test changed you need to create a configuration
+    config_ = disk_test_utils::GenerateOnDiskConfig(testSuite);
+    storage = std::make_unique<StorageType>(config_);
+    prop1 = storage->NameToProperty("prop1");
+    prop2 = storage->NameToProperty("prop2");
+    label1 = storage->NameToLabel("label1");
+    label2 = storage->NameToLabel("label2");
+  }
+
+  void TearDown() override {
+    storage.reset(nullptr);
+
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+  }
+
+  std::unique_ptr<Storage> storage;
+  memgraph::storage::Config config_;
   PropertyId prop1;
   PropertyId prop2;
   LabelId label1;
   LabelId label2;
 };
 
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(ConstraintsTest, StorageTypes);
+
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, ExistenceConstraintsCreateAndDrop) {
-  EXPECT_EQ(storage.ListAllConstraints().existence.size(), 0);
+TYPED_TEST(ConstraintsTest, ExistenceConstraintsCreateAndDrop) {
+  EXPECT_EQ(this->storage->ListAllConstraints().existence.size(), 0);
   {
-    auto res = storage.CreateExistenceConstraint(label1, prop1);
+    auto res = this->storage->CreateExistenceConstraint(this->label1, this->prop1, {});
     EXPECT_FALSE(res.HasError());
   }
-  EXPECT_THAT(storage.ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(label1, prop1)));
+  EXPECT_THAT(this->storage->ListAllConstraints().existence,
+              UnorderedElementsAre(std::make_pair(this->label1, this->prop1)));
   {
-    auto res = storage.CreateExistenceConstraint(label1, prop1);
+    auto res = this->storage->CreateExistenceConstraint(this->label1, this->prop1, {});
     EXPECT_TRUE(res.HasError());
   }
-  EXPECT_THAT(storage.ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(label1, prop1)));
+  EXPECT_THAT(this->storage->ListAllConstraints().existence,
+              UnorderedElementsAre(std::make_pair(this->label1, this->prop1)));
   {
-    auto res = storage.CreateExistenceConstraint(label2, prop1);
+    auto res = this->storage->CreateExistenceConstraint(this->label2, this->prop1, {});
     EXPECT_FALSE(res.HasError());
   }
-  EXPECT_THAT(storage.ListAllConstraints().existence,
-              UnorderedElementsAre(std::make_pair(label1, prop1), std::make_pair(label2, prop1)));
-  EXPECT_FALSE(storage.DropExistenceConstraint(label1, prop1).HasError());
-  EXPECT_TRUE(storage.DropExistenceConstraint(label1, prop1).HasError());
-  EXPECT_THAT(storage.ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(label2, prop1)));
-  EXPECT_FALSE(storage.DropExistenceConstraint(label2, prop1).HasError());
-  EXPECT_TRUE(storage.DropExistenceConstraint(label2, prop2).HasError());
-  EXPECT_EQ(storage.ListAllConstraints().existence.size(), 0);
+  EXPECT_THAT(
+      this->storage->ListAllConstraints().existence,
+      UnorderedElementsAre(std::make_pair(this->label1, this->prop1), std::make_pair(this->label2, this->prop1)));
+  EXPECT_FALSE(this->storage->DropExistenceConstraint(this->label1, this->prop1, {}).HasError());
+  EXPECT_TRUE(this->storage->DropExistenceConstraint(this->label1, this->prop1, {}).HasError());
+  EXPECT_THAT(this->storage->ListAllConstraints().existence,
+              UnorderedElementsAre(std::make_pair(this->label2, this->prop1)));
+  EXPECT_FALSE(this->storage->DropExistenceConstraint(this->label2, this->prop1, {}).HasError());
+  EXPECT_TRUE(this->storage->DropExistenceConstraint(this->label2, this->prop2, {}).HasError());
+  EXPECT_EQ(this->storage->ListAllConstraints().existence.size(), 0);
   {
-    auto res = storage.CreateExistenceConstraint(label2, prop1);
+    auto res = this->storage->CreateExistenceConstraint(this->label2, this->prop1, {});
     EXPECT_FALSE(res.HasError());
   }
-  EXPECT_THAT(storage.ListAllConstraints().existence, UnorderedElementsAre(std::make_pair(label2, prop1)));
+  EXPECT_THAT(this->storage->ListAllConstraints().existence,
+              UnorderedElementsAre(std::make_pair(this->label2, this->prop1)));
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, ExistenceConstraintsCreateFailure1) {
+TYPED_TEST(ConstraintsTest, ExistenceConstraintsCreateFailure1) {
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(acc->Commit());
   }
   {
-    auto res = storage.CreateExistenceConstraint(label1, prop1);
+    auto res = this->storage->CreateExistenceConstraint(this->label1, this->prop1, {});
     ASSERT_TRUE(res.HasError());
-    EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label1, std::set<PropertyId>{prop1}}));
+    EXPECT_EQ(
+        std::get<ConstraintViolation>(res.GetError()),
+        (ConstraintViolation{ConstraintViolation::Type::EXISTENCE, this->label1, std::set<PropertyId>{this->prop1}}));
   }
   {
-    auto acc = storage.Access();
-    for (auto vertex : acc.Vertices(View::OLD)) {
-      ASSERT_NO_ERROR(acc.DeleteVertex(&vertex));
+    auto acc = this->storage->Access();
+    for (auto vertex : acc->Vertices(View::OLD)) {
+      ASSERT_NO_ERROR(acc->DeleteVertex(&vertex));
     }
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
   {
-    auto res = storage.CreateExistenceConstraint(label1, prop1);
+    auto res = this->storage->CreateExistenceConstraint(this->label1, this->prop1, {});
     EXPECT_FALSE(res.HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, ExistenceConstraintsCreateFailure2) {
+TYPED_TEST(ConstraintsTest, ExistenceConstraintsCreateFailure2) {
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(acc->Commit());
   }
   {
-    auto res = storage.CreateExistenceConstraint(label1, prop1);
+    auto res = this->storage->CreateExistenceConstraint(this->label1, this->prop1, {});
     ASSERT_TRUE(res.HasError());
-    EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label1, std::set<PropertyId>{prop1}}));
+    EXPECT_EQ(
+        std::get<ConstraintViolation>(res.GetError()),
+        (ConstraintViolation{ConstraintViolation::Type::EXISTENCE, this->label1, std::set<PropertyId>{this->prop1}}));
   }
   {
-    auto acc = storage.Access();
-    for (auto vertex : acc.Vertices(View::OLD)) {
-      ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
+    auto acc = this->storage->Access();
+    for (auto vertex : acc->Vertices(View::OLD)) {
+      ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
     }
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
   {
-    auto res = storage.CreateExistenceConstraint(label1, prop1);
+    auto res = this->storage->CreateExistenceConstraint(this->label1, this->prop1, {});
     EXPECT_FALSE(res.HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, ExistenceConstraintsViolationOnCommit) {
+TYPED_TEST(ConstraintsTest, ExistenceConstraintsViolationOnCommit) {
   {
-    auto res = storage.CreateExistenceConstraint(label1, prop1);
+    auto res = this->storage->CreateExistenceConstraint(this->label1, this->prop1, {});
     EXPECT_FALSE(res.HasError());
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
 
-    auto res = acc.Commit();
+    auto res = acc->Commit();
     ASSERT_TRUE(res.HasError());
-    EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label1, std::set<PropertyId>{prop1}}));
+    EXPECT_EQ(
+        std::get<ConstraintViolation>(res.GetError()),
+        (ConstraintViolation{ConstraintViolation::Type::EXISTENCE, this->label1, std::set<PropertyId>{this->prop1}}));
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto acc = storage.Access();
-    for (auto vertex : acc.Vertices(View::OLD)) {
-      ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue()));
+    auto acc = this->storage->Access();
+    for (auto vertex : acc->Vertices(View::OLD)) {
+      ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue()));
     }
 
-    auto res = acc.Commit();
+    auto res = acc->Commit();
     ASSERT_TRUE(res.HasError());
-    EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::EXISTENCE, label1, std::set<PropertyId>{prop1}}));
+    EXPECT_EQ(
+        std::get<ConstraintViolation>(res.GetError()),
+        (ConstraintViolation{ConstraintViolation::Type::EXISTENCE, this->label1, std::set<PropertyId>{this->prop1}}));
   }
 
   {
-    auto acc = storage.Access();
-    for (auto vertex : acc.Vertices(View::OLD)) {
-      ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue()));
+    auto acc = this->storage->Access();
+    for (auto vertex : acc->Vertices(View::OLD)) {
+      ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue()));
     }
-    for (auto vertex : acc.Vertices(View::OLD)) {
-      ASSERT_NO_ERROR(acc.DeleteVertex(&vertex));
+    for (auto vertex : acc->Vertices(View::OLD)) {
+      ASSERT_NO_ERROR(acc->DeleteVertex(&vertex));
     }
 
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
-  ASSERT_FALSE(storage.DropExistenceConstraint(label1, prop1).HasError());
+  ASSERT_FALSE(this->storage->DropExistenceConstraint(this->label1, this->prop1, {}).HasError());
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsCreateAndDropAndList) {
-  EXPECT_EQ(storage.ListAllConstraints().unique.size(), 0);
+TYPED_TEST(ConstraintsTest, UniqueConstraintsCreateAndDropAndList) {
+  EXPECT_EQ(this->storage->ListAllConstraints().unique.size(), 0);
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     EXPECT_TRUE(res.HasValue());
     EXPECT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
-  EXPECT_THAT(storage.ListAllConstraints().unique,
-              UnorderedElementsAre(std::make_pair(label1, std::set<PropertyId>{prop1})));
+  EXPECT_THAT(this->storage->ListAllConstraints().unique,
+              UnorderedElementsAre(std::make_pair(this->label1, std::set<PropertyId>{this->prop1})));
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     EXPECT_TRUE(res.HasValue());
     EXPECT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::ALREADY_EXISTS);
   }
-  EXPECT_THAT(storage.ListAllConstraints().unique,
-              UnorderedElementsAre(std::make_pair(label1, std::set<PropertyId>{prop1})));
+  EXPECT_THAT(this->storage->ListAllConstraints().unique,
+              UnorderedElementsAre(std::make_pair(this->label1, std::set<PropertyId>{this->prop1})));
   {
-    auto res = storage.CreateUniqueConstraint(label2, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label2, {this->prop1}, {});
     EXPECT_TRUE(res.HasValue() && res.GetValue() == UniqueConstraints::CreationStatus::SUCCESS);
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
-  EXPECT_THAT(storage.ListAllConstraints().unique,
-              UnorderedElementsAre(std::make_pair(label1, std::set<PropertyId>{prop1}),
-                                   std::make_pair(label2, std::set<PropertyId>{prop1})));
-  EXPECT_EQ(storage.DropUniqueConstraint(label1, {prop1}).GetValue(), UniqueConstraints::DeletionStatus::SUCCESS);
-  EXPECT_EQ(storage.DropUniqueConstraint(label1, {prop1}).GetValue(), UniqueConstraints::DeletionStatus::NOT_FOUND);
-  EXPECT_THAT(storage.ListAllConstraints().unique,
-              UnorderedElementsAre(std::make_pair(label2, std::set<PropertyId>{prop1})));
-  EXPECT_EQ(storage.DropUniqueConstraint(label2, {prop1}).GetValue(), UniqueConstraints::DeletionStatus::SUCCESS);
-  EXPECT_EQ(storage.DropUniqueConstraint(label2, {prop2}).GetValue(), UniqueConstraints::DeletionStatus::NOT_FOUND);
-  EXPECT_EQ(storage.ListAllConstraints().unique.size(), 0);
+  EXPECT_THAT(this->storage->ListAllConstraints().unique,
+              UnorderedElementsAre(std::make_pair(this->label1, std::set<PropertyId>{this->prop1}),
+                                   std::make_pair(this->label2, std::set<PropertyId>{this->prop1})));
+  EXPECT_EQ(this->storage->DropUniqueConstraint(this->label1, {this->prop1}, {}).GetValue(),
+            UniqueConstraints::DeletionStatus::SUCCESS);
+  EXPECT_EQ(this->storage->DropUniqueConstraint(this->label1, {this->prop1}, {}).GetValue(),
+            UniqueConstraints::DeletionStatus::NOT_FOUND);
+  EXPECT_THAT(this->storage->ListAllConstraints().unique,
+              UnorderedElementsAre(std::make_pair(this->label2, std::set<PropertyId>{this->prop1})));
+  EXPECT_EQ(this->storage->DropUniqueConstraint(this->label2, {this->prop1}, {}).GetValue(),
+            UniqueConstraints::DeletionStatus::SUCCESS);
+  EXPECT_EQ(this->storage->DropUniqueConstraint(this->label2, {this->prop2}, {}).GetValue(),
+            UniqueConstraints::DeletionStatus::NOT_FOUND);
+  EXPECT_EQ(this->storage->ListAllConstraints().unique.size(), 0);
   {
-    auto res = storage.CreateUniqueConstraint(label2, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label2, {this->prop1}, {});
     EXPECT_TRUE(res.HasValue());
     EXPECT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
-  EXPECT_THAT(storage.ListAllConstraints().unique,
-              UnorderedElementsAre(std::make_pair(label2, std::set<PropertyId>{prop1})));
+  EXPECT_THAT(this->storage->ListAllConstraints().unique,
+              UnorderedElementsAre(std::make_pair(this->label2, std::set<PropertyId>{this->prop1})));
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsCreateFailure1) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsCreateFailure1) {
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
     for (int i = 0; i < 2; ++i) {
-      auto vertex1 = acc.CreateVertex();
-      ASSERT_NO_ERROR(vertex1.AddLabel(label1));
-      ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
+      auto vertex1 = acc->CreateVertex();
+      ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+      ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
     }
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     ASSERT_TRUE(res.HasError());
-    EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1}}));
+    EXPECT_EQ(
+        std::get<ConstraintViolation>(res.GetError()),
+        (ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set<PropertyId>{this->prop1}}));
   }
 
   {
-    auto acc = storage.Access();
-    for (auto vertex : acc.Vertices(View::OLD)) {
-      ASSERT_NO_ERROR(acc.DeleteVertex(&vertex));
+    auto acc = this->storage->Access();
+    for (auto vertex : acc->Vertices(View::OLD)) {
+      ASSERT_NO_ERROR(acc->DeleteVertex(&vertex));
     }
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsCreateFailure2) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsCreateFailure2) {
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
     for (int i = 0; i < 2; ++i) {
-      auto vertex = acc.CreateVertex();
-      ASSERT_NO_ERROR(vertex.AddLabel(label1));
-      ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
+      auto vertex = acc->CreateVertex();
+      ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+      ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
     }
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     ASSERT_TRUE(res.HasError());
-    EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1}}));
+    EXPECT_EQ(
+        std::get<ConstraintViolation>(res.GetError()),
+        (ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set<PropertyId>{this->prop1}}));
   }
 
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
     int value = 0;
-    for (auto vertex : acc.Vertices(View::OLD)) {
-      ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(value)));
+    for (auto vertex : acc->Vertices(View::OLD)) {
+      ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(value)));
       ++value;
     }
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsNoViolation1) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsNoViolation1) {
   Gid gid1;
   Gid gid2;
   {
-    auto acc = storage.Access();
-    auto vertex1 = acc.CreateVertex();
-    auto vertex2 = acc.CreateVertex();
+    auto acc = this->storage->Access();
+    auto vertex1 = acc->CreateVertex();
+    auto vertex2 = acc->CreateVertex();
     gid1 = vertex1.Gid();
     gid2 = vertex2.Gid();
 
-    ASSERT_NO_ERROR(vertex1.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex1 = acc.FindVertex(gid1, View::OLD);
-    auto vertex2 = acc.FindVertex(gid2, View::OLD);
+    auto acc = this->storage->Access();
+    auto vertex1 = acc->FindVertex(gid1, View::OLD);
+    auto vertex2 = acc->FindVertex(gid2, View::OLD);
 
-    ASSERT_NO_ERROR(vertex1->SetProperty(prop2, PropertyValue(2)));
-    ASSERT_NO_ERROR(vertex2->AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2->SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex2->SetProperty(prop2, PropertyValue(3)));
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(vertex1->SetProperty(this->prop2, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex2->AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2->SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex2->SetProperty(this->prop2, PropertyValue(3)));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex1 = acc.FindVertex(gid1, View::OLD);
-    auto vertex2 = acc.FindVertex(gid2, View::OLD);
-    ASSERT_NO_ERROR(vertex1->SetProperty(prop1, PropertyValue(2)));
-    ASSERT_NO_ERROR(vertex2->SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex1 = acc->FindVertex(gid1, View::OLD);
+    auto vertex2 = acc->FindVertex(gid2, View::OLD);
+    ASSERT_NO_ERROR(vertex1->SetProperty(this->prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex2->SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsNoViolation2) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsNoViolation2) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
@@ -351,28 +392,28 @@ TEST_F(ConstraintsTest, UniqueConstraintsNoViolation2) {
     // tx1: B---SP(v1, 1)---SP(v1, 2)---OK--
     // tx2: -B---SP(v2, 2)---SP(v2, 1)---OK-
 
-    auto acc1 = storage.Access();
-    auto acc2 = storage.Access();
-    auto vertex1 = acc1.CreateVertex();
-    auto vertex2 = acc2.CreateVertex();
+    auto acc1 = this->storage->Access();
+    auto acc2 = this->storage->Access();
+    auto vertex1 = acc1->CreateVertex();
+    auto vertex2 = acc2->CreateVertex();
 
-    ASSERT_NO_ERROR(vertex1.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex2.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(2)));
 
-    ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(2)));
-    ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(1)));
 
-    ASSERT_NO_ERROR(acc1.Commit());
-    ASSERT_NO_ERROR(acc2.Commit());
+    ASSERT_NO_ERROR(acc1->Commit());
+    ASSERT_NO_ERROR(acc2->Commit());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsNoViolation3) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsNoViolation3) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
@@ -382,33 +423,33 @@ TEST_F(ConstraintsTest, UniqueConstraintsNoViolation3) {
     // tx2: --------------------B---SP(v1, 2)---OK--
     // tx3: ---------------------B---SP(v2, 1)---OK-
 
-    auto acc1 = storage.Access();
-    auto vertex1 = acc1.CreateVertex();
+    auto acc1 = this->storage->Access();
+    auto vertex1 = acc1->CreateVertex();
     auto gid = vertex1.Gid();
 
-    ASSERT_NO_ERROR(vertex1.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
 
-    ASSERT_NO_ERROR(acc1.Commit());
+    ASSERT_NO_ERROR(acc1->Commit());
 
-    auto acc2 = storage.Access();
-    auto acc3 = storage.Access();
-    auto vertex2 = acc2.FindVertex(gid, View::NEW);  // vertex1 == vertex2
-    auto vertex3 = acc3.CreateVertex();
+    auto acc2 = this->storage->Access();
+    auto acc3 = this->storage->Access();
+    auto vertex2 = acc2->FindVertex(gid, View::NEW);  // vertex1 == vertex2
+    auto vertex3 = acc3->CreateVertex();
 
-    ASSERT_NO_ERROR(vertex2->SetProperty(prop1, PropertyValue(2)));
-    ASSERT_NO_ERROR(vertex3.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex3.SetProperty(prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex2->SetProperty(this->prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex3.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex3.SetProperty(this->prop1, PropertyValue(1)));
 
-    ASSERT_NO_ERROR(acc2.Commit());
-    ASSERT_NO_ERROR(acc3.Commit());
+    ASSERT_NO_ERROR(acc2->Commit());
+    ASSERT_NO_ERROR(acc3->Commit());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsNoViolation4) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsNoViolation4) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
@@ -418,56 +459,57 @@ TEST_F(ConstraintsTest, UniqueConstraintsNoViolation4) {
     // tx2: --------------------B---SP(v2, 1)-----OK-
     // tx3: ---------------------B---SP(v1, 2)---OK--
 
-    auto acc1 = storage.Access();
-    auto vertex1 = acc1.CreateVertex();
+    auto acc1 = this->storage->Access();
+    auto vertex1 = acc1->CreateVertex();
     auto gid = vertex1.Gid();
 
-    ASSERT_NO_ERROR(vertex1.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
 
-    ASSERT_NO_ERROR(acc1.Commit());
+    ASSERT_NO_ERROR(acc1->Commit());
 
-    auto acc2 = storage.Access();
-    auto acc3 = storage.Access();
-    auto vertex2 = acc2.CreateVertex();
-    auto vertex3 = acc3.FindVertex(gid, View::NEW);
+    auto acc2 = this->storage->Access();
+    auto acc3 = this->storage->Access();
+    auto vertex2 = acc2->CreateVertex();
+    auto vertex3 = acc3->FindVertex(gid, View::NEW);
 
-    ASSERT_NO_ERROR(vertex2.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex3->SetProperty(prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex3->SetProperty(this->prop1, PropertyValue(2)));
 
-    ASSERT_NO_ERROR(acc3.Commit());
-    ASSERT_NO_ERROR(acc2.Commit());
+    ASSERT_NO_ERROR(acc3->Commit());
+    ASSERT_NO_ERROR(acc2->Commit());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsViolationOnCommit1) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsViolationOnCommit1) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex1 = acc.CreateVertex();
-    auto vertex2 = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex1.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex2.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(1)));
-    auto res = acc.Commit();
+    auto acc = this->storage->Access();
+    auto vertex1 = acc->CreateVertex();
+    auto vertex2 = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(1)));
+    auto res = acc->Commit();
     ASSERT_TRUE(res.HasError());
-    EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1}}));
+    EXPECT_EQ(
+        std::get<ConstraintViolation>(res.GetError()),
+        (ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set<PropertyId>{this->prop1}}));
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsViolationOnCommit2) {
+/// TODO: andi consistency problems
+TYPED_TEST(ConstraintsTest, UniqueConstraintsViolationOnCommit2) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
@@ -477,39 +519,41 @@ TEST_F(ConstraintsTest, UniqueConstraintsViolationOnCommit2) {
     // tx2: -------------------------------B---SP(v1, 3)---OK----
     // tx3: --------------------------------B---SP(v2, 3)---FAIL-
 
-    auto acc1 = storage.Access();
-    auto vertex1 = acc1.CreateVertex();
-    auto vertex2 = acc1.CreateVertex();
+    auto acc1 = this->storage->Access();
+    auto vertex1 = acc1->CreateVertex();
+    auto vertex2 = acc1->CreateVertex();
     auto gid1 = vertex1.Gid();
     auto gid2 = vertex2.Gid();
 
-    ASSERT_NO_ERROR(vertex1.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex2.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(2)));
 
-    ASSERT_NO_ERROR(acc1.Commit());
+    ASSERT_NO_ERROR(acc1->Commit());
 
-    auto acc2 = storage.Access();
-    auto acc3 = storage.Access();
-    auto vertex3 = acc2.FindVertex(gid1, View::NEW);  // vertex3 == vertex1
-    auto vertex4 = acc3.FindVertex(gid2, View::NEW);  // vertex4 == vertex2
+    auto acc2 = this->storage->Access();
+    auto acc3 = this->storage->Access();
+    auto vertex3 = acc2->FindVertex(gid1, View::NEW);  // vertex3 == vertex1
+    auto vertex4 = acc3->FindVertex(gid2, View::NEW);  // vertex4 == vertex2
 
-    ASSERT_NO_ERROR(vertex3->SetProperty(prop1, PropertyValue(3)));
-    ASSERT_NO_ERROR(vertex4->SetProperty(prop1, PropertyValue(3)));
+    ASSERT_NO_ERROR(vertex3->SetProperty(this->prop1, PropertyValue(3)));
+    ASSERT_NO_ERROR(vertex4->SetProperty(this->prop1, PropertyValue(3)));
 
-    ASSERT_NO_ERROR(acc2.Commit());
-    auto res = acc3.Commit();
+    ASSERT_NO_ERROR(acc2->Commit());
+    auto res = acc3->Commit();
     ASSERT_TRUE(res.HasError());
-    EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1}}));
+    EXPECT_EQ(
+        std::get<ConstraintViolation>(res.GetError()),
+        (ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set<PropertyId>{this->prop1}}));
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsViolationOnCommit3) {
+/// TODO: andi consistency problems
+TYPED_TEST(ConstraintsTest, UniqueConstraintsViolationOnCommit3) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
@@ -519,46 +563,48 @@ TEST_F(ConstraintsTest, UniqueConstraintsViolationOnCommit3) {
     // tx2: -------------------------------B---SP(v1, 2)---FAIL--
     // tx3: --------------------------------B---SP(v2, 1)---FAIL-
 
-    auto acc1 = storage.Access();
-    auto vertex1 = acc1.CreateVertex();
-    auto vertex2 = acc1.CreateVertex();
+    auto acc1 = this->storage->Access();
+    auto vertex1 = acc1->CreateVertex();
+    auto vertex2 = acc1->CreateVertex();
     auto gid1 = vertex1.Gid();
     auto gid2 = vertex2.Gid();
 
-    ASSERT_NO_ERROR(vertex1.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex2.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(2)));
 
-    ASSERT_NO_ERROR(acc1.Commit());
+    ASSERT_NO_ERROR(acc1->Commit());
 
-    auto acc2 = storage.Access();
-    auto acc3 = storage.Access();
-    auto vertex3 = acc2.FindVertex(gid1, View::OLD);  // vertex3 == vertex1
-    auto vertex4 = acc3.FindVertex(gid2, View::OLD);  // vertex4 == vertex2
+    auto acc2 = this->storage->Access();
+    auto acc3 = this->storage->Access();
+    auto vertex3 = acc2->FindVertex(gid1, View::OLD);  // vertex3 == vertex1
+    auto vertex4 = acc3->FindVertex(gid2, View::OLD);  // vertex4 == vertex2
 
-    // Setting `prop2` shouldn't affect the remaining code.
-    ASSERT_NO_ERROR(vertex3->SetProperty(prop2, PropertyValue(3)));
-    ASSERT_NO_ERROR(vertex4->SetProperty(prop2, PropertyValue(3)));
+    // Setting `this->prop2` shouldn't affect the remaining code.
+    ASSERT_NO_ERROR(vertex3->SetProperty(this->prop2, PropertyValue(3)));
+    ASSERT_NO_ERROR(vertex4->SetProperty(this->prop2, PropertyValue(3)));
 
-    ASSERT_NO_ERROR(vertex3->SetProperty(prop1, PropertyValue(2)));
-    ASSERT_NO_ERROR(vertex4->SetProperty(prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex3->SetProperty(this->prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex4->SetProperty(this->prop1, PropertyValue(1)));
 
-    auto res = acc2.Commit();
+    auto res = acc2->Commit();
     ASSERT_TRUE(res.HasError());
-    EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1}}));
-    res = acc3.Commit();
+    EXPECT_EQ(
+        std::get<ConstraintViolation>(res.GetError()),
+        (ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set<PropertyId>{this->prop1}}));
+    res = acc3->Commit();
     ASSERT_TRUE(res.HasError());
-    EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1}}));
+    EXPECT_EQ(
+        std::get<ConstraintViolation>(res.GetError()),
+        (ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set<PropertyId>{this->prop1}}));
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsLabelAlteration) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsLabelAlteration) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
@@ -568,128 +614,132 @@ TEST_F(ConstraintsTest, UniqueConstraintsLabelAlteration) {
   {
     // B---AL(v2)---SP(v1, 1)---SP(v2, 1)---OK
 
-    auto acc = storage.Access();
-    auto vertex1 = acc.CreateVertex();
-    auto vertex2 = acc.CreateVertex();
+    auto acc = this->storage->Access();
+    auto vertex1 = acc->CreateVertex();
+    auto vertex2 = acc->CreateVertex();
     gid1 = vertex1.Gid();
     gid2 = vertex2.Gid();
 
-    ASSERT_NO_ERROR(vertex1.AddLabel(label2));
-    ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex2.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(1)));
+    spdlog::debug("Vertex1 gid: {} Vertex2 gid: {}\n", memgraph::utils::SerializeIdType(gid1),
+                  memgraph::utils::SerializeIdType(gid2));
 
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label2));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(1)));
+
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
     // tx1: B---AL(v1)-----OK-
     // tx2: -B---RL(v2)---OK--
 
-    auto acc1 = storage.Access();
-    auto acc2 = storage.Access();
-    auto vertex1 = acc1.FindVertex(gid1, View::OLD);
-    auto vertex2 = acc2.FindVertex(gid2, View::OLD);
+    auto acc1 = this->storage->Access();
+    auto acc2 = this->storage->Access();
+    auto vertex1 = acc1->FindVertex(gid1, View::OLD);
+    auto vertex2 = acc2->FindVertex(gid2, View::OLD);
 
-    ASSERT_NO_ERROR(vertex1->AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2->RemoveLabel(label1));
+    ASSERT_NO_ERROR(vertex1->AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2->RemoveLabel(this->label1));
 
     // Reapplying labels shouldn't affect the remaining code.
-    ASSERT_NO_ERROR(vertex1->RemoveLabel(label1));
-    ASSERT_NO_ERROR(vertex2->AddLabel(label1));
-    ASSERT_NO_ERROR(vertex1->AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2->RemoveLabel(label1));
-    ASSERT_NO_ERROR(vertex1->RemoveLabel(label2));
+    ASSERT_NO_ERROR(vertex1->RemoveLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2->AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1->AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2->RemoveLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1->RemoveLabel(this->label2));
 
     // Commit the second transaction.
-    ASSERT_NO_ERROR(acc2.Commit());
+    ASSERT_NO_ERROR(acc2->Commit());
 
     // Reapplying labels after first commit shouldn't affect the remaining code.
-    ASSERT_NO_ERROR(vertex1->RemoveLabel(label1));
-    ASSERT_NO_ERROR(vertex1->AddLabel(label1));
+    ASSERT_NO_ERROR(vertex1->RemoveLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1->AddLabel(this->label1));
 
     // Commit the first transaction.
-    ASSERT_NO_ERROR(acc1.Commit());
+    ASSERT_NO_ERROR(acc1->Commit());
   }
 
   {
     // B---AL(v2)---FAIL
 
-    auto acc = storage.Access();
-    auto vertex2 = acc.FindVertex(gid2, View::OLD);
-    ASSERT_NO_ERROR(vertex2->AddLabel(label1));
+    auto acc = this->storage->Access();
+    auto vertex2 = acc->FindVertex(gid2, View::OLD);
+    ASSERT_NO_ERROR(vertex2->AddLabel(this->label1));
 
-    auto res = acc.Commit();
+    auto res = acc->Commit();
     ASSERT_TRUE(res.HasError());
     EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set{prop1}}));
+              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set{this->prop1}}));
   }
 
   {
     // B---RL(v1)---OK
 
-    auto acc = storage.Access();
-    auto vertex1 = acc.FindVertex(gid1, View::OLD);
-    ASSERT_NO_ERROR(vertex1->RemoveLabel(label1));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex1 = acc->FindVertex(gid1, View::OLD);
+    ASSERT_NO_ERROR(vertex1->RemoveLabel(this->label1));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
     // tx1: B---AL(v1)-----FAIL
     // tx2: -B---AL(v2)---OK---
 
-    auto acc1 = storage.Access();
-    auto acc2 = storage.Access();
-    auto vertex1 = acc1.FindVertex(gid1, View::OLD);
-    auto vertex2 = acc2.FindVertex(gid2, View::OLD);
+    auto acc1 = this->storage->Access();
+    auto acc2 = this->storage->Access();
+    auto vertex1 = acc1->FindVertex(gid1, View::OLD);
+    auto vertex2 = acc2->FindVertex(gid2, View::OLD);
 
-    ASSERT_NO_ERROR(vertex1->AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2->AddLabel(label1));
+    ASSERT_NO_ERROR(vertex1->AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2->AddLabel(this->label1));
 
     // Reapply everything.
-    ASSERT_NO_ERROR(vertex1->RemoveLabel(label1));
-    ASSERT_NO_ERROR(vertex2->RemoveLabel(label1));
-    ASSERT_NO_ERROR(vertex1->AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2->AddLabel(label1));
+    ASSERT_NO_ERROR(vertex1->RemoveLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2->RemoveLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1->AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2->AddLabel(this->label1));
 
-    ASSERT_NO_ERROR(acc2.Commit());
+    ASSERT_NO_ERROR(acc2->Commit());
 
-    auto res = acc1.Commit();
+    auto res = acc1->Commit();
     ASSERT_TRUE(res.HasError());
     EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set{prop1}}));
+              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set{this->prop1}}));
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsPropertySetSize) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsPropertySetSize) {
   {
     // This should fail since unique constraint cannot be created for an empty
     // property set.
-    auto res = storage.CreateUniqueConstraint(label1, {});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::EMPTY_PROPERTIES);
   }
 
   // Removing a constraint with empty property set should also fail.
-  ASSERT_EQ(storage.DropUniqueConstraint(label1, {}).GetValue(), UniqueConstraints::DeletionStatus::EMPTY_PROPERTIES);
+  ASSERT_EQ(this->storage->DropUniqueConstraint(this->label1, {}, {}).GetValue(),
+            UniqueConstraints::DeletionStatus::EMPTY_PROPERTIES);
 
   // Create a set of 33 properties.
   std::set<PropertyId> properties;
   for (int i = 1; i <= 33; ++i) {
-    properties.insert(storage.NameToProperty("prop" + std::to_string(i)));
+    properties.insert(this->storage->NameToProperty("prop" + std::to_string(i)));
   }
 
   {
     // This should fail since list of properties exceeds the maximum number of
     // properties, which is 32.
-    auto res = storage.CreateUniqueConstraint(label1, properties);
+    auto res = this->storage->CreateUniqueConstraint(this->label1, properties, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED);
   }
 
   // An attempt to delete constraint with too large property set should fail.
-  ASSERT_EQ(storage.DropUniqueConstraint(label1, properties).GetValue(),
+  ASSERT_EQ(this->storage->DropUniqueConstraint(this->label1, properties, {}).GetValue(),
             UniqueConstraints::DeletionStatus::PROPERTIES_SIZE_LIMIT_EXCEEDED);
 
   // Remove one property from the set.
@@ -697,29 +747,32 @@ TEST_F(ConstraintsTest, UniqueConstraintsPropertySetSize) {
 
   {
     // Creating a constraint for 32 properties should succeed.
-    auto res = storage.CreateUniqueConstraint(label1, properties);
+    auto res = this->storage->CreateUniqueConstraint(this->label1, properties, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
 
-  EXPECT_THAT(storage.ListAllConstraints().unique, UnorderedElementsAre(std::make_pair(label1, properties)));
+  EXPECT_THAT(this->storage->ListAllConstraints().unique,
+              UnorderedElementsAre(std::make_pair(this->label1, properties)));
 
   // Removing a constraint with 32 properties should succeed.
-  ASSERT_EQ(storage.DropUniqueConstraint(label1, properties).GetValue(), UniqueConstraints::DeletionStatus::SUCCESS);
-  ASSERT_TRUE(storage.ListAllConstraints().unique.empty());
+  ASSERT_EQ(this->storage->DropUniqueConstraint(this->label1, properties, {}).GetValue(),
+            UniqueConstraints::DeletionStatus::SUCCESS);
+  ASSERT_TRUE(this->storage->ListAllConstraints().unique.empty());
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(ConstraintsTest, UniqueConstraintsMultipleProperties) {
+/// TODO: andi consistency problems
+TYPED_TEST(ConstraintsTest, UniqueConstraintsMultipleProperties) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
 
   {
     // An attempt to create an existing unique constraint.
-    auto res = storage.CreateUniqueConstraint(label1, {prop2, prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop2, this->prop1}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::ALREADY_EXISTS);
   }
@@ -727,151 +780,154 @@ TEST_F(ConstraintsTest, UniqueConstraintsMultipleProperties) {
   Gid gid1;
   Gid gid2;
   {
-    auto acc = storage.Access();
-    auto vertex1 = acc.CreateVertex();
-    auto vertex2 = acc.CreateVertex();
+    auto acc = this->storage->Access();
+    auto vertex1 = acc->CreateVertex();
+    auto vertex2 = acc->CreateVertex();
     gid1 = vertex1.Gid();
     gid2 = vertex2.Gid();
 
-    ASSERT_NO_ERROR(vertex1.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex1.SetProperty(prop2, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop2, PropertyValue(2)));
 
-    ASSERT_NO_ERROR(vertex2.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex2.SetProperty(prop2, PropertyValue(3)));
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop2, PropertyValue(3)));
 
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   // Try to change property of the second vertex so it becomes the same as the
-  // first vertex. It should fail.
+  // first vertex-> It should fail.
   {
-    auto acc = storage.Access();
-    auto vertex2 = acc.FindVertex(gid2, View::OLD);
-    ASSERT_NO_ERROR(vertex2->SetProperty(prop2, PropertyValue(2)));
-    auto res = acc.Commit();
+    auto acc = this->storage->Access();
+    auto vertex2 = acc->FindVertex(gid2, View::OLD);
+    ASSERT_NO_ERROR(vertex2->SetProperty(this->prop2, PropertyValue(2)));
+    auto res = acc->Commit();
     ASSERT_TRUE(res.HasError());
     EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set<PropertyId>{prop1, prop2}}));
+              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1,
+                                   std::set<PropertyId>{this->prop1, this->prop2}}));
   }
 
   // Then change the second property of both vertex to null. Property values of
   // both vertices should now be equal. However, this operation should succeed
   // since null value is treated as non-existing property.
   {
-    auto acc = storage.Access();
-    auto vertex1 = acc.FindVertex(gid1, View::OLD);
-    auto vertex2 = acc.FindVertex(gid2, View::OLD);
-    ASSERT_NO_ERROR(vertex1->SetProperty(prop2, PropertyValue()));
-    ASSERT_NO_ERROR(vertex2->SetProperty(prop2, PropertyValue()));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex1 = acc->FindVertex(gid1, View::OLD);
+    auto vertex2 = acc->FindVertex(gid2, View::OLD);
+    ASSERT_NO_ERROR(vertex1->SetProperty(this->prop2, PropertyValue()));
+    ASSERT_NO_ERROR(vertex2->SetProperty(this->prop2, PropertyValue()));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 }
 
-TEST_F(ConstraintsTest, UniqueConstraintsInsertAbortInsert) {
+/// TODO: andi Test passes when ran alone but fails when all tests are run
+TYPED_TEST(ConstraintsTest, UniqueConstraintsInsertAbortInsert) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
-    acc.Abort();
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
+    acc->Abort();
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 }
 
-TEST_F(ConstraintsTest, UniqueConstraintsInsertRemoveInsert) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsInsertRemoveInsert) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
 
   Gid gid;
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.FindVertex(gid, View::OLD);
-    ASSERT_NO_ERROR(acc.DeleteVertex(&*vertex));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex = acc->FindVertex(gid, View::OLD);
+    ASSERT_NO_ERROR(acc->DeleteVertex(&*vertex));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 }
 
-TEST_F(ConstraintsTest, UniqueConstraintsInsertRemoveAbortInsert) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsInsertRemoveAbortInsert) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
 
   Gid gid;
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(2)));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(1)));
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(1)));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.FindVertex(gid, View::OLD);
-    ASSERT_NO_ERROR(acc.DeleteVertex(&*vertex));
-    acc.Abort();
+    auto acc = this->storage->Access();
+    auto vertex = acc->FindVertex(gid, View::OLD);
+    ASSERT_NO_ERROR(acc->DeleteVertex(&*vertex));
+    acc->Abort();
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(2)));
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(2)));
 
-    auto res = acc.Commit();
+    auto res = acc->Commit();
     ASSERT_TRUE(res.HasError());
-    EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set{prop1, prop2}}));
+    EXPECT_EQ(
+        std::get<ConstraintViolation>(res.GetError()),
+        (ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set{this->prop1, this->prop2}}));
   }
 }
 
-TEST_F(ConstraintsTest, UniqueConstraintsDeleteVertexSetProperty) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsDeleteVertexSetProperty) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
@@ -879,101 +935,140 @@ TEST_F(ConstraintsTest, UniqueConstraintsDeleteVertexSetProperty) {
   Gid gid1;
   Gid gid2;
   {
-    auto acc = storage.Access();
-    auto vertex1 = acc.CreateVertex();
-    auto vertex2 = acc.CreateVertex();
+    auto acc = this->storage->Access();
+    auto vertex1 = acc->CreateVertex();
+    auto vertex2 = acc->CreateVertex();
     gid1 = vertex1.Gid();
     gid2 = vertex2.Gid();
 
-    ASSERT_NO_ERROR(vertex1.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex2.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex1.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex2.SetProperty(prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop1, PropertyValue(2)));
 
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto acc1 = storage.Access();
-    auto acc2 = storage.Access();
-    auto vertex1 = acc1.FindVertex(gid1, View::OLD);
-    auto vertex2 = acc2.FindVertex(gid2, View::OLD);
+    auto acc1 = this->storage->Access();
+    auto acc2 = this->storage->Access();
+    auto vertex1 = acc1->FindVertex(gid1, View::OLD);
+    auto vertex2 = acc2->FindVertex(gid2, View::OLD);
 
-    ASSERT_NO_ERROR(acc2.DeleteVertex(&*vertex2));
-    ASSERT_NO_ERROR(vertex1->SetProperty(prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(acc2->DeleteVertex(&*vertex2));
+    ASSERT_NO_ERROR(vertex1->SetProperty(this->prop1, PropertyValue(2)));
 
-    auto res = acc1.Commit();
+    auto res = acc1->Commit();
     ASSERT_TRUE(res.HasError());
     EXPECT_EQ(std::get<ConstraintViolation>(res.GetError()),
-              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, label1, std::set{prop1}}));
+              (ConstraintViolation{ConstraintViolation::Type::UNIQUE, this->label1, std::set{this->prop1}}));
 
-    ASSERT_NO_ERROR(acc2.Commit());
+    ASSERT_NO_ERROR(acc2->Commit());
   }
 }
 
-TEST_F(ConstraintsTest, UniqueConstraintsInsertDropInsert) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsInsertDropInsert) {
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
-  ASSERT_EQ(storage.DropUniqueConstraint(label1, {prop2, prop1}).GetValue(),
+  ASSERT_EQ(this->storage->DropUniqueConstraint(this->label1, {this->prop2, this->prop1}, {}).GetValue(),
             UniqueConstraints::DeletionStatus::SUCCESS);
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 }
 
-TEST_F(ConstraintsTest, UniqueConstraintsComparePropertyValues) {
+TYPED_TEST(ConstraintsTest, UniqueConstraintsComparePropertyValues) {
   // Purpose of this test is to make sure that extracted property values
   // are correctly compared.
 
   {
-    auto res = storage.CreateUniqueConstraint(label1, {prop1, prop2});
+    auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1, this->prop2}, {});
     ASSERT_TRUE(res.HasValue());
     ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(2)));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(1)));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(1)));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(1)));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(2)));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(1)));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(2)));
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto acc = storage.Access();
-    auto vertex = acc.CreateVertex();
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop2, PropertyValue(0)));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop1, PropertyValue(3)));
-    ASSERT_NO_ERROR(acc.Commit());
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop2, PropertyValue(0)));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(3)));
+    ASSERT_NO_ERROR(acc->Commit());
+  }
+}
+
+TYPED_TEST(ConstraintsTest, UniqueConstraintsClearOldData) {
+  // Purpose of this test is to make sure that extracted property values
+  // are correctly compared.
+
+  if constexpr ((std::is_same_v<TypeParam, memgraph::storage::DiskStorage>)) {
+    auto *disk_constraints =
+        static_cast<memgraph::storage::DiskUniqueConstraints *>(this->storage->constraints_.unique_constraints_.get());
+    auto *tx_db = disk_constraints->GetRocksDBStorage()->db_;
+
+    {
+      auto res = this->storage->CreateUniqueConstraint(this->label1, {this->prop1}, {});
+      ASSERT_TRUE(res.HasValue());
+      ASSERT_EQ(res.GetValue(), UniqueConstraints::CreationStatus::SUCCESS);
+    }
+
+    auto acc = this->storage->Access();
+    auto vertex = acc->CreateVertex();
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop1, PropertyValue(2)));
+    ASSERT_NO_ERROR(acc->Commit());
+
+    ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+
+    auto acc2 = this->storage->Access(std::nullopt);
+    auto vertex2 = acc2->FindVertex(vertex.Gid(), memgraph::storage::View::NEW).value();
+    ASSERT_TRUE(vertex2.SetProperty(this->prop1, memgraph::storage::PropertyValue(2)).HasValue());
+    ASSERT_FALSE(acc2->Commit().HasError());
+
+    ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+
+    auto acc3 = this->storage->Access(std::nullopt);
+    auto vertex3 = acc3->FindVertex(vertex.Gid(), memgraph::storage::View::NEW).value();
+    ASSERT_TRUE(vertex3.SetProperty(this->prop1, memgraph::storage::PropertyValue(10)).HasValue());
+    ASSERT_FALSE(acc3->Commit().HasError());
+
+    ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
   }
 }
diff --git a/tests/unit/storage_v2_disk.cpp b/tests/unit/storage_v2_disk.cpp
new file mode 100644
index 000000000..eefc45af9
--- /dev/null
+++ b/tests/unit/storage_v2_disk.cpp
@@ -0,0 +1,29 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include <gtest/gtest.h>
+
+#include "disk_test_utils.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
+#include "utils/file.hpp"
+
+class DiskStorageTest : public ::testing::TestWithParam<bool> {};
+
+TEST_F(DiskStorageTest, CreateDiskStorageInDataDirectory) {
+  const std::string testSuite = "storage_v2_disk";
+
+  memgraph::storage::Config config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  auto storage = std::make_unique<memgraph::storage::DiskStorage>(config);
+  ASSERT_TRUE(memgraph::utils::DirExists(config.disk.main_storage_directory));
+
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
diff --git a/tests/unit/storage_v2_durability.cpp b/tests/unit/storage_v2_durability_inmemory.cpp
similarity index 70%
rename from tests/unit/storage_v2_durability.cpp
rename to tests/unit/storage_v2_durability_inmemory.cpp
index 251f4a311..68e478045 100644
--- a/tests/unit/storage_v2_durability.cpp
+++ b/tests/unit/storage_v2_durability_inmemory.cpp
@@ -22,10 +22,14 @@
 #include <iostream>
 #include <thread>
 
+#include "storage/v2/durability/marker.hpp"
 #include "storage/v2/durability/paths.hpp"
 #include "storage/v2/durability/snapshot.hpp"
 #include "storage/v2/durability/version.hpp"
-#include "storage/v2/storage.hpp"
+#include "storage/v2/durability/wal.hpp"
+#include "storage/v2/edge_accessor.hpp"
+#include "storage/v2/inmemory/storage.hpp"
+#include "storage/v2/vertex_accessor.hpp"
 #include "utils/file.hpp"
 #include "utils/logging.hpp"
 #include "utils/timer.hpp"
@@ -80,15 +84,15 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
     ASSERT_FALSE(store->CreateIndex(label_indexed, property_id).HasError());
 
     // Create existence constraint.
-    ASSERT_FALSE(store->CreateExistenceConstraint(label_unindexed, property_id).HasError());
+    ASSERT_FALSE(store->CreateExistenceConstraint(label_unindexed, property_id, {}).HasError());
 
     // Create unique constraint.
-    ASSERT_FALSE(store->CreateUniqueConstraint(label_unindexed, {property_id, property_extra}).HasError());
+    ASSERT_FALSE(store->CreateUniqueConstraint(label_unindexed, {property_id, property_extra}, {}).HasError());
 
     // Create vertices.
     for (uint64_t i = 0; i < kNumBaseVertices; ++i) {
       auto acc = store->Access();
-      auto vertex = acc.CreateVertex();
+      auto vertex = acc->CreateVertex();
       base_vertex_gids_[i] = vertex.Gid();
       if (i < kNumBaseVertices / 2) {
         ASSERT_TRUE(vertex.AddLabel(label_indexed).HasValue());
@@ -99,15 +103,15 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
         ASSERT_TRUE(
             vertex.SetProperty(property_id, memgraph::storage::PropertyValue(static_cast<int64_t>(i))).HasValue());
       }
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
 
     // Create edges.
     for (uint64_t i = 0; i < kNumBaseEdges; ++i) {
       auto acc = store->Access();
-      auto vertex1 = acc.FindVertex(base_vertex_gids_[(i / 2) % kNumBaseVertices], memgraph::storage::View::OLD);
+      auto vertex1 = acc->FindVertex(base_vertex_gids_[(i / 2) % kNumBaseVertices], memgraph::storage::View::OLD);
       ASSERT_TRUE(vertex1);
-      auto vertex2 = acc.FindVertex(base_vertex_gids_[(i / 3) % kNumBaseVertices], memgraph::storage::View::OLD);
+      auto vertex2 = acc->FindVertex(base_vertex_gids_[(i / 3) % kNumBaseVertices], memgraph::storage::View::OLD);
       ASSERT_TRUE(vertex2);
       memgraph::storage::EdgeTypeId et;
       if (i < kNumBaseEdges / 2) {
@@ -115,18 +119,19 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
       } else {
         et = et2;
       }
-      auto edge = acc.CreateEdge(&*vertex1, &*vertex2, et);
-      ASSERT_TRUE(edge.HasValue());
-      base_edge_gids_[i] = edge->Gid();
+      auto edgeRes = acc->CreateEdge(&*vertex1, &*vertex2, et);
+      ASSERT_TRUE(edgeRes.HasValue());
+      auto edge = std::move(edgeRes.GetValue());
+      base_edge_gids_[i] = edge.Gid();
       if (properties_on_edges) {
         ASSERT_TRUE(
-            edge->SetProperty(property_id, memgraph::storage::PropertyValue(static_cast<int64_t>(i))).HasValue());
+            edge.SetProperty(property_id, memgraph::storage::PropertyValue(static_cast<int64_t>(i))).HasValue());
       } else {
-        auto ret = edge->SetProperty(property_id, memgraph::storage::PropertyValue(static_cast<int64_t>(i)));
+        auto ret = edge.SetProperty(property_id, memgraph::storage::PropertyValue(static_cast<int64_t>(i)));
         ASSERT_TRUE(ret.HasError());
         ASSERT_EQ(ret.GetError(), memgraph::storage::Error::PROPERTIES_DISABLED);
       }
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
   }
 
@@ -144,18 +149,18 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
     ASSERT_FALSE(store->CreateIndex(label_indexed, property_count).HasError());
 
     // Create existence constraint.
-    ASSERT_FALSE(store->CreateExistenceConstraint(label_unused, property_count).HasError());
+    ASSERT_FALSE(store->CreateExistenceConstraint(label_unused, property_count, {}).HasError());
 
     // Create unique constraint.
-    ASSERT_FALSE(store->CreateUniqueConstraint(label_unused, {property_count}).HasError());
+    ASSERT_FALSE(store->CreateUniqueConstraint(label_unused, {property_count}, {}).HasError());
 
     // Storage accessor.
-    std::optional<memgraph::storage::Storage::Accessor> acc;
-    if (single_transaction) acc.emplace(store->Access());
+    std::unique_ptr<memgraph::storage::Storage::Accessor> acc;
+    if (single_transaction) acc = store->Access();
 
     // Create vertices.
     for (uint64_t i = 0; i < kNumExtendedVertices; ++i) {
-      if (!single_transaction) acc.emplace(store->Access());
+      if (!single_transaction) acc = store->Access();
       auto vertex = acc->CreateVertex();
       extended_vertex_gids_[i] = vertex.Gid();
       if (i < kNumExtendedVertices / 2) {
@@ -169,7 +174,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
 
     // Create edges.
     for (uint64_t i = 0; i < kNumExtendedEdges; ++i) {
-      if (!single_transaction) acc.emplace(store->Access());
+      if (!single_transaction) acc = store->Access();
       auto vertex1 =
           acc->FindVertex(extended_vertex_gids_[(i / 5) % kNumExtendedVertices], memgraph::storage::View::NEW);
       ASSERT_TRUE(vertex1);
@@ -182,9 +187,10 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
       } else {
         et = et4;
       }
-      auto edge = acc->CreateEdge(&*vertex1, &*vertex2, et);
-      ASSERT_TRUE(edge.HasValue());
-      extended_edge_gids_[i] = edge->Gid();
+      auto edgeRes = acc->CreateEdge(&*vertex1, &*vertex2, et);
+      ASSERT_TRUE(edgeRes.HasValue());
+      auto edge = std::move(edgeRes.GetValue());
+      extended_edge_gids_[i] = edge.Gid();
       if (!single_transaction) ASSERT_FALSE(acc->Commit().HasError());
     }
 
@@ -281,7 +287,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
     if (have_base_dataset) {
       // Verify vertices.
       for (uint64_t i = 0; i < kNumBaseVertices; ++i) {
-        auto vertex = acc.FindVertex(base_vertex_gids_[i], memgraph::storage::View::OLD);
+        auto vertex = acc->FindVertex(base_vertex_gids_[i], memgraph::storage::View::OLD);
         ASSERT_TRUE(vertex);
         auto labels = vertex->Labels(memgraph::storage::View::OLD);
         ASSERT_TRUE(labels.HasValue());
@@ -302,17 +308,17 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
 
       // Verify edges.
       for (uint64_t i = 0; i < kNumBaseEdges; ++i) {
-        auto find_edge = [&](const auto &edges) -> std::optional<memgraph::storage::EdgeAccessor> {
-          for (const auto &edge : edges) {
+        auto find_edge = [&](auto &edges) -> std::optional<memgraph::storage::EdgeAccessor> {
+          for (auto &edge : edges) {
             if (edge.Gid() == base_edge_gids_[i]) {
               return edge;
             }
           }
-          return std::nullopt;
+          return {};
         };
 
         {
-          auto vertex1 = acc.FindVertex(base_vertex_gids_[(i / 2) % kNumBaseVertices], memgraph::storage::View::OLD);
+          auto vertex1 = acc->FindVertex(base_vertex_gids_[(i / 2) % kNumBaseVertices], memgraph::storage::View::OLD);
           ASSERT_TRUE(vertex1);
           auto out_edges = vertex1->OutEdges(memgraph::storage::View::OLD);
           ASSERT_TRUE(out_edges.HasValue());
@@ -334,7 +340,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
         }
 
         {
-          auto vertex2 = acc.FindVertex(base_vertex_gids_[(i / 3) % kNumBaseVertices], memgraph::storage::View::OLD);
+          auto vertex2 = acc->FindVertex(base_vertex_gids_[(i / 3) % kNumBaseVertices], memgraph::storage::View::OLD);
           ASSERT_TRUE(vertex2);
           auto in_edges = vertex2->InEdges(memgraph::storage::View::OLD);
           ASSERT_TRUE(in_edges.HasValue());
@@ -360,7 +366,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
       {
         std::vector<memgraph::storage::VertexAccessor> vertices;
         vertices.reserve(kNumBaseVertices / 2);
-        for (auto vertex : acc.Vertices(base_label_unindexed, memgraph::storage::View::OLD)) {
+        for (auto vertex : acc->Vertices(base_label_unindexed, memgraph::storage::View::OLD)) {
           vertices.push_back(vertex);
         }
         ASSERT_EQ(vertices.size(), kNumBaseVertices / 2);
@@ -374,7 +380,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
       {
         std::vector<memgraph::storage::VertexAccessor> vertices;
         vertices.reserve(kNumBaseVertices / 3);
-        for (auto vertex : acc.Vertices(base_label_indexed, property_id, memgraph::storage::View::OLD)) {
+        for (auto vertex : acc->Vertices(base_label_indexed, property_id, memgraph::storage::View::OLD)) {
           vertices.push_back(vertex);
         }
         ASSERT_EQ(vertices.size(), kNumBaseVertices / 3);
@@ -386,7 +392,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
     } else {
       // Verify vertices.
       for (uint64_t i = 0; i < kNumBaseVertices; ++i) {
-        auto vertex = acc.FindVertex(base_vertex_gids_[i], memgraph::storage::View::OLD);
+        auto vertex = acc->FindVertex(base_vertex_gids_[i], memgraph::storage::View::OLD);
         ASSERT_FALSE(vertex);
       }
 
@@ -394,7 +400,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
         // Verify label indices.
         {
           uint64_t count = 0;
-          auto iterable = acc.Vertices(base_label_unindexed, memgraph::storage::View::OLD);
+          auto iterable = acc->Vertices(base_label_unindexed, memgraph::storage::View::OLD);
           for (auto it = iterable.begin(); it != iterable.end(); ++it) {
             ++count;
           }
@@ -404,7 +410,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
         // Verify label+property index.
         {
           uint64_t count = 0;
-          auto iterable = acc.Vertices(base_label_indexed, property_id, memgraph::storage::View::OLD);
+          auto iterable = acc->Vertices(base_label_indexed, property_id, memgraph::storage::View::OLD);
           for (auto it = iterable.begin(); it != iterable.end(); ++it) {
             ++count;
           }
@@ -417,7 +423,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
     if (have_extended_dataset) {
       // Verify vertices.
       for (uint64_t i = 0; i < kNumExtendedVertices; ++i) {
-        auto vertex = acc.FindVertex(extended_vertex_gids_[i], memgraph::storage::View::OLD);
+        auto vertex = acc->FindVertex(extended_vertex_gids_[i], memgraph::storage::View::OLD);
         ASSERT_TRUE(vertex);
         auto labels = vertex->Labels(memgraph::storage::View::OLD);
         ASSERT_TRUE(labels.HasValue());
@@ -436,18 +442,18 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
 
       // Verify edges.
       for (uint64_t i = 0; i < kNumExtendedEdges; ++i) {
-        auto find_edge = [&](const auto &edges) -> std::optional<memgraph::storage::EdgeAccessor> {
-          for (const auto &edge : edges) {
+        auto find_edge = [&](auto &edges) -> std::optional<memgraph::storage::EdgeAccessor> {
+          for (auto &edge : edges) {
             if (edge.Gid() == extended_edge_gids_[i]) {
               return edge;
             }
           }
-          return std::nullopt;
+          return {};
         };
 
         {
           auto vertex1 =
-              acc.FindVertex(extended_vertex_gids_[(i / 5) % kNumExtendedVertices], memgraph::storage::View::OLD);
+              acc->FindVertex(extended_vertex_gids_[(i / 5) % kNumExtendedVertices], memgraph::storage::View::OLD);
           ASSERT_TRUE(vertex1);
           auto out_edges = vertex1->OutEdges(memgraph::storage::View::OLD);
           ASSERT_TRUE(out_edges.HasValue());
@@ -465,7 +471,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
 
         {
           auto vertex2 =
-              acc.FindVertex(extended_vertex_gids_[(i / 6) % kNumExtendedVertices], memgraph::storage::View::OLD);
+              acc->FindVertex(extended_vertex_gids_[(i / 6) % kNumExtendedVertices], memgraph::storage::View::OLD);
           ASSERT_TRUE(vertex2);
           auto in_edges = vertex2->InEdges(memgraph::storage::View::OLD);
           ASSERT_TRUE(in_edges.HasValue());
@@ -486,8 +492,8 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
       {
         std::vector<memgraph::storage::VertexAccessor> vertices;
         vertices.reserve(kNumExtendedVertices / 2);
-        for (auto vertex : acc.Vertices(extended_label_unused, memgraph::storage::View::OLD)) {
-          vertices.push_back(vertex);
+        for (auto vertex : acc->Vertices(extended_label_unused, memgraph::storage::View::OLD)) {
+          vertices.emplace_back(vertex);
         }
         ASSERT_EQ(vertices.size(), 0);
       }
@@ -496,8 +502,8 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
       {
         std::vector<memgraph::storage::VertexAccessor> vertices;
         vertices.reserve(kNumExtendedVertices / 3);
-        for (auto vertex : acc.Vertices(extended_label_indexed, property_count, memgraph::storage::View::OLD)) {
-          vertices.push_back(vertex);
+        for (auto vertex : acc->Vertices(extended_label_indexed, property_count, memgraph::storage::View::OLD)) {
+          vertices.emplace_back(vertex);
         }
         ASSERT_EQ(vertices.size(), kNumExtendedVertices / 3);
         std::sort(vertices.begin(), vertices.end(), [](const auto &a, const auto &b) { return a.Gid() < b.Gid(); });
@@ -508,7 +514,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
     } else {
       // Verify vertices.
       for (uint64_t i = 0; i < kNumExtendedVertices; ++i) {
-        auto vertex = acc.FindVertex(extended_vertex_gids_[i], memgraph::storage::View::OLD);
+        auto vertex = acc->FindVertex(extended_vertex_gids_[i], memgraph::storage::View::OLD);
         ASSERT_FALSE(vertex);
       }
 
@@ -516,7 +522,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
         // Verify label indices.
         {
           uint64_t count = 0;
-          auto iterable = acc.Vertices(extended_label_unused, memgraph::storage::View::OLD);
+          auto iterable = acc->Vertices(extended_label_unused, memgraph::storage::View::OLD);
           for (auto it = iterable.begin(); it != iterable.end(); ++it) {
             ++count;
           }
@@ -526,7 +532,7 @@ class DurabilityTest : public ::testing::TestWithParam<bool> {
         // Verify label+property index.
         {
           uint64_t count = 0;
-          auto iterable = acc.Vertices(extended_label_indexed, property_count, memgraph::storage::View::OLD);
+          auto iterable = acc->Vertices(extended_label_indexed, property_count, memgraph::storage::View::OLD);
           for (auto it = iterable.begin(); it != iterable.end(); ++it) {
             ++count;
           }
@@ -663,13 +669,13 @@ INSTANTIATE_TEST_CASE_P(EdgesWithoutProperties, DurabilityTest, ::testing::Value
 TEST_P(DurabilityTest, SnapshotOnExit) {
   // Create snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
-         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}});
-    CreateBaseDataset(&store, GetParam());
-    VerifyDataset(&store, DatasetType::ONLY_BASE, GetParam());
-    CreateExtendedDataset(&store);
-    VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, GetParam());
+         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}}));
+    CreateBaseDataset(store.get(), GetParam());
+    VerifyDataset(store.get(), DatasetType::ONLY_BASE, GetParam());
+    CreateExtendedDataset(store.get());
+    VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, GetParam());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 1);
@@ -678,18 +684,18 @@ TEST_P(DurabilityTest, SnapshotOnExit) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover snapshot.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, GetParam());
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, GetParam());
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -697,12 +703,12 @@ TEST_P(DurabilityTest, SnapshotOnExit) {
 TEST_P(DurabilityTest, SnapshotPeriodic) {
   // Create snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {.storage_directory = storage_directory,
                         .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT,
-                        .snapshot_interval = std::chrono::milliseconds(2000)}});
-    CreateBaseDataset(&store, GetParam());
+                        .snapshot_interval = std::chrono::milliseconds(2000)}}));
+    CreateBaseDataset(store.get(), GetParam());
     std::this_thread::sleep_for(std::chrono::milliseconds(2500));
   }
 
@@ -712,18 +718,18 @@ TEST_P(DurabilityTest, SnapshotPeriodic) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover snapshot.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::ONLY_BASE, GetParam());
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::ONLY_BASE, GetParam());
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -731,15 +737,15 @@ TEST_P(DurabilityTest, SnapshotPeriodic) {
 TEST_P(DurabilityTest, SnapshotFallback) {
   // Create snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {.storage_directory = storage_directory,
                         .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT,
-                        .snapshot_interval = std::chrono::milliseconds(3000)}});
-    CreateBaseDataset(&store, GetParam());
+                        .snapshot_interval = std::chrono::milliseconds(3000)}}));
+    CreateBaseDataset(store.get(), GetParam());
     std::this_thread::sleep_for(std::chrono::milliseconds(3500));
     ASSERT_EQ(GetSnapshotsList().size(), 1);
-    CreateExtendedDataset(&store);
+    CreateExtendedDataset(store.get());
     std::this_thread::sleep_for(std::chrono::milliseconds(3000));
   }
 
@@ -756,18 +762,18 @@ TEST_P(DurabilityTest, SnapshotFallback) {
   }
 
   // Recover snapshot.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::ONLY_BASE, GetParam());
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::ONLY_BASE, GetParam());
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -775,14 +781,14 @@ TEST_P(DurabilityTest, SnapshotFallback) {
 TEST_P(DurabilityTest, SnapshotEverythingCorrupt) {
   // Create unrelated snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
-         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}});
-    auto acc = store.Access();
+         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}}));
+    auto acc = store->Access();
     for (uint64_t i = 0; i < 1000; ++i) {
-      acc.CreateVertex();
+      acc->CreateVertex();
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 1);
@@ -801,14 +807,14 @@ TEST_P(DurabilityTest, SnapshotEverythingCorrupt) {
 
   // Create snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {.storage_directory = storage_directory,
                         .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT,
-                        .snapshot_interval = std::chrono::milliseconds(2000)}});
-    CreateBaseDataset(&store, GetParam());
+                        .snapshot_interval = std::chrono::milliseconds(2000)}}));
+    CreateBaseDataset(store.get(), GetParam());
     std::this_thread::sleep_for(std::chrono::milliseconds(2500));
-    CreateExtendedDataset(&store);
+    CreateExtendedDataset(store.get());
     std::this_thread::sleep_for(std::chrono::milliseconds(2500));
   }
 
@@ -842,9 +848,9 @@ TEST_P(DurabilityTest, SnapshotEverythingCorrupt) {
   // Recover snapshot.
   ASSERT_DEATH(
       {
-        memgraph::storage::Storage store(
+        std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
             {.items = {.properties_on_edges = GetParam()},
-             .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
+             .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
       },
       "");
 }
@@ -853,14 +859,14 @@ TEST_P(DurabilityTest, SnapshotEverythingCorrupt) {
 TEST_P(DurabilityTest, SnapshotRetention) {
   // Create unrelated snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
-         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}});
-    auto acc = store.Access();
+         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}}));
+    auto acc = store->Access();
     for (uint64_t i = 0; i < 1000; ++i) {
-      acc.CreateVertex();
+      acc->CreateVertex();
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   ASSERT_GE(GetSnapshotsList().size(), 1);
@@ -870,15 +876,15 @@ TEST_P(DurabilityTest, SnapshotRetention) {
 
   // Create snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {.storage_directory = storage_directory,
                         .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT,
                         .snapshot_interval = std::chrono::milliseconds(2000),
-                        .snapshot_retention_count = 3}});
+                        .snapshot_retention_count = 3}}));
     // Restore unrelated snapshots after the database has been started.
     RestoreBackups();
-    CreateBaseDataset(&store, GetParam());
+    CreateBaseDataset(store.get(), GetParam());
     // Allow approximately 5 snapshots to be created.
     std::this_thread::sleep_for(std::chrono::milliseconds(10000));
   }
@@ -907,18 +913,18 @@ TEST_P(DurabilityTest, SnapshotRetention) {
   }
 
   // Recover snapshot.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::ONLY_BASE, GetParam());
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::ONLY_BASE, GetParam());
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -926,13 +932,13 @@ TEST_P(DurabilityTest, SnapshotRetention) {
 TEST_P(DurabilityTest, SnapshotMixedUUID) {
   // Create snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
-         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}});
-    CreateBaseDataset(&store, GetParam());
-    VerifyDataset(&store, DatasetType::ONLY_BASE, GetParam());
-    CreateExtendedDataset(&store);
-    VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, GetParam());
+         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}}));
+    CreateBaseDataset(store.get(), GetParam());
+    VerifyDataset(store.get(), DatasetType::ONLY_BASE, GetParam());
+    CreateExtendedDataset(store.get());
+    VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, GetParam());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 1);
@@ -942,19 +948,19 @@ TEST_P(DurabilityTest, SnapshotMixedUUID) {
 
   // Recover snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
-         .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-    VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, GetParam());
+         .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+    VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, GetParam());
   }
 
   // Create another snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
-         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}});
-    CreateBaseDataset(&store, GetParam());
-    VerifyDataset(&store, DatasetType::ONLY_BASE, GetParam());
+         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}}));
+    CreateBaseDataset(store.get(), GetParam());
+    VerifyDataset(store.get(), DatasetType::ONLY_BASE, GetParam());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 1);
@@ -971,18 +977,18 @@ TEST_P(DurabilityTest, SnapshotMixedUUID) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover snapshot.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::ONLY_BASE, GetParam());
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::ONLY_BASE, GetParam());
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -990,14 +996,14 @@ TEST_P(DurabilityTest, SnapshotMixedUUID) {
 TEST_P(DurabilityTest, SnapshotBackup) {
   // Create snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
-         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}});
-    auto acc = store.Access();
+         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}}));
+    auto acc = store->Access();
     for (uint64_t i = 0; i < 1000; ++i) {
-      acc.CreateVertex();
+      acc->CreateVertex();
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 1);
@@ -1007,11 +1013,11 @@ TEST_P(DurabilityTest, SnapshotBackup) {
 
   // Start storage without recovery.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {.storage_directory = storage_directory,
                         .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT,
-                        .snapshot_interval = std::chrono::minutes(20)}});
+                        .snapshot_interval = std::chrono::minutes(20)}}));
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1024,13 +1030,13 @@ TEST_P(DurabilityTest, SnapshotBackup) {
 TEST_F(DurabilityTest, SnapshotWithoutPropertiesOnEdgesRecoveryWithPropertiesOnEdges) {
   // Create snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = false},
-         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}});
-    CreateBaseDataset(&store, false);
-    VerifyDataset(&store, DatasetType::ONLY_BASE, false);
-    CreateExtendedDataset(&store);
-    VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, false);
+         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}}));
+    CreateBaseDataset(store.get(), false);
+    VerifyDataset(store.get(), DatasetType::ONLY_BASE, false);
+    CreateExtendedDataset(store.get());
+    VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, false);
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 1);
@@ -1039,18 +1045,18 @@ TEST_F(DurabilityTest, SnapshotWithoutPropertiesOnEdgesRecoveryWithPropertiesOnE
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover snapshot.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = true},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, false);
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, false);
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -1058,13 +1064,13 @@ TEST_F(DurabilityTest, SnapshotWithoutPropertiesOnEdgesRecoveryWithPropertiesOnE
 TEST_F(DurabilityTest, SnapshotWithPropertiesOnEdgesRecoveryWithoutPropertiesOnEdges) {
   // Create snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = true},
-         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}});
-    CreateBaseDataset(&store, true);
-    VerifyDataset(&store, DatasetType::ONLY_BASE, true);
-    CreateExtendedDataset(&store);
-    VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, true);
+         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}}));
+    CreateBaseDataset(store.get(), true);
+    VerifyDataset(store.get(), DatasetType::ONLY_BASE, true);
+    CreateExtendedDataset(store.get());
+    VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, true);
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 1);
@@ -1075,9 +1081,9 @@ TEST_F(DurabilityTest, SnapshotWithPropertiesOnEdgesRecoveryWithoutPropertiesOnE
   // Recover snapshot.
   ASSERT_DEATH(
       {
-        memgraph::storage::Storage store(
+        std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
             {.items = {.properties_on_edges = false},
-             .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
+             .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
       },
       "");
 }
@@ -1086,20 +1092,20 @@ TEST_F(DurabilityTest, SnapshotWithPropertiesOnEdgesRecoveryWithoutPropertiesOnE
 TEST_F(DurabilityTest, SnapshotWithPropertiesOnEdgesButUnusedRecoveryWithoutPropertiesOnEdges) {
   // Create snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = true},
-         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}});
-    CreateBaseDataset(&store, true);
-    VerifyDataset(&store, DatasetType::ONLY_BASE, true);
-    CreateExtendedDataset(&store);
-    VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, true);
+         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}}));
+    CreateBaseDataset(store.get(), true);
+    VerifyDataset(store.get(), DatasetType::ONLY_BASE, true);
+    CreateExtendedDataset(store.get());
+    VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, true);
     // Remove properties from edges.
     {
-      auto acc = store.Access();
-      for (auto vertex : acc.Vertices(memgraph::storage::View::OLD)) {
+      auto acc = store->Access();
+      for (auto vertex : acc->Vertices(memgraph::storage::View::OLD)) {
         auto in_edges = vertex.InEdges(memgraph::storage::View::OLD);
         ASSERT_TRUE(in_edges.HasValue());
-        for (auto edge : *in_edges) {
+        for (auto &edge : *in_edges) {
           // TODO (mferencevic): Replace with `ClearProperties()`
           auto props = edge.Properties(memgraph::storage::View::NEW);
           ASSERT_TRUE(props.HasValue());
@@ -1109,7 +1115,7 @@ TEST_F(DurabilityTest, SnapshotWithPropertiesOnEdgesButUnusedRecoveryWithoutProp
         }
         auto out_edges = vertex.InEdges(memgraph::storage::View::OLD);
         ASSERT_TRUE(out_edges.HasValue());
-        for (auto edge : *out_edges) {
+        for (auto &edge : *out_edges) {
           // TODO (mferencevic): Replace with `ClearProperties()`
           auto props = edge.Properties(memgraph::storage::View::NEW);
           ASSERT_TRUE(props.HasValue());
@@ -1118,7 +1124,7 @@ TEST_F(DurabilityTest, SnapshotWithPropertiesOnEdgesButUnusedRecoveryWithoutProp
           }
         }
       }
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
   }
 
@@ -1128,18 +1134,18 @@ TEST_F(DurabilityTest, SnapshotWithPropertiesOnEdgesButUnusedRecoveryWithoutProp
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover snapshot.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = false},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, false);
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, false);
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -1147,15 +1153,15 @@ TEST_F(DurabilityTest, SnapshotWithPropertiesOnEdgesButUnusedRecoveryWithoutProp
 TEST_P(DurabilityTest, WalBasic) {
   // Create WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    CreateBaseDataset(&store, GetParam());
-    CreateExtendedDataset(&store);
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    CreateBaseDataset(store.get(), GetParam());
+    CreateExtendedDataset(store.get());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1164,18 +1170,18 @@ TEST_P(DurabilityTest, WalBasic) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, GetParam());
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, GetParam());
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -1183,19 +1189,19 @@ TEST_P(DurabilityTest, WalBasic) {
 TEST_P(DurabilityTest, WalBackup) {
   // Create WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
              .wal_file_size_kibibytes = 1,
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    auto acc = store.Access();
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    auto acc = store->Access();
     for (uint64_t i = 0; i < 1000; ++i) {
-      acc.CreateVertex();
+      acc->CreateVertex();
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1206,12 +1212,12 @@ TEST_P(DurabilityTest, WalBackup) {
 
   // Start storage without recovery.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
-             .snapshot_interval = std::chrono::minutes(20)}});
+             .snapshot_interval = std::chrono::minutes(20)}}));
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1224,14 +1230,14 @@ TEST_P(DurabilityTest, WalBackup) {
 TEST_P(DurabilityTest, WalAppendToExisting) {
   // Create WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    CreateBaseDataset(&store, GetParam());
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    CreateBaseDataset(store.get(), GetParam());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1241,23 +1247,23 @@ TEST_P(DurabilityTest, WalAppendToExisting) {
 
   // Recover WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
-         .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-    VerifyDataset(&store, DatasetType::ONLY_BASE, GetParam());
+         .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+    VerifyDataset(store.get(), DatasetType::ONLY_BASE, GetParam());
   }
 
   // Recover WALs and create more WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .recover_on_startup = true,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    CreateExtendedDataset(&store);
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    CreateExtendedDataset(store.get());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1266,18 +1272,18 @@ TEST_P(DurabilityTest, WalAppendToExisting) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, GetParam());
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, GetParam());
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -1288,34 +1294,35 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) {
 
   // Create WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    auto acc = store.Access();
-    auto v1 = acc.CreateVertex();
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    auto acc = store->Access();
+    auto v1 = acc->CreateVertex();
     gid_v1 = v1.Gid();
-    auto v2 = acc.CreateVertex();
+    auto v2 = acc->CreateVertex();
     gid_v2 = v2.Gid();
-    auto e1 = acc.CreateEdge(&v1, &v2, store.NameToEdgeType("e1"));
-    ASSERT_TRUE(e1.HasValue());
-    gid_e1 = e1->Gid();
-    ASSERT_TRUE(v1.AddLabel(store.NameToLabel("l11")).HasValue());
-    ASSERT_TRUE(v1.AddLabel(store.NameToLabel("l12")).HasValue());
-    ASSERT_TRUE(v1.AddLabel(store.NameToLabel("l13")).HasValue());
+    auto e1Res = acc->CreateEdge(&v1, &v2, store->NameToEdgeType("e1"));
+    ASSERT_TRUE(e1Res.HasValue());
+    auto e1 = std::move(e1Res.GetValue());
+    gid_e1 = e1.Gid();
+    ASSERT_TRUE(v1.AddLabel(store->NameToLabel("l11")).HasValue());
+    ASSERT_TRUE(v1.AddLabel(store->NameToLabel("l12")).HasValue());
+    ASSERT_TRUE(v1.AddLabel(store->NameToLabel("l13")).HasValue());
     if (GetParam()) {
       ASSERT_TRUE(
-          e1->SetProperty(store.NameToProperty("test"), memgraph::storage::PropertyValue("nandare")).HasValue());
+          e1.SetProperty(store->NameToProperty("test"), memgraph::storage::PropertyValue("nandare")).HasValue());
     }
-    ASSERT_TRUE(v2.AddLabel(store.NameToLabel("l21")).HasValue());
-    ASSERT_TRUE(v2.SetProperty(store.NameToProperty("hello"), memgraph::storage::PropertyValue("world")).HasValue());
-    auto v3 = acc.CreateVertex();
+    ASSERT_TRUE(v2.AddLabel(store->NameToLabel("l21")).HasValue());
+    ASSERT_TRUE(v2.SetProperty(store->NameToProperty("hello"), memgraph::storage::PropertyValue("world")).HasValue());
+    auto v3 = acc->CreateVertex();
     gid_v3 = v3.Gid();
-    ASSERT_TRUE(v3.SetProperty(store.NameToProperty("v3"), memgraph::storage::PropertyValue(42)).HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_TRUE(v3.SetProperty(store->NameToProperty("v3"), memgraph::storage::PropertyValue(42)).HasValue());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1324,24 +1331,24 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
   {
-    auto indices = store.ListAllIndices();
+    auto indices = store->ListAllIndices();
     ASSERT_EQ(indices.label.size(), 0);
     ASSERT_EQ(indices.label_property.size(), 0);
-    auto constraints = store.ListAllConstraints();
+    auto constraints = store->ListAllConstraints();
     ASSERT_EQ(constraints.existence.size(), 0);
     ASSERT_EQ(constraints.unique.size(), 0);
-    auto acc = store.Access();
+    auto acc = store->Access();
     {
-      auto v1 = acc.FindVertex(gid_v1, memgraph::storage::View::OLD);
+      auto v1 = acc->FindVertex(gid_v1, memgraph::storage::View::OLD);
       ASSERT_TRUE(v1);
       auto labels = v1->Labels(memgraph::storage::View::OLD);
       ASSERT_TRUE(labels.HasValue());
-      ASSERT_THAT(*labels,
-                  UnorderedElementsAre(store.NameToLabel("l11"), store.NameToLabel("l12"), store.NameToLabel("l13")));
+      ASSERT_THAT(*labels, UnorderedElementsAre(store->NameToLabel("l11"), store->NameToLabel("l12"),
+                                                store->NameToLabel("l13")));
       auto props = v1->Properties(memgraph::storage::View::OLD);
       ASSERT_TRUE(props.HasValue());
       ASSERT_EQ(props->size(), 0);
@@ -1356,21 +1363,21 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) {
       auto edge_props = edge.Properties(memgraph::storage::View::OLD);
       ASSERT_TRUE(edge_props.HasValue());
       if (GetParam()) {
-        ASSERT_THAT(*edge_props, UnorderedElementsAre(std::make_pair(store.NameToProperty("test"),
+        ASSERT_THAT(*edge_props, UnorderedElementsAre(std::make_pair(store->NameToProperty("test"),
                                                                      memgraph::storage::PropertyValue("nandare"))));
       } else {
         ASSERT_EQ(edge_props->size(), 0);
       }
     }
     {
-      auto v2 = acc.FindVertex(gid_v2, memgraph::storage::View::OLD);
+      auto v2 = acc->FindVertex(gid_v2, memgraph::storage::View::OLD);
       ASSERT_TRUE(v2);
       auto labels = v2->Labels(memgraph::storage::View::OLD);
       ASSERT_TRUE(labels.HasValue());
-      ASSERT_THAT(*labels, UnorderedElementsAre(store.NameToLabel("l21")));
+      ASSERT_THAT(*labels, UnorderedElementsAre(store->NameToLabel("l21")));
       auto props = v2->Properties(memgraph::storage::View::OLD);
       ASSERT_TRUE(props.HasValue());
-      ASSERT_THAT(*props, UnorderedElementsAre(std::make_pair(store.NameToProperty("hello"),
+      ASSERT_THAT(*props, UnorderedElementsAre(std::make_pair(store->NameToProperty("hello"),
                                                               memgraph::storage::PropertyValue("world"))));
       auto in_edges = v2->InEdges(memgraph::storage::View::OLD);
       ASSERT_TRUE(in_edges.HasValue());
@@ -1380,7 +1387,7 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) {
       auto edge_props = edge.Properties(memgraph::storage::View::OLD);
       ASSERT_TRUE(edge_props.HasValue());
       if (GetParam()) {
-        ASSERT_THAT(*edge_props, UnorderedElementsAre(std::make_pair(store.NameToProperty("test"),
+        ASSERT_THAT(*edge_props, UnorderedElementsAre(std::make_pair(store->NameToProperty("test"),
                                                                      memgraph::storage::PropertyValue("nandare"))));
       } else {
         ASSERT_EQ(edge_props->size(), 0);
@@ -1390,7 +1397,7 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) {
       ASSERT_EQ(out_edges->size(), 0);
     }
     {
-      auto v3 = acc.FindVertex(gid_v3, memgraph::storage::View::OLD);
+      auto v3 = acc->FindVertex(gid_v3, memgraph::storage::View::OLD);
       ASSERT_TRUE(v3);
       auto labels = v3->Labels(memgraph::storage::View::OLD);
       ASSERT_TRUE(labels.HasValue());
@@ -1398,7 +1405,7 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) {
       auto props = v3->Properties(memgraph::storage::View::OLD);
       ASSERT_TRUE(props.HasValue());
       ASSERT_THAT(*props, UnorderedElementsAre(
-                              std::make_pair(store.NameToProperty("v3"), memgraph::storage::PropertyValue(42))));
+                              std::make_pair(store->NameToProperty("v3"), memgraph::storage::PropertyValue(42))));
       auto in_edges = v3->InEdges(memgraph::storage::View::OLD);
       ASSERT_TRUE(in_edges.HasValue());
       ASSERT_EQ(in_edges->size(), 0);
@@ -1410,11 +1417,11 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) {
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -1422,35 +1429,35 @@ TEST_P(DurabilityTest, WalCreateInSingleTransaction) {
 TEST_P(DurabilityTest, WalCreateAndRemoveEverything) {
   // Create WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    CreateBaseDataset(&store, GetParam());
-    CreateExtendedDataset(&store);
-    auto indices = store.ListAllIndices();
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    CreateBaseDataset(store.get(), GetParam());
+    CreateExtendedDataset(store.get());
+    auto indices = store->ListAllIndices();
     for (const auto &index : indices.label) {
-      ASSERT_FALSE(store.DropIndex(index).HasError());
+      ASSERT_FALSE(store->DropIndex(index).HasError());
     }
     for (const auto &index : indices.label_property) {
-      ASSERT_FALSE(store.DropIndex(index.first, index.second).HasError());
+      ASSERT_FALSE(store->DropIndex(index.first, index.second).HasError());
     }
-    auto constraints = store.ListAllConstraints();
+    auto constraints = store->ListAllConstraints();
     for (const auto &constraint : constraints.existence) {
-      ASSERT_FALSE(store.DropExistenceConstraint(constraint.first, constraint.second).HasError());
+      ASSERT_FALSE(store->DropExistenceConstraint(constraint.first, constraint.second, {}).HasError());
     }
     for (const auto &constraint : constraints.unique) {
-      ASSERT_EQ(store.DropUniqueConstraint(constraint.first, constraint.second).GetValue(),
+      ASSERT_EQ(store->DropUniqueConstraint(constraint.first, constraint.second, {}).GetValue(),
                 memgraph::storage::UniqueConstraints::DeletionStatus::SUCCESS);
     }
-    auto acc = store.Access();
-    for (auto vertex : acc.Vertices(memgraph::storage::View::OLD)) {
-      ASSERT_TRUE(acc.DetachDeleteVertex(&vertex).HasValue());
+    auto acc = store->Access();
+    for (auto vertex : acc->Vertices(memgraph::storage::View::OLD)) {
+      ASSERT_TRUE(acc->DetachDeleteVertex(&vertex).HasValue());
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1459,19 +1466,19 @@ TEST_P(DurabilityTest, WalCreateAndRemoveEverything) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
   {
-    auto indices = store.ListAllIndices();
+    auto indices = store->ListAllIndices();
     ASSERT_EQ(indices.label.size(), 0);
     ASSERT_EQ(indices.label_property.size(), 0);
-    auto constraints = store.ListAllConstraints();
+    auto constraints = store->ListAllConstraints();
     ASSERT_EQ(constraints.existence.size(), 0);
     ASSERT_EQ(constraints.unique.size(), 0);
-    auto acc = store.Access();
+    auto acc = store->Access();
     uint64_t count = 0;
-    auto iterable = acc.Vertices(memgraph::storage::View::OLD);
+    auto iterable = acc->Vertices(memgraph::storage::View::OLD);
     for (auto it = iterable.begin(); it != iterable.end(); ++it) {
       ++count;
     }
@@ -1480,11 +1487,11 @@ TEST_P(DurabilityTest, WalCreateAndRemoveEverything) {
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -1495,7 +1502,7 @@ TEST_P(DurabilityTest, WalTransactionOrdering) {
 
   // Create WAL.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
@@ -1503,37 +1510,37 @@ TEST_P(DurabilityTest, WalTransactionOrdering) {
              .snapshot_interval = std::chrono::minutes(20),
              .wal_file_size_kibibytes = 100000,
              .wal_file_flush_every_n_tx = kFlushWalEvery,
-         }});
-    auto acc1 = store.Access();
-    auto acc2 = store.Access();
+         }}));
+    auto acc1 = store->Access();
+    auto acc2 = store->Access();
 
     // Create vertex in transaction 2.
     {
-      auto vertex2 = acc2.CreateVertex();
+      auto vertex2 = acc2->CreateVertex();
       gid2 = vertex2.Gid();
-      ASSERT_TRUE(vertex2.SetProperty(store.NameToProperty("id"), memgraph::storage::PropertyValue(2)).HasValue());
+      ASSERT_TRUE(vertex2.SetProperty(store->NameToProperty("id"), memgraph::storage::PropertyValue(2)).HasValue());
     }
 
-    auto acc3 = store.Access();
+    auto acc3 = store->Access();
 
     // Create vertex in transaction 3.
     {
-      auto vertex3 = acc3.CreateVertex();
+      auto vertex3 = acc3->CreateVertex();
       gid3 = vertex3.Gid();
-      ASSERT_TRUE(vertex3.SetProperty(store.NameToProperty("id"), memgraph::storage::PropertyValue(3)).HasValue());
+      ASSERT_TRUE(vertex3.SetProperty(store->NameToProperty("id"), memgraph::storage::PropertyValue(3)).HasValue());
     }
 
     // Create vertex in transaction 1.
     {
-      auto vertex1 = acc1.CreateVertex();
+      auto vertex1 = acc1->CreateVertex();
       gid1 = vertex1.Gid();
-      ASSERT_TRUE(vertex1.SetProperty(store.NameToProperty("id"), memgraph::storage::PropertyValue(1)).HasValue());
+      ASSERT_TRUE(vertex1.SetProperty(store->NameToProperty("id"), memgraph::storage::PropertyValue(1)).HasValue());
     }
 
     // Commit transaction 3, then 1, then 2.
-    ASSERT_FALSE(acc3.Commit().HasError());
-    ASSERT_FALSE(acc1.Commit().HasError());
-    ASSERT_FALSE(acc2.Commit().HasError());
+    ASSERT_FALSE(acc3->Commit().HasError());
+    ASSERT_FALSE(acc1->Commit().HasError());
+    ASSERT_FALSE(acc2->Commit().HasError());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1590,13 +1597,13 @@ TEST_P(DurabilityTest, WalTransactionOrdering) {
   }
 
   // Recover WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
   {
-    auto acc = store.Access();
+    auto acc = store->Access();
     for (auto [gid, id] : std::vector<std::pair<memgraph::storage::Gid, int64_t>>{{gid1, 1}, {gid2, 2}, {gid3, 3}}) {
-      auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+      auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
       ASSERT_TRUE(vertex);
       auto labels = vertex->Labels(memgraph::storage::View::OLD);
       ASSERT_TRUE(labels.HasValue());
@@ -1604,17 +1611,17 @@ TEST_P(DurabilityTest, WalTransactionOrdering) {
       auto props = vertex->Properties(memgraph::storage::View::OLD);
       ASSERT_TRUE(props.HasValue());
       ASSERT_EQ(props->size(), 1);
-      ASSERT_EQ(props->at(store.NameToProperty("id")), memgraph::storage::PropertyValue(id));
+      ASSERT_EQ(props->at(store->NameToProperty("id")), memgraph::storage::PropertyValue(id));
     }
   }
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -1622,26 +1629,26 @@ TEST_P(DurabilityTest, WalTransactionOrdering) {
 TEST_P(DurabilityTest, WalCreateAndRemoveOnlyBaseDataset) {
   // Create WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    CreateBaseDataset(&store, GetParam());
-    CreateExtendedDataset(&store);
-    auto label_indexed = store.NameToLabel("base_indexed");
-    auto label_unindexed = store.NameToLabel("base_unindexed");
-    auto acc = store.Access();
-    for (auto vertex : acc.Vertices(memgraph::storage::View::OLD)) {
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    CreateBaseDataset(store.get(), GetParam());
+    CreateExtendedDataset(store.get());
+    auto label_indexed = store->NameToLabel("base_indexed");
+    auto label_unindexed = store->NameToLabel("base_unindexed");
+    auto acc = store->Access();
+    for (auto vertex : acc->Vertices(memgraph::storage::View::OLD)) {
       auto has_indexed = vertex.HasLabel(label_indexed, memgraph::storage::View::OLD);
       ASSERT_TRUE(has_indexed.HasValue());
       auto has_unindexed = vertex.HasLabel(label_unindexed, memgraph::storage::View::OLD);
       if (!*has_indexed && !*has_unindexed) continue;
-      ASSERT_TRUE(acc.DetachDeleteVertex(&vertex).HasValue());
+      ASSERT_TRUE(acc->DetachDeleteVertex(&vertex).HasValue());
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1650,18 +1657,18 @@ TEST_P(DurabilityTest, WalCreateAndRemoveOnlyBaseDataset) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::ONLY_EXTENDED_WITH_BASE_INDICES_AND_CONSTRAINTS, GetParam());
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::ONLY_EXTENDED_WITH_BASE_INDICES_AND_CONSTRAINTS, GetParam());
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -1671,18 +1678,18 @@ TEST_P(DurabilityTest, WalDeathResilience) {
   if (pid == 0) {
     // Create WALs.
     {
-      memgraph::storage::Storage store(
+      std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
           {.items = {.properties_on_edges = GetParam()},
            .durability = {
                .storage_directory = storage_directory,
                .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
                .snapshot_interval = std::chrono::minutes(20),
-               .wal_file_flush_every_n_tx = kFlushWalEvery}});
+               .wal_file_flush_every_n_tx = kFlushWalEvery}}));
       // Create one million vertices.
       for (uint64_t i = 0; i < 1000000; ++i) {
-        auto acc = store.Access();
-        acc.CreateVertex();
-        MG_ASSERT(!acc.Commit().HasError(), "Couldn't commit transaction!");
+        auto acc = store->Access();
+        acc->CreateVertex();
+        MG_ASSERT(!acc->Commit().HasError(), "Couldn't commit transaction!");
       }
     }
   } else if (pid > 0) {
@@ -1706,7 +1713,7 @@ TEST_P(DurabilityTest, WalDeathResilience) {
   const uint64_t kExtraItems = 1000;
   uint64_t count = 0;
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
@@ -1714,10 +1721,10 @@ TEST_P(DurabilityTest, WalDeathResilience) {
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
              .wal_file_flush_every_n_tx = kFlushWalEvery,
-         }});
+         }}));
     {
-      auto acc = store.Access();
-      auto iterable = acc.Vertices(memgraph::storage::View::OLD);
+      auto acc = store->Access();
+      auto iterable = acc->Vertices(memgraph::storage::View::OLD);
       for (auto it = iterable.begin(); it != iterable.end(); ++it) {
         ++count;
       }
@@ -1725,11 +1732,11 @@ TEST_P(DurabilityTest, WalDeathResilience) {
     }
 
     {
-      auto acc = store.Access();
+      auto acc = store->Access();
       for (uint64_t i = 0; i < kExtraItems; ++i) {
-        acc.CreateVertex();
+        acc->CreateVertex();
       }
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
   }
 
@@ -1739,13 +1746,13 @@ TEST_P(DurabilityTest, WalDeathResilience) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
   {
     uint64_t current = 0;
-    auto acc = store.Access();
-    auto iterable = acc.Vertices(memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto iterable = acc->Vertices(memgraph::storage::View::OLD);
     for (auto it = iterable.begin(); it != iterable.end(); ++it) {
       ++current;
     }
@@ -1754,11 +1761,11 @@ TEST_P(DurabilityTest, WalDeathResilience) {
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -1766,19 +1773,19 @@ TEST_P(DurabilityTest, WalDeathResilience) {
 TEST_P(DurabilityTest, WalMissingSecond) {
   // Create unrelated WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
              .wal_file_size_kibibytes = 1,
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    auto acc = store.Access();
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    auto acc = store->Access();
     for (uint64_t i = 0; i < 1000; ++i) {
-      acc.CreateVertex();
+      acc->CreateVertex();
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1790,30 +1797,30 @@ TEST_P(DurabilityTest, WalMissingSecond) {
 
   // Create WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
              .wal_file_size_kibibytes = 1,
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
     const uint64_t kNumVertices = 1000;
     std::vector<memgraph::storage::Gid> gids;
     gids.reserve(kNumVertices);
     for (uint64_t i = 0; i < kNumVertices; ++i) {
-      auto acc = store.Access();
-      auto vertex = acc.CreateVertex();
+      auto acc = store->Access();
+      auto vertex = acc->CreateVertex();
       gids.push_back(vertex.Gid());
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
     for (uint64_t i = 0; i < kNumVertices; ++i) {
-      auto acc = store.Access();
-      auto vertex = acc.FindVertex(gids[i], memgraph::storage::View::OLD);
+      auto acc = store->Access();
+      auto vertex = acc->FindVertex(gids[i], memgraph::storage::View::OLD);
       ASSERT_TRUE(vertex);
-      ASSERT_TRUE(vertex->SetProperty(store.NameToProperty("nandare"), memgraph::storage::PropertyValue("haihaihai!"))
+      ASSERT_TRUE(vertex->SetProperty(store->NameToProperty("nandare"), memgraph::storage::PropertyValue("haihaihai!"))
                       .HasValue());
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
   }
 
@@ -1842,9 +1849,9 @@ TEST_P(DurabilityTest, WalMissingSecond) {
   // Recover WALs.
   ASSERT_DEATH(
       {
-        memgraph::storage::Storage store(
+        std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
             {.items = {.properties_on_edges = GetParam()},
-             .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
+             .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
       },
       "");
 }
@@ -1853,19 +1860,19 @@ TEST_P(DurabilityTest, WalMissingSecond) {
 TEST_P(DurabilityTest, WalCorruptSecond) {
   // Create unrelated WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
              .wal_file_size_kibibytes = 1,
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    auto acc = store.Access();
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    auto acc = store->Access();
     for (uint64_t i = 0; i < 1000; ++i) {
-      acc.CreateVertex();
+      acc->CreateVertex();
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1877,30 +1884,30 @@ TEST_P(DurabilityTest, WalCorruptSecond) {
 
   // Create WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
              .wal_file_size_kibibytes = 1,
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
     const uint64_t kNumVertices = 1000;
     std::vector<memgraph::storage::Gid> gids;
     gids.reserve(kNumVertices);
     for (uint64_t i = 0; i < kNumVertices; ++i) {
-      auto acc = store.Access();
-      auto vertex = acc.CreateVertex();
+      auto acc = store->Access();
+      auto vertex = acc->CreateVertex();
       gids.push_back(vertex.Gid());
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
     for (uint64_t i = 0; i < kNumVertices; ++i) {
-      auto acc = store.Access();
-      auto vertex = acc.FindVertex(gids[i], memgraph::storage::View::OLD);
+      auto acc = store->Access();
+      auto vertex = acc->FindVertex(gids[i], memgraph::storage::View::OLD);
       ASSERT_TRUE(vertex);
-      ASSERT_TRUE(vertex->SetProperty(store.NameToProperty("nandare"), memgraph::storage::PropertyValue("haihaihai!"))
+      ASSERT_TRUE(vertex->SetProperty(store->NameToProperty("nandare"), memgraph::storage::PropertyValue("haihaihai!"))
                       .HasValue());
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
   }
 
@@ -1928,9 +1935,9 @@ TEST_P(DurabilityTest, WalCorruptSecond) {
   // Recover WALs.
   ASSERT_DEATH(
       {
-        memgraph::storage::Storage store(
+        std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
             {.items = {.properties_on_edges = GetParam()},
-             .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
+             .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
       },
       "");
 }
@@ -1939,16 +1946,16 @@ TEST_P(DurabilityTest, WalCorruptSecond) {
 TEST_P(DurabilityTest, WalCorruptLastTransaction) {
   // Create WALs
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
              .wal_file_size_kibibytes = 1,
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    CreateBaseDataset(&store, GetParam());
-    CreateExtendedDataset(&store, /* single_transaction = */ true);
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    CreateBaseDataset(store.get(), GetParam());
+    CreateExtendedDataset(store.get(), /* single_transaction = */ true);
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -1965,20 +1972,20 @@ TEST_P(DurabilityTest, WalCorruptLastTransaction) {
   }
 
   // Recover WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
   // The extended dataset shouldn't be recovered because its WAL transaction was
   // corrupt.
-  VerifyDataset(&store, DatasetType::ONLY_BASE_WITH_EXTENDED_INDICES_AND_CONSTRAINTS, GetParam());
+  VerifyDataset(store.get(), DatasetType::ONLY_BASE_WITH_EXTENDED_INDICES_AND_CONSTRAINTS, GetParam());
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -1986,44 +1993,47 @@ TEST_P(DurabilityTest, WalCorruptLastTransaction) {
 TEST_P(DurabilityTest, WalAllOperationsInSingleTransaction) {
   // Create WALs
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
              .wal_file_size_kibibytes = 1,
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    auto acc = store.Access();
-    auto vertex1 = acc.CreateVertex();
-    auto vertex2 = acc.CreateVertex();
-    ASSERT_TRUE(vertex1.AddLabel(acc.NameToLabel("nandare")).HasValue());
-    ASSERT_TRUE(vertex2.SetProperty(acc.NameToProperty("haihai"), memgraph::storage::PropertyValue(42)).HasValue());
-    ASSERT_TRUE(vertex1.RemoveLabel(acc.NameToLabel("nandare")).HasValue());
-    auto edge1 = acc.CreateEdge(&vertex1, &vertex2, acc.NameToEdgeType("et1"));
-    ASSERT_TRUE(edge1.HasValue());
-    ASSERT_TRUE(vertex2.SetProperty(acc.NameToProperty("haihai"), memgraph::storage::PropertyValue()).HasValue());
-    auto vertex3 = acc.CreateVertex();
-    auto edge2 = acc.CreateEdge(&vertex3, &vertex3, acc.NameToEdgeType("et2"));
-    ASSERT_TRUE(edge2.HasValue());
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    auto acc = store->Access();
+    auto vertex1 = acc->CreateVertex();
+    auto vertex2 = acc->CreateVertex();
+    ASSERT_TRUE(vertex1.AddLabel(acc->NameToLabel("nandare")).HasValue());
+    ASSERT_TRUE(vertex2.SetProperty(acc->NameToProperty("haihai"), memgraph::storage::PropertyValue(42)).HasValue());
+    ASSERT_TRUE(vertex1.RemoveLabel(acc->NameToLabel("nandare")).HasValue());
+    auto edge1Res = acc->CreateEdge(&vertex1, &vertex2, acc->NameToEdgeType("et1"));
+    ASSERT_TRUE(edge1Res.HasValue());
+    auto edge1 = std::move(edge1Res.GetValue());
+
+    ASSERT_TRUE(vertex2.SetProperty(acc->NameToProperty("haihai"), memgraph::storage::PropertyValue()).HasValue());
+    auto vertex3 = acc->CreateVertex();
+    auto edge2Res = acc->CreateEdge(&vertex3, &vertex3, acc->NameToEdgeType("et2"));
+    ASSERT_TRUE(edge2Res.HasValue());
+    auto edge2 = std::move(edge2Res.GetValue());
     if (GetParam()) {
-      ASSERT_TRUE(edge2->SetProperty(acc.NameToProperty("meaning"), memgraph::storage::PropertyValue(true)).HasValue());
+      ASSERT_TRUE(edge2.SetProperty(acc->NameToProperty("meaning"), memgraph::storage::PropertyValue(true)).HasValue());
       ASSERT_TRUE(
-          edge1->SetProperty(acc.NameToProperty("hello"), memgraph::storage::PropertyValue("world")).HasValue());
-      ASSERT_TRUE(edge2->SetProperty(acc.NameToProperty("meaning"), memgraph::storage::PropertyValue()).HasValue());
+          edge1.SetProperty(acc->NameToProperty("hello"), memgraph::storage::PropertyValue("world")).HasValue());
+      ASSERT_TRUE(edge2.SetProperty(acc->NameToProperty("meaning"), memgraph::storage::PropertyValue()).HasValue());
     }
-    ASSERT_TRUE(vertex3.AddLabel(acc.NameToLabel("test")).HasValue());
-    ASSERT_TRUE(vertex3.SetProperty(acc.NameToProperty("nonono"), memgraph::storage::PropertyValue(-1)).HasValue());
-    ASSERT_TRUE(vertex3.SetProperty(acc.NameToProperty("nonono"), memgraph::storage::PropertyValue()).HasValue());
+    ASSERT_TRUE(vertex3.AddLabel(acc->NameToLabel("test")).HasValue());
+    ASSERT_TRUE(vertex3.SetProperty(acc->NameToProperty("nonono"), memgraph::storage::PropertyValue(-1)).HasValue());
+    ASSERT_TRUE(vertex3.SetProperty(acc->NameToProperty("nonono"), memgraph::storage::PropertyValue()).HasValue());
     if (GetParam()) {
-      ASSERT_TRUE(edge1->SetProperty(acc.NameToProperty("hello"), memgraph::storage::PropertyValue()).HasValue());
+      ASSERT_TRUE(edge1.SetProperty(acc->NameToProperty("hello"), memgraph::storage::PropertyValue()).HasValue());
     }
-    ASSERT_TRUE(vertex3.RemoveLabel(acc.NameToLabel("test")).HasValue());
-    ASSERT_TRUE(acc.DetachDeleteVertex(&vertex1).HasValue());
-    ASSERT_TRUE(acc.DeleteEdge(&*edge2).HasValue());
-    ASSERT_TRUE(acc.DeleteVertex(&vertex2).HasValue());
-    ASSERT_TRUE(acc.DeleteVertex(&vertex3).HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_TRUE(vertex3.RemoveLabel(acc->NameToLabel("test")).HasValue());
+    ASSERT_TRUE(acc->DetachDeleteVertex(&vertex1).HasValue());
+    ASSERT_TRUE(acc->DeleteEdge(&edge2).HasValue());
+    ASSERT_TRUE(acc->DeleteVertex(&vertex2).HasValue());
+    ASSERT_TRUE(acc->DeleteVertex(&vertex3).HasValue());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -2032,13 +2042,13 @@ TEST_P(DurabilityTest, WalAllOperationsInSingleTransaction) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
   {
-    auto acc = store.Access();
+    auto acc = store->Access();
     uint64_t count = 0;
-    auto iterable = acc.Vertices(memgraph::storage::View::OLD);
+    auto iterable = acc->Vertices(memgraph::storage::View::OLD);
     for (auto it = iterable.begin(); it != iterable.end(); ++it) {
       ++count;
     }
@@ -2047,11 +2057,11 @@ TEST_P(DurabilityTest, WalAllOperationsInSingleTransaction) {
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -2059,16 +2069,16 @@ TEST_P(DurabilityTest, WalAllOperationsInSingleTransaction) {
 TEST_P(DurabilityTest, WalAndSnapshot) {
   // Create snapshot and WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::milliseconds(2000),
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    CreateBaseDataset(&store, GetParam());
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    CreateBaseDataset(store.get(), GetParam());
     std::this_thread::sleep_for(std::chrono::milliseconds(2500));
-    CreateExtendedDataset(&store);
+    CreateExtendedDataset(store.get());
   }
 
   ASSERT_GE(GetSnapshotsList().size(), 1);
@@ -2077,18 +2087,18 @@ TEST_P(DurabilityTest, WalAndSnapshot) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover snapshot and WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, GetParam());
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, GetParam());
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -2096,10 +2106,10 @@ TEST_P(DurabilityTest, WalAndSnapshot) {
 TEST_P(DurabilityTest, WalAndSnapshotAppendToExistingSnapshot) {
   // Create snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
-         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}});
-    CreateBaseDataset(&store, GetParam());
+         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}}));
+    CreateBaseDataset(store.get(), GetParam());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 1);
@@ -2109,23 +2119,23 @@ TEST_P(DurabilityTest, WalAndSnapshotAppendToExistingSnapshot) {
 
   // Recover snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
-         .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-    VerifyDataset(&store, DatasetType::ONLY_BASE, GetParam());
+         .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+    VerifyDataset(store.get(), DatasetType::ONLY_BASE, GetParam());
   }
 
   // Recover snapshot and create WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .recover_on_startup = true,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    CreateExtendedDataset(&store);
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    CreateExtendedDataset(store.get());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 1);
@@ -2134,18 +2144,18 @@ TEST_P(DurabilityTest, WalAndSnapshotAppendToExistingSnapshot) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover snapshot and WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, GetParam());
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, GetParam());
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -2153,10 +2163,10 @@ TEST_P(DurabilityTest, WalAndSnapshotAppendToExistingSnapshot) {
 TEST_P(DurabilityTest, WalAndSnapshotAppendToExistingSnapshotAndWal) {
   // Create snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
-         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}});
-    CreateBaseDataset(&store, GetParam());
+         .durability = {.storage_directory = storage_directory, .snapshot_on_exit = true}}));
+    CreateBaseDataset(store.get(), GetParam());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 1);
@@ -2166,23 +2176,23 @@ TEST_P(DurabilityTest, WalAndSnapshotAppendToExistingSnapshotAndWal) {
 
   // Recover snapshot.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
-         .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-    VerifyDataset(&store, DatasetType::ONLY_BASE, GetParam());
+         .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+    VerifyDataset(store.get(), DatasetType::ONLY_BASE, GetParam());
   }
 
   // Recover snapshot and create WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .recover_on_startup = true,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    CreateExtendedDataset(&store);
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    CreateExtendedDataset(store.get());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 1);
@@ -2193,22 +2203,23 @@ TEST_P(DurabilityTest, WalAndSnapshotAppendToExistingSnapshotAndWal) {
   // Recover snapshot and WALs and create more WALs.
   memgraph::storage::Gid vertex_gid;
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .recover_on_startup = true,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, GetParam());
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, GetParam());
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
     vertex_gid = vertex.Gid();
     if (GetParam()) {
-      ASSERT_TRUE(vertex.SetProperty(store.NameToProperty("meaning"), memgraph::storage::PropertyValue(42)).HasValue());
+      ASSERT_TRUE(
+          vertex.SetProperty(store->NameToProperty("meaning"), memgraph::storage::PropertyValue(42)).HasValue());
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 1);
@@ -2217,14 +2228,14 @@ TEST_P(DurabilityTest, WalAndSnapshotAppendToExistingSnapshotAndWal) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover snapshot and WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, GetParam(),
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, GetParam(),
                 /* verify_info = */ false);
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(vertex_gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto labels = vertex->Labels(memgraph::storage::View::OLD);
     ASSERT_TRUE(labels.HasValue());
@@ -2233,7 +2244,7 @@ TEST_P(DurabilityTest, WalAndSnapshotAppendToExistingSnapshotAndWal) {
     ASSERT_TRUE(props.HasValue());
     if (GetParam()) {
       ASSERT_THAT(*props, UnorderedElementsAre(
-                              std::make_pair(store.NameToProperty("meaning"), memgraph::storage::PropertyValue(42))));
+                              std::make_pair(store->NameToProperty("meaning"), memgraph::storage::PropertyValue(42))));
     } else {
       ASSERT_EQ(props->size(), 0);
     }
@@ -2241,11 +2252,11 @@ TEST_P(DurabilityTest, WalAndSnapshotAppendToExistingSnapshotAndWal) {
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -2253,19 +2264,19 @@ TEST_P(DurabilityTest, WalAndSnapshotAppendToExistingSnapshotAndWal) {
 TEST_P(DurabilityTest, WalAndSnapshotWalRetention) {
   // Create unrelated WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::minutes(20),
              .wal_file_size_kibibytes = 1,
-             .wal_file_flush_every_n_tx = kFlushWalEvery}});
-    auto acc = store.Access();
+             .wal_file_flush_every_n_tx = kFlushWalEvery}}));
+    auto acc = store->Access();
     for (uint64_t i = 0; i < 1000; ++i) {
-      acc.CreateVertex();
+      acc->CreateVertex();
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   ASSERT_EQ(GetSnapshotsList().size(), 0);
@@ -2279,22 +2290,22 @@ TEST_P(DurabilityTest, WalAndSnapshotWalRetention) {
 
   // Create snapshot and WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_interval = std::chrono::seconds(2),
              .wal_file_size_kibibytes = 1,
-             .wal_file_flush_every_n_tx = 1}});
+             .wal_file_flush_every_n_tx = 1}}));
     // Restore unrelated snapshots after the database has been started.
     RestoreBackups();
     memgraph::utils::Timer timer;
     // Allow at least 6 snapshots to be created.
     while (timer.Elapsed().count() < 13.0) {
-      auto acc = store.Access();
-      acc.CreateVertex();
-      ASSERT_FALSE(acc.Commit().HasError());
+      auto acc = store->Access();
+      acc->CreateVertex();
+      ASSERT_FALSE(acc->Commit().HasError());
       ++items_created;
     }
   }
@@ -2312,12 +2323,12 @@ TEST_P(DurabilityTest, WalAndSnapshotWalRetention) {
 
     // Recover and verify data.
     {
-      memgraph::storage::Storage store(
+      std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
           {.items = {.properties_on_edges = GetParam()},
-           .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-      auto acc = store.Access();
+           .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+      auto acc = store->Access();
       for (uint64_t j = 0; j < items_created; ++j) {
-        auto vertex = acc.FindVertex(memgraph::storage::Gid::FromUint(j), memgraph::storage::View::OLD);
+        auto vertex = acc->FindVertex(memgraph::storage::Gid::FromUint(j), memgraph::storage::View::OLD);
         ASSERT_TRUE(vertex);
       }
     }
@@ -2330,9 +2341,9 @@ TEST_P(DurabilityTest, WalAndSnapshotWalRetention) {
   // shouldn't be possible because the initial WALs are already deleted.
   ASSERT_DEATH(
       {
-        memgraph::storage::Storage store(
+        std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
             {.items = {.properties_on_edges = GetParam()},
-             .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
+             .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
       },
       "");
 }
@@ -2341,17 +2352,17 @@ TEST_P(DurabilityTest, WalAndSnapshotWalRetention) {
 TEST_P(DurabilityTest, SnapshotAndWalMixedUUID) {
   // Create unrelated snapshot and WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
-             .snapshot_interval = std::chrono::seconds(2)}});
-    auto acc = store.Access();
+             .snapshot_interval = std::chrono::seconds(2)}}));
+    auto acc = store->Access();
     for (uint64_t i = 0; i < 1000; ++i) {
-      acc.CreateVertex();
+      acc->CreateVertex();
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
     std::this_thread::sleep_for(std::chrono::milliseconds(2500));
   }
 
@@ -2362,15 +2373,15 @@ TEST_P(DurabilityTest, SnapshotAndWalMixedUUID) {
 
   // Create snapshot and WALs.
   {
-    memgraph::storage::Storage store(
+    std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
         {.items = {.properties_on_edges = GetParam()},
          .durability = {
              .storage_directory = storage_directory,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
-             .snapshot_interval = std::chrono::seconds(2)}});
-    CreateBaseDataset(&store, GetParam());
+             .snapshot_interval = std::chrono::seconds(2)}}));
+    CreateBaseDataset(store.get(), GetParam());
     std::this_thread::sleep_for(std::chrono::milliseconds(2500));
-    CreateExtendedDataset(&store);
+    CreateExtendedDataset(store.get());
     std::this_thread::sleep_for(std::chrono::milliseconds(2500));
   }
 
@@ -2388,17 +2399,17 @@ TEST_P(DurabilityTest, SnapshotAndWalMixedUUID) {
   ASSERT_EQ(GetBackupWalsList().size(), 0);
 
   // Recover snapshot and WALs.
-  memgraph::storage::Storage store(
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::InMemoryStorage(
       {.items = {.properties_on_edges = GetParam()},
-       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}});
-  VerifyDataset(&store, DatasetType::BASE_WITH_EXTENDED, GetParam());
+       .durability = {.storage_directory = storage_directory, .recover_on_startup = true}}));
+  VerifyDataset(store.get(), DatasetType::BASE_WITH_EXTENDED, GetParam());
 
   // Try to use the storage.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
-    auto edge = acc.CreateEdge(&vertex, &vertex, store.NameToEdgeType("et"));
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    auto edge = acc->CreateEdge(&vertex, &vertex, store->NameToEdgeType("et"));
     ASSERT_TRUE(edge.HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
diff --git a/tests/unit/storage_v2_edge.cpp b/tests/unit/storage_v2_edge_inmemory.cpp
similarity index 86%
rename from tests/unit/storage_v2_edge.cpp
rename to tests/unit/storage_v2_edge_inmemory.cpp
index 1d62fe35a..966ac8790 100644
--- a/tests/unit/storage_v2_edge.cpp
+++ b/tests/unit/storage_v2_edge_inmemory.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -14,6 +14,7 @@
 
 #include <limits>
 
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/storage.hpp"
 
 using testing::UnorderedElementsAre;
@@ -25,31 +26,32 @@ INSTANTIATE_TEST_CASE_P(EdgesWithoutProperties, StorageEdgeTest, ::testing::Valu
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertices
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.CreateVertex();
-    auto vertex_to = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
     gid_from = vertex_from.Gid();
     gid_to = vertex_to.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex_from, &*vertex_to, et);
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -92,7 +94,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -110,18 +112,18 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
@@ -177,7 +179,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -209,37 +211,38 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et}, &*vertex_to)->size(), 0);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, EdgeCreateFromLargerCommit) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertices
   {
-    auto acc = store.Access();
-    auto vertex_to = acc.CreateVertex();
-    auto vertex_from = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex_to = acc->CreateVertex();
+    auto vertex_from = acc->CreateVertex();
     gid_to = vertex_to.Gid();
     gid_from = vertex_from.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex_from, &*vertex_to, et);
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -282,7 +285,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -294,18 +297,18 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerCommit) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
@@ -361,7 +364,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -381,32 +384,33 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerCommit) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, EdgeCreateFromSameCommit) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_vertex = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertex
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
     gid_vertex = vertex.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex, &*vertex, et);
+    auto res = acc->CreateEdge(&*vertex, &*vertex, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -441,7 +445,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSameCommit) {
       ASSERT_EQ(e.ToVertex(), *vertex);
     }
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -453,16 +457,16 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSameCommit) {
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     {
@@ -510,7 +514,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSameCommit) {
       ASSERT_EQ(e.ToVertex(), *vertex);
     }
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -526,37 +530,38 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSameCommit) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, EdgeCreateFromSmallerAbort) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertices
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.CreateVertex();
-    auto vertex_to = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
     gid_from = vertex_from.Gid();
     gid_to = vertex_to.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge, but abort the transaction
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex_from, &*vertex_to, et);
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -599,7 +604,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -611,14 +616,14 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
@@ -640,20 +645,20 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex_from, &*vertex_to, et);
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -696,7 +701,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -708,18 +713,18 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
@@ -775,7 +780,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -795,37 +800,38 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSmallerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertices
   {
-    auto acc = store.Access();
-    auto vertex_to = acc.CreateVertex();
-    auto vertex_from = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex_to = acc->CreateVertex();
+    auto vertex_from = acc->CreateVertex();
     gid_to = vertex_to.Gid();
     gid_from = vertex_from.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge, but abort the transaction
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex_from, &*vertex_to, et);
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -868,7 +874,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -880,14 +886,14 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
@@ -909,20 +915,20 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex_from, &*vertex_to, et);
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -965,7 +971,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -977,18 +983,18 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
@@ -1044,7 +1050,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -1064,32 +1070,33 @@ TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, EdgeCreateFromSameAbort) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_vertex = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertex
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
     gid_vertex = vertex.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge, but abort the transaction
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex, &*vertex, et);
+    auto res = acc->CreateEdge(&*vertex, &*vertex, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -1124,7 +1131,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSameAbort) {
       ASSERT_EQ(e.ToVertex(), *vertex);
     }
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -1136,13 +1143,13 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSameAbort) {
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
     // Check edges without filters
@@ -1155,18 +1162,18 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSameAbort) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex, &*vertex, et);
+    auto res = acc->CreateEdge(&*vertex, &*vertex, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -1201,7 +1208,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSameAbort) {
       ASSERT_EQ(e.ToVertex(), *vertex);
     }
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -1213,16 +1220,16 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSameAbort) {
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     {
@@ -1270,7 +1277,7 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSameAbort) {
       ASSERT_EQ(e.ToVertex(), *vertex);
     }
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -1290,37 +1297,38 @@ TEST_P(StorageEdgeTest, EdgeCreateFromSameAbort) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertices
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.CreateVertex();
-    auto vertex_to = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
     gid_from = vertex_from.Gid();
     gid_to = vertex_to.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex_from, &*vertex_to, et);
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -1363,7 +1371,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -1375,18 +1383,18 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
@@ -1442,7 +1450,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -1462,22 +1470,22 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Delete edge
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     auto edge = vertex_from->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto res = acc.DeleteEdge(&edge);
+    auto res = acc->DeleteEdge(&edge);
     ASSERT_TRUE(res.HasValue());
     ASSERT_TRUE(res.GetValue());
 
@@ -1517,7 +1525,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -1529,14 +1537,14 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
@@ -1558,37 +1566,38 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, EdgeDeleteFromLargerCommit) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertices
   {
-    auto acc = store.Access();
-    auto vertex_to = acc.CreateVertex();
-    auto vertex_from = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex_to = acc->CreateVertex();
+    auto vertex_from = acc->CreateVertex();
     gid_from = vertex_from.Gid();
     gid_to = vertex_to.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex_from, &*vertex_to, et);
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -1631,7 +1640,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -1643,18 +1652,18 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerCommit) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
@@ -1710,7 +1719,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -1730,22 +1739,22 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerCommit) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Delete edge
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     auto edge = vertex_from->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto res = acc.DeleteEdge(&edge);
+    auto res = acc->DeleteEdge(&edge);
     ASSERT_TRUE(res.HasValue());
     ASSERT_TRUE(res.GetValue());
 
@@ -1785,7 +1794,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -1797,14 +1806,14 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerCommit) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
@@ -1826,32 +1835,33 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, EdgeDeleteFromSameCommit) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_vertex = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertex
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
     gid_vertex = vertex.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex, &*vertex, et);
+    auto res = acc->CreateEdge(&*vertex, &*vertex, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -1886,7 +1896,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameCommit) {
       ASSERT_EQ(e.ToVertex(), *vertex);
     }
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -1898,16 +1908,16 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameCommit) {
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     {
@@ -1955,7 +1965,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameCommit) {
       ASSERT_EQ(e.ToVertex(), *vertex);
     }
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -1975,20 +1985,20 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameCommit) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Delete edge
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto res = acc.DeleteEdge(&edge);
+    auto res = acc->DeleteEdge(&edge);
     ASSERT_TRUE(res.HasValue());
     ASSERT_TRUE(res.GetValue());
 
@@ -2020,7 +2030,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameCommit) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -2032,13 +2042,13 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameCommit) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
     // Check edges without filters
@@ -2051,37 +2061,38 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameCommit) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertices
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.CreateVertex();
-    auto vertex_to = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
     gid_from = vertex_from.Gid();
     gid_to = vertex_to.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex_from, &*vertex_to, et);
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -2124,7 +2135,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -2136,18 +2147,18 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
@@ -2203,7 +2214,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -2223,22 +2234,22 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Delete the edge, but abort the transaction
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     auto edge = vertex_from->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto res = acc.DeleteEdge(&edge);
+    auto res = acc->DeleteEdge(&edge);
     ASSERT_TRUE(res.HasValue());
     ASSERT_TRUE(res.GetValue());
 
@@ -2278,7 +2289,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -2290,18 +2301,18 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
@@ -2357,7 +2368,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -2377,22 +2388,22 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Delete the edge
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     auto edge = vertex_from->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto res = acc.DeleteEdge(&edge);
+    auto res = acc->DeleteEdge(&edge);
     ASSERT_TRUE(res.HasValue());
     ASSERT_TRUE(res.GetValue());
 
@@ -2432,7 +2443,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -2444,14 +2455,14 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
@@ -2473,37 +2484,38 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertices
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.CreateVertex();
-    auto vertex_to = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
     gid_from = vertex_from.Gid();
     gid_to = vertex_to.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge
   {
-    auto acc = store.Access();
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex_from, &*vertex_to, et);
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -2546,7 +2558,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -2558,18 +2570,18 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
@@ -2625,7 +2637,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -2645,22 +2657,22 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Delete the edge, but abort the transaction
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     auto edge = vertex_from->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto res = acc.DeleteEdge(&edge);
+    auto res = acc->DeleteEdge(&edge);
     ASSERT_TRUE(res.HasValue());
     ASSERT_TRUE(res.GetValue());
 
@@ -2700,7 +2712,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -2712,18 +2724,18 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
@@ -2780,7 +2792,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -2800,22 +2812,22 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Delete the edge
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     auto edge = vertex_from->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto res = acc.DeleteEdge(&edge);
+    auto res = acc->DeleteEdge(&edge);
     ASSERT_TRUE(res.HasValue());
     ASSERT_TRUE(res.GetValue());
 
@@ -2855,7 +2867,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -2867,14 +2879,14 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
     ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
@@ -2896,32 +2908,33 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_vertex = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create vertex
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
     gid_vertex = vertex.Gid();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Create edge
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&*vertex, &*vertex, et);
+    auto res = acc->CreateEdge(&*vertex, &*vertex, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -2956,7 +2969,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
       ASSERT_EQ(e.ToVertex(), *vertex);
     }
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
@@ -2968,16 +2981,16 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     {
@@ -3025,7 +3038,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
       ASSERT_EQ(e.ToVertex(), *vertex);
     }
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -3045,20 +3058,20 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Delete the edge, but abort the transaction
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto res = acc.DeleteEdge(&edge);
+    auto res = acc->DeleteEdge(&edge);
     ASSERT_TRUE(res.HasValue());
     ASSERT_TRUE(res.GetValue());
 
@@ -3090,7 +3103,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -3102,16 +3115,16 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges without filters
     {
@@ -3159,7 +3172,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
       ASSERT_EQ(e.ToVertex(), *vertex);
     }
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -3179,20 +3192,20 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Delete the edge
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto res = acc.DeleteEdge(&edge);
+    auto res = acc->DeleteEdge(&edge);
     ASSERT_TRUE(res.HasValue());
     ASSERT_TRUE(res.GetValue());
 
@@ -3224,7 +3237,7 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 0);
 
-    auto other_et = acc.NameToEdgeType("other");
+    auto other_et = acc->NameToEdgeType("other");
 
     // Check edges with filters
     ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
@@ -3236,13 +3249,13 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check whether the edge exists
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex);
 
     // Check edges without filters
@@ -3255,25 +3268,26 @@ TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
     ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create dataset
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.CreateVertex();
-    auto vertex_to = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&vertex_from, &vertex_to, et);
+    auto res = acc->CreateEdge(&vertex_from, &vertex_to, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -3311,29 +3325,29 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) {
     ASSERT_EQ(vertex_to.OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to.OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Detach delete vertex
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Delete must fail
     {
-      auto ret = acc.DeleteVertex(&*vertex_from);
+      auto ret = acc->DeleteVertex(&*vertex_from);
       ASSERT_TRUE(ret.HasError());
       ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES);
     }
 
     // Detach delete vertex
     {
-      auto ret = acc.DetachDeleteVertex(&*vertex_from);
+      auto ret = acc->DetachDeleteVertex(&*vertex_from);
       ASSERT_TRUE(ret.HasValue());
       ASSERT_TRUE(*ret);
     }
@@ -3375,14 +3389,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check dataset
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_FALSE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
@@ -3400,46 +3414,47 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) {
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleCommit) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_vertex1 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid_vertex2 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create dataset
   {
-    auto acc = store.Access();
-    auto vertex1 = acc.CreateVertex();
-    auto vertex2 = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex1 = acc->CreateVertex();
+    auto vertex2 = acc->CreateVertex();
 
     gid_vertex1 = vertex1.Gid();
     gid_vertex2 = vertex2.Gid();
 
-    auto et1 = acc.NameToEdgeType("et1");
-    auto et2 = acc.NameToEdgeType("et2");
-    auto et3 = acc.NameToEdgeType("et3");
-    auto et4 = acc.NameToEdgeType("et4");
+    auto et1 = acc->NameToEdgeType("et1");
+    auto et2 = acc->NameToEdgeType("et2");
+    auto et3 = acc->NameToEdgeType("et3");
+    auto et4 = acc->NameToEdgeType("et4");
 
-    auto res1 = acc.CreateEdge(&vertex1, &vertex2, et1);
+    auto res1 = acc->CreateEdge(&vertex1, &vertex2, et1);
     ASSERT_TRUE(res1.HasValue());
     auto edge1 = res1.GetValue();
     ASSERT_EQ(edge1.EdgeType(), et1);
     ASSERT_EQ(edge1.FromVertex(), vertex1);
     ASSERT_EQ(edge1.ToVertex(), vertex2);
 
-    auto res2 = acc.CreateEdge(&vertex2, &vertex1, et2);
+    auto res2 = acc->CreateEdge(&vertex2, &vertex1, et2);
     ASSERT_TRUE(res2.HasValue());
     auto edge2 = res2.GetValue();
     ASSERT_EQ(edge2.EdgeType(), et2);
     ASSERT_EQ(edge2.FromVertex(), vertex2);
     ASSERT_EQ(edge2.ToVertex(), vertex1);
 
-    auto res3 = acc.CreateEdge(&vertex1, &vertex1, et3);
+    auto res3 = acc->CreateEdge(&vertex1, &vertex1, et3);
     ASSERT_TRUE(res3.HasValue());
     auto edge3 = res3.GetValue();
     ASSERT_EQ(edge3.EdgeType(), et3);
     ASSERT_EQ(edge3.FromVertex(), vertex1);
     ASSERT_EQ(edge3.ToVertex(), vertex1);
 
-    auto res4 = acc.CreateEdge(&vertex2, &vertex2, et4);
+    auto res4 = acc->CreateEdge(&vertex2, &vertex2, et4);
     ASSERT_TRUE(res4.HasValue());
     auto edge4 = res4.GetValue();
     ASSERT_EQ(edge4.EdgeType(), et4);
@@ -3528,32 +3543,32 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleCommit) {
       }
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Detach delete vertex
   {
-    auto acc = store.Access();
-    auto vertex1 = acc.FindVertex(gid_vertex1, memgraph::storage::View::NEW);
-    auto vertex2 = acc.FindVertex(gid_vertex2, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex1 = acc->FindVertex(gid_vertex1, memgraph::storage::View::NEW);
+    auto vertex2 = acc->FindVertex(gid_vertex2, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex1);
     ASSERT_TRUE(vertex2);
 
-    auto et1 = acc.NameToEdgeType("et1");
-    auto et2 = acc.NameToEdgeType("et2");
-    auto et3 = acc.NameToEdgeType("et3");
-    auto et4 = acc.NameToEdgeType("et4");
+    auto et1 = acc->NameToEdgeType("et1");
+    auto et2 = acc->NameToEdgeType("et2");
+    auto et3 = acc->NameToEdgeType("et3");
+    auto et4 = acc->NameToEdgeType("et4");
 
     // Delete must fail
     {
-      auto ret = acc.DeleteVertex(&*vertex1);
+      auto ret = acc->DeleteVertex(&*vertex1);
       ASSERT_TRUE(ret.HasError());
       ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES);
     }
 
     // Detach delete vertex
     {
-      auto ret = acc.DetachDeleteVertex(&*vertex1);
+      auto ret = acc->DetachDeleteVertex(&*vertex1);
       ASSERT_TRUE(ret.HasValue());
       ASSERT_TRUE(*ret);
     }
@@ -3666,18 +3681,18 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleCommit) {
       ASSERT_EQ(e.ToVertex(), *vertex2);
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check dataset
   {
-    auto acc = store.Access();
-    auto vertex1 = acc.FindVertex(gid_vertex1, memgraph::storage::View::NEW);
-    auto vertex2 = acc.FindVertex(gid_vertex2, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex1 = acc->FindVertex(gid_vertex1, memgraph::storage::View::NEW);
+    auto vertex2 = acc->FindVertex(gid_vertex2, memgraph::storage::View::NEW);
     ASSERT_FALSE(vertex1);
     ASSERT_TRUE(vertex2);
 
-    auto et4 = acc.NameToEdgeType("et4");
+    auto et4 = acc->NameToEdgeType("et4");
 
     // Check edges
     {
@@ -3729,19 +3744,20 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleCommit) {
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, VertexDetachDeleteSingleAbort) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create dataset
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.CreateVertex();
-    auto vertex_to = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
-    auto res = acc.CreateEdge(&vertex_from, &vertex_to, et);
+    auto res = acc->CreateEdge(&vertex_from, &vertex_to, et);
     ASSERT_TRUE(res.HasValue());
     auto edge = res.GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
@@ -3779,29 +3795,29 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleAbort) {
     ASSERT_EQ(vertex_to.OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to.OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Detach delete vertex, but abort the transaction
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Delete must fail
     {
-      auto ret = acc.DeleteVertex(&*vertex_from);
+      auto ret = acc->DeleteVertex(&*vertex_from);
       ASSERT_TRUE(ret.HasError());
       ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES);
     }
 
     // Detach delete vertex
     {
-      auto ret = acc.DetachDeleteVertex(&*vertex_from);
+      auto ret = acc->DetachDeleteVertex(&*vertex_from);
       ASSERT_TRUE(ret.HasValue());
       ASSERT_TRUE(*ret);
     }
@@ -3843,18 +3859,18 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check dataset
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Check edges
     ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
@@ -3884,29 +3900,29 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Detach delete vertex
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
-    auto et = acc.NameToEdgeType("et5");
+    auto et = acc->NameToEdgeType("et5");
 
     // Delete must fail
     {
-      auto ret = acc.DeleteVertex(&*vertex_from);
+      auto ret = acc->DeleteVertex(&*vertex_from);
       ASSERT_TRUE(ret.HasError());
       ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES);
     }
 
     // Detach delete vertex
     {
-      auto ret = acc.DetachDeleteVertex(&*vertex_from);
+      auto ret = acc->DetachDeleteVertex(&*vertex_from);
       ASSERT_TRUE(ret.HasValue());
       ASSERT_TRUE(*ret);
     }
@@ -3948,14 +3964,14 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleAbort) {
     ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
     ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check dataset
   {
-    auto acc = store.Access();
-    auto vertex_from = acc.FindVertex(gid_from, memgraph::storage::View::NEW);
-    auto vertex_to = acc.FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
     ASSERT_FALSE(vertex_from);
     ASSERT_TRUE(vertex_to);
 
@@ -3973,46 +3989,47 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteSingleAbort) {
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleAbort) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = GetParam()}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = GetParam()}}));
   memgraph::storage::Gid gid_vertex1 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   memgraph::storage::Gid gid_vertex2 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create dataset
   {
-    auto acc = store.Access();
-    auto vertex1 = acc.CreateVertex();
-    auto vertex2 = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex1 = acc->CreateVertex();
+    auto vertex2 = acc->CreateVertex();
 
     gid_vertex1 = vertex1.Gid();
     gid_vertex2 = vertex2.Gid();
 
-    auto et1 = acc.NameToEdgeType("et1");
-    auto et2 = acc.NameToEdgeType("et2");
-    auto et3 = acc.NameToEdgeType("et3");
-    auto et4 = acc.NameToEdgeType("et4");
+    auto et1 = acc->NameToEdgeType("et1");
+    auto et2 = acc->NameToEdgeType("et2");
+    auto et3 = acc->NameToEdgeType("et3");
+    auto et4 = acc->NameToEdgeType("et4");
 
-    auto res1 = acc.CreateEdge(&vertex1, &vertex2, et1);
+    auto res1 = acc->CreateEdge(&vertex1, &vertex2, et1);
     ASSERT_TRUE(res1.HasValue());
     auto edge1 = res1.GetValue();
     ASSERT_EQ(edge1.EdgeType(), et1);
     ASSERT_EQ(edge1.FromVertex(), vertex1);
     ASSERT_EQ(edge1.ToVertex(), vertex2);
 
-    auto res2 = acc.CreateEdge(&vertex2, &vertex1, et2);
+    auto res2 = acc->CreateEdge(&vertex2, &vertex1, et2);
     ASSERT_TRUE(res2.HasValue());
     auto edge2 = res2.GetValue();
     ASSERT_EQ(edge2.EdgeType(), et2);
     ASSERT_EQ(edge2.FromVertex(), vertex2);
     ASSERT_EQ(edge2.ToVertex(), vertex1);
 
-    auto res3 = acc.CreateEdge(&vertex1, &vertex1, et3);
+    auto res3 = acc->CreateEdge(&vertex1, &vertex1, et3);
     ASSERT_TRUE(res3.HasValue());
     auto edge3 = res3.GetValue();
     ASSERT_EQ(edge3.EdgeType(), et3);
     ASSERT_EQ(edge3.FromVertex(), vertex1);
     ASSERT_EQ(edge3.ToVertex(), vertex1);
 
-    auto res4 = acc.CreateEdge(&vertex2, &vertex2, et4);
+    auto res4 = acc->CreateEdge(&vertex2, &vertex2, et4);
     ASSERT_TRUE(res4.HasValue());
     auto edge4 = res4.GetValue();
     ASSERT_EQ(edge4.EdgeType(), et4);
@@ -4101,32 +4118,32 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleAbort) {
       }
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Detach delete vertex, but abort the transaction
   {
-    auto acc = store.Access();
-    auto vertex1 = acc.FindVertex(gid_vertex1, memgraph::storage::View::NEW);
-    auto vertex2 = acc.FindVertex(gid_vertex2, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex1 = acc->FindVertex(gid_vertex1, memgraph::storage::View::NEW);
+    auto vertex2 = acc->FindVertex(gid_vertex2, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex1);
     ASSERT_TRUE(vertex2);
 
-    auto et1 = acc.NameToEdgeType("et1");
-    auto et2 = acc.NameToEdgeType("et2");
-    auto et3 = acc.NameToEdgeType("et3");
-    auto et4 = acc.NameToEdgeType("et4");
+    auto et1 = acc->NameToEdgeType("et1");
+    auto et2 = acc->NameToEdgeType("et2");
+    auto et3 = acc->NameToEdgeType("et3");
+    auto et4 = acc->NameToEdgeType("et4");
 
     // Delete must fail
     {
-      auto ret = acc.DeleteVertex(&*vertex1);
+      auto ret = acc->DeleteVertex(&*vertex1);
       ASSERT_TRUE(ret.HasError());
       ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES);
     }
 
     // Detach delete vertex
     {
-      auto ret = acc.DetachDeleteVertex(&*vertex1);
+      auto ret = acc->DetachDeleteVertex(&*vertex1);
       ASSERT_TRUE(ret.HasValue());
       ASSERT_TRUE(*ret);
     }
@@ -4239,21 +4256,21 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleAbort) {
       ASSERT_EQ(e.ToVertex(), *vertex2);
     }
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check dataset
   {
-    auto acc = store.Access();
-    auto vertex1 = acc.FindVertex(gid_vertex1, memgraph::storage::View::NEW);
-    auto vertex2 = acc.FindVertex(gid_vertex2, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex1 = acc->FindVertex(gid_vertex1, memgraph::storage::View::NEW);
+    auto vertex2 = acc->FindVertex(gid_vertex2, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex1);
     ASSERT_TRUE(vertex2);
 
-    auto et1 = acc.NameToEdgeType("et1");
-    auto et2 = acc.NameToEdgeType("et2");
-    auto et3 = acc.NameToEdgeType("et3");
-    auto et4 = acc.NameToEdgeType("et4");
+    auto et1 = acc->NameToEdgeType("et1");
+    auto et2 = acc->NameToEdgeType("et2");
+    auto et3 = acc->NameToEdgeType("et3");
+    auto et4 = acc->NameToEdgeType("et4");
 
     // Check edges
     {
@@ -4417,32 +4434,32 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleAbort) {
       }
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Detach delete vertex
   {
-    auto acc = store.Access();
-    auto vertex1 = acc.FindVertex(gid_vertex1, memgraph::storage::View::NEW);
-    auto vertex2 = acc.FindVertex(gid_vertex2, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex1 = acc->FindVertex(gid_vertex1, memgraph::storage::View::NEW);
+    auto vertex2 = acc->FindVertex(gid_vertex2, memgraph::storage::View::NEW);
     ASSERT_TRUE(vertex1);
     ASSERT_TRUE(vertex2);
 
-    auto et1 = acc.NameToEdgeType("et1");
-    auto et2 = acc.NameToEdgeType("et2");
-    auto et3 = acc.NameToEdgeType("et3");
-    auto et4 = acc.NameToEdgeType("et4");
+    auto et1 = acc->NameToEdgeType("et1");
+    auto et2 = acc->NameToEdgeType("et2");
+    auto et3 = acc->NameToEdgeType("et3");
+    auto et4 = acc->NameToEdgeType("et4");
 
     // Delete must fail
     {
-      auto ret = acc.DeleteVertex(&*vertex1);
+      auto ret = acc->DeleteVertex(&*vertex1);
       ASSERT_TRUE(ret.HasError());
       ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES);
     }
 
     // Detach delete vertex
     {
-      auto ret = acc.DetachDeleteVertex(&*vertex1);
+      auto ret = acc->DetachDeleteVertex(&*vertex1);
       ASSERT_TRUE(ret.HasValue());
       ASSERT_TRUE(*ret);
     }
@@ -4555,18 +4572,18 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleAbort) {
       ASSERT_EQ(e.ToVertex(), *vertex2);
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check dataset
   {
-    auto acc = store.Access();
-    auto vertex1 = acc.FindVertex(gid_vertex1, memgraph::storage::View::NEW);
-    auto vertex2 = acc.FindVertex(gid_vertex2, memgraph::storage::View::NEW);
+    auto acc = store->Access();
+    auto vertex1 = acc->FindVertex(gid_vertex1, memgraph::storage::View::NEW);
+    auto vertex2 = acc->FindVertex(gid_vertex2, memgraph::storage::View::NEW);
     ASSERT_FALSE(vertex1);
     ASSERT_TRUE(vertex2);
 
-    auto et4 = acc.NameToEdgeType("et4");
+    auto et4 = acc->NameToEdgeType("et4");
 
     // Check edges
     {
@@ -4618,19 +4635,20 @@ TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleAbort) {
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST(StorageWithProperties, EdgePropertyCommit) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = true}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = true}}));
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    auto et = acc.NameToEdgeType("et5");
-    auto edge = acc.CreateEdge(&vertex, &vertex, et).GetValue();
+    auto et = acc->NameToEdgeType("et5");
+    auto edge = acc->CreateEdge(&vertex, &vertex, et).GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
     ASSERT_EQ(edge.FromVertex(), vertex);
     ASSERT_EQ(edge.ToVertex(), vertex);
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
@@ -4661,15 +4679,15 @@ TEST(StorageWithProperties, EdgePropertyCommit) {
       ASSERT_EQ(properties[property].ValueString(), "nandare");
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
     {
@@ -4685,20 +4703,20 @@ TEST(StorageWithProperties, EdgePropertyCommit) {
       ASSERT_EQ(properties[property].ValueString(), "nandare");
     }
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     {
       auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue());
@@ -4722,56 +4740,57 @@ TEST(StorageWithProperties, EdgePropertyCommit) {
       ASSERT_TRUE(old_value->IsNull());
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::OLD)->size(), 0);
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST(StorageWithProperties, EdgePropertyAbort) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = true}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = true}}));
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
 
   // Create the vertex.
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    auto et = acc.NameToEdgeType("et5");
-    auto edge = acc.CreateEdge(&vertex, &vertex, et).GetValue();
+    auto et = acc->NameToEdgeType("et5");
+    auto edge = acc->CreateEdge(&vertex, &vertex, et).GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
     ASSERT_EQ(edge.FromVertex(), vertex);
     ASSERT_EQ(edge.ToVertex(), vertex);
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Set property 5 to "nandare", but abort the transaction.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
@@ -4802,39 +4821,39 @@ TEST(StorageWithProperties, EdgePropertyAbort) {
       ASSERT_EQ(properties[property].ValueString(), "nandare");
     }
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check that property 5 is null.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::OLD)->size(), 0);
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Set property 5 to "nandare".
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
@@ -4865,17 +4884,17 @@ TEST(StorageWithProperties, EdgePropertyAbort) {
       ASSERT_EQ(properties[property].ValueString(), "nandare");
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check that property 5 is "nandare".
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
     {
@@ -4891,22 +4910,22 @@ TEST(StorageWithProperties, EdgePropertyAbort) {
       ASSERT_EQ(properties[property].ValueString(), "nandare");
     }
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Set property 5 to null, but abort the transaction.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
     {
@@ -4938,17 +4957,17 @@ TEST(StorageWithProperties, EdgePropertyAbort) {
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Check that property 5 is "nandare".
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
     {
@@ -4964,22 +4983,22 @@ TEST(StorageWithProperties, EdgePropertyAbort) {
       ASSERT_EQ(properties[property].ValueString(), "nandare");
     }
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
 
   // Set property 5 to null.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
     {
@@ -5011,59 +5030,60 @@ TEST(StorageWithProperties, EdgePropertyAbort) {
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Check that property 5 is null.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::OLD)->size(), 0);
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST(StorageWithProperties, EdgePropertySerializationError) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = true}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = true}}));
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    auto et = acc.NameToEdgeType("et5");
-    auto edge = acc.CreateEdge(&vertex, &vertex, et).GetValue();
+    auto et = acc->NameToEdgeType("et5");
+    auto edge = acc->CreateEdge(&vertex, &vertex, et).GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
     ASSERT_EQ(edge.FromVertex(), vertex);
     ASSERT_EQ(edge.ToVertex(), vertex);
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
-  auto acc1 = store.Access();
-  auto acc2 = store.Access();
+  auto acc1 = store->Access();
+  auto acc2 = store->Access();
 
   // Set property 1 to 123 in accessor 1.
   {
-    auto vertex = acc1.FindVertex(gid, memgraph::storage::View::OLD);
+    auto vertex = acc1->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property1 = acc1.NameToProperty("property1");
-    auto property2 = acc1.NameToProperty("property2");
+    auto property1 = acc1->NameToProperty("property1");
+    auto property2 = acc1->NameToProperty("property2");
 
     ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::NEW)->IsNull());
@@ -5092,12 +5112,12 @@ TEST(StorageWithProperties, EdgePropertySerializationError) {
 
   // Set property 2 to "nandare" in accessor 2.
   {
-    auto vertex = acc2.FindVertex(gid, memgraph::storage::View::OLD);
+    auto vertex = acc2->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property1 = acc2.NameToProperty("property1");
-    auto property2 = acc2.NameToProperty("property2");
+    auto property1 = acc2->NameToProperty("property1");
+    auto property2 = acc2->NameToProperty("property2");
 
     ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::NEW)->IsNull());
@@ -5114,18 +5134,18 @@ TEST(StorageWithProperties, EdgePropertySerializationError) {
   }
 
   // Finalize both accessors.
-  ASSERT_FALSE(acc1.Commit().HasError());
-  acc2.Abort();
+  ASSERT_FALSE(acc1->Commit().HasError());
+  acc2->Abort();
 
   // Check which properties exist.
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property1 = acc.NameToProperty("property1");
-    auto property2 = acc.NameToProperty("property2");
+    auto property1 = acc->NameToProperty("property1");
+    auto property2 = acc->NameToProperty("property2");
 
     ASSERT_EQ(edge.GetProperty(property1, memgraph::storage::View::OLD)->ValueInt(), 123);
     ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::OLD)->IsNull());
@@ -5143,21 +5163,22 @@ TEST(StorageWithProperties, EdgePropertySerializationError) {
       ASSERT_EQ(properties[property1].ValueInt(), 123);
     }
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 TEST(StorageWithProperties, EdgePropertyClear) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = true}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = true}}));
   memgraph::storage::Gid gid;
-  auto property1 = store.NameToProperty("property1");
-  auto property2 = store.NameToProperty("property2");
+  auto property1 = store->NameToProperty("property1");
+  auto property2 = store->NameToProperty("property2");
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    auto et = acc.NameToEdgeType("et5");
-    auto edge = acc.CreateEdge(&vertex, &vertex, et).GetValue();
+    auto et = acc->NameToEdgeType("et5");
+    auto edge = acc->CreateEdge(&vertex, &vertex, et).GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
     ASSERT_EQ(edge.FromVertex(), vertex);
     ASSERT_EQ(edge.ToVertex(), vertex);
@@ -5166,11 +5187,11 @@ TEST(StorageWithProperties, EdgePropertyClear) {
     ASSERT_TRUE(old_value.HasValue());
     ASSERT_TRUE(old_value->IsNull());
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
@@ -5199,11 +5220,11 @@ TEST(StorageWithProperties, EdgePropertyClear) {
     ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW).GetValue().size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
@@ -5211,11 +5232,11 @@ TEST(StorageWithProperties, EdgePropertyClear) {
     ASSERT_TRUE(old_value.HasValue());
     ASSERT_TRUE(old_value->IsNull());
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
@@ -5245,11 +5266,11 @@ TEST(StorageWithProperties, EdgePropertyClear) {
     ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW).GetValue().size(), 0);
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
@@ -5257,32 +5278,33 @@ TEST(StorageWithProperties, EdgePropertyClear) {
     ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW).GetValue().size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST(StorageWithoutProperties, EdgePropertyAbort) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = false}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = false}}));
   memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    auto et = acc.NameToEdgeType("et5");
-    auto edge = acc.CreateEdge(&vertex, &vertex, et).GetValue();
+    auto et = acc->NameToEdgeType("et5");
+    auto edge = acc->CreateEdge(&vertex, &vertex, et).GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
     ASSERT_EQ(edge.FromVertex(), vertex);
     ASSERT_EQ(edge.ToVertex(), vertex);
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
@@ -5305,15 +5327,15 @@ TEST(StorageWithoutProperties, EdgePropertyAbort) {
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
 
-    acc.Abort();
+    acc->Abort();
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
-    auto property = acc.NameToProperty("property5");
+    auto property = acc->NameToProperty("property5");
 
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::OLD)->size(), 0);
@@ -5321,49 +5343,51 @@ TEST(StorageWithoutProperties, EdgePropertyAbort) {
     ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
     ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
 
-    auto other_property = acc.NameToProperty("other");
+    auto other_property = acc->NameToProperty("other");
 
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
     ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 TEST(StorageWithoutProperties, EdgePropertyClear) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = false}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = false}}));
   memgraph::storage::Gid gid;
   {
-    auto acc = store.Access();
-    auto vertex = acc.CreateVertex();
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
     gid = vertex.Gid();
-    auto et = acc.NameToEdgeType("et5");
-    auto edge = acc.CreateEdge(&vertex, &vertex, et).GetValue();
+    auto et = acc->NameToEdgeType("et5");
+    auto edge = acc->CreateEdge(&vertex, &vertex, et).GetValue();
     ASSERT_EQ(edge.EdgeType(), et);
     ASSERT_EQ(edge.FromVertex(), vertex);
     ASSERT_EQ(edge.ToVertex(), vertex);
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = store.Access();
-    auto vertex = acc.FindVertex(gid, memgraph::storage::View::OLD);
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(vertex);
     auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
 
     ASSERT_EQ(edge.ClearProperties().GetError(), memgraph::storage::Error::PROPERTIES_DISABLED);
 
-    acc.Abort();
+    acc->Abort();
   }
 }
 
 TEST(StorageWithProperties, EdgeNonexistentPropertyAPI) {
-  memgraph::storage::Storage store({.items = {.properties_on_edges = true}});
+  std::unique_ptr<memgraph::storage::Storage> store(
+      new memgraph::storage::InMemoryStorage({.items = {.properties_on_edges = true}}));
 
-  auto property = store.NameToProperty("property");
+  auto property = store->NameToProperty("property");
 
-  auto acc = store.Access();
-  auto vertex = acc.CreateVertex();
-  auto edge = acc.CreateEdge(&vertex, &vertex, acc.NameToEdgeType("edge"));
+  auto acc = store->Access();
+  auto vertex = acc->CreateVertex();
+  auto edge = acc->CreateEdge(&vertex, &vertex, acc->NameToEdgeType("edge"));
   ASSERT_TRUE(edge.HasValue());
 
   // Check state before (OLD view).
@@ -5387,5 +5411,5 @@ TEST(StorageWithProperties, EdgeNonexistentPropertyAPI) {
   ASSERT_EQ(edge->Properties(memgraph::storage::View::NEW)->size(), 1);
   ASSERT_EQ(*edge->GetProperty(property, memgraph::storage::View::NEW), memgraph::storage::PropertyValue("value"));
 
-  ASSERT_FALSE(acc.Commit().HasError());
+  ASSERT_FALSE(acc->Commit().HasError());
 }
diff --git a/tests/unit/storage_v2_edge_ondisk.cpp b/tests/unit/storage_v2_edge_ondisk.cpp
new file mode 100644
index 000000000..4edc099d1
--- /dev/null
+++ b/tests/unit/storage_v2_edge_ondisk.cpp
@@ -0,0 +1,5815 @@
+// Copyright 2023 Memgraph Ltd.
+//
+// Use of this software is governed by the Business Source License
+// included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
+// License, and you may not use this file except in compliance with the Business Source License.
+//
+// As of the Change Date specified in that file, in accordance with
+// the Business Source License, use of this software will be governed
+// by the Apache License, Version 2.0, included in the file
+// licenses/APL.txt.
+
+#include <gmock/gmock.h>
+#include <gtest/gtest.h>
+
+#include <limits>
+
+#include "disk_test_utils.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/storage.hpp"
+
+using testing::UnorderedElementsAre;
+
+class StorageEdgeTest : public ::testing::TestWithParam<bool> {};
+
+INSTANTIATE_TEST_CASE_P(EdgesWithProperties, StorageEdgeTest, ::testing::Values(true));
+INSTANTIATE_TEST_CASE_P(EdgesWithoutProperties, StorageEdgeTest, ::testing::Values(false));
+
+const std::string testSuite = "storage_v2_edge_ondisk";
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, EdgeCreateFromSmallerCommit) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create vertices
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
+    gid_from = vertex_from.Gid();
+    gid_to = vertex_to.Gid();
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex_from);
+    ASSERT_EQ(edge.ToVertex(), *vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, EdgeCreateFromLargerCommit) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create vertices
+  {
+    auto acc = store->Access();
+    auto vertex_to = acc->CreateVertex();
+    auto vertex_from = acc->CreateVertex();
+    gid_to = vertex_to.Gid();
+    gid_from = vertex_from.Gid();
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex_from);
+    ASSERT_EQ(edge.ToVertex(), *vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, EdgeCreateFromSameCommit) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_vertex = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create vertex
+  {
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    gid_vertex = vertex.Gid();
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex, &*vertex, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex);
+    ASSERT_EQ(edge.ToVertex(), *vertex);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, EdgeCreateFromSmallerAbort) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create vertices
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
+    gid_from = vertex_from.Gid();
+    gid_to = vertex_to.Gid();
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge, but abort the transaction
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex_from);
+    ASSERT_EQ(edge.ToVertex(), *vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    acc->Abort();
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex_from);
+    ASSERT_EQ(edge.ToVertex(), *vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, EdgeCreateFromLargerAbort) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create vertices
+  {
+    auto acc = store->Access();
+    auto vertex_to = acc->CreateVertex();
+    auto vertex_from = acc->CreateVertex();
+    gid_to = vertex_to.Gid();
+    gid_from = vertex_from.Gid();
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge, but abort the transaction
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex_from);
+    ASSERT_EQ(edge.ToVertex(), *vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    acc->Abort();
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex_from);
+    ASSERT_EQ(edge.ToVertex(), *vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, EdgeCreateFromSameAbort) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_vertex = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create vertex
+  {
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    gid_vertex = vertex.Gid();
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge, but abort the transaction
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex, &*vertex, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex);
+    ASSERT_EQ(edge.ToVertex(), *vertex);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+
+    acc->Abort();
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex, &*vertex, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex);
+    ASSERT_EQ(edge.ToVertex(), *vertex);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerCommit) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create vertices
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
+    gid_from = vertex_from.Gid();
+    gid_to = vertex_to.Gid();
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex_from);
+    ASSERT_EQ(edge.ToVertex(), *vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Delete edge
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto edge = vertex_from->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto res = acc->DeleteEdge(&edge);
+    ASSERT_TRUE(res.HasValue());
+    ASSERT_TRUE(res.GetValue());
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, EdgeDeleteFromLargerCommit) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create vertices
+  {
+    auto acc = store->Access();
+    auto vertex_to = acc->CreateVertex();
+    auto vertex_from = acc->CreateVertex();
+    gid_from = vertex_from.Gid();
+    gid_to = vertex_to.Gid();
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex_from);
+    ASSERT_EQ(edge.ToVertex(), *vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Delete edge
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto edge = vertex_from->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto res = acc->DeleteEdge(&edge);
+    ASSERT_TRUE(res.HasValue());
+    ASSERT_TRUE(res.GetValue());
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, EdgeDeleteFromSameCommit) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_vertex = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create vertex
+  {
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    gid_vertex = vertex.Gid();
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex, &*vertex, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex);
+    ASSERT_EQ(edge.ToVertex(), *vertex);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Delete edge
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto res = acc->DeleteEdge(&edge);
+    ASSERT_TRUE(res.HasValue());
+    ASSERT_TRUE(res.GetValue());
+
+    // Check edges without filters
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, EdgeDeleteFromSmallerAbort) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create vertices
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
+    gid_from = vertex_from.Gid();
+    gid_to = vertex_to.Gid();
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex_from);
+    ASSERT_EQ(edge.ToVertex(), *vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Delete the edge, but abort the transaction
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto edge = vertex_from->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto res = acc->DeleteEdge(&edge);
+    ASSERT_TRUE(res.HasValue());
+    ASSERT_TRUE(res.GetValue());
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+
+    acc->Abort();
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Delete the edge
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto edge = vertex_from->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto res = acc->DeleteEdge(&edge);
+    ASSERT_TRUE(res.HasValue());
+    ASSERT_TRUE(res.GetValue());
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, EdgeDeleteFromLargerAbort) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create vertices
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
+    gid_from = vertex_from.Gid();
+    gid_to = vertex_to.Gid();
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge
+  {
+    auto acc = store->Access();
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex_from, &*vertex_to, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex_from);
+    ASSERT_EQ(edge.ToVertex(), *vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Delete the edge, but abort the transaction
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto edge = vertex_from->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto res = acc->DeleteEdge(&edge);
+    ASSERT_TRUE(res.HasValue());
+    ASSERT_TRUE(res.GetValue());
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+
+    acc->Abort();
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Delete the edge
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto edge = vertex_from->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto res = acc->DeleteEdge(&edge);
+    ASSERT_TRUE(res.HasValue());
+    ASSERT_TRUE(res.GetValue());
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 1);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_from)->size(), 1);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD, {}, &*vertex_to)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, EdgeDeleteFromSameAbort) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_vertex = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create vertex
+  {
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    gid_vertex = vertex.Gid();
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Create edge
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&*vertex, &*vertex, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), *vertex);
+    ASSERT_EQ(edge.ToVertex(), *vertex);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 0);
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Delete the edge, but abort the transaction
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto res = acc->DeleteEdge(&edge);
+    ASSERT_TRUE(res.HasValue());
+    ASSERT_TRUE(res.GetValue());
+
+    // Check edges without filters
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+
+    acc->Abort();
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges without filters
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW, {other_et}, &*vertex)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Delete the edge
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto res = acc->DeleteEdge(&edge);
+    ASSERT_TRUE(res.HasValue());
+    ASSERT_TRUE(res.GetValue());
+
+    // Check edges without filters
+    {
+      auto ret = vertex->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex);
+      ASSERT_EQ(e.ToVertex(), *vertex);
+    }
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 0);
+
+    auto other_et = acc->NameToEdgeType("other");
+
+    // Check edges with filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et})->size(), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {et, other_et})->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {}, &*vertex)->size(), 1);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD, {other_et}, &*vertex)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check whether the edge exists
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid_vertex, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    // Check edges without filters
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex->OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, VertexDetachDeleteSingleCommit) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create dataset
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&vertex_from, &vertex_to, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), vertex_from);
+    ASSERT_EQ(edge.ToVertex(), vertex_to);
+
+    gid_from = vertex_from.Gid();
+    gid_to = vertex_to.Gid();
+
+    // Check edges
+    ASSERT_EQ(vertex_from.InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from.InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from.OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from.OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), vertex_from);
+      ASSERT_EQ(e.ToVertex(), vertex_to);
+    }
+    {
+      auto ret = vertex_to.InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to.InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), vertex_from);
+      ASSERT_EQ(e.ToVertex(), vertex_to);
+    }
+    ASSERT_EQ(vertex_to.OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to.OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Detach delete vertex
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Delete must fail
+    {
+      auto ret = acc->DeleteVertex(&*vertex_from);
+      ASSERT_TRUE(ret.HasError());
+      ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES);
+    }
+
+    // Detach delete vertex
+    {
+      auto ret = acc->DetachDeleteVertex(&*vertex_from);
+      ASSERT_TRUE(ret.HasValue());
+      ASSERT_TRUE(*ret);
+    }
+
+    // Check edges
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    ASSERT_EQ(vertex_from->InDegree(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    ASSERT_EQ(vertex_from->OutDegree(memgraph::storage::View::NEW).GetError(),
+              memgraph::storage::Error::DELETED_OBJECT);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check dataset
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_FALSE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    // Check edges
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleCommit) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_vertex1 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  memgraph::storage::Gid gid_vertex2 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create dataset
+  {
+    auto acc = store->Access();
+    auto vertex1 = acc->CreateVertex();
+    auto vertex2 = acc->CreateVertex();
+
+    gid_vertex1 = vertex1.Gid();
+    gid_vertex2 = vertex2.Gid();
+
+    auto et1 = acc->NameToEdgeType("et1");
+    auto et2 = acc->NameToEdgeType("et2");
+    auto et3 = acc->NameToEdgeType("et3");
+    auto et4 = acc->NameToEdgeType("et4");
+
+    auto res1 = acc->CreateEdge(&vertex1, &vertex2, et1);
+    ASSERT_TRUE(res1.HasValue());
+    auto edge1 = res1.GetValue();
+    ASSERT_EQ(edge1.EdgeType(), et1);
+    ASSERT_EQ(edge1.FromVertex(), vertex1);
+    ASSERT_EQ(edge1.ToVertex(), vertex2);
+
+    auto res2 = acc->CreateEdge(&vertex2, &vertex1, et2);
+    ASSERT_TRUE(res2.HasValue());
+    auto edge2 = res2.GetValue();
+    ASSERT_EQ(edge2.EdgeType(), et2);
+    ASSERT_EQ(edge2.FromVertex(), vertex2);
+    ASSERT_EQ(edge2.ToVertex(), vertex1);
+
+    auto res3 = acc->CreateEdge(&vertex1, &vertex1, et3);
+    ASSERT_TRUE(res3.HasValue());
+    auto edge3 = res3.GetValue();
+    ASSERT_EQ(edge3.EdgeType(), et3);
+    ASSERT_EQ(edge3.FromVertex(), vertex1);
+    ASSERT_EQ(edge3.ToVertex(), vertex1);
+
+    auto res4 = acc->CreateEdge(&vertex2, &vertex2, et4);
+    ASSERT_TRUE(res4.HasValue());
+    auto edge4 = res4.GetValue();
+    ASSERT_EQ(edge4.EdgeType(), et4);
+    ASSERT_EQ(edge4.FromVertex(), vertex2);
+    ASSERT_EQ(edge4.ToVertex(), vertex2);
+
+    // Check edges
+    {
+      auto ret = vertex1.InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1.InDegree(memgraph::storage::View::NEW), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), vertex2);
+        ASSERT_EQ(e.ToVertex(), vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), vertex1);
+        ASSERT_EQ(e.ToVertex(), vertex1);
+      }
+    }
+    {
+      auto ret = vertex1.OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1.OutDegree(memgraph::storage::View::NEW), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), vertex1);
+        ASSERT_EQ(e.ToVertex(), vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), vertex1);
+        ASSERT_EQ(e.ToVertex(), vertex1);
+      }
+    }
+    {
+      auto ret = vertex2.InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2.InDegree(memgraph::storage::View::NEW), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), vertex1);
+        ASSERT_EQ(e.ToVertex(), vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), vertex2);
+        ASSERT_EQ(e.ToVertex(), vertex2);
+      }
+    }
+    {
+      auto ret = vertex2.OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2.OutDegree(memgraph::storage::View::NEW), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), vertex2);
+        ASSERT_EQ(e.ToVertex(), vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), vertex2);
+        ASSERT_EQ(e.ToVertex(), vertex2);
+      }
+    }
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Detach delete vertex
+  {
+    auto acc = store->Access();
+    auto vertex1 = acc->FindVertex(gid_vertex1, memgraph::storage::View::NEW);
+    auto vertex2 = acc->FindVertex(gid_vertex2, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex1);
+    ASSERT_TRUE(vertex2);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex1);
+    acc->PrefetchInEdges(*vertex1);
+    acc->PrefetchOutEdges(*vertex2);
+    acc->PrefetchInEdges(*vertex2);
+
+    auto et1 = acc->NameToEdgeType("et1");
+    auto et2 = acc->NameToEdgeType("et2");
+    auto et3 = acc->NameToEdgeType("et3");
+    auto et4 = acc->NameToEdgeType("et4");
+
+    // Delete must fail
+    {
+      auto ret = acc->DeleteVertex(&*vertex1);
+      ASSERT_TRUE(ret.HasError());
+      ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES);
+    }
+
+    // Detach delete vertex
+    {
+      auto ret = acc->DetachDeleteVertex(&*vertex1);
+      ASSERT_TRUE(ret.HasValue());
+      ASSERT_TRUE(*ret);
+    }
+
+    // Check edges
+    {
+      auto ret = vertex1->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1->InDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+    }
+    ASSERT_EQ(vertex1->InEdges(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    ASSERT_EQ(vertex1->InDegree(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    {
+      auto ret = vertex1->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1->OutDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+    }
+    ASSERT_EQ(vertex1->OutEdges(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    ASSERT_EQ(vertex1->OutDegree(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    {
+      auto ret = vertex2->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2->InDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+    }
+    {
+      auto ret = vertex2->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+    {
+      auto ret = vertex2->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2->OutDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+    }
+    {
+      auto ret = vertex2->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check dataset
+  {
+    auto acc = store->Access();
+    auto vertex1 = acc->FindVertex(gid_vertex1, memgraph::storage::View::NEW);
+    auto vertex2 = acc->FindVertex(gid_vertex2, memgraph::storage::View::NEW);
+    ASSERT_FALSE(vertex1);
+    ASSERT_TRUE(vertex2);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex2);
+    acc->PrefetchInEdges(*vertex2);
+
+    auto et4 = acc->NameToEdgeType("et4");
+
+    // Check edges
+    {
+      auto ret = vertex2->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+    {
+      auto ret = vertex2->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+    {
+      auto ret = vertex2->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+    {
+      auto ret = vertex2->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, VertexDetachDeleteSingleAbort) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_from = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  memgraph::storage::Gid gid_to = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create dataset
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->CreateVertex();
+    auto vertex_to = acc->CreateVertex();
+
+    auto et = acc->NameToEdgeType("et5");
+
+    auto res = acc->CreateEdge(&vertex_from, &vertex_to, et);
+    ASSERT_TRUE(res.HasValue());
+    auto edge = res.GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), vertex_from);
+    ASSERT_EQ(edge.ToVertex(), vertex_to);
+
+    gid_from = vertex_from.Gid();
+    gid_to = vertex_to.Gid();
+
+    // Check edges
+    ASSERT_EQ(vertex_from.InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from.InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from.OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from.OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), vertex_from);
+      ASSERT_EQ(e.ToVertex(), vertex_to);
+    }
+    {
+      auto ret = vertex_to.InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to.InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), vertex_from);
+      ASSERT_EQ(e.ToVertex(), vertex_to);
+    }
+    ASSERT_EQ(vertex_to.OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to.OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Detach delete vertex, but abort the transaction
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Delete must fail
+    {
+      auto ret = acc->DeleteVertex(&*vertex_from);
+      ASSERT_TRUE(ret.HasError());
+      ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES);
+    }
+
+    // Detach delete vertex
+    {
+      auto ret = acc->DetachDeleteVertex(&*vertex_from);
+      ASSERT_TRUE(ret.HasValue());
+      ASSERT_TRUE(*ret);
+    }
+
+    // Check edges
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    ASSERT_EQ(vertex_from->InDegree(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    ASSERT_EQ(vertex_from->OutDegree(memgraph::storage::View::NEW).GetError(),
+              memgraph::storage::Error::DELETED_OBJECT);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    acc->Abort();
+  }
+
+  // Check dataset
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Check edges
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::NEW), 0);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Detach delete vertex
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    auto et = acc->NameToEdgeType("et5");
+
+    // Delete must fail
+    {
+      auto ret = acc->DeleteVertex(&*vertex_from);
+      ASSERT_TRUE(ret.HasError());
+      ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES);
+    }
+
+    // Detach delete vertex
+    {
+      auto ret = acc->DetachDeleteVertex(&*vertex_from);
+      ASSERT_TRUE(ret.HasValue());
+      ASSERT_TRUE(*ret);
+    }
+
+    // Check edges
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_from->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_from->InEdges(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    ASSERT_EQ(vertex_from->InDegree(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    {
+      auto ret = vertex_from->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_from->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_from->OutEdges(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    ASSERT_EQ(vertex_from->OutDegree(memgraph::storage::View::NEW).GetError(),
+              memgraph::storage::Error::DELETED_OBJECT);
+    {
+      auto ret = vertex_to->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et);
+      ASSERT_EQ(e.FromVertex(), *vertex_from);
+      ASSERT_EQ(e.ToVertex(), *vertex_to);
+    }
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check dataset
+  {
+    auto acc = store->Access();
+    auto vertex_from = acc->FindVertex(gid_from, memgraph::storage::View::NEW);
+    auto vertex_to = acc->FindVertex(gid_to, memgraph::storage::View::NEW);
+    ASSERT_FALSE(vertex_from);
+    ASSERT_TRUE(vertex_to);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex_from);
+    acc->PrefetchInEdges(*vertex_from);
+    acc->PrefetchOutEdges(*vertex_to);
+    acc->PrefetchInEdges(*vertex_to);
+
+    // Check edges
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->InEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->InDegree(memgraph::storage::View::NEW), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::OLD), 0);
+    ASSERT_EQ(vertex_to->OutEdges(memgraph::storage::View::NEW)->size(), 0);
+    ASSERT_EQ(*vertex_to->OutDegree(memgraph::storage::View::NEW), 0);
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST_P(StorageEdgeTest, VertexDetachDeleteMultipleAbort) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = GetParam();
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid_vertex1 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  memgraph::storage::Gid gid_vertex2 = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create dataset
+  {
+    auto acc = store->Access();
+    auto vertex1 = acc->CreateVertex();
+    auto vertex2 = acc->CreateVertex();
+
+    gid_vertex1 = vertex1.Gid();
+    gid_vertex2 = vertex2.Gid();
+
+    auto et1 = acc->NameToEdgeType("et1");
+    auto et2 = acc->NameToEdgeType("et2");
+    auto et3 = acc->NameToEdgeType("et3");
+    auto et4 = acc->NameToEdgeType("et4");
+
+    auto res1 = acc->CreateEdge(&vertex1, &vertex2, et1);
+    ASSERT_TRUE(res1.HasValue());
+    auto edge1 = res1.GetValue();
+    ASSERT_EQ(edge1.EdgeType(), et1);
+    ASSERT_EQ(edge1.FromVertex(), vertex1);
+    ASSERT_EQ(edge1.ToVertex(), vertex2);
+
+    auto res2 = acc->CreateEdge(&vertex2, &vertex1, et2);
+    ASSERT_TRUE(res2.HasValue());
+    auto edge2 = res2.GetValue();
+    ASSERT_EQ(edge2.EdgeType(), et2);
+    ASSERT_EQ(edge2.FromVertex(), vertex2);
+    ASSERT_EQ(edge2.ToVertex(), vertex1);
+
+    auto res3 = acc->CreateEdge(&vertex1, &vertex1, et3);
+    ASSERT_TRUE(res3.HasValue());
+    auto edge3 = res3.GetValue();
+    ASSERT_EQ(edge3.EdgeType(), et3);
+    ASSERT_EQ(edge3.FromVertex(), vertex1);
+    ASSERT_EQ(edge3.ToVertex(), vertex1);
+
+    auto res4 = acc->CreateEdge(&vertex2, &vertex2, et4);
+    ASSERT_TRUE(res4.HasValue());
+    auto edge4 = res4.GetValue();
+    ASSERT_EQ(edge4.EdgeType(), et4);
+    ASSERT_EQ(edge4.FromVertex(), vertex2);
+    ASSERT_EQ(edge4.ToVertex(), vertex2);
+
+    // Check edges
+    {
+      auto ret = vertex1.InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1.InDegree(memgraph::storage::View::NEW), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), vertex2);
+        ASSERT_EQ(e.ToVertex(), vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), vertex1);
+        ASSERT_EQ(e.ToVertex(), vertex1);
+      }
+    }
+    {
+      auto ret = vertex1.OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1.OutDegree(memgraph::storage::View::NEW), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), vertex1);
+        ASSERT_EQ(e.ToVertex(), vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), vertex1);
+        ASSERT_EQ(e.ToVertex(), vertex1);
+      }
+    }
+    {
+      auto ret = vertex2.InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2.InDegree(memgraph::storage::View::NEW), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), vertex1);
+        ASSERT_EQ(e.ToVertex(), vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), vertex2);
+        ASSERT_EQ(e.ToVertex(), vertex2);
+      }
+    }
+    {
+      auto ret = vertex2.OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2.OutDegree(memgraph::storage::View::NEW), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), vertex2);
+        ASSERT_EQ(e.ToVertex(), vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), vertex2);
+        ASSERT_EQ(e.ToVertex(), vertex2);
+      }
+    }
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Detach delete vertex, but abort the transaction
+  {
+    auto acc = store->Access();
+    auto vertex1 = acc->FindVertex(gid_vertex1, memgraph::storage::View::NEW);
+    auto vertex2 = acc->FindVertex(gid_vertex2, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex1);
+    ASSERT_TRUE(vertex2);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex1);
+    acc->PrefetchInEdges(*vertex1);
+    acc->PrefetchOutEdges(*vertex2);
+    acc->PrefetchInEdges(*vertex2);
+
+    auto et1 = acc->NameToEdgeType("et1");
+    auto et2 = acc->NameToEdgeType("et2");
+    auto et3 = acc->NameToEdgeType("et3");
+    auto et4 = acc->NameToEdgeType("et4");
+
+    // Delete must fail
+    {
+      auto ret = acc->DeleteVertex(&*vertex1);
+      ASSERT_TRUE(ret.HasError());
+      ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES);
+    }
+
+    // Detach delete vertex
+    {
+      auto ret = acc->DetachDeleteVertex(&*vertex1);
+      ASSERT_TRUE(ret.HasValue());
+      ASSERT_TRUE(*ret);
+    }
+
+    // Check edges
+    {
+      auto ret = vertex1->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1->InDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+    }
+    ASSERT_EQ(vertex1->InEdges(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    ASSERT_EQ(vertex1->InDegree(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    {
+      auto ret = vertex1->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1->OutDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+    }
+    ASSERT_EQ(vertex1->OutEdges(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    ASSERT_EQ(vertex1->OutDegree(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    {
+      auto ret = vertex2->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2->InDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+    }
+    {
+      auto ret = vertex2->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+    {
+      auto ret = vertex2->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2->OutDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+    }
+    {
+      auto ret = vertex2->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+
+    acc->Abort();
+  }
+
+  // Check dataset
+  {
+    auto acc = store->Access();
+    auto vertex1 = acc->FindVertex(gid_vertex1, memgraph::storage::View::NEW);
+    auto vertex2 = acc->FindVertex(gid_vertex2, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex1);
+    ASSERT_TRUE(vertex2);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex1);
+    acc->PrefetchInEdges(*vertex1);
+    acc->PrefetchOutEdges(*vertex2);
+    acc->PrefetchInEdges(*vertex2);
+
+    auto et1 = acc->NameToEdgeType("et1");
+    auto et2 = acc->NameToEdgeType("et2");
+    auto et3 = acc->NameToEdgeType("et3");
+    auto et4 = acc->NameToEdgeType("et4");
+
+    // Check edges
+    {
+      auto ret = vertex1->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1->InDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+    }
+    {
+      auto ret = vertex1->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1->InDegree(memgraph::storage::View::NEW), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+    }
+    {
+      auto ret = vertex1->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1->OutDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+    }
+    {
+      auto ret = vertex1->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1->OutDegree(memgraph::storage::View::NEW), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+    }
+    {
+      auto ret = vertex2->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2->InDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+    }
+    {
+      auto ret = vertex2->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2->InDegree(memgraph::storage::View::NEW), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+    }
+    {
+      auto ret = vertex2->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2->OutDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+    }
+    {
+      auto ret = vertex2->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2->OutDegree(memgraph::storage::View::NEW), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+    }
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Detach delete vertex
+  {
+    auto acc = store->Access();
+    auto vertex1 = acc->FindVertex(gid_vertex1, memgraph::storage::View::NEW);
+    auto vertex2 = acc->FindVertex(gid_vertex2, memgraph::storage::View::NEW);
+    ASSERT_TRUE(vertex1);
+    ASSERT_TRUE(vertex2);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex1);
+    acc->PrefetchInEdges(*vertex1);
+    acc->PrefetchOutEdges(*vertex2);
+    acc->PrefetchInEdges(*vertex2);
+
+    auto et1 = acc->NameToEdgeType("et1");
+    auto et2 = acc->NameToEdgeType("et2");
+    auto et3 = acc->NameToEdgeType("et3");
+    auto et4 = acc->NameToEdgeType("et4");
+
+    // Delete must fail
+    {
+      auto ret = acc->DeleteVertex(&*vertex1);
+      ASSERT_TRUE(ret.HasError());
+      ASSERT_EQ(ret.GetError(), memgraph::storage::Error::VERTEX_HAS_EDGES);
+    }
+
+    // Detach delete vertex
+    {
+      auto ret = acc->DetachDeleteVertex(&*vertex1);
+      ASSERT_TRUE(ret.HasValue());
+      ASSERT_TRUE(*ret);
+    }
+
+    // Check edges
+    {
+      auto ret = vertex1->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1->InDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+    }
+    ASSERT_EQ(vertex1->InEdges(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    ASSERT_EQ(vertex1->InDegree(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    {
+      auto ret = vertex1->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex1->OutDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et3);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+    }
+    ASSERT_EQ(vertex1->OutEdges(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    ASSERT_EQ(vertex1->OutDegree(memgraph::storage::View::NEW).GetError(), memgraph::storage::Error::DELETED_OBJECT);
+    {
+      auto ret = vertex2->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2->InDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et1);
+        ASSERT_EQ(e.FromVertex(), *vertex1);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+    }
+    {
+      auto ret = vertex2->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+    {
+      auto ret = vertex2->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      std::sort(edges.begin(), edges.end(), [](const auto &a, const auto &b) { return a.EdgeType() < b.EdgeType(); });
+      ASSERT_EQ(edges.size(), 2);
+      ASSERT_EQ(*vertex2->OutDegree(memgraph::storage::View::OLD), 2);
+      {
+        auto e = edges[0];
+        ASSERT_EQ(e.EdgeType(), et2);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex1);
+      }
+      {
+        auto e = edges[1];
+        ASSERT_EQ(e.EdgeType(), et4);
+        ASSERT_EQ(e.FromVertex(), *vertex2);
+        ASSERT_EQ(e.ToVertex(), *vertex2);
+      }
+    }
+    {
+      auto ret = vertex2->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check dataset
+  {
+    auto acc = store->Access();
+    auto vertex1 = acc->FindVertex(gid_vertex1, memgraph::storage::View::NEW);
+    auto vertex2 = acc->FindVertex(gid_vertex2, memgraph::storage::View::NEW);
+    ASSERT_FALSE(vertex1);
+    ASSERT_TRUE(vertex2);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex2);
+    acc->PrefetchInEdges(*vertex2);
+
+    auto et4 = acc->NameToEdgeType("et4");
+
+    // Check edges
+    {
+      auto ret = vertex2->InEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->InDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+    {
+      auto ret = vertex2->InEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->InDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+    {
+      auto ret = vertex2->OutEdges(memgraph::storage::View::OLD);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->OutDegree(memgraph::storage::View::OLD), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+    {
+      auto ret = vertex2->OutEdges(memgraph::storage::View::NEW);
+      ASSERT_TRUE(ret.HasValue());
+      auto edges = ret.GetValue();
+      ASSERT_EQ(edges.size(), 1);
+      ASSERT_EQ(*vertex2->OutDegree(memgraph::storage::View::NEW), 1);
+      auto e = edges[0];
+      ASSERT_EQ(e.EdgeType(), et4);
+      ASSERT_EQ(e.FromVertex(), *vertex2);
+      ASSERT_EQ(e.ToVertex(), *vertex2);
+    }
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST(StorageWithProperties, EdgePropertyCommit) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = true;
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  {
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    gid = vertex.Gid();
+    auto et = acc->NameToEdgeType("et5");
+    auto edge = acc->CreateEdge(&vertex, &vertex, et).GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), vertex);
+    ASSERT_EQ(edge.ToVertex(), vertex);
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    {
+      auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue("temporary"));
+      ASSERT_TRUE(old_value.HasValue());
+      ASSERT_TRUE(old_value->IsNull());
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::NEW)->ValueString(), "temporary");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "temporary");
+    }
+
+    {
+      auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue("nandare"));
+      ASSERT_TRUE(old_value.HasValue());
+      ASSERT_FALSE(old_value->IsNull());
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::NEW)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::OLD).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::NEW)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    auto other_property = acc->NameToProperty("other");
+
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
+
+    acc->Abort();
+  }
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    {
+      auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue());
+      ASSERT_TRUE(old_value.HasValue());
+      ASSERT_FALSE(old_value->IsNull());
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::OLD).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    {
+      auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue());
+      ASSERT_TRUE(old_value.HasValue());
+      ASSERT_TRUE(old_value->IsNull());
+    }
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    auto other_property = acc->NameToProperty("other");
+
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
+
+    acc->Abort();
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST(StorageWithProperties, EdgePropertyAbort) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = true;
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+
+  // Create the vertex.
+  {
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    gid = vertex.Gid();
+    auto et = acc->NameToEdgeType("et5");
+    auto edge = acc->CreateEdge(&vertex, &vertex, et).GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), vertex);
+    ASSERT_EQ(edge.ToVertex(), vertex);
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Set property 5 to "nandare", but abort the transaction.
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    {
+      auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue("temporary"));
+      ASSERT_TRUE(old_value.HasValue());
+      ASSERT_TRUE(old_value->IsNull());
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::NEW)->ValueString(), "temporary");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "temporary");
+    }
+
+    {
+      auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue("nandare"));
+      ASSERT_TRUE(old_value.HasValue());
+      ASSERT_FALSE(old_value->IsNull());
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::NEW)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    acc->Abort();
+  }
+
+  // Check that property 5 is null.
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    auto other_property = acc->NameToProperty("other");
+
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
+
+    acc->Abort();
+  }
+
+  // Set property 5 to "nandare".
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    {
+      auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue("temporary"));
+      ASSERT_TRUE(old_value.HasValue());
+      ASSERT_TRUE(old_value->IsNull());
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::NEW)->ValueString(), "temporary");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "temporary");
+    }
+
+    {
+      auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue("nandare"));
+      ASSERT_TRUE(old_value.HasValue());
+      ASSERT_FALSE(old_value->IsNull());
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::NEW)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check that property 5 is "nandare".
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::OLD).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::NEW)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    auto other_property = acc->NameToProperty("other");
+
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
+
+    acc->Abort();
+  }
+
+  // Set property 5 to null, but abort the transaction.
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::OLD).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::NEW)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    {
+      auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue());
+      ASSERT_TRUE(old_value.HasValue());
+      ASSERT_FALSE(old_value->IsNull());
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::OLD).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    acc->Abort();
+  }
+
+  // Check that property 5 is "nandare".
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::OLD).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::NEW)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    auto other_property = acc->NameToProperty("other");
+
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
+
+    acc->Abort();
+  }
+
+  // Set property 5 to null.
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::OLD).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::NEW)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    {
+      auto old_value = edge.SetProperty(property, memgraph::storage::PropertyValue());
+      ASSERT_TRUE(old_value.HasValue());
+      ASSERT_FALSE(old_value->IsNull());
+    }
+
+    ASSERT_EQ(edge.GetProperty(property, memgraph::storage::View::OLD)->ValueString(), "nandare");
+    {
+      auto properties = edge.Properties(memgraph::storage::View::OLD).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property].ValueString(), "nandare");
+    }
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  // Check that property 5 is null.
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    auto other_property = acc->NameToProperty("other");
+
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
+
+    acc->Abort();
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST(StorageWithProperties, EdgePropertySerializationError) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = true;
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  {
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    gid = vertex.Gid();
+    auto et = acc->NameToEdgeType("et5");
+    auto edge = acc->CreateEdge(&vertex, &vertex, et).GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), vertex);
+    ASSERT_EQ(edge.ToVertex(), vertex);
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+
+  auto acc1 = store->Access();
+  auto acc2 = store->Access();
+
+  // Set property 1 to 123 in accessor 1.
+  {
+    auto vertex = acc1->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc1->PrefetchOutEdges(*vertex);
+    acc1->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property1 = acc1->NameToProperty("property1");
+    auto property2 = acc1->NameToProperty("property2");
+
+    ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    {
+      auto old_value = edge.SetProperty(property1, memgraph::storage::PropertyValue(123));
+      ASSERT_TRUE(old_value.HasValue());
+      ASSERT_TRUE(old_value->IsNull());
+    }
+
+    ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_EQ(edge.GetProperty(property1, memgraph::storage::View::NEW)->ValueInt(), 123);
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::OLD)->size(), 0);
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property1].ValueInt(), 123);
+    }
+  }
+
+  // Set property 2 to "nandare" in accessor 2.
+  {
+    auto vertex = acc2->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc2->PrefetchOutEdges(*vertex);
+    acc2->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property1 = acc2->NameToProperty("property1");
+    auto property2 = acc2->NameToProperty("property2");
+
+    ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::OLD)->size(), 0);
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    {
+      // Conflict write.
+      // DiskStorage has SNAPSHOT isolation level by default, so it will not see this change until commit.
+      // DiskStorage has optimistic transactions so it will fail on Commit.
+      auto res = edge.SetProperty(property2, memgraph::storage::PropertyValue("nandare"));
+      ASSERT_FALSE(res.HasError());
+    }
+  }
+
+  // Finalize both accessors.
+  ASSERT_FALSE(acc1->Commit().HasError());
+  auto res = acc2->Commit();
+  ASSERT_TRUE(res.HasError());
+  ASSERT_EQ(std::get<memgraph::storage::SerializationError>(res.GetError()), memgraph::storage::SerializationError());
+
+  // Check which properties exist.
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property1 = acc->NameToProperty("property1");
+    auto property2 = acc->NameToProperty("property2");
+
+    ASSERT_EQ(edge.GetProperty(property1, memgraph::storage::View::OLD)->ValueInt(), 123);
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::OLD)->IsNull());
+    {
+      auto properties = edge.Properties(memgraph::storage::View::OLD).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property1].ValueInt(), 123);
+    }
+
+    ASSERT_EQ(edge.GetProperty(property1, memgraph::storage::View::NEW)->ValueInt(), 123);
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
+    {
+      auto properties = edge.Properties(memgraph::storage::View::NEW).GetValue();
+      ASSERT_EQ(properties.size(), 1);
+      ASSERT_EQ(properties[property1].ValueInt(), 123);
+    }
+
+    acc->Abort();
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+TEST(StorageWithProperties, EdgePropertyClear) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = true;
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid;
+  auto property1 = store->NameToProperty("property1");
+  auto property2 = store->NameToProperty("property2");
+  {
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    gid = vertex.Gid();
+    auto et = acc->NameToEdgeType("et5");
+    auto edge = acc->CreateEdge(&vertex, &vertex, et).GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), vertex);
+    ASSERT_EQ(edge.ToVertex(), vertex);
+
+    auto old_value = edge.SetProperty(property1, memgraph::storage::PropertyValue("value"));
+    ASSERT_TRUE(old_value.HasValue());
+    ASSERT_TRUE(old_value->IsNull());
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    ASSERT_EQ(edge.GetProperty(property1, memgraph::storage::View::OLD)->ValueString(), "value");
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_THAT(edge.Properties(memgraph::storage::View::OLD).GetValue(),
+                UnorderedElementsAre(std::pair(property1, memgraph::storage::PropertyValue("value"))));
+
+    {
+      auto old_values = edge.ClearProperties();
+      ASSERT_TRUE(old_values.HasValue());
+      ASSERT_FALSE(old_values->empty());
+    }
+
+    ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW).GetValue().size(), 0);
+
+    {
+      auto old_values = edge.ClearProperties();
+      ASSERT_TRUE(old_values.HasValue());
+      ASSERT_TRUE(old_values->empty());
+    }
+
+    ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW).GetValue().size(), 0);
+
+    acc->Abort();
+  }
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto old_value = edge.SetProperty(property2, memgraph::storage::PropertyValue(42));
+    ASSERT_TRUE(old_value.HasValue());
+    ASSERT_TRUE(old_value->IsNull());
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    ASSERT_EQ(edge.GetProperty(property1, memgraph::storage::View::OLD)->ValueString(), "value");
+    ASSERT_EQ(edge.GetProperty(property2, memgraph::storage::View::OLD)->ValueInt(), 42);
+    ASSERT_THAT(edge.Properties(memgraph::storage::View::OLD).GetValue(),
+                UnorderedElementsAre(std::pair(property1, memgraph::storage::PropertyValue("value")),
+                                     std::pair(property2, memgraph::storage::PropertyValue(42))));
+
+    {
+      auto old_values = edge.ClearProperties();
+      ASSERT_TRUE(old_values.HasValue());
+      ASSERT_FALSE(old_values->empty());
+    }
+
+    ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW).GetValue().size(), 0);
+
+    {
+      auto old_values = edge.ClearProperties();
+      ASSERT_TRUE(old_values.HasValue());
+      ASSERT_TRUE(old_values->empty());
+    }
+
+    ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW).GetValue().size(), 0);
+
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    ASSERT_TRUE(edge.GetProperty(property1, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(property2, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW).GetValue().size(), 0);
+
+    acc->Abort();
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+// NOLINTNEXTLINE(hicpp-special-member-functions)
+TEST(StorageWithoutProperties, EdgePropertyAbort) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = false;
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid = memgraph::storage::Gid::FromUint(std::numeric_limits<uint64_t>::max());
+  {
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    gid = vertex.Gid();
+    auto et = acc->NameToEdgeType("et5");
+    auto edge = acc->CreateEdge(&vertex, &vertex, et).GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), vertex);
+    ASSERT_EQ(edge.ToVertex(), vertex);
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    {
+      auto res = edge.SetProperty(property, memgraph::storage::PropertyValue("temporary"));
+      ASSERT_TRUE(res.HasError());
+      ASSERT_EQ(res.GetError(), memgraph::storage::Error::PROPERTIES_DISABLED);
+    }
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    {
+      auto res = edge.SetProperty(property, memgraph::storage::PropertyValue("nandare"));
+      ASSERT_TRUE(res.HasError());
+      ASSERT_EQ(res.GetError(), memgraph::storage::Error::PROPERTIES_DISABLED);
+    }
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    acc->Abort();
+  }
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    auto property = acc->NameToProperty("property5");
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::OLD)->size(), 0);
+
+    ASSERT_TRUE(edge.GetProperty(property, memgraph::storage::View::NEW)->IsNull());
+    ASSERT_EQ(edge.Properties(memgraph::storage::View::NEW)->size(), 0);
+
+    auto other_property = acc->NameToProperty("other");
+
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::OLD)->IsNull());
+    ASSERT_TRUE(edge.GetProperty(other_property, memgraph::storage::View::NEW)->IsNull());
+
+    acc->Abort();
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+TEST(StorageWithoutProperties, EdgePropertyClear) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = false;
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+  memgraph::storage::Gid gid;
+  {
+    auto acc = store->Access();
+    auto vertex = acc->CreateVertex();
+    gid = vertex.Gid();
+    auto et = acc->NameToEdgeType("et5");
+    auto edge = acc->CreateEdge(&vertex, &vertex, et).GetValue();
+    ASSERT_EQ(edge.EdgeType(), et);
+    ASSERT_EQ(edge.FromVertex(), vertex);
+    ASSERT_EQ(edge.ToVertex(), vertex);
+    ASSERT_FALSE(acc->Commit().HasError());
+  }
+  {
+    auto acc = store->Access();
+    auto vertex = acc->FindVertex(gid, memgraph::storage::View::OLD);
+    ASSERT_TRUE(vertex);
+    // We prefetch edges implicitly when go thorough query Accessor
+    acc->PrefetchOutEdges(*vertex);
+    acc->PrefetchInEdges(*vertex);
+
+    auto edge = vertex->OutEdges(memgraph::storage::View::NEW).GetValue()[0];
+
+    ASSERT_EQ(edge.ClearProperties().GetError(), memgraph::storage::Error::PROPERTIES_DISABLED);
+
+    acc->Abort();
+  }
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
+
+TEST(StorageWithProperties, EdgeNonexistentPropertyAPI) {
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.items.properties_on_edges = true;
+  std::unique_ptr<memgraph::storage::Storage> store(new memgraph::storage::DiskStorage(config));
+
+  auto property = store->NameToProperty("property");
+
+  auto acc = store->Access();
+  auto vertex = acc->CreateVertex();
+  auto edge = acc->CreateEdge(&vertex, &vertex, acc->NameToEdgeType("edge"));
+  ASSERT_TRUE(edge.HasValue());
+
+  // Check state before (OLD view).
+  ASSERT_EQ(edge->Properties(memgraph::storage::View::OLD).GetError(), memgraph::storage::Error::NONEXISTENT_OBJECT);
+  ASSERT_EQ(edge->GetProperty(property, memgraph::storage::View::OLD).GetError(),
+            memgraph::storage::Error::NONEXISTENT_OBJECT);
+
+  // Check state before (NEW view).
+  ASSERT_EQ(edge->Properties(memgraph::storage::View::NEW)->size(), 0);
+  ASSERT_EQ(*edge->GetProperty(property, memgraph::storage::View::NEW), memgraph::storage::PropertyValue());
+
+  // Modify edge.
+  ASSERT_TRUE(edge->SetProperty(property, memgraph::storage::PropertyValue("value"))->IsNull());
+
+  // Check state after (OLD view).
+  ASSERT_EQ(edge->Properties(memgraph::storage::View::OLD).GetError(), memgraph::storage::Error::NONEXISTENT_OBJECT);
+  ASSERT_EQ(edge->GetProperty(property, memgraph::storage::View::OLD).GetError(),
+            memgraph::storage::Error::NONEXISTENT_OBJECT);
+
+  // Check state after (NEW view).
+  ASSERT_EQ(edge->Properties(memgraph::storage::View::NEW)->size(), 1);
+  ASSERT_EQ(*edge->GetProperty(property, memgraph::storage::View::NEW), memgraph::storage::PropertyValue("value"));
+
+  ASSERT_FALSE(acc->Commit().HasError());
+  disk_test_utils::RemoveRocksDbDirs(testSuite);
+}
diff --git a/tests/unit/storage_v2_gc.cpp b/tests/unit/storage_v2_gc.cpp
index 401e1a155..8074e833c 100644
--- a/tests/unit/storage_v2_gc.cpp
+++ b/tests/unit/storage_v2_gc.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -12,7 +12,7 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
-#include "storage/v2/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 using testing::UnorderedElementsAre;
 
@@ -24,26 +24,27 @@ using testing::UnorderedElementsAre;
 // then verify that GC didn't delete anything it shouldn't have.
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST(StorageV2Gc, Sanity) {
-  memgraph::storage::Storage storage(memgraph::storage::Config{
-      .gc = {.type = memgraph::storage::Config::Gc::Type::PERIODIC, .interval = std::chrono::milliseconds(100)}});
+  std::unique_ptr<memgraph::storage::Storage> storage(
+      std::make_unique<memgraph::storage::InMemoryStorage>(memgraph::storage::Config{
+          .gc = {.type = memgraph::storage::Config::Gc::Type::PERIODIC, .interval = std::chrono::milliseconds(100)}}));
 
   std::vector<memgraph::storage::Gid> vertices;
 
   {
-    auto acc = storage.Access();
+    auto acc = storage->Access();
     // Create some vertices, but delete some of them immediately.
     for (uint64_t i = 0; i < 1000; ++i) {
-      auto vertex = acc.CreateVertex();
+      auto vertex = acc->CreateVertex();
       vertices.push_back(vertex.Gid());
     }
 
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
     for (uint64_t i = 0; i < 1000; ++i) {
-      auto vertex = acc.FindVertex(vertices[i], memgraph::storage::View::OLD);
+      auto vertex = acc->FindVertex(vertices[i], memgraph::storage::View::OLD);
       ASSERT_TRUE(vertex.has_value());
       if (i % 5 == 0) {
-        EXPECT_FALSE(acc.DeleteVertex(&vertex.value()).HasError());
+        EXPECT_FALSE(acc->DeleteVertex(&vertex.value()).HasError());
       }
     }
 
@@ -51,20 +52,20 @@ TEST(StorageV2Gc, Sanity) {
     std::this_thread::sleep_for(std::chrono::milliseconds(300));
 
     for (uint64_t i = 0; i < 1000; ++i) {
-      auto vertex_old = acc.FindVertex(vertices[i], memgraph::storage::View::OLD);
-      auto vertex_new = acc.FindVertex(vertices[i], memgraph::storage::View::NEW);
+      auto vertex_old = acc->FindVertex(vertices[i], memgraph::storage::View::OLD);
+      auto vertex_new = acc->FindVertex(vertices[i], memgraph::storage::View::NEW);
       EXPECT_TRUE(vertex_old.has_value());
       EXPECT_EQ(vertex_new.has_value(), i % 5 != 0);
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Verify existing vertices and add labels to some of them.
   {
-    auto acc = storage.Access();
+    auto acc = storage->Access();
     for (uint64_t i = 0; i < 1000; ++i) {
-      auto vertex = acc.FindVertex(vertices[i], memgraph::storage::View::OLD);
+      auto vertex = acc->FindVertex(vertices[i], memgraph::storage::View::OLD);
       EXPECT_EQ(vertex.has_value(), i % 5 != 0);
 
       if (vertex.has_value()) {
@@ -79,7 +80,7 @@ TEST(StorageV2Gc, Sanity) {
 
     // Verify labels.
     for (uint64_t i = 0; i < 1000; ++i) {
-      auto vertex = acc.FindVertex(vertices[i], memgraph::storage::View::NEW);
+      auto vertex = acc->FindVertex(vertices[i], memgraph::storage::View::NEW);
       EXPECT_EQ(vertex.has_value(), i % 5 != 0);
 
       if (vertex.has_value()) {
@@ -95,32 +96,32 @@ TEST(StorageV2Gc, Sanity) {
       }
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Add and remove some edges.
   {
-    auto acc = storage.Access();
+    auto acc = storage->Access();
     for (uint64_t i = 0; i < 1000; ++i) {
-      auto from_vertex = acc.FindVertex(vertices[i], memgraph::storage::View::OLD);
-      auto to_vertex = acc.FindVertex(vertices[(i + 1) % 1000], memgraph::storage::View::OLD);
+      auto from_vertex = acc->FindVertex(vertices[i], memgraph::storage::View::OLD);
+      auto to_vertex = acc->FindVertex(vertices[(i + 1) % 1000], memgraph::storage::View::OLD);
       EXPECT_EQ(from_vertex.has_value(), i % 5 != 0);
       EXPECT_EQ(to_vertex.has_value(), (i + 1) % 5 != 0);
 
       if (from_vertex.has_value() && to_vertex.has_value()) {
         EXPECT_FALSE(
-            acc.CreateEdge(&from_vertex.value(), &to_vertex.value(), memgraph::storage::EdgeTypeId::FromUint(i))
+            acc->CreateEdge(&from_vertex.value(), &to_vertex.value(), memgraph::storage::EdgeTypeId::FromUint(i))
                 .HasError());
       }
     }
 
     // Detach delete some vertices.
     for (uint64_t i = 0; i < 1000; ++i) {
-      auto vertex = acc.FindVertex(vertices[i], memgraph::storage::View::NEW);
+      auto vertex = acc->FindVertex(vertices[i], memgraph::storage::View::NEW);
       EXPECT_EQ(vertex.has_value(), i % 5 != 0);
       if (vertex.has_value()) {
         if (i % 3 == 0) {
-          EXPECT_FALSE(acc.DetachDeleteVertex(&vertex.value()).HasError());
+          EXPECT_FALSE(acc->DetachDeleteVertex(&vertex.value()).HasError());
         }
       }
     }
@@ -130,7 +131,7 @@ TEST(StorageV2Gc, Sanity) {
 
     // Vertify edges.
     for (uint64_t i = 0; i < 1000; ++i) {
-      auto vertex = acc.FindVertex(vertices[i], memgraph::storage::View::NEW);
+      auto vertex = acc->FindVertex(vertices[i], memgraph::storage::View::NEW);
       EXPECT_EQ(vertex.has_value(), i % 5 != 0 && i % 3 != 0);
       if (vertex.has_value()) {
         auto out_edges = vertex->OutEdges(memgraph::storage::View::NEW);
@@ -153,7 +154,7 @@ TEST(StorageV2Gc, Sanity) {
       }
     }
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -166,33 +167,34 @@ TEST(StorageV2Gc, Sanity) {
 //    transaction 1 can still see them with that label.
 // NOLINTNEXTLINE(hicpp-special-member-functions)
 TEST(StorageV2Gc, Indices) {
-  memgraph::storage::Storage storage(memgraph::storage::Config{
-      .gc = {.type = memgraph::storage::Config::Gc::Type::PERIODIC, .interval = std::chrono::milliseconds(100)}});
+  std::unique_ptr<memgraph::storage::Storage> storage(
+      std::make_unique<memgraph::storage::InMemoryStorage>(memgraph::storage::Config{
+          .gc = {.type = memgraph::storage::Config::Gc::Type::PERIODIC, .interval = std::chrono::milliseconds(100)}}));
 
-  ASSERT_FALSE(storage.CreateIndex(storage.NameToLabel("label")).HasError());
+  ASSERT_FALSE(storage->CreateIndex(storage->NameToLabel("label")).HasError());
 
   {
-    auto acc0 = storage.Access();
+    auto acc0 = storage->Access();
     for (uint64_t i = 0; i < 1000; ++i) {
-      auto vertex = acc0.CreateVertex();
-      ASSERT_TRUE(*vertex.AddLabel(acc0.NameToLabel("label")));
+      auto vertex = acc0->CreateVertex();
+      ASSERT_TRUE(*vertex.AddLabel(acc0->NameToLabel("label")));
     }
-    ASSERT_FALSE(acc0.Commit().HasError());
+    ASSERT_FALSE(acc0->Commit().HasError());
   }
   {
-    auto acc1 = storage.Access();
+    auto acc1 = storage->Access();
 
-    auto acc2 = storage.Access();
-    for (auto vertex : acc2.Vertices(memgraph::storage::View::OLD)) {
-      ASSERT_TRUE(*vertex.RemoveLabel(acc2.NameToLabel("label")));
+    auto acc2 = storage->Access();
+    for (auto vertex : acc2->Vertices(memgraph::storage::View::OLD)) {
+      ASSERT_TRUE(*vertex.RemoveLabel(acc2->NameToLabel("label")));
     }
-    ASSERT_FALSE(acc2.Commit().HasError());
+    ASSERT_FALSE(acc2->Commit().HasError());
 
     // Wait for GC.
     std::this_thread::sleep_for(std::chrono::milliseconds(300));
 
     std::set<memgraph::storage::Gid> gids;
-    for (auto vertex : acc1.Vertices(acc1.NameToLabel("label"), memgraph::storage::View::OLD)) {
+    for (auto vertex : acc1->Vertices(acc1->NameToLabel("label"), memgraph::storage::View::OLD)) {
       gids.insert(vertex.Gid());
     }
     EXPECT_EQ(gids.size(), 1000);
diff --git a/tests/unit/storage_v2_indices.cpp b/tests/unit/storage_v2_indices.cpp
index eb05277f7..9805e970f 100644
--- a/tests/unit/storage_v2_indices.cpp
+++ b/tests/unit/storage_v2_indices.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -10,33 +10,51 @@
 // licenses/APL.txt.
 
 #include <gmock/gmock.h>
+#include <gtest/gtest-typed-test.h>
 #include <gtest/gtest.h>
+#include <gtest/internal/gtest-type-util.h>
 
+#include "disk_test_utils.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/property_value.hpp"
-#include "storage/v2/storage.hpp"
 #include "storage/v2/temporal.hpp"
+#include "utils/rocksdb_serialization.hpp"
 
 // NOLINTNEXTLINE(google-build-using-namespace)
 using namespace memgraph::storage;
 
 using testing::IsEmpty;
+using testing::Types;
 using testing::UnorderedElementsAre;
 
 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage)
 #define ASSERT_NO_ERROR(result) ASSERT_FALSE((result).HasError())
 
+template <typename StorageType>
 class IndexTest : public testing::Test {
  protected:
   void SetUp() override {
-    auto acc = storage.Access();
-    prop_id = acc.NameToProperty("id");
-    prop_val = acc.NameToProperty("val");
-    label1 = acc.NameToLabel("label1");
-    label2 = acc.NameToLabel("label2");
+    config_ = disk_test_utils::GenerateOnDiskConfig(testSuite);
+    this->storage = std::make_unique<StorageType>(config_);
+    auto acc = this->storage->Access();
+    this->prop_id = acc->NameToProperty("id");
+    this->prop_val = acc->NameToProperty("val");
+    this->label1 = acc->NameToLabel("label1");
+    this->label2 = acc->NameToLabel("label2");
     vertex_id = 0;
   }
 
-  Storage storage;
+  void TearDown() override {
+    if (std::is_same<StorageType, memgraph::storage::DiskStorage>::value) {
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+    }
+    this->storage.reset(nullptr);
+  }
+
+  const std::string testSuite = "storage_v2_indices";
+  memgraph::storage::Config config_;
+  std::unique_ptr<memgraph::storage::Storage> storage;
   PropertyId prop_id;
   PropertyId prop_val;
   LabelId label1;
@@ -44,7 +62,7 @@ class IndexTest : public testing::Test {
 
   VertexAccessor CreateVertex(Storage::Accessor *accessor) {
     VertexAccessor vertex = accessor->CreateVertex();
-    MG_ASSERT(!vertex.SetProperty(prop_id, PropertyValue(vertex_id++)).HasError());
+    MG_ASSERT(!vertex.SetProperty(this->prop_id, PropertyValue(vertex_id++)).HasError());
     return vertex;
   }
 
@@ -52,7 +70,7 @@ class IndexTest : public testing::Test {
   std::vector<int64_t> GetIds(TIterable iterable, View view = View::OLD) {
     std::vector<int64_t> ret;
     for (auto vertex : iterable) {
-      ret.push_back(vertex.GetProperty(prop_id, view)->ValueInt());
+      ret.push_back(vertex.GetProperty(this->prop_id, view)->ValueInt());
     }
     return ret;
   }
@@ -61,165 +79,171 @@ class IndexTest : public testing::Test {
   int vertex_id;
 };
 
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+
+TYPED_TEST_CASE(IndexTest, StorageTypes);
+// TYPED_TEST_CASE(IndexTest, InMemoryStorageType);
+
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(IndexTest, LabelIndexCreate) {
+TYPED_TEST(IndexTest, LabelIndexCreate) {
   {
-    auto acc = storage.Access();
-    EXPECT_FALSE(acc.LabelIndexExists(label1));
+    auto acc = this->storage->Access();
+    EXPECT_FALSE(acc->LabelIndexExists(this->label1));
   }
-  EXPECT_EQ(storage.ListAllIndices().label.size(), 0);
+  EXPECT_EQ(this->storage->ListAllIndices().label.size(), 0);
 
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
     for (int i = 0; i < 10; ++i) {
-      auto vertex = CreateVertex(&acc);
-      ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
+      auto vertex = this->CreateVertex(acc.get());
+      ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? this->label1 : this->label2));
     }
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
-  EXPECT_FALSE(storage.CreateIndex(label1).HasError());
+  EXPECT_FALSE(this->storage->CreateIndex(this->label1).HasError());
 
   {
-    auto acc = storage.Access();
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
+    auto acc = this->storage->Access();
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
   }
 
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
     for (int i = 10; i < 20; ++i) {
-      auto vertex = CreateVertex(&acc);
-      ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
+      auto vertex = this->CreateVertex(acc.get());
+      ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? this->label1 : this->label2));
     }
 
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
+    /// Vertices needs to go to label index rocksdb
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
 
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::NEW),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::NEW),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
 
-    acc.Abort();
+    acc->Abort();
   }
 
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
     for (int i = 10; i < 20; ++i) {
-      auto vertex = CreateVertex(&acc);
-      ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
+      auto vertex = this->CreateVertex(acc.get());
+      ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? this->label1 : this->label2));
     }
 
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
 
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::NEW),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::NEW),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
 
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto acc = storage.Access();
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD),
+    auto acc = this->storage->Access();
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
 
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::NEW),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::NEW),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 21, 23, 25, 27, 29));
 
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(IndexTest, LabelIndexDrop) {
+TYPED_TEST(IndexTest, LabelIndexDrop) {
   {
-    auto acc = storage.Access();
-    EXPECT_FALSE(acc.LabelIndexExists(label1));
+    auto acc = this->storage->Access();
+    EXPECT_FALSE(acc->LabelIndexExists(this->label1));
   }
-  EXPECT_EQ(storage.ListAllIndices().label.size(), 0);
+  EXPECT_EQ(this->storage->ListAllIndices().label.size(), 0);
 
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
     for (int i = 0; i < 10; ++i) {
-      auto vertex = CreateVertex(&acc);
-      ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
+      auto vertex = this->CreateVertex(acc.get());
+      ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? this->label1 : this->label2));
     }
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
-  EXPECT_FALSE(storage.CreateIndex(label1).HasError());
+  EXPECT_FALSE(this->storage->CreateIndex(this->label1).HasError());
 
   {
-    auto acc = storage.Access();
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
+    auto acc = this->storage->Access();
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
   }
 
-  EXPECT_FALSE(storage.DropIndex(label1).HasError());
+  EXPECT_FALSE(this->storage->DropIndex(this->label1).HasError());
   {
-    auto acc = storage.Access();
-    EXPECT_FALSE(acc.LabelIndexExists(label1));
+    auto acc = this->storage->Access();
+    EXPECT_FALSE(acc->LabelIndexExists(this->label1));
   }
-  EXPECT_EQ(storage.ListAllIndices().label.size(), 0);
+  EXPECT_EQ(this->storage->ListAllIndices().label.size(), 0);
 
-  EXPECT_TRUE(storage.DropIndex(label1).HasError());
+  EXPECT_TRUE(this->storage->DropIndex(this->label1).HasError());
   {
-    auto acc = storage.Access();
-    EXPECT_FALSE(acc.LabelIndexExists(label1));
+    auto acc = this->storage->Access();
+    EXPECT_FALSE(acc->LabelIndexExists(this->label1));
   }
-  EXPECT_EQ(storage.ListAllIndices().label.size(), 0);
+  EXPECT_EQ(this->storage->ListAllIndices().label.size(), 0);
 
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
     for (int i = 10; i < 20; ++i) {
-      auto vertex = CreateVertex(&acc);
-      ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
+      auto vertex = this->CreateVertex(acc.get());
+      ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? this->label1 : this->label2));
     }
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
-  EXPECT_FALSE(storage.CreateIndex(label1).HasError());
+  EXPECT_FALSE(this->storage->CreateIndex(this->label1).HasError());
   {
-    auto acc = storage.Access();
-    EXPECT_TRUE(acc.LabelIndexExists(label1));
+    auto acc = this->storage->Access();
+    EXPECT_TRUE(acc->LabelIndexExists(this->label1));
   }
-  EXPECT_THAT(storage.ListAllIndices().label, UnorderedElementsAre(label1));
+  EXPECT_THAT(this->storage->ListAllIndices().label, UnorderedElementsAre(this->label1));
 
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
 
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
 
-    acc.AdvanceCommand();
+    acc->AdvanceCommand();
 
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::NEW),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::NEW),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW),
                 UnorderedElementsAre(1, 3, 5, 7, 9, 11, 13, 15, 17, 19));
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(IndexTest, LabelIndexBasic) {
+TYPED_TEST(IndexTest, LabelIndexBasic) {
   // The following steps are performed and index correctness is validated after
   // each step:
   // 1. Create 10 vertices numbered from 0 to 9.
@@ -227,186 +251,294 @@ TEST_F(IndexTest, LabelIndexBasic) {
   // 3. Remove Label1 from odd numbered vertices, and add it to even numbered
   //    vertices.
   // 4. Delete even numbered vertices.
-  EXPECT_FALSE(storage.CreateIndex(label1).HasError());
-  EXPECT_FALSE(storage.CreateIndex(label2).HasError());
+  EXPECT_FALSE(this->storage->CreateIndex(this->label1).HasError());
+  EXPECT_FALSE(this->storage->CreateIndex(this->label2).HasError());
 
-  auto acc = storage.Access();
-  EXPECT_THAT(storage.ListAllIndices().label, UnorderedElementsAre(label1, label2));
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label2, View::OLD), View::OLD), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label2, View::NEW), View::NEW), IsEmpty());
+  auto acc = this->storage->Access();
+  EXPECT_THAT(this->storage->ListAllIndices().label, UnorderedElementsAre(this->label1, this->label2));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, View::OLD), View::OLD), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, View::NEW), View::NEW), IsEmpty());
 
   for (int i = 0; i < 10; ++i) {
-    auto vertex = CreateVertex(&acc);
-    ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
+    auto vertex = this->CreateVertex(acc.get());
+    spdlog::debug("Created vertex with gid: {}", memgraph::utils::SerializeIdType(vertex.Gid()));
+    ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? this->label1 : this->label2));
   }
 
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label2, View::OLD), View::OLD), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
-  EXPECT_THAT(GetIds(acc.Vertices(label2, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, View::OLD), View::OLD), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
 
-  acc.AdvanceCommand();
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
-  EXPECT_THAT(GetIds(acc.Vertices(label2, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
-  EXPECT_THAT(GetIds(acc.Vertices(label2, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
+  acc->AdvanceCommand();
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
 
-  for (auto vertex : acc.Vertices(View::OLD)) {
-    int64_t id = vertex.GetProperty(prop_id, View::OLD)->ValueInt();
+  for (auto vertex : acc->Vertices(View::OLD)) {
+    int64_t id = vertex.GetProperty(this->prop_id, View::OLD)->ValueInt();
     if (id % 2) {
-      ASSERT_NO_ERROR(vertex.RemoveLabel(label1));
+      ASSERT_NO_ERROR(vertex.RemoveLabel(this->label1));
     } else {
-      ASSERT_NO_ERROR(vertex.AddLabel(label1));
+      ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
     }
   }
 
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
-  EXPECT_THAT(GetIds(acc.Vertices(label2, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
-  EXPECT_THAT(GetIds(acc.Vertices(label2, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
 
-  for (auto vertex : acc.Vertices(View::OLD)) {
-    int64_t id = vertex.GetProperty(prop_id, View::OLD)->ValueInt();
+  for (auto vertex : acc->Vertices(View::OLD)) {
+    int64_t id = vertex.GetProperty(this->prop_id, View::OLD)->ValueInt();
     if (id % 2 == 0) {
-      ASSERT_NO_ERROR(acc.DeleteVertex(&vertex));
+      ASSERT_NO_ERROR(acc->DeleteVertex(&vertex));
     }
   }
 
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
-  EXPECT_THAT(GetIds(acc.Vertices(label2, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label2, View::NEW), View::NEW), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, View::NEW), View::NEW), IsEmpty());
 
-  acc.AdvanceCommand();
+  acc->AdvanceCommand();
 
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label2, View::OLD), View::OLD), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label2, View::NEW), View::NEW), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, View::OLD), View::OLD), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, View::NEW), View::NEW), IsEmpty());
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(IndexTest, LabelIndexDuplicateVersions) {
+TYPED_TEST(IndexTest, LabelIndexDuplicateVersions) {
   // By removing labels and adding them again we create duplicate entries for
   // the same vertex in the index (they only differ by the timestamp). This test
   // checks that duplicates are properly filtered out.
-  EXPECT_FALSE(storage.CreateIndex(label1).HasError());
-  EXPECT_FALSE(storage.CreateIndex(label2).HasError());
+  EXPECT_FALSE(this->storage->CreateIndex(this->label1).HasError());
+  EXPECT_FALSE(this->storage->CreateIndex(this->label2).HasError());
 
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
     for (int i = 0; i < 5; ++i) {
-      auto vertex = CreateVertex(&acc);
-      ASSERT_NO_ERROR(vertex.AddLabel(label1));
+      auto vertex = this->CreateVertex(acc.get());
+      ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
     }
 
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
 
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto acc = storage.Access();
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
+    auto acc = this->storage->Access();
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
 
-    for (auto vertex : acc.Vertices(View::OLD)) {
-      ASSERT_NO_ERROR(vertex.RemoveLabel(label1));
+    for (auto vertex : acc->Vertices(View::OLD)) {
+      ASSERT_NO_ERROR(vertex.RemoveLabel(this->label1));
     }
 
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), IsEmpty());
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
 
-    for (auto vertex : acc.Vertices(View::OLD)) {
-      ASSERT_NO_ERROR(vertex.AddLabel(label1));
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW), IsEmpty());
+
+    for (auto vertex : acc->Vertices(View::OLD)) {
+      ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
     }
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
+
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(IndexTest, LabelIndexTransactionalIsolation) {
+// passes
+TYPED_TEST(IndexTest, LabelIndexTransactionalIsolation) {
   // Check that transactions only see entries they are supposed to see.
-  EXPECT_FALSE(storage.CreateIndex(label1).HasError());
-  EXPECT_FALSE(storage.CreateIndex(label2).HasError());
+  EXPECT_FALSE(this->storage->CreateIndex(this->label1).HasError());
+  EXPECT_FALSE(this->storage->CreateIndex(this->label2).HasError());
 
-  auto acc_before = storage.Access();
-  auto acc = storage.Access();
-  auto acc_after = storage.Access();
+  auto acc_before = this->storage->Access();
+  auto acc = this->storage->Access();
+  auto acc_after = this->storage->Access();
 
   for (int i = 0; i < 5; ++i) {
-    auto vertex = CreateVertex(&acc);
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
+    auto vertex = this->CreateVertex(acc.get());
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
   }
 
-  EXPECT_THAT(GetIds(acc.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
-  EXPECT_THAT(GetIds(acc_before.Vertices(label1, View::NEW), View::NEW), IsEmpty());
-  EXPECT_THAT(GetIds(acc_after.Vertices(label1, View::NEW), View::NEW), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
 
-  ASSERT_NO_ERROR(acc.Commit());
+  EXPECT_THAT(this->GetIds(acc_before->Vertices(this->label1, View::NEW), View::NEW), IsEmpty());
 
-  auto acc_after_commit = storage.Access();
+  EXPECT_THAT(this->GetIds(acc_after->Vertices(this->label1, View::NEW), View::NEW), IsEmpty());
 
-  EXPECT_THAT(GetIds(acc_before.Vertices(label1, View::NEW), View::NEW), IsEmpty());
-  EXPECT_THAT(GetIds(acc_after.Vertices(label1, View::NEW), View::NEW), IsEmpty());
-  EXPECT_THAT(GetIds(acc_after_commit.Vertices(label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
+  ASSERT_NO_ERROR(acc->Commit());
+
+  auto acc_after_commit = this->storage->Access();
+
+  EXPECT_THAT(this->GetIds(acc_before->Vertices(this->label1, View::NEW), View::NEW), IsEmpty());
+
+  EXPECT_THAT(this->GetIds(acc_after->Vertices(this->label1, View::NEW), View::NEW), IsEmpty());
+
+  EXPECT_THAT(this->GetIds(acc_after_commit->Vertices(this->label1, View::NEW), View::NEW),
+              UnorderedElementsAre(0, 1, 2, 3, 4));
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(IndexTest, LabelIndexCountEstimate) {
-  EXPECT_FALSE(storage.CreateIndex(label1).HasError());
-  EXPECT_FALSE(storage.CreateIndex(label2).HasError());
+TYPED_TEST(IndexTest, LabelIndexCountEstimate) {
+  if constexpr ((std::is_same_v<TypeParam, memgraph::storage::InMemoryStorage>)) {
+    EXPECT_FALSE(this->storage->CreateIndex(this->label1).HasError());
+    EXPECT_FALSE(this->storage->CreateIndex(this->label2).HasError());
 
-  auto acc = storage.Access();
-  for (int i = 0; i < 20; ++i) {
-    auto vertex = CreateVertex(&acc);
-    ASSERT_NO_ERROR(vertex.AddLabel(i % 3 ? label1 : label2));
+    auto acc = this->storage->Access();
+    for (int i = 0; i < 20; ++i) {
+      auto vertex = this->CreateVertex(acc.get());
+      ASSERT_NO_ERROR(vertex.AddLabel(i % 3 ? this->label1 : this->label2));
+    }
+
+    EXPECT_EQ(acc->ApproximateVertexCount(this->label1), 13);
+    EXPECT_EQ(acc->ApproximateVertexCount(this->label2), 7);
   }
+}
 
-  EXPECT_EQ(acc.ApproximateVertexCount(label1), 13);
-  EXPECT_EQ(acc.ApproximateVertexCount(label2), 7);
+TYPED_TEST(IndexTest, LabelIndexDeletedVertex) {
+  if constexpr ((std::is_same_v<TypeParam, memgraph::storage::DiskStorage>)) {
+    EXPECT_FALSE(this->storage->CreateIndex(this->label1).HasError());
+    auto acc1 = this->storage->Access();
+    auto vertex1 = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    auto vertex2 = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    EXPECT_THAT(this->GetIds(acc1->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1));
+    ASSERT_NO_ERROR(acc1->Commit());
+    auto acc2 = this->storage->Access();
+    auto vertex_to_delete = acc2->FindVertex(vertex1.Gid(), memgraph::storage::View::NEW);
+    auto res = acc2->DeleteVertex(&*vertex_to_delete);
+    ASSERT_FALSE(res.HasError());
+    ASSERT_NO_ERROR(acc2->Commit());
+    auto acc3 = this->storage->Access();
+    EXPECT_THAT(this->GetIds(acc3->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(1));
+  }
+}
+
+TYPED_TEST(IndexTest, LabelIndexRemoveIndexedLabel) {
+  if constexpr ((std::is_same_v<TypeParam, memgraph::storage::DiskStorage>)) {
+    EXPECT_FALSE(this->storage->CreateIndex(this->label1).HasError());
+    auto acc1 = this->storage->Access();
+    auto vertex1 = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    auto vertex2 = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(acc1->Commit());
+    auto acc2 = this->storage->Access();
+    EXPECT_THAT(this->GetIds(acc2->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1));
+    auto vertex_to_delete = acc2->FindVertex(vertex1.Gid(), memgraph::storage::View::NEW);
+    auto res = vertex_to_delete->RemoveLabel(this->label1);
+    ASSERT_FALSE(res.HasError());
+    ASSERT_NO_ERROR(acc2->Commit());
+    auto acc3 = this->storage->Access();
+    EXPECT_THAT(this->GetIds(acc3->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(1));
+  }
+}
+
+TYPED_TEST(IndexTest, LabelIndexRemoveAndAddIndexedLabel) {
+  if constexpr ((std::is_same_v<TypeParam, memgraph::storage::DiskStorage>)) {
+    EXPECT_FALSE(this->storage->CreateIndex(this->label1).HasError());
+    auto acc1 = this->storage->Access();
+    auto vertex1 = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    auto vertex2 = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(acc1->Commit());
+    auto acc2 = this->storage->Access();
+    EXPECT_THAT(this->GetIds(acc2->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1));
+    auto vertex_to_delete = acc2->FindVertex(vertex1.Gid(), memgraph::storage::View::NEW);
+    auto res_remove = vertex_to_delete->RemoveLabel(this->label1);
+    ASSERT_FALSE(res_remove.HasError());
+    auto res_add = vertex_to_delete->AddLabel(this->label1);
+    ASSERT_FALSE(res_add.HasError());
+    ASSERT_NO_ERROR(acc2->Commit());
+    auto acc3 = this->storage->Access();
+    EXPECT_THAT(this->GetIds(acc3->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1));
+  }
+}
+
+TYPED_TEST(IndexTest, LabelIndexClearOldDataFromDisk) {
+  if constexpr ((std::is_same_v<TypeParam, memgraph::storage::DiskStorage>)) {
+    auto *disk_label_index =
+        static_cast<memgraph::storage::DiskLabelIndex *>(this->storage->indices_.label_index_.get());
+
+    EXPECT_FALSE(this->storage->CreateIndex(this->label1).HasError());
+    auto acc1 = this->storage->Access();
+    auto vertex = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop_val, PropertyValue(10)));
+    ASSERT_NO_ERROR(acc1->Commit());
+
+    auto *tx_db = disk_label_index->GetRocksDBStorage()->db_;
+    ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+
+    auto acc2 = this->storage->Access(std::nullopt);
+    auto vertex2 = acc2->FindVertex(vertex.Gid(), memgraph::storage::View::NEW).value();
+    ASSERT_TRUE(vertex2.SetProperty(this->prop_val, memgraph::storage::PropertyValue(10)).HasValue());
+    ASSERT_FALSE(acc2->Commit().HasError());
+
+    ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+
+    auto acc3 = this->storage->Access(std::nullopt);
+    auto vertex3 = acc3->FindVertex(vertex.Gid(), memgraph::storage::View::NEW).value();
+    ASSERT_TRUE(vertex3.SetProperty(this->prop_val, memgraph::storage::PropertyValue(15)).HasValue());
+    ASSERT_FALSE(acc3->Commit().HasError());
+
+    ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+  }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(IndexTest, LabelPropertyIndexCreateAndDrop) {
-  EXPECT_EQ(storage.ListAllIndices().label_property.size(), 0);
-  EXPECT_FALSE(storage.CreateIndex(label1, prop_id).HasError());
+TYPED_TEST(IndexTest, LabelPropertyIndexCreateAndDrop) {
+  EXPECT_EQ(this->storage->ListAllIndices().label_property.size(), 0);
+  EXPECT_FALSE(this->storage->CreateIndex(this->label1, this->prop_id).HasError());
   {
-    auto acc = storage.Access();
-    EXPECT_TRUE(acc.LabelPropertyIndexExists(label1, prop_id));
+    auto acc = this->storage->Access();
+    EXPECT_TRUE(acc->LabelPropertyIndexExists(this->label1, this->prop_id));
   }
-  EXPECT_THAT(storage.ListAllIndices().label_property, UnorderedElementsAre(std::make_pair(label1, prop_id)));
+  EXPECT_THAT(this->storage->ListAllIndices().label_property,
+              UnorderedElementsAre(std::make_pair(this->label1, this->prop_id)));
   {
-    auto acc = storage.Access();
-    EXPECT_FALSE(acc.LabelPropertyIndexExists(label2, prop_id));
+    auto acc = this->storage->Access();
+    EXPECT_FALSE(acc->LabelPropertyIndexExists(this->label2, this->prop_id));
   }
-  EXPECT_TRUE(storage.CreateIndex(label1, prop_id).HasError());
-  EXPECT_THAT(storage.ListAllIndices().label_property, UnorderedElementsAre(std::make_pair(label1, prop_id)));
+  EXPECT_TRUE(this->storage->CreateIndex(this->label1, this->prop_id).HasError());
+  EXPECT_THAT(this->storage->ListAllIndices().label_property,
+              UnorderedElementsAre(std::make_pair(this->label1, this->prop_id)));
 
-  EXPECT_FALSE(storage.CreateIndex(label2, prop_id).HasError());
+  EXPECT_FALSE(this->storage->CreateIndex(this->label2, this->prop_id).HasError());
   {
-    auto acc = storage.Access();
-    EXPECT_TRUE(acc.LabelPropertyIndexExists(label2, prop_id));
+    auto acc = this->storage->Access();
+    EXPECT_TRUE(acc->LabelPropertyIndexExists(this->label2, this->prop_id));
   }
-  EXPECT_THAT(storage.ListAllIndices().label_property,
-              UnorderedElementsAre(std::make_pair(label1, prop_id), std::make_pair(label2, prop_id)));
+  EXPECT_THAT(
+      this->storage->ListAllIndices().label_property,
+      UnorderedElementsAre(std::make_pair(this->label1, this->prop_id), std::make_pair(this->label2, this->prop_id)));
 
-  EXPECT_FALSE(storage.DropIndex(label1, prop_id).HasError());
+  EXPECT_FALSE(this->storage->DropIndex(this->label1, this->prop_id).HasError());
   {
-    auto acc = storage.Access();
-    EXPECT_FALSE(acc.LabelPropertyIndexExists(label1, prop_id));
+    auto acc = this->storage->Access();
+    EXPECT_FALSE(acc->LabelPropertyIndexExists(this->label1, this->prop_id));
   }
-  EXPECT_THAT(storage.ListAllIndices().label_property, UnorderedElementsAre(std::make_pair(label2, prop_id)));
-  EXPECT_TRUE(storage.DropIndex(label1, prop_id).HasError());
+  EXPECT_THAT(this->storage->ListAllIndices().label_property,
+              UnorderedElementsAre(std::make_pair(this->label2, this->prop_id)));
+  EXPECT_TRUE(this->storage->DropIndex(this->label1, this->prop_id).HasError());
 
-  EXPECT_FALSE(storage.DropIndex(label2, prop_id).HasError());
+  EXPECT_FALSE(this->storage->DropIndex(this->label2, this->prop_id).HasError());
   {
-    auto acc = storage.Access();
-    EXPECT_FALSE(acc.LabelPropertyIndexExists(label2, prop_id));
+    auto acc = this->storage->Access();
+    EXPECT_FALSE(acc->LabelPropertyIndexExists(this->label2, this->prop_id));
   }
-  EXPECT_EQ(storage.ListAllIndices().label_property.size(), 0);
+  EXPECT_EQ(this->storage->ListAllIndices().label_property.size(), 0);
 }
 
 // The following three tests are almost an exact copy-paste of the corresponding
@@ -415,217 +547,270 @@ TEST_F(IndexTest, LabelPropertyIndexCreateAndDrop) {
 // test.
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(IndexTest, LabelPropertyIndexBasic) {
-  EXPECT_FALSE(storage.CreateIndex(label1, prop_val).HasError());
-  EXPECT_FALSE(storage.CreateIndex(label2, prop_val).HasError());
+TYPED_TEST(IndexTest, LabelPropertyIndexBasic) {
+  EXPECT_FALSE(this->storage->CreateIndex(this->label1, this->prop_val).HasError());
+  EXPECT_FALSE(this->storage->CreateIndex(this->label2, this->prop_val).HasError());
 
-  auto acc = storage.Access();
-  EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), IsEmpty());
+  auto acc = this->storage->Access();
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::OLD), View::OLD), IsEmpty());
 
   for (int i = 0; i < 10; ++i) {
-    auto vertex = CreateVertex(&acc);
-    ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? label1 : label2));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue(i)));
+    auto vertex = this->CreateVertex(acc.get());
+    ASSERT_NO_ERROR(vertex.AddLabel(i % 2 ? this->label1 : this->label2));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop_val, PropertyValue(i)));
   }
 
-  EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::OLD), View::OLD), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
-  EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::OLD), View::OLD), IsEmpty());
 
-  acc.AdvanceCommand();
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, this->prop_val, View::OLD), View::OLD), IsEmpty());
 
-  EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
-  EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
-  EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), UnorderedElementsAre(1, 3, 5, 7, 9));
-  EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::NEW), View::NEW),
+              UnorderedElementsAre(1, 3, 5, 7, 9));
 
-  for (auto vertex : acc.Vertices(View::OLD)) {
-    int64_t id = vertex.GetProperty(prop_id, View::OLD)->ValueInt();
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, this->prop_val, View::NEW), View::NEW),
+              UnorderedElementsAre(0, 2, 4, 6, 8));
+
+  acc->AdvanceCommand();
+
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::OLD), View::OLD),
+              UnorderedElementsAre(1, 3, 5, 7, 9));
+
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, this->prop_val, View::OLD), View::OLD),
+              UnorderedElementsAre(0, 2, 4, 6, 8));
+
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::NEW), View::NEW),
+              UnorderedElementsAre(1, 3, 5, 7, 9));
+
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, this->prop_val, View::NEW), View::NEW),
+              UnorderedElementsAre(0, 2, 4, 6, 8));
+
+  for (auto vertex : acc->Vertices(View::OLD)) {
+    int64_t id = vertex.GetProperty(this->prop_id, View::OLD)->ValueInt();
     if (id % 2) {
-      ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue()));
+      ASSERT_NO_ERROR(vertex.SetProperty(this->prop_val, PropertyValue()));
     } else {
-      ASSERT_NO_ERROR(vertex.AddLabel(label1));
+      ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
     }
   }
 
-  EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
-  EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
-  EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
-  EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 2, 4, 6, 8));
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::OLD), View::OLD),
+              UnorderedElementsAre(1, 3, 5, 7, 9));
 
-  for (auto vertex : acc.Vertices(View::OLD)) {
-    int64_t id = vertex.GetProperty(prop_id, View::OLD)->ValueInt();
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, this->prop_val, View::OLD), View::OLD),
+              UnorderedElementsAre(0, 2, 4, 6, 8));
+
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::NEW), View::NEW),
+              UnorderedElementsAre(0, 2, 4, 6, 8));
+
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, this->prop_val, View::NEW), View::NEW),
+              UnorderedElementsAre(0, 2, 4, 6, 8));
+
+  for (auto vertex : acc->Vertices(View::OLD)) {
+    int64_t id = vertex.GetProperty(this->prop_id, View::OLD)->ValueInt();
     if (id % 2 == 0) {
-      ASSERT_NO_ERROR(acc.DeleteVertex(&vertex));
+      ASSERT_NO_ERROR(acc->DeleteVertex(&vertex));
     }
   }
 
-  EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), UnorderedElementsAre(1, 3, 5, 7, 9));
-  EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::OLD), View::OLD), UnorderedElementsAre(0, 2, 4, 6, 8));
-  EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::NEW), View::NEW), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::OLD), View::OLD),
+              UnorderedElementsAre(1, 3, 5, 7, 9));
 
-  acc.AdvanceCommand();
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, this->prop_val, View::OLD), View::OLD),
+              UnorderedElementsAre(0, 2, 4, 6, 8));
 
-  EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::OLD), View::OLD), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
-  EXPECT_THAT(GetIds(acc.Vertices(label2, prop_val, View::NEW), View::NEW), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::NEW), View::NEW), IsEmpty());
+
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, this->prop_val, View::NEW), View::NEW), IsEmpty());
+
+  acc->AdvanceCommand();
+
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::OLD), View::OLD), IsEmpty());
+
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, this->prop_val, View::OLD), View::OLD), IsEmpty());
+
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::NEW), View::NEW), IsEmpty());
+
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label2, this->prop_val, View::NEW), View::NEW), IsEmpty());
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(IndexTest, LabelPropertyIndexDuplicateVersions) {
-  EXPECT_FALSE(storage.CreateIndex(label1, prop_val).HasError());
+TYPED_TEST(IndexTest, LabelPropertyIndexDuplicateVersions) {
+  EXPECT_FALSE(this->storage->CreateIndex(this->label1, this->prop_val).HasError());
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
     for (int i = 0; i < 5; ++i) {
-      auto vertex = CreateVertex(&acc);
-      ASSERT_NO_ERROR(vertex.AddLabel(label1));
-      ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue(i)));
+      auto vertex = this->CreateVertex(acc.get());
+      ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+      ASSERT_NO_ERROR(vertex.SetProperty(this->prop_val, PropertyValue(i)));
     }
 
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::NEW), View::NEW),
+                UnorderedElementsAre(0, 1, 2, 3, 4));
 
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
 
   {
-    auto acc = storage.Access();
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
+    auto acc = this->storage->Access();
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::OLD), View::OLD),
+                UnorderedElementsAre(0, 1, 2, 3, 4));
 
-    for (auto vertex : acc.Vertices(View::OLD)) {
-      ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue()));
+    for (auto vertex : acc->Vertices(View::OLD)) {
+      ASSERT_NO_ERROR(vertex.SetProperty(this->prop_val, PropertyValue()));
     }
 
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::OLD), View::OLD),
+                UnorderedElementsAre(0, 1, 2, 3, 4));
 
-    for (auto vertex : acc.Vertices(View::OLD)) {
-      ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue(42)));
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::NEW), View::NEW), IsEmpty());
+
+    for (auto vertex : acc->Vertices(View::OLD)) {
+      ASSERT_NO_ERROR(vertex.SetProperty(this->prop_val, PropertyValue(42)));
     }
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::OLD), View::OLD), UnorderedElementsAre(0, 1, 2, 3, 4));
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::OLD), View::OLD),
+                UnorderedElementsAre(0, 1, 2, 3, 4));
+
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::NEW), View::NEW),
+                UnorderedElementsAre(0, 1, 2, 3, 4));
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(IndexTest, LabelPropertyIndexTransactionalIsolation) {
-  EXPECT_FALSE(storage.CreateIndex(label1, prop_val).HasError());
+TYPED_TEST(IndexTest, LabelPropertyIndexTransactionalIsolation) {
+  EXPECT_FALSE(this->storage->CreateIndex(this->label1, this->prop_val).HasError());
 
-  auto acc_before = storage.Access();
-  auto acc = storage.Access();
-  auto acc_after = storage.Access();
+  auto acc_before = this->storage->Access();
+  auto acc = this->storage->Access();
+  auto acc_after = this->storage->Access();
 
   for (int i = 0; i < 5; ++i) {
-    auto vertex = CreateVertex(&acc);
-    ASSERT_NO_ERROR(vertex.AddLabel(label1));
-    ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue(i)));
+    auto vertex = this->CreateVertex(acc.get());
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop_val, PropertyValue(i)));
   }
 
-  EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, View::NEW), View::NEW), UnorderedElementsAre(0, 1, 2, 3, 4));
-  EXPECT_THAT(GetIds(acc_before.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
-  EXPECT_THAT(GetIds(acc_after.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
+  EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, View::NEW), View::NEW),
+              UnorderedElementsAre(0, 1, 2, 3, 4));
 
-  ASSERT_NO_ERROR(acc.Commit());
+  EXPECT_THAT(this->GetIds(acc_before->Vertices(this->label1, this->prop_val, View::NEW), View::NEW), IsEmpty());
 
-  auto acc_after_commit = storage.Access();
+  EXPECT_THAT(this->GetIds(acc_after->Vertices(this->label1, this->prop_val, View::NEW), View::NEW), IsEmpty());
 
-  EXPECT_THAT(GetIds(acc_before.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
-  EXPECT_THAT(GetIds(acc_after.Vertices(label1, prop_val, View::NEW), View::NEW), IsEmpty());
-  EXPECT_THAT(GetIds(acc_after_commit.Vertices(label1, prop_val, View::NEW), View::NEW),
+  ASSERT_NO_ERROR(acc->Commit());
+
+  auto acc_after_commit = this->storage->Access();
+
+  EXPECT_THAT(this->GetIds(acc_before->Vertices(this->label1, this->prop_val, View::NEW), View::NEW), IsEmpty());
+
+  EXPECT_THAT(this->GetIds(acc_after->Vertices(this->label1, this->prop_val, View::NEW), View::NEW), IsEmpty());
+
+  EXPECT_THAT(this->GetIds(acc_after_commit->Vertices(this->label1, this->prop_val, View::NEW), View::NEW),
               UnorderedElementsAre(0, 1, 2, 3, 4));
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(IndexTest, LabelPropertyIndexFiltering) {
+TYPED_TEST(IndexTest, LabelPropertyIndexFiltering) {
   // We insert vertices with values:
   // 0 0.0 1 1.0 2 2.0 3 3.0 4 4.0
   // Then we check all combinations of inclusive and exclusive bounds.
   // We also have a mix of doubles and integers to verify that they are sorted
   // properly.
 
-  EXPECT_FALSE(storage.CreateIndex(label1, prop_val).HasError());
+  EXPECT_FALSE(this->storage->CreateIndex(this->label1, this->prop_val).HasError());
 
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
 
     for (int i = 0; i < 10; ++i) {
-      auto vertex = CreateVertex(&acc);
-      ASSERT_NO_ERROR(vertex.AddLabel(label1));
-      ASSERT_NO_ERROR(vertex.SetProperty(prop_val, i % 2 ? PropertyValue(i / 2) : PropertyValue(i / 2.0)));
+      auto vertex = this->CreateVertex(acc.get());
+      ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+      ASSERT_NO_ERROR(vertex.SetProperty(this->prop_val, i % 2 ? PropertyValue(i / 2) : PropertyValue(i / 2.0)));
     }
-    ASSERT_NO_ERROR(acc.Commit());
+    ASSERT_NO_ERROR(acc->Commit());
   }
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
     for (int i = 0; i < 5; ++i) {
-      EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, PropertyValue(i), View::OLD)),
+      EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, PropertyValue(i), View::OLD)),
                   UnorderedElementsAre(2 * i, 2 * i + 1));
     }
 
     // [1, +inf>
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, memgraph::utils::MakeBoundInclusive(PropertyValue(1)),
-                                    std::nullopt, View::OLD)),
-                UnorderedElementsAre(2, 3, 4, 5, 6, 7, 8, 9));
+    EXPECT_THAT(
+        this->GetIds(acc->Vertices(this->label1, this->prop_val, memgraph::utils::MakeBoundInclusive(PropertyValue(1)),
+                                   std::nullopt, View::OLD)),
+        UnorderedElementsAre(2, 3, 4, 5, 6, 7, 8, 9));
+
     // <1, +inf>
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, memgraph::utils::MakeBoundExclusive(PropertyValue(1)),
-                                    std::nullopt, View::OLD)),
-                UnorderedElementsAre(4, 5, 6, 7, 8, 9));
+    EXPECT_THAT(
+        this->GetIds(acc->Vertices(this->label1, this->prop_val, memgraph::utils::MakeBoundExclusive(PropertyValue(1)),
+                                   std::nullopt, View::OLD)),
+        UnorderedElementsAre(4, 5, 6, 7, 8, 9));
 
     // <-inf, 3]
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, std::nullopt,
-                                    memgraph::utils::MakeBoundInclusive(PropertyValue(3)), View::OLD)),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, std::nullopt,
+                                           memgraph::utils::MakeBoundInclusive(PropertyValue(3)), View::OLD)),
                 UnorderedElementsAre(0, 1, 2, 3, 4, 5, 6, 7));
+
     // <-inf, 3>
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, std::nullopt,
-                                    memgraph::utils::MakeBoundExclusive(PropertyValue(3)), View::OLD)),
+    EXPECT_THAT(this->GetIds(acc->Vertices(this->label1, this->prop_val, std::nullopt,
+                                           memgraph::utils::MakeBoundExclusive(PropertyValue(3)), View::OLD)),
                 UnorderedElementsAre(0, 1, 2, 3, 4, 5));
 
     // [1, 3]
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, memgraph::utils::MakeBoundInclusive(PropertyValue(1)),
-                                    memgraph::utils::MakeBoundInclusive(PropertyValue(3)), View::OLD)),
-                UnorderedElementsAre(2, 3, 4, 5, 6, 7));
+    EXPECT_THAT(
+        this->GetIds(acc->Vertices(this->label1, this->prop_val, memgraph::utils::MakeBoundInclusive(PropertyValue(1)),
+                                   memgraph::utils::MakeBoundInclusive(PropertyValue(3)), View::OLD)),
+        UnorderedElementsAre(2, 3, 4, 5, 6, 7));
+
     // <1, 3]
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, memgraph::utils::MakeBoundExclusive(PropertyValue(1)),
-                                    memgraph::utils::MakeBoundInclusive(PropertyValue(3)), View::OLD)),
-                UnorderedElementsAre(4, 5, 6, 7));
+    EXPECT_THAT(
+        this->GetIds(acc->Vertices(this->label1, this->prop_val, memgraph::utils::MakeBoundExclusive(PropertyValue(1)),
+                                   memgraph::utils::MakeBoundInclusive(PropertyValue(3)), View::OLD)),
+        UnorderedElementsAre(4, 5, 6, 7));
+
     // [1, 3>
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, memgraph::utils::MakeBoundInclusive(PropertyValue(1)),
-                                    memgraph::utils::MakeBoundExclusive(PropertyValue(3)), View::OLD)),
-                UnorderedElementsAre(2, 3, 4, 5));
+    EXPECT_THAT(
+        this->GetIds(acc->Vertices(this->label1, this->prop_val, memgraph::utils::MakeBoundInclusive(PropertyValue(1)),
+                                   memgraph::utils::MakeBoundExclusive(PropertyValue(3)), View::OLD)),
+        UnorderedElementsAre(2, 3, 4, 5));
+
     // <1, 3>
-    EXPECT_THAT(GetIds(acc.Vertices(label1, prop_val, memgraph::utils::MakeBoundExclusive(PropertyValue(1)),
-                                    memgraph::utils::MakeBoundExclusive(PropertyValue(3)), View::OLD)),
-                UnorderedElementsAre(4, 5));
+    EXPECT_THAT(
+        this->GetIds(acc->Vertices(this->label1, this->prop_val, memgraph::utils::MakeBoundExclusive(PropertyValue(1)),
+                                   memgraph::utils::MakeBoundExclusive(PropertyValue(3)), View::OLD)),
+        UnorderedElementsAre(4, 5));
   }
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(IndexTest, LabelPropertyIndexCountEstimate) {
-  EXPECT_FALSE(storage.CreateIndex(label1, prop_val).HasError());
+TYPED_TEST(IndexTest, LabelPropertyIndexCountEstimate) {
+  if constexpr ((std::is_same_v<TypeParam, memgraph::storage::InMemoryStorage>)) {
+    EXPECT_FALSE(this->storage->CreateIndex(this->label1, this->prop_val).HasError());
 
-  auto acc = storage.Access();
-  for (int i = 1; i <= 10; ++i) {
-    for (int j = 0; j < i; ++j) {
-      auto vertex = CreateVertex(&acc);
-      ASSERT_NO_ERROR(vertex.AddLabel(label1));
-      ASSERT_NO_ERROR(vertex.SetProperty(prop_val, PropertyValue(i)));
+    auto acc = this->storage->Access();
+    for (int i = 1; i <= 10; ++i) {
+      for (int j = 0; j < i; ++j) {
+        auto vertex = this->CreateVertex(acc.get());
+        ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+        ASSERT_NO_ERROR(vertex.SetProperty(this->prop_val, PropertyValue(i)));
+      }
     }
-  }
 
-  EXPECT_EQ(acc.ApproximateVertexCount(label1, prop_val), 55);
-  for (int i = 1; i <= 10; ++i) {
-    EXPECT_EQ(acc.ApproximateVertexCount(label1, prop_val, PropertyValue(i)), i);
-  }
+    EXPECT_EQ(acc->ApproximateVertexCount(this->label1, this->prop_val), 55);
+    for (int i = 1; i <= 10; ++i) {
+      EXPECT_EQ(acc->ApproximateVertexCount(this->label1, this->prop_val, PropertyValue(i)), i);
+    }
 
-  EXPECT_EQ(acc.ApproximateVertexCount(label1, prop_val, memgraph::utils::MakeBoundInclusive(PropertyValue(2)),
-                                       memgraph::utils::MakeBoundInclusive(PropertyValue(6))),
-            2 + 3 + 4 + 5 + 6);
+    EXPECT_EQ(
+        acc->ApproximateVertexCount(this->label1, this->prop_val, memgraph::utils::MakeBoundInclusive(PropertyValue(2)),
+                                    memgraph::utils::MakeBoundInclusive(PropertyValue(6))),
+        2 + 3 + 4 + 5 + 6);
+  }
 }
 
-TEST_F(IndexTest, LabelPropertyIndexMixedIteration) {
-  EXPECT_FALSE(storage.CreateIndex(label1, prop_val).HasError());
+TYPED_TEST(IndexTest, LabelPropertyIndexMixedIteration) {
+  EXPECT_FALSE(this->storage->CreateIndex(this->label1, this->prop_val).HasError());
 
   const std::array temporals{TemporalData{TemporalType::Date, 23}, TemporalData{TemporalType::Date, 28},
                              TemporalData{TemporalType::LocalDateTime, 20}};
@@ -661,24 +846,24 @@ TEST_F(IndexTest, LabelPropertyIndexMixedIteration) {
 
   // Create vertices, each with one of the values above.
   {
-    auto acc = storage.Access();
+    auto acc = this->storage->Access();
     for (const auto &value : values) {
-      auto v = acc.CreateVertex();
-      ASSERT_TRUE(v.AddLabel(label1).HasValue());
-      ASSERT_TRUE(v.SetProperty(prop_val, value).HasValue());
+      auto v = acc->CreateVertex();
+      ASSERT_TRUE(v.AddLabel(this->label1).HasValue());
+      ASSERT_TRUE(v.SetProperty(this->prop_val, value).HasValue());
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // Verify that all nodes are in the index.
   {
-    auto acc = storage.Access();
-    auto iterable = acc.Vertices(label1, prop_val, View::OLD);
+    auto acc = this->storage->Access();
+    auto iterable = acc->Vertices(this->label1, this->prop_val, View::OLD);
     auto it = iterable.begin();
     for (const auto &value : values) {
       ASSERT_NE(it, iterable.end());
       auto vertex = *it;
-      auto maybe_value = vertex.GetProperty(prop_val, View::OLD);
+      auto maybe_value = vertex.GetProperty(this->prop_val, View::OLD);
       ASSERT_TRUE(maybe_value.HasValue());
       ASSERT_EQ(value, *maybe_value);
       ++it;
@@ -689,12 +874,12 @@ TEST_F(IndexTest, LabelPropertyIndexMixedIteration) {
   auto verify = [&](const std::optional<memgraph::utils::Bound<PropertyValue>> &from,
                     const std::optional<memgraph::utils::Bound<PropertyValue>> &to,
                     const std::vector<PropertyValue> &expected) {
-    auto acc = storage.Access();
-    auto iterable = acc.Vertices(label1, prop_val, from, to, View::OLD);
+    auto acc = this->storage->Access();
+    auto iterable = acc->Vertices(this->label1, this->prop_val, from, to, View::OLD);
     size_t i = 0;
     for (auto it = iterable.begin(); it != iterable.end(); ++it, ++i) {
       auto vertex = *it;
-      auto maybe_value = vertex.GetProperty(prop_val, View::OLD);
+      auto maybe_value = vertex.GetProperty(this->prop_val, View::OLD);
       ASSERT_TRUE(maybe_value.HasValue());
       ASSERT_EQ(*maybe_value, expected[i]);
     }
@@ -830,3 +1015,117 @@ TEST_F(IndexTest, LabelPropertyIndexMixedIteration) {
   // Iteration without any bounds should return all items of the index.
   verify(std::nullopt, std::nullopt, values);
 }
+
+TYPED_TEST(IndexTest, LabelPropertyIndexDeletedVertex) {
+  if constexpr ((std::is_same_v<TypeParam, memgraph::storage::DiskStorage>)) {
+    EXPECT_FALSE(this->storage->CreateIndex(this->label1, this->prop_val).HasError());
+    auto acc1 = this->storage->Access();
+
+    auto vertex1 = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop_val, PropertyValue(0)));
+
+    auto vertex2 = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop_val, PropertyValue(1)));
+
+    EXPECT_THAT(this->GetIds(acc1->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1));
+    ASSERT_NO_ERROR(acc1->Commit());
+
+    auto acc2 = this->storage->Access();
+    auto vertex_to_delete = acc2->FindVertex(vertex1.Gid(), memgraph::storage::View::NEW);
+    auto res = acc2->DeleteVertex(&*vertex_to_delete);
+    ASSERT_FALSE(res.HasError());
+    ASSERT_NO_ERROR(acc2->Commit());
+
+    auto acc3 = this->storage->Access();
+    EXPECT_THAT(this->GetIds(acc3->Vertices(this->label1, this->prop_val, View::NEW), View::NEW),
+                UnorderedElementsAre(1));
+  }
+}
+
+/// TODO: empty lines, so it is easier to read what is actually going on here
+TYPED_TEST(IndexTest, LabelPropertyIndexRemoveIndexedLabel) {
+  if constexpr ((std::is_same_v<TypeParam, memgraph::storage::DiskStorage>)) {
+    EXPECT_FALSE(this->storage->CreateIndex(this->label1, this->prop_val).HasError());
+    auto acc1 = this->storage->Access();
+
+    auto vertex1 = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop_val, PropertyValue(0)));
+
+    auto vertex2 = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop_val, PropertyValue(1)));
+
+    EXPECT_THAT(this->GetIds(acc1->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1));
+    ASSERT_NO_ERROR(acc1->Commit());
+
+    auto acc2 = this->storage->Access();
+    auto vertex_to_delete = acc2->FindVertex(vertex1.Gid(), memgraph::storage::View::NEW);
+    auto res = vertex_to_delete->RemoveLabel(this->label1);
+    ASSERT_FALSE(res.HasError());
+    ASSERT_NO_ERROR(acc2->Commit());
+
+    auto acc3 = this->storage->Access();
+    EXPECT_THAT(this->GetIds(acc3->Vertices(this->label1, this->prop_val, View::NEW), View::NEW),
+                UnorderedElementsAre(1));
+  }
+}
+
+TYPED_TEST(IndexTest, LabelPropertyIndexRemoveAndAddIndexedLabel) {
+  if constexpr ((std::is_same_v<TypeParam, memgraph::storage::DiskStorage>)) {
+    EXPECT_FALSE(this->storage->CreateIndex(this->label1, this->prop_val).HasError());
+    auto acc1 = this->storage->Access();
+
+    auto vertex1 = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex1.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex1.SetProperty(this->prop_val, PropertyValue(0)));
+
+    auto vertex2 = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex2.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex2.SetProperty(this->prop_val, PropertyValue(1)));
+
+    EXPECT_THAT(this->GetIds(acc1->Vertices(this->label1, View::NEW), View::NEW), UnorderedElementsAre(0, 1));
+    ASSERT_NO_ERROR(acc1->Commit());
+
+    auto acc2 = this->storage->Access();
+    auto target_vertex = acc2->FindVertex(vertex1.Gid(), memgraph::storage::View::NEW);
+    auto remove_res = target_vertex->RemoveLabel(this->label1);
+    ASSERT_FALSE(remove_res.HasError());
+    auto add_res = target_vertex->AddLabel(this->label1);
+    ASSERT_FALSE(add_res.HasError());
+    ASSERT_NO_ERROR(acc2->Commit());
+  }
+}
+
+TYPED_TEST(IndexTest, LabelPropertyIndexClearOldDataFromDisk) {
+  if constexpr ((std::is_same_v<TypeParam, memgraph::storage::DiskStorage>)) {
+    auto *disk_label_property_index =
+        static_cast<memgraph::storage::DiskLabelPropertyIndex *>(this->storage->indices_.label_property_index_.get());
+
+    EXPECT_FALSE(this->storage->CreateIndex(this->label1, this->prop_val).HasError());
+    auto acc1 = this->storage->Access();
+    auto vertex = this->CreateVertex(acc1.get());
+    ASSERT_NO_ERROR(vertex.AddLabel(this->label1));
+    ASSERT_NO_ERROR(vertex.SetProperty(this->prop_val, PropertyValue(10)));
+    ASSERT_NO_ERROR(acc1->Commit());
+
+    auto *tx_db = disk_label_property_index->GetRocksDBStorage()->db_;
+    ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+
+    auto acc2 = this->storage->Access(std::nullopt);
+    auto vertex2 = acc2->FindVertex(vertex.Gid(), memgraph::storage::View::NEW).value();
+    ASSERT_TRUE(vertex2.SetProperty(this->prop_val, memgraph::storage::PropertyValue(10)).HasValue());
+    ASSERT_FALSE(acc2->Commit().HasError());
+
+    ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+
+    auto acc3 = this->storage->Access(std::nullopt);
+    auto vertex3 = acc3->FindVertex(vertex.Gid(), memgraph::storage::View::NEW).value();
+    ASSERT_TRUE(vertex3.SetProperty(this->prop_val, memgraph::storage::PropertyValue(15)).HasValue());
+    ASSERT_FALSE(acc3->Commit().HasError());
+
+    ASSERT_EQ(disk_test_utils::GetRealNumberOfEntriesInRocksDB(tx_db), 1);
+  }
+}
diff --git a/tests/unit/storage_v2_isolation_level.cpp b/tests/unit/storage_v2_isolation_level.cpp
index dc0bb0d62..5cdfb5656 100644
--- a/tests/unit/storage_v2_isolation_level.cpp
+++ b/tests/unit/storage_v2_isolation_level.cpp
@@ -11,13 +11,15 @@
 
 #include <gtest/gtest.h>
 
+#include "disk_test_utils.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/isolation_level.hpp"
-#include "storage/v2/storage.hpp"
 
 namespace {
-int64_t VerticesCount(memgraph::storage::Storage::Accessor &accessor) {
+int64_t VerticesCount(memgraph::storage::Storage::Accessor *accessor) {
   int64_t count{0};
-  for ([[maybe_unused]] const auto &vertex : accessor.Vertices(memgraph::storage::View::NEW)) {
+  for ([[maybe_unused]] const auto &vertex : accessor->Vertices(memgraph::storage::View::NEW)) {
     ++count;
   }
 
@@ -37,20 +39,16 @@ class StorageIsolationLevelTest : public ::testing::TestWithParam<memgraph::stor
       return std::string(IsolationLevelToString(static_cast<memgraph::storage::IsolationLevel>(info.param)));
     }
   };
-};
 
-TEST_P(StorageIsolationLevelTest, Visibility) {
-  const auto default_isolation_level = GetParam();
+  void TestVisibility(std::unique_ptr<memgraph::storage::Storage> &storage,
+                      const memgraph::storage::IsolationLevel &default_isolation_level,
+                      const memgraph::storage::IsolationLevel &override_isolation_level) {
+    auto creator = storage->Access();
+    auto default_isolation_level_reader = storage->Access();
+    auto override_isolation_level_reader = storage->Access(override_isolation_level);
 
-  for (const auto override_isolation_level : isolation_levels) {
-    memgraph::storage::Storage storage{
-        memgraph::storage::Config{.transaction = {.isolation_level = default_isolation_level}}};
-    auto creator = storage.Access();
-    auto default_isolation_level_reader = storage.Access();
-    auto override_isolation_level_reader = storage.Access(override_isolation_level);
-
-    ASSERT_EQ(VerticesCount(default_isolation_level_reader), 0);
-    ASSERT_EQ(VerticesCount(override_isolation_level_reader), 0);
+    ASSERT_EQ(VerticesCount(default_isolation_level_reader.get()), 0);
+    ASSERT_EQ(VerticesCount(override_isolation_level_reader.get()), 0);
 
     static constexpr auto iteration_count = 10;
     {
@@ -59,18 +57,18 @@ TEST_P(StorageIsolationLevelTest, Visibility) {
           "(default isolation level = {}, override isolation level = {})",
           IsolationLevelToString(default_isolation_level), IsolationLevelToString(override_isolation_level)));
       for (size_t i = 1; i <= iteration_count; ++i) {
-        creator.CreateVertex();
+        creator->CreateVertex();
 
         const auto check_vertices_count = [i](auto &accessor, const auto isolation_level) {
           const auto expected_count = isolation_level == memgraph::storage::IsolationLevel::READ_UNCOMMITTED ? i : 0;
-          EXPECT_EQ(VerticesCount(accessor), expected_count);
+          EXPECT_EQ(VerticesCount(accessor.get()), expected_count);
         };
         check_vertices_count(default_isolation_level_reader, default_isolation_level);
         check_vertices_count(override_isolation_level_reader, override_isolation_level);
       }
     }
 
-    ASSERT_FALSE(creator.Commit().HasError());
+    ASSERT_FALSE(creator->Commit().HasError());
     {
       SCOPED_TRACE(fmt::format(
           "Visibility after the creator transaction is committed "
@@ -79,20 +77,53 @@ TEST_P(StorageIsolationLevelTest, Visibility) {
       const auto check_vertices_count = [](auto &accessor, const auto isolation_level) {
         const auto expected_count =
             isolation_level == memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION ? 0 : iteration_count;
-        ASSERT_EQ(VerticesCount(accessor), expected_count);
+        ASSERT_EQ(VerticesCount(accessor.get()), expected_count);
       };
 
       check_vertices_count(default_isolation_level_reader, default_isolation_level);
       check_vertices_count(override_isolation_level_reader, override_isolation_level);
     }
 
-    ASSERT_FALSE(default_isolation_level_reader.Commit().HasError());
-    ASSERT_FALSE(override_isolation_level_reader.Commit().HasError());
+    ASSERT_FALSE(default_isolation_level_reader->Commit().HasError());
+    ASSERT_FALSE(override_isolation_level_reader->Commit().HasError());
 
     SCOPED_TRACE("Visibility after a new transaction is started");
-    auto verifier = storage.Access();
-    ASSERT_EQ(VerticesCount(verifier), iteration_count);
-    ASSERT_FALSE(verifier.Commit().HasError());
+    auto verifier = storage->Access();
+    ASSERT_EQ(VerticesCount(verifier.get()), iteration_count);
+    ASSERT_FALSE(verifier->Commit().HasError());
+  }
+};
+
+TEST_P(StorageIsolationLevelTest, VisibilityInMemoryStorage) {
+  const auto default_isolation_level = GetParam();
+
+  for (const auto override_isolation_level : isolation_levels) {
+    std::unique_ptr<memgraph::storage::Storage> storage(new memgraph::storage::InMemoryStorage(
+        {memgraph::storage::Config{.transaction = {.isolation_level = default_isolation_level}}}));
+    this->TestVisibility(storage, default_isolation_level, override_isolation_level);
+  }
+}
+
+TEST_P(StorageIsolationLevelTest, VisibilityOnDiskStorage) {
+  const auto default_isolation_level = GetParam();
+
+  const std::string testSuite = "storage_v2_isolation_level";
+  auto config = disk_test_utils::GenerateOnDiskConfig(testSuite);
+  config.transaction.isolation_level = default_isolation_level;
+
+  for (const auto override_isolation_level : isolation_levels) {
+    std::unique_ptr<memgraph::storage::Storage> storage(new memgraph::storage::DiskStorage(config));
+    try {
+      this->TestVisibility(storage, default_isolation_level, override_isolation_level);
+    } catch (memgraph::utils::NotYetImplemented &) {
+      if (default_isolation_level != memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION ||
+          override_isolation_level != memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION) {
+        continue;
+      }
+      disk_test_utils::RemoveRocksDbDirs(testSuite);
+      throw;
+    }
+    disk_test_utils::RemoveRocksDbDirs(testSuite);
   }
 }
 
diff --git a/tests/unit/storage_v2_property_store.cpp b/tests/unit/storage_v2_property_store.cpp
index c803bfbc4..9da503f71 100644
--- a/tests/unit/storage_v2_property_store.cpp
+++ b/tests/unit/storage_v2_property_store.cpp
@@ -689,3 +689,41 @@ TEST(PropertyStore, SetMultipleProperties) {
     EXPECT_FALSE(store.InitProperties(data));
   }
 }
+
+TEST(PropertyStore, HasAllProperties) {
+  const std::vector<std::pair<memgraph::storage::PropertyId, memgraph::storage::PropertyValue>> data{
+      {memgraph::storage::PropertyId::FromInt(1), memgraph::storage::PropertyValue(true)},
+      {memgraph::storage::PropertyId::FromInt(2), memgraph::storage::PropertyValue(123)},
+      {memgraph::storage::PropertyId::FromInt(3), memgraph::storage::PropertyValue("three")},
+      {memgraph::storage::PropertyId::FromInt(5), memgraph::storage::PropertyValue("0.0")}};
+
+  memgraph::storage::PropertyStore store;
+  EXPECT_TRUE(store.InitProperties(data));
+  EXPECT_TRUE(
+      store.HasAllProperties({memgraph::storage::PropertyId::FromInt(1), memgraph::storage::PropertyId::FromInt(2),
+                              memgraph::storage::PropertyId::FromInt(3)}));
+}
+
+TEST(PropertyStore, HasAllPropertyValues) {
+  const std::vector<std::pair<memgraph::storage::PropertyId, memgraph::storage::PropertyValue>> data{
+      {memgraph::storage::PropertyId::FromInt(1), memgraph::storage::PropertyValue(true)},
+      {memgraph::storage::PropertyId::FromInt(2), memgraph::storage::PropertyValue(123)},
+      {memgraph::storage::PropertyId::FromInt(3), memgraph::storage::PropertyValue("three")},
+      {memgraph::storage::PropertyId::FromInt(5), memgraph::storage::PropertyValue(0.0)}};
+
+  memgraph::storage::PropertyStore store;
+  EXPECT_TRUE(store.InitProperties(data));
+  EXPECT_TRUE(store.HasAllPropertyValues({memgraph::storage::PropertyValue(0.0), memgraph::storage::PropertyValue(123),
+                                          memgraph::storage::PropertyValue("three")}));
+}
+
+TEST(PropertyStore, HasAnyProperties) {
+  const std::vector<std::pair<memgraph::storage::PropertyId, memgraph::storage::PropertyValue>> data{
+      {memgraph::storage::PropertyId::FromInt(3), memgraph::storage::PropertyValue("three")},
+      {memgraph::storage::PropertyId::FromInt(5), memgraph::storage::PropertyValue("0.0")}};
+
+  memgraph::storage::PropertyStore store;
+  EXPECT_TRUE(store.InitProperties(data));
+  EXPECT_FALSE(store.HasAllPropertyValues({memgraph::storage::PropertyValue(0.0), memgraph::storage::PropertyValue(123),
+                                           memgraph::storage::PropertyValue("three")}));
+}
diff --git a/tests/unit/storage_v2_replication.cpp b/tests/unit/storage_v2_replication.cpp
index 54baa2a65..f61d05c67 100644
--- a/tests/unit/storage_v2_replication.cpp
+++ b/tests/unit/storage_v2_replication.cpp
@@ -10,6 +10,7 @@
 // licenses/APL.txt.
 
 #include <chrono>
+#include <memory>
 #include <thread>
 
 #include <fmt/format.h>
@@ -17,9 +18,11 @@
 #include <gmock/gmock.h>
 #include <gtest/gtest.h>
 
+#include <storage/v2/inmemory/storage.hpp>
 #include <storage/v2/property_value.hpp>
 #include <storage/v2/replication/enums.hpp>
-#include <storage/v2/storage.hpp>
+#include "storage/v2/replication/config.hpp"
+#include "storage/v2/storage.hpp"
 #include "storage/v2/view.hpp"
 
 using testing::UnorderedElementsAre;
@@ -51,15 +54,22 @@ class ReplicationTest : public ::testing::Test {
 };
 
 TEST_F(ReplicationTest, BasicSynchronousReplicationTest) {
-  memgraph::storage::Storage main_store(configuration);
+  std::unique_ptr<memgraph::storage::Storage> main_store =
+      std::make_unique<memgraph::storage::InMemoryStorage>(configuration);
+  std::unique_ptr<memgraph::storage::Storage> replica_store =
+      std::make_unique<memgraph::storage::InMemoryStorage>(configuration);
 
-  memgraph::storage::Storage replica_store(configuration);
-  replica_store.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
+  auto *main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(main_store.get());
+  auto *replica_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(replica_store.get());
 
-  ASSERT_FALSE(main_store
-                   .RegisterReplica("REPLICA", memgraph::io::network::Endpoint{local_host, ports[0]},
-                                    memgraph::storage::replication::ReplicationMode::SYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
+  replica_mem_store->SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]},
+                                    memgraph::storage::replication::ReplicationServerConfig{});
+
+  ASSERT_FALSE(main_mem_store
+                   ->RegisterReplica("REPLICA", memgraph::io::network::Endpoint{local_host, ports[0]},
+                                     memgraph::storage::replication::ReplicationMode::SYNC,
+                                     memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                     memgraph::storage::replication::ReplicationClientConfig{})
                    .HasError());
 
   // vertex create
@@ -70,68 +80,68 @@ TEST_F(ReplicationTest, BasicSynchronousReplicationTest) {
   const auto *vertex_property_value = "vertex_property_value";
   std::optional<memgraph::storage::Gid> vertex_gid;
   {
-    auto acc = main_store.Access();
-    auto v = acc.CreateVertex();
+    auto acc = main_store->Access();
+    auto v = acc->CreateVertex();
     vertex_gid.emplace(v.Gid());
-    ASSERT_TRUE(v.AddLabel(main_store.NameToLabel(vertex_label)).HasValue());
-    ASSERT_TRUE(v.SetProperty(main_store.NameToProperty(vertex_property),
+    ASSERT_TRUE(v.AddLabel(main_store->NameToLabel(vertex_label)).HasValue());
+    ASSERT_TRUE(v.SetProperty(main_store->NameToProperty(vertex_property),
                               memgraph::storage::PropertyValue(vertex_property_value))
                     .HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   {
-    auto acc = replica_store.Access();
-    const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = replica_store->Access();
+    const auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(v);
     const auto labels = v->Labels(memgraph::storage::View::OLD);
     ASSERT_TRUE(labels.HasValue());
     ASSERT_EQ(labels->size(), 1);
-    ASSERT_THAT(*labels, UnorderedElementsAre(replica_store.NameToLabel(vertex_label)));
+    ASSERT_THAT(*labels, UnorderedElementsAre(replica_store->NameToLabel(vertex_label)));
     const auto properties = v->Properties(memgraph::storage::View::OLD);
     ASSERT_TRUE(properties.HasValue());
     ASSERT_EQ(properties->size(), 1);
     ASSERT_THAT(*properties,
-                UnorderedElementsAre(std::make_pair(replica_store.NameToProperty(vertex_property),
+                UnorderedElementsAre(std::make_pair(replica_store->NameToProperty(vertex_property),
                                                     memgraph::storage::PropertyValue(vertex_property_value))));
 
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // vertex remove label
   {
-    auto acc = main_store.Access();
-    auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = main_store->Access();
+    auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(v);
-    ASSERT_TRUE(v->RemoveLabel(main_store.NameToLabel(vertex_label)).HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_TRUE(v->RemoveLabel(main_store->NameToLabel(vertex_label)).HasValue());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   {
-    auto acc = replica_store.Access();
-    const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = replica_store->Access();
+    const auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(v);
     const auto labels = v->Labels(memgraph::storage::View::OLD);
     ASSERT_TRUE(labels.HasValue());
     ASSERT_EQ(labels->size(), 0);
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // vertex delete
   {
-    auto acc = main_store.Access();
-    auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = main_store->Access();
+    auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(v);
-    ASSERT_TRUE(acc.DeleteVertex(&*v).HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_TRUE(acc->DeleteVertex(&*v).HasValue());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   {
-    auto acc = replica_store.Access();
-    const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = replica_store->Access();
+    const auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_FALSE(v);
     vertex_gid.reset();
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // edge create
@@ -141,16 +151,17 @@ TEST_F(ReplicationTest, BasicSynchronousReplicationTest) {
   const auto *edge_property_value = "edge_property_value";
   std::optional<memgraph::storage::Gid> edge_gid;
   {
-    auto acc = main_store.Access();
-    auto v = acc.CreateVertex();
+    auto acc = main_store->Access();
+    auto v = acc->CreateVertex();
     vertex_gid.emplace(v.Gid());
-    auto edge = acc.CreateEdge(&v, &v, main_store.NameToEdgeType(edge_type));
-    ASSERT_TRUE(edge.HasValue());
-    ASSERT_TRUE(edge->SetProperty(main_store.NameToProperty(edge_property),
-                                  memgraph::storage::PropertyValue(edge_property_value))
+    auto edgeRes = acc->CreateEdge(&v, &v, main_store->NameToEdgeType(edge_type));
+    ASSERT_TRUE(edgeRes.HasValue());
+    auto edge = edgeRes.GetValue();
+    ASSERT_TRUE(edge.SetProperty(main_store->NameToProperty(edge_property),
+                                 memgraph::storage::PropertyValue(edge_property_value))
                     .HasValue());
-    edge_gid.emplace(edge->Gid());
-    ASSERT_FALSE(acc.Commit().HasError());
+    edge_gid.emplace(edge.Gid());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   const auto find_edge = [&](const auto &edges,
@@ -164,42 +175,42 @@ TEST_F(ReplicationTest, BasicSynchronousReplicationTest) {
   };
 
   {
-    auto acc = replica_store.Access();
-    const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = replica_store->Access();
+    const auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(v);
     const auto out_edges = v->OutEdges(memgraph::storage::View::OLD);
     ASSERT_TRUE(out_edges.HasValue());
     const auto edge = find_edge(*out_edges, *edge_gid);
-    ASSERT_EQ(edge->EdgeType(), replica_store.NameToEdgeType(edge_type));
+    ASSERT_EQ(edge->EdgeType(), replica_store->NameToEdgeType(edge_type));
     const auto properties = edge->Properties(memgraph::storage::View::OLD);
     ASSERT_TRUE(properties.HasValue());
     ASSERT_EQ(properties->size(), 1);
     ASSERT_THAT(*properties,
-                UnorderedElementsAre(std::make_pair(replica_store.NameToProperty(edge_property),
+                UnorderedElementsAre(std::make_pair(replica_store->NameToProperty(edge_property),
                                                     memgraph::storage::PropertyValue(edge_property_value))));
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // delete edge
   {
-    auto acc = main_store.Access();
-    auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = main_store->Access();
+    auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(v);
     auto out_edges = v->OutEdges(memgraph::storage::View::OLD);
     auto edge = find_edge(*out_edges, *edge_gid);
     ASSERT_TRUE(edge);
-    ASSERT_TRUE(acc.DeleteEdge(&*edge).HasValue());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_TRUE(acc->DeleteEdge(&*edge).HasValue());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   {
-    auto acc = replica_store.Access();
-    const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = replica_store->Access();
+    const auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(v);
     const auto out_edges = v->OutEdges(memgraph::storage::View::OLD);
     ASSERT_TRUE(out_edges.HasValue());
     ASSERT_FALSE(find_edge(*out_edges, *edge_gid));
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // label index create
@@ -210,30 +221,32 @@ TEST_F(ReplicationTest, BasicSynchronousReplicationTest) {
   const auto *property = "property";
   const auto *property_extra = "property_extra";
   {
-    ASSERT_FALSE(main_store.CreateIndex(main_store.NameToLabel(label)).HasError());
-    ASSERT_FALSE(main_store.CreateIndex(main_store.NameToLabel(label), main_store.NameToProperty(property)).HasError());
+    ASSERT_FALSE(main_store->CreateIndex(main_store->NameToLabel(label)).HasError());
     ASSERT_FALSE(
-        main_store.CreateExistenceConstraint(main_store.NameToLabel(label), main_store.NameToProperty(property))
+        main_store->CreateIndex(main_store->NameToLabel(label), main_store->NameToProperty(property)).HasError());
+    ASSERT_FALSE(
+        main_store->CreateExistenceConstraint(main_store->NameToLabel(label), main_store->NameToProperty(property), {})
             .HasError());
     ASSERT_FALSE(main_store
-                     .CreateUniqueConstraint(main_store.NameToLabel(label), {main_store.NameToProperty(property),
-                                                                             main_store.NameToProperty(property_extra)})
+                     ->CreateUniqueConstraint(
+                         main_store->NameToLabel(label),
+                         {main_store->NameToProperty(property), main_store->NameToProperty(property_extra)}, {})
                      .HasError());
   }
 
   {
-    const auto indices = replica_store.ListAllIndices();
-    ASSERT_THAT(indices.label, UnorderedElementsAre(replica_store.NameToLabel(label)));
-    ASSERT_THAT(indices.label_property, UnorderedElementsAre(std::make_pair(replica_store.NameToLabel(label),
-                                                                            replica_store.NameToProperty(property))));
+    const auto indices = replica_store->ListAllIndices();
+    ASSERT_THAT(indices.label, UnorderedElementsAre(replica_store->NameToLabel(label)));
+    ASSERT_THAT(indices.label_property, UnorderedElementsAre(std::make_pair(replica_store->NameToLabel(label),
+                                                                            replica_store->NameToProperty(property))));
 
-    const auto constraints = replica_store.ListAllConstraints();
-    ASSERT_THAT(constraints.existence, UnorderedElementsAre(std::make_pair(replica_store.NameToLabel(label),
-                                                                           replica_store.NameToProperty(property))));
+    const auto constraints = replica_store->ListAllConstraints();
+    ASSERT_THAT(constraints.existence, UnorderedElementsAre(std::make_pair(replica_store->NameToLabel(label),
+                                                                           replica_store->NameToProperty(property))));
     ASSERT_THAT(constraints.unique,
                 UnorderedElementsAre(std::make_pair(
-                    replica_store.NameToLabel(label),
-                    std::set{replica_store.NameToProperty(property), replica_store.NameToProperty(property_extra)})));
+                    replica_store->NameToLabel(label),
+                    std::set{replica_store->NameToProperty(property), replica_store->NameToProperty(property_extra)})));
   }
 
   // label index drop
@@ -241,58 +254,69 @@ TEST_F(ReplicationTest, BasicSynchronousReplicationTest) {
   // existence constraint drop
   // unique constriant drop
   {
-    ASSERT_FALSE(main_store.DropIndex(main_store.NameToLabel(label)).HasError());
-    ASSERT_FALSE(main_store.DropIndex(main_store.NameToLabel(label), main_store.NameToProperty(property)).HasError());
-    ASSERT_FALSE(main_store.DropExistenceConstraint(main_store.NameToLabel(label), main_store.NameToProperty(property))
-                     .HasError());
+    ASSERT_FALSE(main_store->DropIndex(main_store->NameToLabel(label)).HasError());
+    ASSERT_FALSE(
+        main_store->DropIndex(main_store->NameToLabel(label), main_store->NameToProperty(property)).HasError());
+    ASSERT_FALSE(
+        main_store->DropExistenceConstraint(main_store->NameToLabel(label), main_store->NameToProperty(property), {})
+            .HasError());
     ASSERT_EQ(main_store
-                  .DropUniqueConstraint(main_store.NameToLabel(label), {main_store.NameToProperty(property),
-                                                                        main_store.NameToProperty(property_extra)})
+                  ->DropUniqueConstraint(
+                      main_store->NameToLabel(label),
+                      {main_store->NameToProperty(property), main_store->NameToProperty(property_extra)}, {})
                   .GetValue(),
               memgraph::storage::UniqueConstraints::DeletionStatus::SUCCESS);
   }
 
   {
-    const auto indices = replica_store.ListAllIndices();
+    const auto indices = replica_store->ListAllIndices();
     ASSERT_EQ(indices.label.size(), 0);
     ASSERT_EQ(indices.label_property.size(), 0);
 
-    const auto constraints = replica_store.ListAllConstraints();
+    const auto constraints = replica_store->ListAllConstraints();
     ASSERT_EQ(constraints.existence.size(), 0);
     ASSERT_EQ(constraints.unique.size(), 0);
   }
 }
 
 TEST_F(ReplicationTest, MultipleSynchronousReplicationTest) {
-  memgraph::storage::Storage main_store(
+  std::unique_ptr<memgraph::storage::Storage> main_store{new memgraph::storage::InMemoryStorage(
       {.durability = {
            .storage_directory = storage_directory,
            .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
-       }});
+       }})};
+  auto *main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(main_store.get());
 
-  memgraph::storage::Storage replica_store1(
+  std::unique_ptr<memgraph::storage::Storage> replica_store1{new memgraph::storage::InMemoryStorage(
       {.durability = {
            .storage_directory = storage_directory,
            .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
-       }});
-  replica_store1.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
+       }})};
+  auto *replica_mem_store1 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store1.get());
 
-  memgraph::storage::Storage replica_store2(
+  replica_mem_store1->SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]},
+                                     memgraph::storage::replication::ReplicationServerConfig{});
+
+  std::unique_ptr<memgraph::storage::Storage> replica_store2{new memgraph::storage::InMemoryStorage(
       {.durability = {
            .storage_directory = storage_directory,
            .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
-       }});
-  replica_store2.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[1]});
+       }})};
+  auto *replica_mem_store2 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store2.get());
+  replica_mem_store2->SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[1]},
+                                     memgraph::storage::replication::ReplicationServerConfig{});
 
-  ASSERT_FALSE(main_store
-                   .RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
-                                    memgraph::storage::replication::ReplicationMode::SYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
+  ASSERT_FALSE(main_mem_store
+                   ->RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
+                                     memgraph::storage::replication::ReplicationMode::SYNC,
+                                     memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                     memgraph::storage::replication::ReplicationClientConfig{})
                    .HasError());
-  ASSERT_FALSE(main_store
-                   .RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, ports[1]},
-                                    memgraph::storage::replication::ReplicationMode::SYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
+  ASSERT_FALSE(main_mem_store
+                   ->RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, ports[1]},
+                                     memgraph::storage::replication::ReplicationMode::SYNC,
+                                     memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                     memgraph::storage::replication::ReplicationClientConfig{})
                    .HasError());
 
   const auto *vertex_label = "label";
@@ -300,51 +324,51 @@ TEST_F(ReplicationTest, MultipleSynchronousReplicationTest) {
   const auto *vertex_property_value = "property_value";
   std::optional<memgraph::storage::Gid> vertex_gid;
   {
-    auto acc = main_store.Access();
-    auto v = acc.CreateVertex();
-    ASSERT_TRUE(v.AddLabel(main_store.NameToLabel(vertex_label)).HasValue());
-    ASSERT_TRUE(v.SetProperty(main_store.NameToProperty(vertex_property),
+    auto acc = main_store->Access();
+    auto v = acc->CreateVertex();
+    ASSERT_TRUE(v.AddLabel(main_store->NameToLabel(vertex_label)).HasValue());
+    ASSERT_TRUE(v.SetProperty(main_store->NameToProperty(vertex_property),
                               memgraph::storage::PropertyValue(vertex_property_value))
                     .HasValue());
     vertex_gid.emplace(v.Gid());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   const auto check_replica = [&](memgraph::storage::Storage *replica_store) {
     auto acc = replica_store->Access();
-    const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    const auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(v);
     const auto labels = v->Labels(memgraph::storage::View::OLD);
     ASSERT_TRUE(labels.HasValue());
     ASSERT_THAT(*labels, UnorderedElementsAre(replica_store->NameToLabel(vertex_label)));
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   };
 
-  check_replica(&replica_store1);
-  check_replica(&replica_store2);
+  check_replica(replica_store1.get());
+  check_replica(replica_store2.get());
 
-  main_store.UnregisterReplica(replicas[1]);
+  main_mem_store->UnregisterReplica(replicas[1]);
   {
-    auto acc = main_store.Access();
-    auto v = acc.CreateVertex();
+    auto acc = main_store->Access();
+    auto v = acc->CreateVertex();
     vertex_gid.emplace(v.Gid());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // REPLICA1 should contain the new vertex
   {
-    auto acc = replica_store1.Access();
-    const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = replica_store1->Access();
+    const auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(v);
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   // REPLICA2 should not contain the new vertex
   {
-    auto acc = replica_store2.Access();
-    const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = replica_store2->Access();
+    const auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_FALSE(v);
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
@@ -352,64 +376,65 @@ TEST_F(ReplicationTest, RecoveryProcess) {
   std::vector<memgraph::storage::Gid> vertex_gids;
   // Force the creation of snapshot
   {
-    memgraph::storage::Storage main_store(
+    std::unique_ptr<memgraph::storage::Storage> main_store{new memgraph::storage::InMemoryStorage(
         {.durability = {
              .storage_directory = storage_directory,
              .recover_on_startup = true,
              .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
              .snapshot_on_exit = true,
-         }});
+         }})};
     {
-      auto acc = main_store.Access();
+      auto acc = main_store->Access();
       // Create the vertex before registering a replica
-      auto v = acc.CreateVertex();
+      auto v = acc->CreateVertex();
       vertex_gids.emplace_back(v.Gid());
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
   }
 
   {
     // Create second WAL
-    memgraph::storage::Storage main_store(
-        {.durability = {
-             .storage_directory = storage_directory,
-             .recover_on_startup = true,
-             .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL}});
+    std::unique_ptr<memgraph::storage::Storage> main_store{new memgraph::storage::InMemoryStorage(
+        {.durability = {.storage_directory = storage_directory,
+                        .recover_on_startup = true,
+                        .snapshot_wal_mode =
+                            memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL}})};
     // Create vertices in 2 different transactions
     {
-      auto acc = main_store.Access();
-      auto v = acc.CreateVertex();
+      auto acc = main_store->Access();
+      auto v = acc->CreateVertex();
       vertex_gids.emplace_back(v.Gid());
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
     {
-      auto acc = main_store.Access();
-      auto v = acc.CreateVertex();
+      auto acc = main_store->Access();
+      auto v = acc->CreateVertex();
       vertex_gids.emplace_back(v.Gid());
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
   }
 
-  memgraph::storage::Storage main_store(
+  std::unique_ptr<memgraph::storage::Storage> main_store{new memgraph::storage::InMemoryStorage(
       {.durability = {
            .storage_directory = storage_directory,
            .recover_on_startup = true,
            .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL,
-       }});
+       }})};
+  auto *main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(main_store.get());
 
   static constexpr const auto *property_name = "property_name";
   static constexpr const auto property_value = 1;
   {
     // Force the creation of current WAL file
-    auto acc = main_store.Access();
+    auto acc = main_store->Access();
     for (const auto &vertex_gid : vertex_gids) {
-      auto v = acc.FindVertex(vertex_gid, memgraph::storage::View::OLD);
+      auto v = acc->FindVertex(vertex_gid, memgraph::storage::View::OLD);
       ASSERT_TRUE(v);
       ASSERT_TRUE(
-          v->SetProperty(main_store.NameToProperty(property_name), memgraph::storage::PropertyValue(property_value))
+          v->SetProperty(main_store->NameToProperty(property_name), memgraph::storage::PropertyValue(property_value))
               .HasValue());
     }
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
   std::filesystem::path replica_storage_directory{std::filesystem::temp_directory_path() /
@@ -419,244 +444,271 @@ TEST_F(ReplicationTest, RecoveryProcess) {
 
   static constexpr const auto *vertex_label = "vertex_label";
   {
-    memgraph::storage::Storage replica_store(
-        {.durability = {
-             .storage_directory = replica_storage_directory,
-             .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL}});
+    std::unique_ptr<memgraph::storage::Storage> replica_store{new memgraph::storage::InMemoryStorage(
+        {.durability = {.storage_directory = replica_storage_directory,
+                        .snapshot_wal_mode =
+                            memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL}})};
+    auto *replica_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(replica_store.get());
 
-    replica_store.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
+    replica_mem_store->SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]},
+                                      memgraph::storage::replication::ReplicationServerConfig{});
 
-    ASSERT_FALSE(main_store
-                     .RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
-                                      memgraph::storage::replication::ReplicationMode::SYNC,
-                                      memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
+    ASSERT_FALSE(main_mem_store
+                     ->RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
+                                       memgraph::storage::replication::ReplicationMode::SYNC,
+                                       memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                       memgraph::storage::replication::ReplicationClientConfig{})
                      .HasError());
 
-    ASSERT_EQ(main_store.GetReplicaState(replicas[0]), memgraph::storage::replication::ReplicaState::RECOVERY);
+    ASSERT_EQ(main_mem_store->GetReplicaState(replicas[0]), memgraph::storage::replication::ReplicaState::RECOVERY);
 
-    while (main_store.GetReplicaState(replicas[0]) != memgraph::storage::replication::ReplicaState::READY) {
+    while (main_mem_store->GetReplicaState(replicas[0]) != memgraph::storage::replication::ReplicaState::READY) {
       std::this_thread::sleep_for(std::chrono::milliseconds(10));
     }
 
     {
-      auto acc = main_store.Access();
+      auto acc = main_store->Access();
       for (const auto &vertex_gid : vertex_gids) {
-        auto v = acc.FindVertex(vertex_gid, memgraph::storage::View::OLD);
+        auto v = acc->FindVertex(vertex_gid, memgraph::storage::View::OLD);
         ASSERT_TRUE(v);
-        ASSERT_TRUE(v->AddLabel(main_store.NameToLabel(vertex_label)).HasValue());
+        ASSERT_TRUE(v->AddLabel(main_store->NameToLabel(vertex_label)).HasValue());
       }
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
     {
-      auto acc = replica_store.Access();
+      auto acc = replica_store->Access();
       for (const auto &vertex_gid : vertex_gids) {
-        auto v = acc.FindVertex(vertex_gid, memgraph::storage::View::OLD);
+        auto v = acc->FindVertex(vertex_gid, memgraph::storage::View::OLD);
         ASSERT_TRUE(v);
         const auto labels = v->Labels(memgraph::storage::View::OLD);
         ASSERT_TRUE(labels.HasValue());
-        ASSERT_THAT(*labels, UnorderedElementsAre(replica_store.NameToLabel(vertex_label)));
+        ASSERT_THAT(*labels, UnorderedElementsAre(replica_store->NameToLabel(vertex_label)));
         const auto properties = v->Properties(memgraph::storage::View::OLD);
         ASSERT_TRUE(properties.HasValue());
         ASSERT_THAT(*properties,
-                    UnorderedElementsAre(std::make_pair(replica_store.NameToProperty(property_name),
+                    UnorderedElementsAre(std::make_pair(replica_store->NameToProperty(property_name),
                                                         memgraph::storage::PropertyValue(property_value))));
       }
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
   }
   {
-    memgraph::storage::Storage replica_store(
-        {.durability = {
-             .storage_directory = replica_storage_directory,
-             .recover_on_startup = true,
-             .snapshot_wal_mode = memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL}});
+    std::unique_ptr<memgraph::storage::Storage> replica_store{new memgraph::storage::InMemoryStorage(
+        {.durability = {.storage_directory = replica_storage_directory,
+                        .recover_on_startup = true,
+                        .snapshot_wal_mode =
+                            memgraph::storage::Config::Durability::SnapshotWalMode::PERIODIC_SNAPSHOT_WITH_WAL}})};
     {
-      auto acc = replica_store.Access();
+      auto acc = replica_store->Access();
       for (const auto &vertex_gid : vertex_gids) {
-        auto v = acc.FindVertex(vertex_gid, memgraph::storage::View::OLD);
+        auto v = acc->FindVertex(vertex_gid, memgraph::storage::View::OLD);
         ASSERT_TRUE(v);
         const auto labels = v->Labels(memgraph::storage::View::OLD);
         ASSERT_TRUE(labels.HasValue());
-        ASSERT_THAT(*labels, UnorderedElementsAre(replica_store.NameToLabel(vertex_label)));
+        ASSERT_THAT(*labels, UnorderedElementsAre(replica_store->NameToLabel(vertex_label)));
         const auto properties = v->Properties(memgraph::storage::View::OLD);
         ASSERT_TRUE(properties.HasValue());
         ASSERT_THAT(*properties,
-                    UnorderedElementsAre(std::make_pair(replica_store.NameToProperty(property_name),
+                    UnorderedElementsAre(std::make_pair(replica_store->NameToProperty(property_name),
                                                         memgraph::storage::PropertyValue(property_value))));
       }
-      ASSERT_FALSE(acc.Commit().HasError());
+      ASSERT_FALSE(acc->Commit().HasError());
     }
   }
 }
 
 TEST_F(ReplicationTest, BasicAsynchronousReplicationTest) {
-  memgraph::storage::Storage main_store(configuration);
+  std::unique_ptr<memgraph::storage::Storage> main_store{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(main_store.get());
 
-  memgraph::storage::Storage replica_store_async(configuration);
+  std::unique_ptr<memgraph::storage::Storage> replica_store_async{
+      new memgraph::storage::InMemoryStorage(configuration)};
 
-  replica_store_async.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[1]});
+  auto *replica_mem_store_async = static_cast<memgraph::storage::InMemoryStorage *>(replica_store_async.get());
 
-  ASSERT_FALSE(main_store
-                   .RegisterReplica("REPLICA_ASYNC", memgraph::io::network::Endpoint{local_host, ports[1]},
-                                    memgraph::storage::replication::ReplicationMode::ASYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
+  replica_mem_store_async->SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[1]},
+                                          memgraph::storage::replication::ReplicationServerConfig{});
+
+  ASSERT_FALSE(main_mem_store
+                   ->RegisterReplica("REPLICA_ASYNC", memgraph::io::network::Endpoint{local_host, ports[1]},
+                                     memgraph::storage::replication::ReplicationMode::ASYNC,
+                                     memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                     memgraph::storage::replication::ReplicationClientConfig{})
                    .HasError());
 
   static constexpr size_t vertices_create_num = 10;
   std::vector<memgraph::storage::Gid> created_vertices;
   for (size_t i = 0; i < vertices_create_num; ++i) {
-    auto acc = main_store.Access();
-    auto v = acc.CreateVertex();
+    auto acc = main_store->Access();
+    auto v = acc->CreateVertex();
     created_vertices.push_back(v.Gid());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
 
     if (i == 0) {
-      ASSERT_EQ(main_store.GetReplicaState("REPLICA_ASYNC"), memgraph::storage::replication::ReplicaState::REPLICATING);
+      ASSERT_EQ(main_mem_store->GetReplicaState("REPLICA_ASYNC"),
+                memgraph::storage::replication::ReplicaState::REPLICATING);
     } else {
-      ASSERT_EQ(main_store.GetReplicaState("REPLICA_ASYNC"), memgraph::storage::replication::ReplicaState::RECOVERY);
+      ASSERT_EQ(main_mem_store->GetReplicaState("REPLICA_ASYNC"),
+                memgraph::storage::replication::ReplicaState::RECOVERY);
     }
   }
 
-  while (main_store.GetReplicaState("REPLICA_ASYNC") != memgraph::storage::replication::ReplicaState::READY) {
+  while (main_mem_store->GetReplicaState("REPLICA_ASYNC") != memgraph::storage::replication::ReplicaState::READY) {
     std::this_thread::sleep_for(std::chrono::milliseconds(10));
   }
 
   ASSERT_TRUE(std::all_of(created_vertices.begin(), created_vertices.end(), [&](const auto vertex_gid) {
-    auto acc = replica_store_async.Access();
-    auto v = acc.FindVertex(vertex_gid, memgraph::storage::View::OLD);
+    auto acc = replica_store_async->Access();
+    auto v = acc->FindVertex(vertex_gid, memgraph::storage::View::OLD);
     const bool exists = v.has_value();
-    EXPECT_FALSE(acc.Commit().HasError());
+    EXPECT_FALSE(acc->Commit().HasError());
     return exists;
   }));
 }
 
 TEST_F(ReplicationTest, EpochTest) {
-  memgraph::storage::Storage main_store(configuration);
+  std::unique_ptr<memgraph::storage::Storage> main_store{new memgraph::storage::InMemoryStorage(configuration)};
+  std::unique_ptr<memgraph::storage::Storage> replica_store1{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(main_store.get());
+  auto *replica_mem_store1 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store1.get());
 
-  memgraph::storage::Storage replica_store1(configuration);
+  replica_mem_store1->SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]},
+                                     memgraph::storage::replication::ReplicationServerConfig{});
 
-  replica_store1.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
+  std::unique_ptr<memgraph::storage::Storage> replica_store2{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *replica_mem_store2 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store2.get());
 
-  memgraph::storage::Storage replica_store2(configuration);
+  replica_mem_store2->SetReplicaRole(memgraph::io::network::Endpoint{local_host, 10001},
+                                     memgraph::storage::replication::ReplicationServerConfig{});
 
-  replica_store2.SetReplicaRole(memgraph::io::network::Endpoint{local_host, 10001});
-
-  ASSERT_FALSE(main_store
-                   .RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
-                                    memgraph::storage::replication::ReplicationMode::SYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
+  ASSERT_FALSE(main_mem_store
+                   ->RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
+                                     memgraph::storage::replication::ReplicationMode::SYNC,
+                                     memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                     memgraph::storage::replication::ReplicationClientConfig{})
                    .HasError());
 
-  ASSERT_FALSE(main_store
-                   .RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, 10001},
-                                    memgraph::storage::replication::ReplicationMode::SYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
+  ASSERT_FALSE(main_mem_store
+                   ->RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, 10001},
+                                     memgraph::storage::replication::ReplicationMode::SYNC,
+                                     memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                     memgraph::storage::replication::ReplicationClientConfig{})
                    .HasError());
 
   std::optional<memgraph::storage::Gid> vertex_gid;
   {
-    auto acc = main_store.Access();
-    const auto v = acc.CreateVertex();
+    auto acc = main_store->Access();
+    const auto v = acc->CreateVertex();
     vertex_gid.emplace(v.Gid());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = replica_store1.Access();
-    const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = replica_store1->Access();
+    const auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(v);
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = replica_store2.Access();
-    const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = replica_store2->Access();
+    const auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(v);
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
-  main_store.UnregisterReplica(replicas[0]);
-  main_store.UnregisterReplica(replicas[1]);
+  main_mem_store->UnregisterReplica(replicas[0]);
+  main_mem_store->UnregisterReplica(replicas[1]);
+
+  replica_mem_store1->SetMainReplicationRole();
+  ASSERT_FALSE(replica_mem_store1
+                   ->RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, 10001},
+                                     memgraph::storage::replication::ReplicationMode::SYNC,
+                                     memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                     memgraph::storage::replication::ReplicationClientConfig{})
 
-  replica_store1.SetMainReplicationRole();
-  ASSERT_FALSE(replica_store1
-                   .RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, 10001},
-                                    memgraph::storage::replication::ReplicationMode::SYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
                    .HasError());
 
   {
-    auto acc = main_store.Access();
-    acc.CreateVertex();
-    ASSERT_FALSE(acc.Commit().HasError());
+    auto acc = main_store->Access();
+    acc->CreateVertex();
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   {
-    auto acc = replica_store1.Access();
-    auto v = acc.CreateVertex();
+    auto acc = replica_store1->Access();
+    auto v = acc->CreateVertex();
     vertex_gid.emplace(v.Gid());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   // Replica1 should forward it's vertex to Replica2
   {
-    auto acc = replica_store2.Access();
-    const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = replica_store2->Access();
+    const auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_TRUE(v);
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 
-  replica_store1.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
-  ASSERT_TRUE(main_store
-                  .RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
-                                   memgraph::storage::replication::ReplicationMode::SYNC,
-                                   memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
+  replica_mem_store1->SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]},
+                                     memgraph::storage::replication::ReplicationServerConfig{});
+  ASSERT_TRUE(main_mem_store
+                  ->RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
+                                    memgraph::storage::replication::ReplicationMode::SYNC,
+                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                    memgraph::storage::replication::ReplicationClientConfig{})
+
                   .HasError());
 
   {
-    auto acc = main_store.Access();
-    const auto v = acc.CreateVertex();
+    auto acc = main_store->Access();
+    const auto v = acc->CreateVertex();
     vertex_gid.emplace(v.Gid());
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
   // Replica1 is not compatible with the main so it shouldn't contain
   // it's newest vertex
   {
-    auto acc = replica_store1.Access();
-    const auto v = acc.FindVertex(*vertex_gid, memgraph::storage::View::OLD);
+    auto acc = replica_store1->Access();
+    const auto v = acc->FindVertex(*vertex_gid, memgraph::storage::View::OLD);
     ASSERT_FALSE(v);
-    ASSERT_FALSE(acc.Commit().HasError());
+    ASSERT_FALSE(acc->Commit().HasError());
   }
 }
 
 TEST_F(ReplicationTest, ReplicationInformation) {
-  memgraph::storage::Storage main_store(configuration);
-
-  memgraph::storage::Storage replica_store1(configuration);
+  std::unique_ptr<memgraph::storage::Storage> main_store{new memgraph::storage::InMemoryStorage(configuration)};
+  std::unique_ptr<memgraph::storage::Storage> replica_store1{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(main_store.get());
+  auto *replica_mem_store1 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store1.get());
 
   const memgraph::io::network::Endpoint replica1_endpoint{local_host, 10001};
-  replica_store1.SetReplicaRole(replica1_endpoint);
+  replica_mem_store1->SetReplicaRole(replica1_endpoint, memgraph::storage::replication::ReplicationServerConfig{});
 
   const memgraph::io::network::Endpoint replica2_endpoint{local_host, 10002};
-  memgraph::storage::Storage replica_store2(configuration);
-
-  replica_store2.SetReplicaRole(replica2_endpoint);
+  std::unique_ptr<memgraph::storage::Storage> replica_store2{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *replica_mem_store2 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store2.get());
+  replica_mem_store2->SetReplicaRole(replica2_endpoint, memgraph::storage::replication::ReplicationServerConfig{});
 
   const std::string replica1_name{replicas[0]};
-  ASSERT_FALSE(main_store
-                   .RegisterReplica(replica1_name, replica1_endpoint,
-                                    memgraph::storage::replication::ReplicationMode::SYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
+  ASSERT_FALSE(main_mem_store
+                   ->RegisterReplica(replica1_name, replica1_endpoint,
+                                     memgraph::storage::replication::ReplicationMode::SYNC,
+                                     memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                     memgraph::storage::replication::ReplicationClientConfig{})
+
                    .HasError());
 
   const std::string replica2_name{replicas[1]};
-  ASSERT_FALSE(main_store
-                   .RegisterReplica(replica2_name, replica2_endpoint,
-                                    memgraph::storage::replication::ReplicationMode::ASYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
+  ASSERT_FALSE(main_mem_store
+                   ->RegisterReplica(replica2_name, replica2_endpoint,
+                                     memgraph::storage::replication::ReplicationMode::ASYNC,
+                                     memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                     memgraph::storage::replication::ReplicationClientConfig{})
+
                    .HasError());
 
-  ASSERT_EQ(main_store.GetReplicationRole(), memgraph::storage::replication::ReplicationRole::MAIN);
-  ASSERT_EQ(replica_store1.GetReplicationRole(), memgraph::storage::replication::ReplicationRole::REPLICA);
-  ASSERT_EQ(replica_store2.GetReplicationRole(), memgraph::storage::replication::ReplicationRole::REPLICA);
+  ASSERT_EQ(main_mem_store->GetReplicationRole(), memgraph::storage::replication::ReplicationRole::MAIN);
+  ASSERT_EQ(replica_mem_store1->GetReplicationRole(), memgraph::storage::replication::ReplicationRole::REPLICA);
+  ASSERT_EQ(replica_mem_store2->GetReplicationRole(), memgraph::storage::replication::ReplicationRole::REPLICA);
 
-  const auto replicas_info = main_store.ReplicasInfo();
+  const auto replicas_info = main_mem_store->ReplicasInfo();
   ASSERT_EQ(replicas_info.size(), 2);
 
   const auto &first_info = replicas_info[0];
@@ -673,82 +725,96 @@ TEST_F(ReplicationTest, ReplicationInformation) {
 }
 
 TEST_F(ReplicationTest, ReplicationReplicaWithExistingName) {
-  memgraph::storage::Storage main_store(configuration);
-
-  memgraph::storage::Storage replica_store1(configuration);
+  std::unique_ptr<memgraph::storage::Storage> main_store{new memgraph::storage::InMemoryStorage(configuration)};
+  std::unique_ptr<memgraph::storage::Storage> replica_store1{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(main_store.get());
+  auto *replica_mem_store1 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store1.get());
 
   const memgraph::io::network::Endpoint replica1_endpoint{local_host, 10001};
-  replica_store1.SetReplicaRole(replica1_endpoint);
+  replica_mem_store1->SetReplicaRole(replica1_endpoint, memgraph::storage::replication::ReplicationServerConfig{});
 
   const memgraph::io::network::Endpoint replica2_endpoint{local_host, 10002};
-  memgraph::storage::Storage replica_store2(configuration);
-
-  replica_store2.SetReplicaRole(replica2_endpoint);
+  std::unique_ptr<memgraph::storage::Storage> replica_store2{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *replica_mem_store2 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store2.get());
+  replica_mem_store2->SetReplicaRole(replica2_endpoint, memgraph::storage::replication::ReplicationServerConfig{});
 
   const std::string replica1_name{replicas[0]};
-  ASSERT_FALSE(main_store
-                   .RegisterReplica(replica1_name, replica1_endpoint,
-                                    memgraph::storage::replication::ReplicationMode::SYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
+  ASSERT_FALSE(main_mem_store
+                   ->RegisterReplica(replica1_name, replica1_endpoint,
+                                     memgraph::storage::replication::ReplicationMode::SYNC,
+                                     memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                     memgraph::storage::replication::ReplicationClientConfig{})
                    .HasError());
 
   const std::string replica2_name{replicas[0]};
-  ASSERT_TRUE(main_store
-                  .RegisterReplica(replica2_name, replica2_endpoint,
-                                   memgraph::storage::replication::ReplicationMode::ASYNC,
-                                   memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
-                  .GetError() == memgraph::storage::Storage::RegisterReplicaError::NAME_EXISTS);
+  ASSERT_TRUE(main_mem_store
+                  ->RegisterReplica(replica2_name, replica2_endpoint,
+                                    memgraph::storage::replication::ReplicationMode::ASYNC,
+                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                    memgraph::storage::replication::ReplicationClientConfig{})
+                  .GetError() == memgraph::storage::InMemoryStorage::RegisterReplicaError::NAME_EXISTS);
 }
 
 TEST_F(ReplicationTest, ReplicationReplicaWithExistingEndPoint) {
-  memgraph::storage::Storage main_store(configuration);
-
-  memgraph::storage::Storage replica_store1(configuration);
+  std::unique_ptr<memgraph::storage::Storage> main_store{new memgraph::storage::InMemoryStorage(configuration)};
+  std::unique_ptr<memgraph::storage::Storage> replica_store1{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(main_store.get());
+  auto *replica_mem_store1 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store1.get());
 
   const memgraph::io::network::Endpoint replica1_endpoint{local_host, 10001};
-  replica_store1.SetReplicaRole(replica1_endpoint);
+  replica_mem_store1->SetReplicaRole(replica1_endpoint, memgraph::storage::replication::ReplicationServerConfig{});
+
+  std::unique_ptr<memgraph::storage::Storage> replica_store2{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *replica_mem_store2 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store2.get());
 
   const memgraph::io::network::Endpoint replica2_endpoint{local_host, 10001};
-  memgraph::storage::Storage replica_store2(configuration);
-
-  replica_store2.SetReplicaRole(replica2_endpoint);
+  replica_mem_store2->SetReplicaRole(replica2_endpoint, memgraph::storage::replication::ReplicationServerConfig{});
 
   const std::string replica1_name{replicas[0]};
-  ASSERT_FALSE(main_store
-                   .RegisterReplica(replica1_name, replica1_endpoint,
-                                    memgraph::storage::replication::ReplicationMode::SYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
+  ASSERT_FALSE(main_mem_store
+                   ->RegisterReplica(replica1_name, replica1_endpoint,
+                                     memgraph::storage::replication::ReplicationMode::SYNC,
+                                     memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                     memgraph::storage::replication::ReplicationClientConfig{})
                    .HasError());
 
   const std::string replica2_name{replicas[1]};
-  ASSERT_TRUE(main_store
-                  .RegisterReplica(replica2_name, replica2_endpoint,
-                                   memgraph::storage::replication::ReplicationMode::ASYNC,
-                                   memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
-                  .GetError() == memgraph::storage::Storage::RegisterReplicaError::END_POINT_EXISTS);
+  ASSERT_TRUE(main_mem_store
+                  ->RegisterReplica(replica2_name, replica2_endpoint,
+                                    memgraph::storage::replication::ReplicationMode::ASYNC,
+                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                    memgraph::storage::replication::ReplicationClientConfig{})
+                  .GetError() == memgraph::storage::InMemoryStorage::RegisterReplicaError::END_POINT_EXISTS);
 }
 
 TEST_F(ReplicationTest, RestoringReplicationAtStartupAftgerDroppingReplica) {
   auto main_config = configuration;
   main_config.durability.restore_replication_state_on_startup = true;
-  auto main_store = std::make_unique<memgraph::storage::Storage>(main_config);
+  std::unique_ptr<memgraph::storage::Storage> main_store{new memgraph::storage::InMemoryStorage(main_config)};
+  std::unique_ptr<memgraph::storage::Storage> replica_store1{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(main_store.get());
+  auto *replica_mem_store1 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store1.get());
 
-  memgraph::storage::Storage replica_store1(configuration);
-  replica_store1.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
+  replica_mem_store1->SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]},
+                                     memgraph::storage::replication::ReplicationServerConfig{});
 
-  memgraph::storage::Storage replica_store2(configuration);
-  replica_store2.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[1]});
+  std::unique_ptr<memgraph::storage::Storage> replica_store2{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *replica_mem_store2 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store2.get());
+  replica_mem_store2->SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[1]},
+                                     memgraph::storage::replication::ReplicationServerConfig{});
 
-  auto res = main_store->RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
-                                         memgraph::storage::replication::ReplicationMode::SYNC,
-                                         memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID);
+  auto res = main_mem_store->RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
+                                             memgraph::storage::replication::ReplicationMode::SYNC,
+                                             memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                             memgraph::storage::replication::ReplicationClientConfig{});
   ASSERT_FALSE(res.HasError());
-  res = main_store->RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, ports[1]},
-                                    memgraph::storage::replication::ReplicationMode::SYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID);
+  res = main_mem_store->RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, ports[1]},
+                                        memgraph::storage::replication::ReplicationMode::SYNC,
+                                        memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                        memgraph::storage::replication::ReplicationClientConfig{});
   ASSERT_FALSE(res.HasError());
 
-  auto replica_infos = main_store->ReplicasInfo();
+  auto replica_infos = main_mem_store->ReplicasInfo();
 
   ASSERT_EQ(replica_infos.size(), 2);
   ASSERT_EQ(replica_infos[0].name, replicas[0]);
@@ -760,8 +826,10 @@ TEST_F(ReplicationTest, RestoringReplicationAtStartupAftgerDroppingReplica) {
 
   main_store.reset();
 
-  auto other_main_store = std::make_unique<memgraph::storage::Storage>(main_config);
-  replica_infos = other_main_store->ReplicasInfo();
+  std::unique_ptr<memgraph::storage::Storage> other_main_store{new memgraph::storage::InMemoryStorage(main_config)};
+  auto *other_main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(other_main_store.get());
+
+  replica_infos = other_main_mem_store->ReplicasInfo();
   ASSERT_EQ(replica_infos.size(), 2);
   ASSERT_EQ(replica_infos[0].name, replicas[0]);
   ASSERT_EQ(replica_infos[0].endpoint.address, local_host);
@@ -774,23 +842,33 @@ TEST_F(ReplicationTest, RestoringReplicationAtStartupAftgerDroppingReplica) {
 TEST_F(ReplicationTest, RestoringReplicationAtStartup) {
   auto main_config = configuration;
   main_config.durability.restore_replication_state_on_startup = true;
-  auto main_store = std::make_unique<memgraph::storage::Storage>(main_config);
-  memgraph::storage::Storage replica_store1(configuration);
-  replica_store1.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]});
 
-  memgraph::storage::Storage replica_store2(configuration);
-  replica_store2.SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[1]});
+  std::unique_ptr<memgraph::storage::Storage> main_store{new memgraph::storage::InMemoryStorage(main_config)};
+  std::unique_ptr<memgraph::storage::Storage> replica_store1{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(main_store.get());
+  auto *replica_mem_store1 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store1.get());
 
-  auto res = main_store->RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
-                                         memgraph::storage::replication::ReplicationMode::SYNC,
-                                         memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID);
+  replica_mem_store1->SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[0]},
+                                     memgraph::storage::replication::ReplicationServerConfig{});
+
+  std::unique_ptr<memgraph::storage::Storage> replica_store2{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *replica_mem_store2 = static_cast<memgraph::storage::InMemoryStorage *>(replica_store2.get());
+
+  replica_mem_store2->SetReplicaRole(memgraph::io::network::Endpoint{local_host, ports[1]},
+                                     memgraph::storage::replication::ReplicationServerConfig{});
+
+  auto res = main_mem_store->RegisterReplica(replicas[0], memgraph::io::network::Endpoint{local_host, ports[0]},
+                                             memgraph::storage::replication::ReplicationMode::SYNC,
+                                             memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                             memgraph::storage::replication::ReplicationClientConfig{});
   ASSERT_FALSE(res.HasError());
-  res = main_store->RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, ports[1]},
-                                    memgraph::storage::replication::ReplicationMode::SYNC,
-                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID);
+  res = main_mem_store->RegisterReplica(replicas[1], memgraph::io::network::Endpoint{local_host, ports[1]},
+                                        memgraph::storage::replication::ReplicationMode::SYNC,
+                                        memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                        memgraph::storage::replication::ReplicationClientConfig{});
   ASSERT_FALSE(res.HasError());
 
-  auto replica_infos = main_store->ReplicasInfo();
+  auto replica_infos = main_mem_store->ReplicasInfo();
 
   ASSERT_EQ(replica_infos.size(), 2);
   ASSERT_EQ(replica_infos[0].name, replicas[0]);
@@ -800,10 +878,10 @@ TEST_F(ReplicationTest, RestoringReplicationAtStartup) {
   ASSERT_EQ(replica_infos[1].endpoint.address, local_host);
   ASSERT_EQ(replica_infos[1].endpoint.port, ports[1]);
 
-  const auto unregister_res = main_store->UnregisterReplica(replicas[0]);
+  const auto unregister_res = main_mem_store->UnregisterReplica(replicas[0]);
   ASSERT_TRUE(unregister_res);
 
-  replica_infos = main_store->ReplicasInfo();
+  replica_infos = main_mem_store->ReplicasInfo();
   ASSERT_EQ(replica_infos.size(), 1);
   ASSERT_EQ(replica_infos[0].name, replicas[1]);
   ASSERT_EQ(replica_infos[0].endpoint.address, local_host);
@@ -811,8 +889,9 @@ TEST_F(ReplicationTest, RestoringReplicationAtStartup) {
 
   main_store.reset();
 
-  auto other_main_store = std::make_unique<memgraph::storage::Storage>(main_config);
-  replica_infos = other_main_store->ReplicasInfo();
+  std::unique_ptr<memgraph::storage::Storage> other_main_store{new memgraph::storage::InMemoryStorage(main_config)};
+  auto *other_main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(other_main_store.get());
+  replica_infos = other_main_mem_store->ReplicasInfo();
   ASSERT_EQ(replica_infos.size(), 1);
   ASSERT_EQ(replica_infos[0].name, replicas[1]);
   ASSERT_EQ(replica_infos[0].endpoint.address, local_host);
@@ -820,11 +899,13 @@ TEST_F(ReplicationTest, RestoringReplicationAtStartup) {
 }
 
 TEST_F(ReplicationTest, AddingInvalidReplica) {
-  memgraph::storage::Storage main_store(configuration);
+  std::unique_ptr<memgraph::storage::Storage> main_store{new memgraph::storage::InMemoryStorage(configuration)};
+  auto *main_mem_store = static_cast<memgraph::storage::InMemoryStorage *>(main_store.get());
 
-  ASSERT_TRUE(main_store
-                  .RegisterReplica("REPLICA", memgraph::io::network::Endpoint{local_host, ports[0]},
-                                   memgraph::storage::replication::ReplicationMode::SYNC,
-                                   memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID)
-                  .GetError() == memgraph::storage::Storage::RegisterReplicaError::CONNECTION_FAILED);
+  ASSERT_TRUE(main_mem_store
+                  ->RegisterReplica("REPLICA", memgraph::io::network::Endpoint{local_host, ports[0]},
+                                    memgraph::storage::replication::ReplicationMode::SYNC,
+                                    memgraph::storage::replication::RegistrationMode::MUST_BE_INSTANTLY_VALID,
+                                    memgraph::storage::replication::ReplicationClientConfig{})
+                  .GetError() == memgraph::storage::InMemoryStorage::RegisterReplicaError::CONNECTION_FAILED);
 }
diff --git a/tests/unit/storage_v2_storage_mode.cpp b/tests/unit/storage_v2_storage_mode.cpp
index a82d0b21a..ad0def8ce 100644
--- a/tests/unit/storage_v2_storage_mode.cpp
+++ b/tests/unit/storage_v2_storage_mode.cpp
@@ -18,11 +18,12 @@
 
 #include "interpreter_faker.hpp"
 #include "query/exceptions.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 #include "storage/v2/isolation_level.hpp"
-#include "storage/v2/storage.hpp"
 #include "storage/v2/storage_mode.hpp"
 #include "storage/v2/vertex_accessor.hpp"
 #include "storage_test_utils.hpp"
+#include "utils/exceptions.hpp"
 
 class StorageModeTest : public ::testing::TestWithParam<memgraph::storage::StorageMode> {
  public:
@@ -37,27 +38,29 @@ class StorageModeTest : public ::testing::TestWithParam<memgraph::storage::Stora
 TEST_P(StorageModeTest, Mode) {
   const memgraph::storage::StorageMode storage_mode = GetParam();
 
-  memgraph::storage::Storage storage{
-      {.transaction{.isolation_level = memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION}}};
-  storage.SetStorageMode(storage_mode);
-  auto creator = storage.Access();
-  auto other_analytics_mode_reader = storage.Access();
+  std::unique_ptr<memgraph::storage::Storage> storage =
+      std::make_unique<memgraph::storage::InMemoryStorage>(memgraph::storage::Config{
+          .transaction{.isolation_level = memgraph::storage::IsolationLevel::SNAPSHOT_ISOLATION}});
 
-  ASSERT_EQ(CountVertices(creator, memgraph::storage::View::OLD), 0);
-  ASSERT_EQ(CountVertices(other_analytics_mode_reader, memgraph::storage::View::OLD), 0);
+  storage->SetStorageMode(storage_mode);
+  auto creator = storage->Access();
+  auto other_analytics_mode_reader = storage->Access();
+
+  ASSERT_EQ(CountVertices(*creator, memgraph::storage::View::OLD), 0);
+  ASSERT_EQ(CountVertices(*other_analytics_mode_reader, memgraph::storage::View::OLD), 0);
 
   static constexpr int vertex_creation_count = 10;
   {
     for (size_t i = 1; i <= vertex_creation_count; i++) {
-      creator.CreateVertex();
+      creator->CreateVertex();
 
       int64_t expected_vertices_count = storage_mode == memgraph::storage::StorageMode::IN_MEMORY_ANALYTICAL ? i : 0;
-      ASSERT_EQ(CountVertices(creator, memgraph::storage::View::OLD), expected_vertices_count);
-      ASSERT_EQ(CountVertices(other_analytics_mode_reader, memgraph::storage::View::OLD), expected_vertices_count);
+      ASSERT_EQ(CountVertices(*creator, memgraph::storage::View::OLD), expected_vertices_count);
+      ASSERT_EQ(CountVertices(*other_analytics_mode_reader, memgraph::storage::View::OLD), expected_vertices_count);
     }
   }
 
-  ASSERT_FALSE(creator.Commit().HasError());
+  ASSERT_FALSE(creator->Commit().HasError());
 }
 
 INSTANTIATE_TEST_CASE_P(ParameterizedStorageModeTests, StorageModeTest, ::testing::ValuesIn(storage_modes),
@@ -65,9 +68,9 @@ INSTANTIATE_TEST_CASE_P(ParameterizedStorageModeTests, StorageModeTest, ::testin
 
 class StorageModeMultiTxTest : public ::testing::Test {
  protected:
-  memgraph::storage::Storage db_;
   std::filesystem::path data_directory{std::filesystem::temp_directory_path() / "MG_tests_unit_storage_mode"};
-  memgraph::query::InterpreterContext interpreter_context{&db_, {}, data_directory};
+  memgraph::query::InterpreterContext interpreter_context{
+      std::make_unique<memgraph::storage::InMemoryStorage>(), {}, data_directory};
   InterpreterFaker running_interpreter{&interpreter_context}, main_interpreter{&interpreter_context};
 };
 
@@ -84,11 +87,11 @@ TEST_F(StorageModeMultiTxTest, ModeSwitchInactiveTransaction) {
     while (!started) {
       std::this_thread::sleep_for(std::chrono::milliseconds(20));
     }
-    ASSERT_EQ(db_.GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL);
+    ASSERT_EQ(interpreter_context.db->GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL);
     main_interpreter.Interpret("STORAGE MODE IN_MEMORY_ANALYTICAL");
 
     // should change state
-    ASSERT_EQ(db_.GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_ANALYTICAL);
+    ASSERT_EQ(interpreter_context.db->GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_ANALYTICAL);
 
     // finish thread
     running_thread.request_stop();
@@ -97,7 +100,7 @@ TEST_F(StorageModeMultiTxTest, ModeSwitchInactiveTransaction) {
 
 TEST_F(StorageModeMultiTxTest, ModeSwitchActiveTransaction) {
   // transactional state
-  ASSERT_EQ(db_.GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL);
+  ASSERT_EQ(interpreter_context.db->GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL);
   main_interpreter.Interpret("BEGIN");
 
   bool started = false;
@@ -116,7 +119,7 @@ TEST_F(StorageModeMultiTxTest, ModeSwitchActiveTransaction) {
       std::this_thread::sleep_for(std::chrono::milliseconds(20));
     }
     // should not change still
-    ASSERT_EQ(db_.GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL);
+    ASSERT_EQ(interpreter_context.db->GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL);
 
     main_interpreter.Interpret("COMMIT");
 
@@ -124,7 +127,7 @@ TEST_F(StorageModeMultiTxTest, ModeSwitchActiveTransaction) {
       std::this_thread::sleep_for(std::chrono::milliseconds(20));
     }
     // should change state
-    ASSERT_EQ(db_.GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_ANALYTICAL);
+    ASSERT_EQ(interpreter_context.db->GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_ANALYTICAL);
 
     // finish thread
     running_thread.request_stop();
@@ -132,11 +135,11 @@ TEST_F(StorageModeMultiTxTest, ModeSwitchActiveTransaction) {
 }
 
 TEST_F(StorageModeMultiTxTest, ErrorChangeIsolationLevel) {
-  ASSERT_EQ(db_.GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL);
+  ASSERT_EQ(interpreter_context.db->GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_TRANSACTIONAL);
   main_interpreter.Interpret("STORAGE MODE IN_MEMORY_ANALYTICAL");
 
   // should change state
-  ASSERT_EQ(db_.GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_ANALYTICAL);
+  ASSERT_EQ(interpreter_context.db->GetStorageMode(), memgraph::storage::StorageMode::IN_MEMORY_ANALYTICAL);
 
   ASSERT_THROW(running_interpreter.Interpret("SET GLOBAL TRANSACTION ISOLATION LEVEL READ COMMITTED;"),
                memgraph::query::IsolationLevelModificationInAnalyticsException);
diff --git a/tests/unit/transaction_queue.cpp b/tests/unit/transaction_queue.cpp
index 812f9cb2b..d04006a32 100644
--- a/tests/unit/transaction_queue.cpp
+++ b/tests/unit/transaction_queue.cpp
@@ -17,25 +17,34 @@
 #include <gtest/gtest.h>
 #include "gmock/gmock.h"
 
+#include "disk_test_utils.hpp"
 #include "interpreter_faker.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 /*
 Tests rely on the fact that interpreters are sequentially added to runninng_interpreters to get transaction_id of its
 corresponding interpreter/.
 */
+template <typename StorageType>
 class TransactionQueueSimpleTest : public ::testing::Test {
  protected:
-  memgraph::storage::Storage db_;
+  const std::string testSuite = "transactin_queue";
   std::filesystem::path data_directory{std::filesystem::temp_directory_path() / "MG_tests_unit_transaction_queue_intr"};
-  memgraph::query::InterpreterContext interpreter_context{&db_, {}, data_directory};
+  memgraph::query::InterpreterContext interpreter_context{
+      std::make_unique<StorageType>(disk_test_utils::GenerateOnDiskConfig(testSuite)), {}, data_directory};
   InterpreterFaker running_interpreter{&interpreter_context}, main_interpreter{&interpreter_context};
+
+  void TearDown() override { disk_test_utils::RemoveRocksDbDirs(testSuite); }
 };
 
-TEST_F(TransactionQueueSimpleTest, TwoInterpretersInterleaving) {
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(TransactionQueueSimpleTest, StorageTypes);
+
+TYPED_TEST(TransactionQueueSimpleTest, TwoInterpretersInterleaving) {
   bool started = false;
   std::jthread running_thread = std::jthread(
       [this, &started](std::stop_token st, int thread_index) {
-        running_interpreter.Interpret("BEGIN");
+        this->running_interpreter.Interpret("BEGIN");
         started = true;
       },
       0);
@@ -44,8 +53,8 @@ TEST_F(TransactionQueueSimpleTest, TwoInterpretersInterleaving) {
     while (!started) {
       std::this_thread::sleep_for(std::chrono::milliseconds(20));
     }
-    main_interpreter.Interpret("CREATE (:Person {prop: 1})");
-    auto show_stream = main_interpreter.Interpret("SHOW TRANSACTIONS");
+    this->main_interpreter.Interpret("CREATE (:Person {prop: 1})");
+    auto show_stream = this->main_interpreter.Interpret("SHOW TRANSACTIONS");
     ASSERT_EQ(show_stream.GetResults().size(), 2U);
     // superadmin executing the transaction
     EXPECT_EQ(show_stream.GetResults()[0][0].ValueString(), "");
@@ -57,18 +66,18 @@ TEST_F(TransactionQueueSimpleTest, TwoInterpretersInterleaving) {
     // Kill the other transaction
     std::string run_trans_id = show_stream.GetResults()[1][1].ValueString();
     std::string esc_run_trans_id = "'" + run_trans_id + "'";
-    auto terminate_stream = main_interpreter.Interpret("TERMINATE TRANSACTIONS " + esc_run_trans_id);
+    auto terminate_stream = this->main_interpreter.Interpret("TERMINATE TRANSACTIONS " + esc_run_trans_id);
     // check result of killing
     ASSERT_EQ(terminate_stream.GetResults().size(), 1U);
     EXPECT_EQ(terminate_stream.GetResults()[0][0].ValueString(), run_trans_id);
     ASSERT_TRUE(terminate_stream.GetResults()[0][1].ValueBool());  // that the transaction is actually killed
     // check the number of transactions now
-    auto show_stream_after_killing = main_interpreter.Interpret("SHOW TRANSACTIONS");
+    auto show_stream_after_killing = this->main_interpreter.Interpret("SHOW TRANSACTIONS");
     ASSERT_EQ(show_stream_after_killing.GetResults().size(), 1U);
     // test the state of the database
-    auto results_stream = main_interpreter.Interpret("MATCH (n) RETURN n");
+    auto results_stream = this->main_interpreter.Interpret("MATCH (n) RETURN n");
     ASSERT_EQ(results_stream.GetResults().size(), 1U);  // from the main interpreter
-    main_interpreter.Interpret("MATCH (n) DETACH DELETE n");
+    this->main_interpreter.Interpret("MATCH (n) DETACH DELETE n");
     // finish thread
     running_thread.request_stop();
   }
diff --git a/tests/unit/transaction_queue_multiple.cpp b/tests/unit/transaction_queue_multiple.cpp
index 64cd10ad8..c2feb89aa 100644
--- a/tests/unit/transaction_queue_multiple.cpp
+++ b/tests/unit/transaction_queue_multiple.cpp
@@ -19,8 +19,12 @@
 #include "gmock/gmock.h"
 #include "spdlog/spdlog.h"
 
+#include "disk_test_utils.hpp"
 #include "interpreter_faker.hpp"
 #include "query/exceptions.hpp"
+#include "storage/v2/config.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 constexpr int NUM_INTERPRETERS = 4, INSERTIONS = 4000;
 
@@ -28,12 +32,14 @@ constexpr int NUM_INTERPRETERS = 4, INSERTIONS = 4000;
 Tests rely on the fact that interpreters are sequentially added to running_interpreters to get transaction_id of its
 corresponding interpreter.
 */
+template <typename StorageType>
 class TransactionQueueMultipleTest : public ::testing::Test {
  protected:
-  memgraph::storage::Storage db_;
+  const std::string testSuite = "transactin_queue_multiple";
   std::filesystem::path data_directory{std::filesystem::temp_directory_path() /
                                        "MG_tests_unit_transaction_queue_multiple_intr"};
-  memgraph::query::InterpreterContext interpreter_context{&db_, {}, data_directory};
+  memgraph::query::InterpreterContext interpreter_context{
+      std::make_unique<StorageType>(disk_test_utils::GenerateOnDiskConfig(testSuite)), {}, data_directory};
   InterpreterFaker main_interpreter{&interpreter_context};
   std::vector<InterpreterFaker *> running_interpreters;
 
@@ -48,19 +54,24 @@ class TransactionQueueMultipleTest : public ::testing::Test {
     for (int i = 0; i < NUM_INTERPRETERS; ++i) {
       delete running_interpreters[i];
     }
+    disk_test_utils::RemoveRocksDbDirs(testSuite);
   }
 };
 
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(TransactionQueueMultipleTest, StorageTypes);
+
 // Tests whether admin can see transaction of superadmin
-TEST_F(TransactionQueueMultipleTest, TerminateTransaction) {
+TYPED_TEST(TransactionQueueMultipleTest, TerminateTransaction) {
   std::vector<bool> started(NUM_INTERPRETERS, false);
   auto thread_func = [this, &started](int thread_index) {
     try {
-      running_interpreters[thread_index]->Interpret("BEGIN");
+      this->running_interpreters[thread_index]->Interpret("BEGIN");
       started[thread_index] = true;
       // add try-catch block
       for (int j = 0; j < INSERTIONS; ++j) {
-        running_interpreters[thread_index]->Interpret("CREATE (:Person {prop: " + std::to_string(thread_index) + "})");
+        this->running_interpreters[thread_index]->Interpret("CREATE (:Person {prop: " + std::to_string(thread_index) +
+                                                            "})");
       }
     } catch (memgraph::query::HintedAbortError &e) {
     }
@@ -77,7 +88,7 @@ TEST_F(TransactionQueueMultipleTest, TerminateTransaction) {
       std::this_thread::sleep_for(std::chrono::milliseconds(20));
     }
 
-    auto show_stream = main_interpreter.Interpret("SHOW TRANSACTIONS");
+    auto show_stream = this->main_interpreter.Interpret("SHOW TRANSACTIONS");
     ASSERT_EQ(show_stream.GetResults().size(), NUM_INTERPRETERS + 1);
     // Choose random transaction to kill
     std::random_device rd;
@@ -86,15 +97,15 @@ TEST_F(TransactionQueueMultipleTest, TerminateTransaction) {
     int index_to_terminate = distr(gen);
     // Kill random transaction
     std::string run_trans_id =
-        std::to_string(running_interpreters[index_to_terminate]->interpreter.GetTransactionId().value());
+        std::to_string(this->running_interpreters[index_to_terminate]->interpreter.GetTransactionId().value());
     std::string esc_run_trans_id = "'" + run_trans_id + "'";
-    auto terminate_stream = main_interpreter.Interpret("TERMINATE TRANSACTIONS " + esc_run_trans_id);
+    auto terminate_stream = this->main_interpreter.Interpret("TERMINATE TRANSACTIONS " + esc_run_trans_id);
     // check result of killing
     ASSERT_EQ(terminate_stream.GetResults().size(), 1U);
     EXPECT_EQ(terminate_stream.GetResults()[0][0].ValueString(), run_trans_id);
     ASSERT_TRUE(terminate_stream.GetResults()[0][1].ValueBool());  // that the transaction is actually killed
     // test here show transactions
-    auto show_stream_after_kill = main_interpreter.Interpret("SHOW TRANSACTIONS");
+    auto show_stream_after_kill = this->main_interpreter.Interpret("SHOW TRANSACTIONS");
     ASSERT_EQ(show_stream_after_kill.GetResults().size(), NUM_INTERPRETERS);
     // wait to finish for threads
     for (int i = 0; i < NUM_INTERPRETERS; ++i) {
@@ -103,16 +114,16 @@ TEST_F(TransactionQueueMultipleTest, TerminateTransaction) {
     // test the state of the database
     for (int i = 0; i < NUM_INTERPRETERS; ++i) {
       if (i != index_to_terminate) {
-        running_interpreters[i]->Interpret("COMMIT");
+        this->running_interpreters[i]->Interpret("COMMIT");
       }
       std::string fetch_query = "MATCH (n:Person) WHERE n.prop=" + std::to_string(i) + " RETURN n";
-      auto results_stream = main_interpreter.Interpret(fetch_query);
+      auto results_stream = this->main_interpreter.Interpret(fetch_query);
       if (i == index_to_terminate) {
         ASSERT_EQ(results_stream.GetResults().size(), 0);
       } else {
         ASSERT_EQ(results_stream.GetResults().size(), INSERTIONS);
       }
     }
-    main_interpreter.Interpret("MATCH (n) DETACH DELETE n");
+    this->main_interpreter.Interpret("MATCH (n) DETACH DELETE n");
   }
 }
diff --git a/tests/unit/typed_value.cpp b/tests/unit/typed_value.cpp
index 0238363b6..5f2f8f3bf 100644
--- a/tests/unit/typed_value.cpp
+++ b/tests/unit/typed_value.cpp
@@ -1,4 +1,4 @@
-// Copyright 2022 Memgraph Ltd.
+// Copyright 2023 Memgraph Ltd.
 //
 // Use of this software is governed by the Business Source License
 // included in the file licenses/BSL.txt; by using this file, you agree to be bound by the terms of the Business Source
@@ -20,19 +20,25 @@
 
 #include "gtest/gtest.h"
 
+#include "disk_test_utils.hpp"
 #include "query/graph.hpp"
 #include "query/typed_value.hpp"
-#include "storage/v2/storage.hpp"
+#include "storage/v2/disk/storage.hpp"
+#include "storage/v2/inmemory/storage.hpp"
 
 using memgraph::query::TypedValue;
 using memgraph::query::TypedValueException;
 
+template <typename StorageType>
 class AllTypesFixture : public testing::Test {
  protected:
+  const std::string testSuite = "typed_value";
+
   std::vector<TypedValue> values_;
-  memgraph::storage::Storage db;
-  memgraph::storage::Storage::Accessor storage_dba{db.Access()};
-  memgraph::query::DbAccessor dba{&storage_dba};
+  memgraph::storage::Config config_{disk_test_utils::GenerateOnDiskConfig(testSuite)};
+  std::unique_ptr<memgraph::storage::Storage> db{new StorageType(config_)};
+  std::unique_ptr<memgraph::storage::Storage::Accessor> storage_dba{db->Access()};
+  memgraph::query::DbAccessor dba{storage_dba.get()};
 
   void SetUp() override {
     values_.emplace_back(TypedValue());
@@ -49,16 +55,21 @@ class AllTypesFixture : public testing::Test {
                                                            {"e", TypedValue()}});
     auto vertex = dba.InsertVertex();
     values_.emplace_back(vertex);
-    auto edge = *dba.InsertEdge(&vertex, &vertex, dba.NameToEdgeType("et"));
-    values_.emplace_back(edge);
+    auto edge = dba.InsertEdge(&vertex, &vertex, dba.NameToEdgeType("et"));
+    values_.emplace_back(*edge);
     values_.emplace_back(memgraph::query::Path(dba.InsertVertex()));
     memgraph::query::Graph graph{memgraph::utils::NewDeleteResource()};
     graph.InsertVertex(vertex);
-    graph.InsertEdge(edge);
+    graph.InsertEdge(*edge);
     values_.emplace_back(std::move(graph));
   }
+
+  void TearDown() override { disk_test_utils::RemoveRocksDbDirs(testSuite); }
 };
 
+using StorageTypes = ::testing::Types<memgraph::storage::InMemoryStorage, memgraph::storage::DiskStorage>;
+TYPED_TEST_CASE(AllTypesFixture, StorageTypes);
+
 void EXPECT_PROP_FALSE(const TypedValue &a) { EXPECT_TRUE(a.type() == TypedValue::Type::Bool && !a.ValueBool()); }
 
 void EXPECT_PROP_TRUE(const TypedValue &a) { EXPECT_TRUE(a.type() == TypedValue::Type::Bool && a.ValueBool()); }
@@ -168,12 +179,12 @@ TEST(TypedValue, Hash) {
             hash(TypedValue(std::map<std::string, TypedValue>{{"a", TypedValue(1)}})));
 }
 
-TEST_F(AllTypesFixture, Less) {
+TYPED_TEST(AllTypesFixture, Less) {
   // 'Less' is legal only between numerics, Null and strings.
   auto is_string_compatible = [](const TypedValue &v) { return v.IsNull() || v.type() == TypedValue::Type::String; };
   auto is_numeric_compatible = [](const TypedValue &v) { return v.IsNull() || v.IsNumeric(); };
-  for (TypedValue &a : values_) {
-    for (TypedValue &b : values_) {
+  for (TypedValue &a : this->values_) {
+    for (TypedValue &b : this->values_) {
       if (is_numeric_compatible(a) && is_numeric_compatible(b)) continue;
       if (is_string_compatible(a) && is_string_compatible(b)) continue;
       // Comparison should raise an exception. Cast to (void) so the compiler
@@ -183,7 +194,7 @@ TEST_F(AllTypesFixture, Less) {
   }
 
   // legal_type < Null = Null
-  for (TypedValue &value : values_) {
+  for (TypedValue &value : this->values_) {
     if (!(value.IsNumeric() || value.type() == TypedValue::Type::String)) continue;
     EXPECT_PROP_ISNULL(value < TypedValue());
     EXPECT_PROP_ISNULL(TypedValue() < value);
@@ -239,7 +250,8 @@ TEST(TypedValue, UnaryPlus) {
   EXPECT_THROW(+TypedValue("something"), TypedValueException);
 }
 
-class TypedValueArithmeticTest : public AllTypesFixture {
+template <typename StorageType>
+class TypedValueArithmeticTest : public AllTypesFixture<StorageType> {
  protected:
   /**
    * Performs a series of tests on properties of all types. The tests
@@ -271,8 +283,8 @@ class TypedValueArithmeticTest : public AllTypesFixture {
       }
     };
 
-    for (const TypedValue &a : values_) {
-      for (const TypedValue &b : values_) {
+    for (const TypedValue &a : this->values_) {
+      for (const TypedValue &b : this->values_) {
         if (always_valid(a) || always_valid(b)) continue;
         if (valid(a) && valid(b)) continue;
         EXPECT_THROW(op(a, b), TypedValueException);
@@ -281,15 +293,17 @@ class TypedValueArithmeticTest : public AllTypesFixture {
     }
 
     // null resulting ops
-    for (const TypedValue &value : values_) {
+    for (const TypedValue &value : this->values_) {
       EXPECT_PROP_ISNULL(op(value, TypedValue()));
       EXPECT_PROP_ISNULL(op(TypedValue(), value));
     }
   }
 };
 
-TEST_F(TypedValueArithmeticTest, Sum) {
-  ExpectArithmeticThrowsAndNull(true, [](const TypedValue &a, const TypedValue &b) { return a + b; });
+TYPED_TEST_CASE(TypedValueArithmeticTest, StorageTypes);
+
+TYPED_TEST(TypedValueArithmeticTest, Sum) {
+  this->ExpectArithmeticThrowsAndNull(true, [](const TypedValue &a, const TypedValue &b) { return a + b; });
 
   // sum of props of the same type
   EXPECT_EQ((TypedValue(2) + TypedValue(3)).ValueInt(), 5);
@@ -329,8 +343,8 @@ TEST_F(TypedValueArithmeticTest, Sum) {
                TypedValueException);
 }
 
-TEST_F(TypedValueArithmeticTest, Difference) {
-  ExpectArithmeticThrowsAndNull(false, [](const TypedValue &a, const TypedValue &b) { return a - b; });
+TYPED_TEST(TypedValueArithmeticTest, Difference) {
+  this->ExpectArithmeticThrowsAndNull(false, [](const TypedValue &a, const TypedValue &b) { return a - b; });
 
   // difference of props of the same type
   EXPECT_EQ((TypedValue(2) - TypedValue(3)).ValueInt(), -1);
@@ -358,10 +372,10 @@ TEST_F(TypedValueArithmeticTest, Difference) {
                TypedValueException);
 }
 
-TEST_F(TypedValueArithmeticTest, Negate) { EXPECT_NO_THROW(-TypedValue(memgraph::utils::Duration(1))); }
+TYPED_TEST(TypedValueArithmeticTest, Negate) { EXPECT_NO_THROW(-TypedValue(memgraph::utils::Duration(1))); }
 
-TEST_F(TypedValueArithmeticTest, Divison) {
-  ExpectArithmeticThrowsAndNull(false, [](const TypedValue &a, const TypedValue &b) { return a / b; });
+TYPED_TEST(TypedValueArithmeticTest, Divison) {
+  this->ExpectArithmeticThrowsAndNull(false, [](const TypedValue &a, const TypedValue &b) { return a / b; });
   EXPECT_THROW(TypedValue(1) / TypedValue(0), TypedValueException);
 
   EXPECT_PROP_EQ(TypedValue(10) / TypedValue(2), TypedValue(5));
@@ -374,8 +388,8 @@ TEST_F(TypedValueArithmeticTest, Divison) {
   EXPECT_FLOAT_EQ((TypedValue(10.0) / TypedValue(4)).ValueDouble(), 2.5);
 }
 
-TEST_F(TypedValueArithmeticTest, Multiplication) {
-  ExpectArithmeticThrowsAndNull(false, [](const TypedValue &a, const TypedValue &b) { return a * b; });
+TYPED_TEST(TypedValueArithmeticTest, Multiplication) {
+  this->ExpectArithmeticThrowsAndNull(false, [](const TypedValue &a, const TypedValue &b) { return a * b; });
 
   EXPECT_PROP_EQ(TypedValue(10) * TypedValue(2), TypedValue(20));
   EXPECT_FLOAT_EQ((TypedValue(12.5) * TypedValue(6.6)).ValueDouble(), 12.5 * 6.6);
@@ -383,8 +397,8 @@ TEST_F(TypedValueArithmeticTest, Multiplication) {
   EXPECT_FLOAT_EQ((TypedValue(10.2) * TypedValue(4)).ValueDouble(), 10.2 * 4);
 }
 
-TEST_F(TypedValueArithmeticTest, Modulo) {
-  ExpectArithmeticThrowsAndNull(false, [](const TypedValue &a, const TypedValue &b) { return a % b; });
+TYPED_TEST(TypedValueArithmeticTest, Modulo) {
+  this->ExpectArithmeticThrowsAndNull(false, [](const TypedValue &a, const TypedValue &b) { return a % b; });
   EXPECT_THROW(TypedValue(1) % TypedValue(0), TypedValueException);
 
   EXPECT_PROP_EQ(TypedValue(10) % TypedValue(2), TypedValue(0));
@@ -397,7 +411,8 @@ TEST_F(TypedValueArithmeticTest, Modulo) {
   EXPECT_FLOAT_EQ((TypedValue(10.0) % TypedValue(4)).ValueDouble(), 2.0);
 }
 
-class TypedValueLogicTest : public AllTypesFixture {
+template <typename StorageType>
+class TypedValueLogicTest : public AllTypesFixture<StorageType> {
  protected:
   /**
    * Logical operations (logical and, or) are only legal on bools
@@ -408,8 +423,8 @@ class TypedValueLogicTest : public AllTypesFixture {
    * @param op The logical operation to test.
    */
   void TestLogicalThrows(std::function<TypedValue(const TypedValue &, const TypedValue &)> op) {
-    for (const auto &p1 : values_) {
-      for (const auto &p2 : values_) {
+    for (const auto &p1 : this->values_) {
+      for (const auto &p2 : this->values_) {
         // skip situations when both p1 and p2 are either bool or null
         auto p1_ok = p1.type() == TypedValue::Type::Bool || p1.IsNull();
         auto p2_ok = p2.type() == TypedValue::Type::Bool || p2.IsNull();
@@ -421,8 +436,10 @@ class TypedValueLogicTest : public AllTypesFixture {
   }
 };
 
-TEST_F(TypedValueLogicTest, LogicalAnd) {
-  TestLogicalThrows([](const TypedValue &p1, const TypedValue &p2) { return p1 && p2; });
+TYPED_TEST_CASE(TypedValueLogicTest, StorageTypes);
+
+TYPED_TEST(TypedValueLogicTest, LogicalAnd) {
+  this->TestLogicalThrows([](const TypedValue &p1, const TypedValue &p2) { return p1 && p2; });
 
   EXPECT_PROP_ISNULL(TypedValue() && TypedValue(true));
   EXPECT_PROP_EQ(TypedValue() && TypedValue(false), TypedValue(false));
@@ -430,8 +447,8 @@ TEST_F(TypedValueLogicTest, LogicalAnd) {
   EXPECT_PROP_EQ(TypedValue(false) && TypedValue(true), TypedValue(false));
 }
 
-TEST_F(TypedValueLogicTest, LogicalOr) {
-  TestLogicalThrows([](const TypedValue &p1, const TypedValue &p2) { return p1 || p2; });
+TYPED_TEST(TypedValueLogicTest, LogicalOr) {
+  this->TestLogicalThrows([](const TypedValue &p1, const TypedValue &p2) { return p1 || p2; });
 
   EXPECT_PROP_ISNULL(TypedValue() || TypedValue(false));
   EXPECT_PROP_EQ(TypedValue() || TypedValue(true), TypedValue(true));
@@ -439,8 +456,8 @@ TEST_F(TypedValueLogicTest, LogicalOr) {
   EXPECT_PROP_EQ(TypedValue(false) || TypedValue(true), TypedValue(true));
 }
 
-TEST_F(TypedValueLogicTest, LogicalXor) {
-  TestLogicalThrows([](const TypedValue &p1, const TypedValue &p2) { return p1 ^ p2; });
+TYPED_TEST(TypedValueLogicTest, LogicalXor) {
+  this->TestLogicalThrows([](const TypedValue &p1, const TypedValue &p2) { return p1 ^ p2; });
 
   EXPECT_PROP_ISNULL(TypedValue() && TypedValue(true));
   EXPECT_PROP_EQ(TypedValue(true) ^ TypedValue(true), TypedValue(false));
@@ -450,10 +467,10 @@ TEST_F(TypedValueLogicTest, LogicalXor) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(AllTypesFixture, ConstructionWithMemoryResource) {
+TYPED_TEST(AllTypesFixture, ConstructionWithMemoryResource) {
   memgraph::utils::MonotonicBufferResource monotonic_memory(1024);
   std::vector<TypedValue> values_with_custom_memory;
-  for (const auto &value : values_) {
+  for (const auto &value : this->values_) {
     EXPECT_EQ(value.GetMemoryResource(), memgraph::utils::NewDeleteResource());
     TypedValue copy_constructed_value(value, &monotonic_memory);
     EXPECT_EQ(copy_constructed_value.GetMemoryResource(), &monotonic_memory);
@@ -464,10 +481,10 @@ TEST_F(AllTypesFixture, ConstructionWithMemoryResource) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(AllTypesFixture, AssignmentWithMemoryResource) {
+TYPED_TEST(AllTypesFixture, AssignmentWithMemoryResource) {
   std::vector<TypedValue> values_with_default_memory;
   memgraph::utils::MonotonicBufferResource monotonic_memory(1024);
-  for (const auto &value : values_) {
+  for (const auto &value : this->values_) {
     EXPECT_EQ(value.GetMemoryResource(), memgraph::utils::NewDeleteResource());
     TypedValue copy_assigned_value(&monotonic_memory);
     copy_assigned_value = value;
@@ -480,10 +497,10 @@ TEST_F(AllTypesFixture, AssignmentWithMemoryResource) {
 }
 
 // NOLINTNEXTLINE(hicpp-special-member-functions)
-TEST_F(AllTypesFixture, PropagationOfMemoryOnConstruction) {
+TYPED_TEST(AllTypesFixture, PropagationOfMemoryOnConstruction) {
   memgraph::utils::MonotonicBufferResource monotonic_memory(1024);
   std::vector<TypedValue, memgraph::utils::Allocator<TypedValue>> values_with_custom_memory(&monotonic_memory);
-  for (const auto &value : values_) {
+  for (const auto &value : this->values_) {
     EXPECT_EQ(value.GetMemoryResource(), memgraph::utils::NewDeleteResource());
     values_with_custom_memory.emplace_back(value);
     const auto &copy_constructed_value = values_with_custom_memory.back();