Ctrl K
ComunidadGuías…

query_modules/convert.c

Rama master · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
// Copyright 2026 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 <ctype.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mg_procedure.h"

// Constants
#define MAX_STRING_LENGTH 256
#define MAX_PROPERTIES 16
#define SMALL_MERGE_THRESHOLD 64
#define HASH_TABLE_MIN_CAPACITY 8

// Function names
static const char *kProcedureToTree = "to_tree";

// Parameter names
static const char *kParameterPaths = "paths";
static const char *kParameterConfig = "config";
static const char *kParameterLowerCaseRels = "lowerCaseRels";

// Return value name
static const char *kReturnValue = "value";

// Filter modes
typedef enum { FILTER_MODE_INCLUDE, FILTER_MODE_EXCLUDE, FILTER_MODE_WILDCARD, FILTER_MODE_INVALID } filter_mode_t;

// Generic property filter structure
typedef struct {
  filter_mode_t mode;
  char properties[MAX_PROPERTIES][MAX_STRING_LENGTH];
  size_t property_count;
} property_filter_t;

// Node filter config structure
typedef struct {
  size_t filter_count;
  property_filter_t filters[MAX_PROPERTIES];
  char labels[MAX_PROPERTIES][MAX_STRING_LENGTH];
} node_property_filter_config_t;

// Relationship filter config structure
typedef struct {
  size_t filter_count;
  property_filter_t filters[MAX_PROPERTIES];
  char rel_types[MAX_PROPERTIES][MAX_STRING_LENGTH];
} rel_property_filter_config_t;

// Global filter config structure
typedef struct {
  node_property_filter_config_t node_config;
  rel_property_filter_config_t rel_config;
  bool has_node_config;
  bool has_rel_config;
} filter_config_t;

// Safe string length function
static size_t safe_strlen(const char *str) { return str ? strlen(str) : 0; }

// Safe string copy function
static bool safe_strcpy(char *dest, const char *src, size_t dest_size) {
  if (!dest || !src || dest_size == 0) {
    return false;
  }

  size_t src_len = safe_strlen(src);
  if (src_len >= dest_size) {
    return false;  // Would overflow
  }

  strcpy(dest, src);
  return true;
}

// Safe string copy with truncation
static bool safe_strcpy_truncate(char *dest, const char *src, size_t dest_size) {
  if (!dest || !src || dest_size == 0) {
    return false;
  }

  size_t src_len = safe_strlen(src);
  if (src_len >= dest_size) {
    // Truncate to fit
    strncpy(dest, src, dest_size - 1);
    dest[dest_size - 1] = '\0';
  } else {
    strcpy(dest, src);
  }
  return true;
}

// Safe string copy with dash removal
static bool safe_strcpy_remove_dash(char *dest, const char *src, size_t dest_size) {
  if (!dest || !src || dest_size == 0) {
    return false;
  }

  size_t src_len = safe_strlen(src);
  if (src_len == 0) {
    return false;
  }

  // Skip leading dash if present
  const char *start = (src[0] == '-') ? src + 1 : src;
  size_t actual_len = safe_strlen(start);

  if (actual_len >= dest_size) {
    // Truncate to fit
    strncpy(dest, start, dest_size - 1);
    dest[dest_size - 1] = '\0';
  } else {
    strcpy(dest, start);
  }
  return true;
}

// Helper function to convert string to lowercase
static char *to_lowercase(const char *str, struct mgp_memory *memory) {
  if (!str) return NULL;
  size_t len = safe_strlen(str);
  void *ptr;
  // Align to 64 bytes
  size_t alloc_size = len + 1;
  alloc_size = (alloc_size + 63) & ~63ULL;
  if (mgp_alloc(memory, alloc_size, &ptr) != MGP_ERROR_NO_ERROR) {
    return NULL;
  }
  char *lower = (char *)ptr;
  for (size_t i = 0; i < len; i++) {
    lower[i] = tolower(str[i]);
  }
  lower[len] = '\0';
  return lower;
}

// Helper function to concatenate strings
static char *concatenate_strings(const char *str1, const char *str2, struct mgp_memory *memory) {
  if (!str1 || !str2) return NULL;
  size_t len1 = safe_strlen(str1);
  size_t len2 = safe_strlen(str2);
  void *ptr = NULL;
  // Align to 64 bytes
  size_t alloc_size = len1 + len2 + 1;
  alloc_size = (alloc_size + 63) & ~63ULL;
  if (mgp_alloc(memory, alloc_size, &ptr) != MGP_ERROR_NO_ERROR || ptr == NULL) {
    return NULL;
  }
  char *result = (char *)ptr;

  // Safe string operations
  if (!safe_strcpy(result, str1, alloc_size)) {
    mgp_free(memory, ptr);
    return NULL;
  }

  if (!safe_strcpy(result + len1, str2, alloc_size - len1)) {
    mgp_free(memory, ptr);
    return NULL;
  }

  return result;
}

typedef struct {
  int64_t id;
  uint64_t type_hash;
  struct mgp_map *map;
  bool used;
} bucket_t;

// Optimized hash function - combines type and id more efficiently
static inline uint64_t optimized_hash(uint64_t type_hash, int64_t id) {
  uint64_t h = type_hash;
  h ^= (uint64_t)id;
  h *= 1099511628211ULL;
  return h;
}

// Optimized probe function with better collision handling
static inline ssize_t optimized_probe(bucket_t *tbl, size_t cap, int64_t id, uint64_t type_hash, uint64_t h) {
  size_t mask = cap - 1;
  size_t pos = (size_t)h & mask;
  size_t step = 1;

  // Use quadratic probing for better distribution
  while (true) {
    if (!tbl[pos].used) return (ssize_t)pos;  // empty slot
    if (tbl[pos].id == id && tbl[pos].type_hash == type_hash) return (ssize_t)pos;

    // Quadratic probing: pos = (pos + step) & mask
    pos = (pos + step) & mask;
    step = (step + 1) & mask;  // increment step, but keep it within bounds
    if (step == 0) step = 1;   // avoid step = 0
  }
}

// Helper function to check if a string is in the properties array
static bool property_in_list(const char *prop_name, const char properties[][MAX_STRING_LENGTH], size_t count) {
  if (!prop_name) {
    return false;
  }

  for (size_t i = 0; i < count; i++) {
    if (strcmp(prop_name, properties[i]) == 0) {
      return true;
    }
  }
  return false;
}

// Initialize property filter
static void init_property_filter(property_filter_t *filter) {
  filter->mode = FILTER_MODE_INVALID;
  filter->property_count = 0;
}

// Parse property filter from list
static void parse_property_filter(property_filter_t *filter, struct mgp_list *props) {
  init_property_filter(filter);

  size_t list_size;
  if (mgp_list_size(props, &list_size) != MGP_ERROR_NO_ERROR || list_size == 0) {
    return;
  }

  // Check if all entries are strings
  for (size_t i = 0; i < list_size; i++) {
    struct mgp_value *item;
    if (mgp_list_at(props, i, &item) != MGP_ERROR_NO_ERROR) {
      return;
    }
    enum mgp_value_type item_type;
    if (mgp_value_get_type(item, &item_type) != MGP_ERROR_NO_ERROR || item_type != MGP_VALUE_TYPE_STRING) {
      return;
    }
  }

  // Get first item to determine mode
  struct mgp_value *first_item;
  if (mgp_list_at(props, 0, &first_item) != MGP_ERROR_NO_ERROR) {
    return;
  }
  const char *first_str;
  if (mgp_value_get_string(first_item, &first_str) != MGP_ERROR_NO_ERROR) {
    return;
  }

  if (strcmp(first_str, "*") == 0) {
    filter->mode = FILTER_MODE_WILDCARD;
    return;
  }

  if (first_str[0] == '-') {
    filter->mode = FILTER_MODE_EXCLUDE;
    for (size_t i = 0; i < list_size && i < MAX_PROPERTIES; i++) {
      struct mgp_value *item;
      if (mgp_list_at(props, i, &item) == MGP_ERROR_NO_ERROR) {
        const char *val_str;
        if (mgp_value_get_string(item, &val_str) == MGP_ERROR_NO_ERROR) {
          if (safe_strcpy_remove_dash(filter->properties[filter->property_count], val_str, MAX_STRING_LENGTH)) {
            filter->property_count++;
          }
        }
      }
    }
    return;
  }

  // Inclusion mode
  filter->mode = FILTER_MODE_INCLUDE;
  for (size_t i = 0; i < list_size && i < MAX_PROPERTIES; i++) {
    struct mgp_value *item;
    if (mgp_list_at(props, i, &item) == MGP_ERROR_NO_ERROR) {
      const char *val_str;
      if (mgp_value_get_string(item, &val_str) == MGP_ERROR_NO_ERROR) {
        if (safe_strcpy_truncate(filter->properties[filter->property_count], val_str, MAX_STRING_LENGTH)) {
          filter->property_count++;
        }
      }
    }
  }
}

// Get node property filter for a given label
static const property_filter_t *get_node_property_filter(const node_property_filter_config_t *config,
                                                         const char *label) {
  if (!config) {
    return NULL;
  }

  for (size_t i = 0; i < config->filter_count; i++) {
    if (strcmp(config->labels[i], label) == 0) {
      return &config->filters[i];
    }
  }

  // Return NULL if no filter found for this label
  return NULL;
}

// Check if node property should be included
static bool should_include_node_property(const char *prop_name, const filter_config_t *filter_config,
                                         struct mgp_vertex *node) {
  if (!filter_config || !filter_config->has_node_config || !node) {
    return true;  // No filtering config or invalid node: include all properties
  }

  // Get node labels with proper validation
  size_t label_count = 0;
  if (mgp_vertex_labels_count(node, &label_count) != MGP_ERROR_NO_ERROR) {
    return true;
  }

  property_filter_t filters[MAX_PROPERTIES];
  size_t filter_count = 0;
  bool has_invalid_filter = false;

  // Check each label for filters
  for (size_t label_idx = 0; label_idx < label_count; label_idx++) {
    struct mgp_label label = {.name = NULL};
    if (mgp_vertex_label_at(node, label_idx, &label) != MGP_ERROR_NO_ERROR || !label.name) {
      continue;  // Skip invalid labels
    }

    const property_filter_t *filter = get_node_property_filter(&filter_config->node_config, label.name);
    if (!filter) continue;
    if (filter->mode != FILTER_MODE_INVALID && filter_count < MAX_PROPERTIES) {
      filters[filter_count] = *filter;
      filter_count++;
    } else if (filter->mode == FILTER_MODE_INVALID) {
      has_invalid_filter = true;
    }
  }

  // If we have invalid filters, exclude all properties for affected labels
  if (has_invalid_filter && filter_count == 0) {
    return false;
  }
  if (filter_count == 0) {
    return true;
  }

  // Exclusion mode takes precedence if any filter is exclusion
  for (size_t i = 0; i < filter_count; i++) {
    if (filters[i].mode == FILTER_MODE_EXCLUDE) {
      return !property_in_list(prop_name, filters[i].properties, filters[i].property_count);
    }
  }

  // Wildcard mode if any filter is wildcard
  for (size_t i = 0; i < filter_count; i++) {
    if (filters[i].mode == FILTER_MODE_WILDCARD) {
      return true;
    }
  }

  // Otherwise, inclusion mode: include if any filter includes the property
  for (size_t i = 0; i < filter_count; i++) {
    if (filters[i].mode == FILTER_MODE_INCLUDE &&
        property_in_list(prop_name, filters[i].properties, filters[i].property_count)) {
      return true;
    }
  }

  return false;
}

// Initialize node property filter config
static void init_node_property_filter_config(node_property_filter_config_t *config) { config->filter_count = 0; }

// Initialize relationship property filter config
static void init_rel_property_filter_config(rel_property_filter_config_t *config) { config->filter_count = 0; }

// Initialize global filter config
static void init_filter_config(filter_config_t *config) {
  init_node_property_filter_config(&config->node_config);
  init_rel_property_filter_config(&config->rel_config);
  config->has_node_config = false;
  config->has_rel_config = false;
}

// Parse node property filter config from map
static void parse_node_property_filter_config(node_property_filter_config_t *config, struct mgp_map *nodes_map,
                                              struct mgp_memory *memory) {
  init_node_property_filter_config(config);

  if (!nodes_map) {
    return;
  }

  struct mgp_map_items_iterator *iter = NULL;
  if (mgp_map_iter_items(nodes_map, memory, &iter) != MGP_ERROR_NO_ERROR || iter == NULL) {
    return;
  }

  struct mgp_map_item *item;
  if (mgp_map_items_iterator_get(iter, &item) == MGP_ERROR_NO_ERROR && item != NULL) {
    do {
      const char *key_str;
      if (mgp_map_item_key(item, &key_str) != MGP_ERROR_NO_ERROR || key_str == NULL) {
        continue;
      }

      struct mgp_value *value;
      if (mgp_map_item_value(item, &value) != MGP_ERROR_NO_ERROR || value == NULL) {
        continue;
      }

      enum mgp_value_type value_type;
      if (mgp_value_get_type(value, &value_type) != MGP_ERROR_NO_ERROR || value_type != MGP_VALUE_TYPE_LIST) {
        continue;
      }

      struct mgp_list *props_list;
      if (mgp_value_get_list(value, &props_list) != MGP_ERROR_NO_ERROR) {
        continue;
      }

      if (config->filter_count < MAX_PROPERTIES) {
        if (safe_strcpy_truncate(config->labels[config->filter_count], key_str, MAX_STRING_LENGTH)) {
          parse_property_filter(&config->filters[config->filter_count], props_list);
          config->filter_count++;
        }
      }
    } while (mgp_map_items_iterator_next(iter, &item) == MGP_ERROR_NO_ERROR && item != NULL);
  }
  mgp_map_items_iterator_destroy(iter);
}

// Parse relationship property filter config from map
static void parse_rel_property_filter_config(rel_property_filter_config_t *config, struct mgp_map *rels_map,
                                             struct mgp_memory *memory) {
  init_rel_property_filter_config(config);

  if (!rels_map) {
    return;
  }

  struct mgp_map_items_iterator *iter = NULL;
  if (mgp_map_iter_items(rels_map, memory, &iter) != MGP_ERROR_NO_ERROR || iter == NULL) {
    return;
  }

  struct mgp_map_item *item;
  if (mgp_map_items_iterator_get(iter, &item) == MGP_ERROR_NO_ERROR && item != NULL) {
    do {
      const char *key_str;
      if (mgp_map_item_key(item, &key_str) != MGP_ERROR_NO_ERROR || key_str == NULL) {
        continue;
      }

      struct mgp_value *value;
      if (mgp_map_item_value(item, &value) != MGP_ERROR_NO_ERROR || value == NULL) {
        continue;
      }

      enum mgp_value_type value_type;
      if (mgp_value_get_type(value, &value_type) != MGP_ERROR_NO_ERROR || value_type != MGP_VALUE_TYPE_LIST) {
        continue;
      }

      struct mgp_list *props_list;
      if (mgp_value_get_list(value, &props_list) != MGP_ERROR_NO_ERROR) {
        continue;
      }

      if (config->filter_count < MAX_PROPERTIES) {
        if (safe_strcpy_truncate(config->rel_types[config->filter_count], key_str, MAX_STRING_LENGTH)) {
          parse_property_filter(&config->filters[config->filter_count], props_list);
          config->filter_count++;
        }
      }
    } while (mgp_map_items_iterator_next(iter, &item) == MGP_ERROR_NO_ERROR && item != NULL);
  }
  mgp_map_items_iterator_destroy(iter);
}

// Parse entire filter config once
static void parse_filter_config(filter_config_t *filter_config, struct mgp_map *config, struct mgp_memory *memory) {
  init_filter_config(filter_config);

  if (!config) {
    return;
  }

  // Parse node filters
  struct mgp_value *nodes_value = NULL;
  if (mgp_map_at(config, "nodes", &nodes_value) == MGP_ERROR_NO_ERROR && nodes_value != NULL) {
    enum mgp_value_type nodes_type = MGP_VALUE_TYPE_NULL;
    if (mgp_value_get_type(nodes_value, &nodes_type) == MGP_ERROR_NO_ERROR && nodes_type == MGP_VALUE_TYPE_MAP) {
      struct mgp_map *nodes_map = NULL;
      if (mgp_value_get_map(nodes_value, &nodes_map) == MGP_ERROR_NO_ERROR && nodes_map != NULL) {
        parse_node_property_filter_config(&filter_config->node_config, nodes_map, memory);
        filter_config->has_node_config = true;
      }
    }
  }

  // Parse relationship filters
  struct mgp_value *rels_value = NULL;
  if (mgp_map_at(config, "rels", &rels_value) == MGP_ERROR_NO_ERROR && rels_value != NULL) {
    enum mgp_value_type rels_type = MGP_VALUE_TYPE_NULL;
    if (mgp_value_get_type(rels_value, &rels_type) == MGP_ERROR_NO_ERROR && rels_type == MGP_VALUE_TYPE_MAP) {
      struct mgp_map *rels_map = NULL;
      if (mgp_value_get_map(rels_value, &rels_map) == MGP_ERROR_NO_ERROR && rels_map != NULL) {
        parse_rel_property_filter_config(&filter_config->rel_config, rels_map, memory);
        filter_config->has_rel_config = true;
      }
    }
  }
}

// Get relationship property filter for a given relationship type
static const property_filter_t *get_rel_property_filter(const rel_property_filter_config_t *config,
                                                        const char *rel_type) {
  if (!config) {
    return NULL;
  }

  for (size_t i = 0; i < config->filter_count; i++) {
    if (strcmp(config->rel_types[i], rel_type) == 0) {
      return &config->filters[i];
    }
  }

  // Return NULL if no filter found for this relationship type
  return NULL;
}

// Check if relationship property should be included
static bool should_include_rel_property(const char *prop_name, const filter_config_t *filter_config,
                                        const char *rel_type) {
  if (!filter_config || !filter_config->has_rel_config) {
    return true;  // No filtering config: include all properties
  }

  const property_filter_t *filter = get_rel_property_filter(&filter_config->rel_config, rel_type);

  if (!filter) {
    return true;
  }

  switch (filter->mode) {
    case FILTER_MODE_WILDCARD:
      return true;
    case FILTER_MODE_EXCLUDE:
      return !property_in_list(prop_name, filter->properties, filter->property_count);
    case FILTER_MODE_INCLUDE:
      return property_in_list(prop_name, filter->properties, filter->property_count);
    default:
      return false;
  }
}

// Forward declarations
static void merge_list_into_list_hashed(struct mgp_list *existing_list, struct mgp_list *new_list,
                                        struct mgp_memory *memory);

static void merge_trees(struct mgp_map *target, struct mgp_map *source, struct mgp_memory *memory);

typedef struct {
  int64_t id;
  uint64_t type_hash;
  bool valid;
} merge_identity_t;

/* Extracts (_id:int, _type:string) from a node/edge map item.
   Returns .valid=false if anything is missing or types mismatch. */
static inline merge_identity_t extract_identity(struct mgp_map *map) {
  merge_identity_t out = {.id = 0, .type_hash = 0, .valid = false};
  if (!map) return out;

  struct mgp_value *id_v = NULL, *type_v = NULL;
  if (mgp_map_at(map, "_id", &id_v) != MGP_ERROR_NO_ERROR || id_v == NULL) return out;
  if (mgp_map_at(map, "_type", &type_v) != MGP_ERROR_NO_ERROR || type_v == NULL) return out;

  enum mgp_value_type t_id = MGP_VALUE_TYPE_NULL, t_type = MGP_VALUE_TYPE_NULL;
  if (mgp_value_get_type(id_v, &t_id) != MGP_ERROR_NO_ERROR || t_id != MGP_VALUE_TYPE_INT) return out;
  if (mgp_value_get_type(type_v, &t_type) != MGP_ERROR_NO_ERROR) return out;

  int64_t id;
  uint64_t type_hash = 0;
  if (mgp_value_get_int(id_v, &id) != MGP_ERROR_NO_ERROR) return out;

  if (t_type == MGP_VALUE_TYPE_STRING) {
    const char *type_s = NULL;
    if (mgp_value_get_string(type_v, &type_s) != MGP_ERROR_NO_ERROR || !type_s) return out;
    type_hash = 14695981039346656037ULL;
    for (const char *p = type_s; *p; p++) {
      type_hash ^= (unsigned char)*p;
      type_hash *= 1099511628211ULL;
    }
  } else if (t_type == MGP_VALUE_TYPE_LIST) {
    struct mgp_list *type_list = NULL;
    if (mgp_value_get_list(type_v, &type_list) != MGP_ERROR_NO_ERROR || !type_list) return out;
    size_t list_size = 0;
    if (mgp_list_size(type_list, &list_size) != MGP_ERROR_NO_ERROR || list_size == 0) return out;

    type_hash = 14695981039346656037ULL;
    for (size_t i = 0; i < list_size; i++) {
      struct mgp_value *label_val = NULL;
      if (mgp_list_at(type_list, i, &label_val) != MGP_ERROR_NO_ERROR || !label_val) return out;
      const char *label_str = NULL;
      if (mgp_value_get_string(label_val, &label_str) != MGP_ERROR_NO_ERROR || !label_str) return out;

      for (const char *p = label_str; *p; p++) {
        type_hash ^= (unsigned char)*p;
        type_hash *= 1099511628211ULL;
      }

      // Concatenate strings using `:` as a delimiter, as we know that can't
      // appear in the labels.
      if (i < list_size - 1) {
        type_hash ^= ':';
        type_hash *= 1099511628211ULL;
      }
    }
  } else {
    return out;
  }

  out.id = id;
  out.type_hash = type_hash;
  out.valid = true;
  return out;
}

/* Build a hash table over existing_list's map items keyed by (_id,_type). cap is power-of-two. */
static bool index_existing_children(struct mgp_list *existing_list, bucket_t *tbl, size_t cap) {
  size_t n = 0;
  if (mgp_list_size(existing_list, &n) != MGP_ERROR_NO_ERROR) return false;
  for (size_t i = 0; i < n; ++i) {
    struct mgp_value *item_v = NULL;
    if (mgp_list_at(existing_list, i, &item_v) != MGP_ERROR_NO_ERROR || !item_v) continue;
    enum mgp_value_type t = MGP_VALUE_TYPE_NULL;
    if (mgp_value_get_type(item_v, &t) != MGP_ERROR_NO_ERROR || t != MGP_VALUE_TYPE_MAP) continue;
    struct mgp_map *m = NULL;
    if (mgp_value_get_map(item_v, &m) != MGP_ERROR_NO_ERROR || !m) continue;

    merge_identity_t id = extract_identity(m);
    if (!id.valid) continue;

    /* Use optimized hash function */
    uint64_t h = optimized_hash(id.type_hash, id.id);

    ssize_t pos = optimized_probe(tbl, cap, id.id, id.type_hash, h);
    if (pos < 0) return false;
    if (!tbl[pos].used) {
      tbl[pos].used = true;
      tbl[pos].id = id.id;
      tbl[pos].type_hash = id.type_hash;
      tbl[pos].map = m;
    }
    /* if it already existed, we ignore duplicates in existing_list */
  }
  return true;
}

/* Optimized merge for small lists using stack-allocated hash table */
static void merge_list_into_list_small(struct mgp_list *existing_list, struct mgp_list *new_list,
                                       struct mgp_memory *memory) {
  size_t exist_n = 0, new_n = 0;
  if (mgp_list_size(existing_list, &exist_n) != MGP_ERROR_NO_ERROR) return;
  if (mgp_list_size(new_list, &new_n) != MGP_ERROR_NO_ERROR) return;
  if (new_n == 0) return;

  // Use stack-allocated hash table for small merges
  bucket_t small_table[SMALL_MERGE_THRESHOLD];
  memset(small_table, 0, sizeof(small_table));

  // Index existing children using small table
  if (!index_existing_children(existing_list, small_table, SMALL_MERGE_THRESHOLD)) {
    return;
  }

  // Process new items
  for (size_t i = 0; i < new_n; ++i) {
    struct mgp_value *new_item = NULL;
    if (mgp_list_at(new_list, i, &new_item) != MGP_ERROR_NO_ERROR || !new_item) continue;

    enum mgp_value_type tnew = MGP_VALUE_TYPE_NULL;
    if (mgp_value_get_type(new_item, &tnew) != MGP_ERROR_NO_ERROR) continue;

    if (tnew == MGP_VALUE_TYPE_MAP) {
      struct mgp_map *new_map = NULL;
      if (mgp_value_get_map(new_item, &new_map) != MGP_ERROR_NO_ERROR || !new_map) {
        mgp_list_append_move(existing_list, new_item);
        continue;
      }

      merge_identity_t nid = extract_identity(new_map);
      if (!nid.valid) {
        mgp_list_append_move(existing_list, new_item);
        continue;
      }

      uint64_t h = optimized_hash(nid.type_hash, nid.id);
      ssize_t pos = optimized_probe(small_table, SMALL_MERGE_THRESHOLD, nid.id, nid.type_hash, h);

      if (pos >= 0 && small_table[pos].used) {
        /* Merge into existing */
        merge_trees(small_table[pos].map, new_map, memory);
      } else {
        /* Append and index */
        mgp_list_append_move(existing_list, new_item);
        if (pos >= 0) {
          small_table[pos].used = true;
          small_table[pos].id = nid.id;
          small_table[pos].type_hash = nid.type_hash;
          small_table[pos].map = new_map;
        }
      }
    } else {
      /* Non-map items: just append, as before. */
      mgp_list_append_move(existing_list, new_item);
    }
  }
}

/* Merges new_list into existing_list by hashing existing_list.
   Average O(n) with low constants; preserves old behavior. */
static void merge_list_into_list_hashed(struct mgp_list *existing_list, struct mgp_list *new_list,
                                        struct mgp_memory *memory) {
  size_t exist_n = 0, new_n = 0;
  if (mgp_list_size(existing_list, &exist_n) != MGP_ERROR_NO_ERROR) return;
  if (mgp_list_size(new_list, &new_n) != MGP_ERROR_NO_ERROR) return;
  if (new_n == 0) return;

  // Use optimized small merge for small lists
  if (exist_n < SMALL_MERGE_THRESHOLD) {
    merge_list_into_list_small(existing_list, new_list, memory);
    return;
  }

  /* Capacity: next power of two >= 2*exist_n (at least 8). */
  size_t cap = HASH_TABLE_MIN_CAPACITY;
  while (cap < (exist_n << 1)) cap <<= 1;

  bucket_t *tbl = NULL;
  size_t bytes = cap * sizeof(bucket_t);
  void *ptr = NULL;
  if (mgp_alloc(memory, bytes, &ptr) != MGP_ERROR_NO_ERROR || !ptr) {
    /* Fallback to optimized small merge instead of O(n²) */
    merge_list_into_list_small(existing_list, new_list, memory);
    return;
  }

  tbl = (bucket_t *)ptr;
  memset(tbl, 0, bytes);
  if (!index_existing_children(existing_list, tbl, cap)) {
    mgp_free(memory, tbl);
    return;
  }

  /* For each new child: merge or append, updating the index when appending. */
  for (size_t i = 0; i < new_n; ++i) {
    struct mgp_value *new_item = NULL;
    if (mgp_list_at(new_list, i, &new_item) != MGP_ERROR_NO_ERROR || !new_item) continue;

    enum mgp_value_type tnew = MGP_VALUE_TYPE_NULL;
    if (mgp_value_get_type(new_item, &tnew) != MGP_ERROR_NO_ERROR) continue;

    if (tnew == MGP_VALUE_TYPE_MAP) {
      struct mgp_map *new_map = NULL;
      if (mgp_value_get_map(new_item, &new_map) != MGP_ERROR_NO_ERROR || !new_map) {
        mgp_list_append_move(existing_list, new_item);
        continue;
      }

      merge_identity_t nid = extract_identity(new_map);
      if (!nid.valid) {
        mgp_list_append_move(existing_list, new_item);
        continue;
      }

      uint64_t h = optimized_hash(nid.type_hash, nid.id);
      ssize_t pos = optimized_probe(tbl, cap, nid.id, nid.type_hash, h);

      if (pos >= 0 && tbl[pos].used) {
        /* Merge into existing */
        merge_trees(tbl[pos].map, new_map, memory);
      } else {
        /* Append and index */
        /* NOTE: we need the map pointer after append, so keep it now. */
        mgp_list_append_move(existing_list, new_item);
        if (pos >= 0) {
          tbl[pos].used = true;
          tbl[pos].id = nid.id;
          tbl[pos].type_hash = nid.type_hash;
          tbl[pos].map = new_map;
        }
      }
    } else {
      /* Non-map items: just append, as before. */
      mgp_list_append_move(existing_list, new_item);
    }
  }

  mgp_free(memory, tbl);
}

static void merge_trees(struct mgp_map *target, struct mgp_map *source, struct mgp_memory *memory) {
  struct mgp_map_items_iterator *iter;
  if (mgp_map_iter_items(source, memory, &iter) != MGP_ERROR_NO_ERROR || !iter) return;

  struct mgp_map_item *item;
  if (mgp_map_items_iterator_get(iter, &item) == MGP_ERROR_NO_ERROR && item) {
    do {
      const char *key_str = NULL;
      if (mgp_map_item_key(item, &key_str) != MGP_ERROR_NO_ERROR || !key_str) continue;

      /* Skip metadata keys */
      if (key_str[0] == '_' && (strcmp(key_str, "_type") == 0 || strcmp(key_str, "_id") == 0)) continue;

      struct mgp_value *src_val = NULL;
      if (mgp_map_item_value(item, &src_val) != MGP_ERROR_NO_ERROR || !src_val) continue;

      struct mgp_value *dst_val = NULL;
      if (mgp_map_at(target, key_str, &dst_val) == MGP_ERROR_NO_ERROR && dst_val) {
        enum mgp_value_type td = MGP_VALUE_TYPE_NULL, ts = MGP_VALUE_TYPE_NULL;
        if (mgp_value_get_type(dst_val, &td) == MGP_ERROR_NO_ERROR &&
            mgp_value_get_type(src_val, &ts) == MGP_ERROR_NO_ERROR && td == MGP_VALUE_TYPE_LIST &&
            ts == MGP_VALUE_TYPE_LIST) {
          struct mgp_list *dst_list = NULL, *src_list = NULL;
          if (mgp_value_get_list(dst_val, &dst_list) == MGP_ERROR_NO_ERROR &&
              mgp_value_get_list(src_val, &src_list) == MGP_ERROR_NO_ERROR && dst_list && src_list) {
            /* Optimized merge: hash-based */
            merge_list_into_list_hashed(dst_list, src_list, memory);
          }
        } else {
          /* If types mismatch or not lists, last one wins (keep old behavior: overwrite by insert_move?) */
          /* We mimic original intent: when key exists and both are lists, we merge; otherwise ignore. */
        }
      } else {
        /* Direct move insert when key doesn't exist */
        mgp_map_insert_move(target, key_str, src_val);
      }
    } while (mgp_map_items_iterator_next(iter, &item) == MGP_ERROR_NO_ERROR && item);
  }
  mgp_map_items_iterator_destroy(iter);
}

// Helper function to create a complete node map with properties and labels
static struct mgp_map *create_complete_node_map(struct mgp_vertex *node, const filter_config_t *filter_config,
                                                struct mgp_memory *memory) {
  if (!node || !memory) {
    return NULL;
  }

  struct mgp_map *node_map = NULL;
  if (mgp_unordered_map_make_empty(memory, &node_map) != MGP_ERROR_NO_ERROR || node_map == NULL) {
    return NULL;
  }

  // Add node properties with filtering
  struct mgp_properties_iterator *iter = NULL;
  enum mgp_error error = mgp_vertex_iter_properties(node, memory, &iter);
  if (error == MGP_ERROR_NO_ERROR && iter) {
    struct mgp_property *prop = NULL;
    if (mgp_properties_iterator_get(iter, &prop) == MGP_ERROR_NO_ERROR && prop != NULL) {
      do {
        // Check if property should be included based on filtering
        if (should_include_node_property(prop->name, filter_config, node)) {
          mgp_map_insert_move(node_map, prop->name, prop->value);  // inputs are copied, so should be okay to move
        }
      } while (mgp_properties_iterator_next(iter, &prop) == MGP_ERROR_NO_ERROR && prop != NULL);
    }
    mgp_properties_iterator_destroy(iter);
  }

  size_t label_count = 0;
  if (mgp_vertex_labels_count(node, &label_count) == MGP_ERROR_NO_ERROR && label_count > 0) {
    if (label_count == 1) {
      // Single label
      struct mgp_label label = {.name = NULL};
      if (mgp_vertex_label_at(node, 0, &label) == MGP_ERROR_NO_ERROR && label.name) {
        struct mgp_value *type_value = NULL;
        if (mgp_value_make_string(label.name, memory, &type_value) == MGP_ERROR_NO_ERROR && type_value != NULL) {
          mgp_map_insert_move(node_map, "_type", type_value);
        }
      }
    } else {
      // Multiple labels - create a list
      struct mgp_list *label_list = NULL;
      if (mgp_list_make_empty(label_count, memory, &label_list) == MGP_ERROR_NO_ERROR && label_list != NULL) {
        for (size_t i = 0; i < label_count; i++) {
          struct mgp_label label = {.name = NULL};
          if (mgp_vertex_label_at(node, i, &label) == MGP_ERROR_NO_ERROR && label.name) {
            struct mgp_value *label_value = NULL;
            if (mgp_value_make_string(label.name, memory, &label_value) == MGP_ERROR_NO_ERROR && label_value != NULL) {
              mgp_list_append_move(label_list, label_value);
            }
          }
        }
        struct mgp_value *type_value = NULL;
        if (mgp_value_make_list(label_list, &type_value) == MGP_ERROR_NO_ERROR && type_value != NULL) {
          mgp_map_insert_move(node_map, "_type", type_value);
        }
      }
    }
  }

  // Add node ID
  struct mgp_vertex_id id;
  if (mgp_vertex_get_id(node, &id) == MGP_ERROR_NO_ERROR) {
    struct mgp_value *id_value = NULL;
    if (mgp_value_make_int(id.as_int, memory, &id_value) == MGP_ERROR_NO_ERROR && id_value != NULL) {
      mgp_map_insert_move(node_map, "_id", id_value);
    }
  }

  return node_map;
}

// Helper function to build tree from a single path recursively
static struct mgp_value *build_tree_from_path_recursive(struct mgp_path *path, size_t start_index, bool lower_case_rels,
                                                        const filter_config_t *filter_config,
                                                        struct mgp_memory *memory) {
  if (!path || !memory) {
    return NULL;
  }

  // Get current node
  struct mgp_vertex *current_node = NULL;
  if (mgp_path_vertex_at(path, start_index, &current_node) != MGP_ERROR_NO_ERROR || current_node == NULL) {
    return NULL;
  }

  // Create node map with properties and labels
  struct mgp_map *node_map = create_complete_node_map(current_node, filter_config, memory);
  if (!node_map) {
    return NULL;
  }

  size_t path_size = 0;
  if (mgp_path_size(path, &path_size) != MGP_ERROR_NO_ERROR) {
    mgp_map_destroy(node_map);
    return NULL;
  }

  // If this is the last node in the path, return just the node
  if (start_index >= path_size) {
    struct mgp_value *result = NULL;
    if (mgp_value_make_map(node_map, &result) != MGP_ERROR_NO_ERROR || result == NULL) {
      mgp_map_destroy(node_map);
      return NULL;
    }
    return result;
  }

  // Get the relationship
  struct mgp_edge *edge = NULL;
  if (mgp_path_edge_at(path, start_index, &edge) != MGP_ERROR_NO_ERROR || edge == NULL) {
    mgp_map_destroy(node_map);
    return NULL;
  }

  // Get relationship type
  struct mgp_edge_type edge_type = {.name = NULL};
  if (mgp_edge_get_type(edge, &edge_type) != MGP_ERROR_NO_ERROR || edge_type.name == NULL) {
    mgp_map_destroy(node_map);
    return NULL;
  }

  const char *rel_type_str = edge_type.name;
  if (!rel_type_str) {
    mgp_map_destroy(node_map);
    return NULL;
  }

  // Process relationship type (lowercase if requested)
  char *rel_type_processed = (char *)rel_type_str;
  if (lower_case_rels) {
    rel_type_processed = to_lowercase(rel_type_str, memory);
    if (!rel_type_processed) {
      mgp_map_destroy(node_map);
      return NULL;
    }
  }

  // Recursively build the subtree for the next part of the path
  struct mgp_value *subtree_value =
      build_tree_from_path_recursive(path, start_index + 1, lower_case_rels, filter_config, memory);
  if (!subtree_value) {
    if (lower_case_rels && rel_type_processed != rel_type_str) {
      mgp_free(memory, rel_type_processed);
    }
    mgp_map_destroy(node_map);
    return NULL;
  }

  // Add relationship ID to the subtree
  struct mgp_edge_id edge_id;
  if (mgp_edge_get_id(edge, &edge_id) == MGP_ERROR_NO_ERROR) {
    char *id_key = concatenate_strings(rel_type_processed, "._id", memory);
    if (id_key) {
      struct mgp_value *id_value = NULL;
      if (mgp_value_make_int(edge_id.as_int, memory, &id_value) == MGP_ERROR_NO_ERROR && id_value != NULL) {
        // Get the subtree map to add the ID
        struct mgp_map *subtree_map = NULL;
        if (mgp_value_get_map(subtree_value, &subtree_map) == MGP_ERROR_NO_ERROR && subtree_map != NULL) {
          mgp_map_insert_move(subtree_map, id_key, id_value);
          // mgp_value_destroy(id_value);
        }
      }
      mgp_free(memory, id_key);
    }
  }

  // Add relationship properties to the subtree with filtering
  struct mgp_properties_iterator *iter = NULL;
  enum mgp_error error = mgp_edge_iter_properties(edge, memory, &iter);
  if (error == MGP_ERROR_NO_ERROR && iter) {
    struct mgp_property *prop = NULL;
    if (mgp_properties_iterator_get(iter, &prop) == MGP_ERROR_NO_ERROR && prop) {
      do {
        if (should_include_rel_property(prop->name, filter_config, rel_type_processed)) {
          /* Build key "<rel>.<prop>" without heap allocs */
          char keybuf[(MAX_STRING_LENGTH * 2) + 2];
          int nw = snprintf(keybuf, sizeof(keybuf), "%s.%s", rel_type_processed, prop->name);
          if (nw > 0 && (size_t)nw < sizeof(keybuf)) {
            struct mgp_map *subtree_map = NULL;
            if (mgp_value_get_map(subtree_value, &subtree_map) == MGP_ERROR_NO_ERROR && subtree_map) {
              mgp_map_insert_move(subtree_map, keybuf, prop->value);
            }
          }
        }
      } while (mgp_properties_iterator_next(iter, &prop) == MGP_ERROR_NO_ERROR && prop);
    }
    mgp_properties_iterator_destroy(iter);
  }

  // Create list for this relationship type
  struct mgp_list *rel_list = NULL;
  if (mgp_list_make_empty(1, memory, &rel_list) != MGP_ERROR_NO_ERROR || rel_list == NULL) {
    if (lower_case_rels && rel_type_processed != rel_type_str) {
      mgp_free(memory, rel_type_processed);
    }
    mgp_value_destroy(subtree_value);
    mgp_map_destroy(node_map);
    return NULL;
  }

  // Add the subtree to the relationship list
  if (mgp_list_append_move(rel_list, subtree_value) != MGP_ERROR_NO_ERROR) {
    if (lower_case_rels && rel_type_processed != rel_type_str) {
      mgp_free(memory, rel_type_processed);
    }
    mgp_list_destroy(rel_list);
    mgp_value_destroy(subtree_value);
    mgp_map_destroy(node_map);
    return NULL;
  }
  // Don't destroy subtree_value - it was moved

  // Add relationship list to node map
  struct mgp_value *rel_list_value = NULL;
  if (mgp_value_make_list(rel_list, &rel_list_value) != MGP_ERROR_NO_ERROR || rel_list_value == NULL) {
    if (lower_case_rels && rel_type_processed != rel_type_str) {
      mgp_free(memory, rel_type_processed);
    }
    mgp_list_destroy(rel_list);
    mgp_map_destroy(node_map);
    return NULL;
  }

  mgp_map_insert_move(node_map, rel_type_processed, rel_list_value);
  // Don't destroy rel_list_value - it was moved

  if (lower_case_rels && rel_type_processed != rel_type_str) {
    mgp_free(memory, rel_type_processed);
  }

  // Convert node map to value
  struct mgp_value *result = NULL;
  if (mgp_value_make_map(node_map, &result) != MGP_ERROR_NO_ERROR || result == NULL) {
    mgp_map_destroy(node_map);
    return NULL;
  }

  return result;
}

// Helper function to build tree from a single path (wrapper for backward compatibility)
static struct mgp_value *build_simple_tree_from_path(struct mgp_path *path, bool lower_case_rels,
                                                     const filter_config_t *filter_config, struct mgp_memory *memory) {
  return build_tree_from_path_recursive(path, 0, lower_case_rels, filter_config, memory);
}

// Main tree conversion function
static struct mgp_value *convert_to_tree_impl(struct mgp_value *value, bool lower_case_rels, struct mgp_map *config,
                                              struct mgp_memory *memory) {
  // Parse filter config once at the start
  filter_config_t filter_config;
  parse_filter_config(&filter_config, config, memory);
  if (!value) {
    // Null input: return empty map
    struct mgp_map *empty_map = NULL;
    if (mgp_unordered_map_make_empty(memory, &empty_map) != MGP_ERROR_NO_ERROR || empty_map == NULL) {
      return NULL;
    }
    struct mgp_value *result = NULL;
    if (mgp_value_make_map(empty_map, &result) != MGP_ERROR_NO_ERROR || result == NULL) {
      mgp_map_destroy(empty_map);
      return NULL;
    }
    return result;
  }

  enum mgp_value_type value_type = MGP_VALUE_TYPE_NULL;
  if (mgp_value_get_type(value, &value_type) != MGP_ERROR_NO_ERROR) {
    return NULL;
  }

  if (value_type == MGP_VALUE_TYPE_NULL) {
    // Null input: return empty map
    struct mgp_map *empty_map = NULL;
    if (mgp_unordered_map_make_empty(memory, &empty_map) != MGP_ERROR_NO_ERROR || empty_map == NULL) {
      return NULL;
    }
    struct mgp_value *result = NULL;
    if (mgp_value_make_map(empty_map, &result) != MGP_ERROR_NO_ERROR || result == NULL) {
      mgp_map_destroy(empty_map);
      return NULL;
    }
    return result;
  }

  // Handle different input types
  if (value_type == MGP_VALUE_TYPE_PATH) {
    // Convert single path to tree
    struct mgp_path *path = NULL;
    if (mgp_value_get_path(value, &path) != MGP_ERROR_NO_ERROR || path == NULL) {
      return NULL;
    }
    return build_simple_tree_from_path(path, lower_case_rels, &filter_config, memory);
  }

  if (value_type == MGP_VALUE_TYPE_LIST) {
    // Convert list of paths to tree
    struct mgp_list *paths = NULL;
    if (mgp_value_get_list(value, &paths) != MGP_ERROR_NO_ERROR || paths == NULL) {
      return NULL;
    }

    size_t paths_size = 0;
    if (mgp_list_size(paths, &paths_size) != MGP_ERROR_NO_ERROR) {
      return NULL;
    }

    if (paths_size == 0) {
      // Empty list: return empty map
      struct mgp_map *empty_map = NULL;
      if (mgp_unordered_map_make_empty(memory, &empty_map) != MGP_ERROR_NO_ERROR || empty_map == NULL) {
        return NULL;
      }
      struct mgp_value *result = NULL;
      if (mgp_value_make_map(empty_map, &result) != MGP_ERROR_NO_ERROR || result == NULL) {
        mgp_map_destroy(empty_map);
        return NULL;
      }
      return result;
    }

    // Group paths by root node ID to build separate trees
    struct mgp_map *root_groups[MAX_PROPERTIES];  // Map from root_id to tree
    size_t root_group_count = 0;
    int64_t root_ids[MAX_PROPERTIES];

    for (size_t i = 0; i < paths_size; i++) {
      struct mgp_value *path_value = NULL;
      if (mgp_list_at(paths, i, &path_value) != MGP_ERROR_NO_ERROR || path_value == NULL) {
        continue;
      }

      struct mgp_path *path = NULL;
      if (mgp_value_get_path(path_value, &path) != MGP_ERROR_NO_ERROR || path == NULL) {
        continue;
      }

      // Get root node ID
      struct mgp_vertex *root_node = NULL;
      if (mgp_path_vertex_at(path, 0, &root_node) != MGP_ERROR_NO_ERROR || root_node == NULL) {
        continue;
      }

      struct mgp_vertex_id root_id;
      if (mgp_vertex_get_id(root_node, &root_id) != MGP_ERROR_NO_ERROR) {
        continue;
      }

      // Find existing root group or create new one
      size_t group_index = SIZE_MAX;
      for (size_t j = 0; j < root_group_count && j < MAX_PROPERTIES; j++) {
        if (root_ids[j] == root_id.as_int) {
          group_index = j;
          break;
        }
      }

      if (group_index == SIZE_MAX) {
        // New root group
        if (root_group_count >= MAX_PROPERTIES) {
          continue;  // Too many root groups
        }
        group_index = root_group_count;
        root_ids[group_index] = root_id.as_int;
        root_groups[group_index] = NULL;
        root_group_count++;
      }

      // Convert this path to a tree
      struct mgp_value *tree_value = build_simple_tree_from_path(path, lower_case_rels, &filter_config, memory);
      if (!tree_value) {
        continue;
      }

      // Get the tree map
      struct mgp_map *tree_map = NULL;
      if (mgp_value_get_map(tree_value, &tree_map) != MGP_ERROR_NO_ERROR || tree_map == NULL) {
        mgp_value_destroy(tree_value);
        continue;
      }

      if (root_groups[group_index] == NULL) {
        // First tree for this root, use it as the base
        root_groups[group_index] = tree_map;
        // Don't destroy tree_value here since we're keeping the map
      } else {
        // Merge with existing tree for this root
        merge_trees(root_groups[group_index], tree_map, memory);
        mgp_value_destroy(tree_value);
      }
    }

    if (root_group_count == 0) {
      // No valid paths found, return empty map
      struct mgp_map *empty_map = NULL;
      if (mgp_unordered_map_make_empty(memory, &empty_map) != MGP_ERROR_NO_ERROR || empty_map == NULL) {
        return NULL;
      }
      struct mgp_value *result = NULL;
      if (mgp_value_make_map(empty_map, &result) != MGP_ERROR_NO_ERROR || result == NULL) {
        mgp_map_destroy(empty_map);
        return NULL;
      }
      return result;
    }

    if (root_group_count == 1) {
      if (root_groups[0] == NULL) {
        return NULL;
      }
      // Single root: return the tree directly
      struct mgp_value *result = NULL;
      if (mgp_value_make_map(root_groups[0], &result) != MGP_ERROR_NO_ERROR || result == NULL) {
        mgp_map_destroy(root_groups[0]);
        return NULL;
      }
      return result;
    }

    // Multiple roots: return a list of all root trees
    struct mgp_list *tree_list = NULL;
    if (mgp_list_make_empty(root_group_count, memory, &tree_list) != MGP_ERROR_NO_ERROR || tree_list == NULL) {
      // Clean up on error
      for (size_t i = 0; i < root_group_count && i < MAX_PROPERTIES; i++) {
        if (root_groups[i]) {
          mgp_map_destroy(root_groups[i]);
        }
      }
      return NULL;
    }

    for (size_t i = 0; i < root_group_count && i < MAX_PROPERTIES; i++) {
      if (root_groups[i]) {
        struct mgp_value *tree_value = NULL;
        if (mgp_value_make_map(root_groups[i], &tree_value) != MGP_ERROR_NO_ERROR || tree_value == NULL) {
          mgp_list_destroy(tree_list);
          // Clean up remaining trees
          for (size_t j = i; j < root_group_count && j < MAX_PROPERTIES; j++) {
            if (root_groups[j]) {
              mgp_map_destroy(root_groups[j]);
            }
          }
          return NULL;
        }
        mgp_list_append_move(tree_list, tree_value);
        // Don't destroy tree_value - it was moved
      }
    }

    struct mgp_value *result = NULL;
    if (mgp_value_make_list(tree_list, &result) != MGP_ERROR_NO_ERROR || result == NULL) {
      mgp_list_destroy(tree_list);
      return NULL;
    }
    return result;
  }

  // For other types, return as-is
  struct mgp_value *result = NULL;
  if (mgp_value_copy(value, memory, &result) != MGP_ERROR_NO_ERROR || result == NULL) {
    return NULL;
  }
  return result;
}

// Main to_tree procedure implementation
static void to_tree(struct mgp_list *args, struct mgp_graph *graph, struct mgp_result *result,
                    struct mgp_memory *memory) {
  size_t args_size = 0;
  if (mgp_list_size(args, &args_size) != MGP_ERROR_NO_ERROR) {
    mgp_result_set_error_msg(result, "Failed to get arguments size");
    return;
  }

  if (args_size == 0) {
    mgp_result_set_error_msg(result, "to_tree requires at least one argument");
    return;
  }

  // Get the input argument
  struct mgp_value *input = NULL;
  if (mgp_list_at(args, 0, &input) != MGP_ERROR_NO_ERROR || input == NULL) {
    mgp_result_set_error_msg(result, "Failed to get input argument");
    return;
  }

  // Get lowerCaseRels parameter (default to true)
  bool lower_case_rels = true;
  if (args_size > 1) {
    struct mgp_value *lower_case_arg = NULL;
    if (mgp_list_at(args, 1, &lower_case_arg) == MGP_ERROR_NO_ERROR && lower_case_arg != NULL) {
      enum mgp_value_type arg_type = MGP_VALUE_TYPE_NULL;
      if (mgp_value_get_type(lower_case_arg, &arg_type) == MGP_ERROR_NO_ERROR && arg_type == MGP_VALUE_TYPE_BOOL) {
        int bool_val = 0;
        if (mgp_value_get_bool(lower_case_arg, &bool_val) == MGP_ERROR_NO_ERROR) {
          lower_case_rels = (bool_val != 0);
        }
      }
    }
  }

  // Get config parameter (default to empty map)
  struct mgp_map *config = NULL;
  if (args_size > 2) {
    struct mgp_value *config_arg = NULL;
    if (mgp_list_at(args, 2, &config_arg) == MGP_ERROR_NO_ERROR && config_arg != NULL) {
      enum mgp_value_type arg_type = MGP_VALUE_TYPE_NULL;
      if (mgp_value_get_type(config_arg, &arg_type) == MGP_ERROR_NO_ERROR && arg_type == MGP_VALUE_TYPE_MAP) {
        if (mgp_value_get_map(config_arg, &config) != MGP_ERROR_NO_ERROR || config == NULL) {
          mgp_result_set_error_msg(result, "Failed to get config map");
          return;
        }
        size_t size = 0;
        if (mgp_map_size(config, &size) == MGP_ERROR_NO_ERROR && size == 0) {
          // Config map is empty, set to NULL
          config = NULL;
        }
      }
    }
  }

  // Convert the input value to tree structure
  struct mgp_value *result_value = convert_to_tree_impl(input, lower_case_rels, config, memory);
  if (!result_value) {
    mgp_result_set_error_msg(result, "Failed to convert input to tree");
    return;
  }

  // Handle multiple results (APOC behavior for multiple roots)
  enum mgp_value_type result_type = MGP_VALUE_TYPE_NULL;
  if (mgp_value_get_type(result_value, &result_type) != MGP_ERROR_NO_ERROR) {
    mgp_value_destroy(result_value);
    mgp_result_set_error_msg(result, "Failed to get result type");
    return;
  }

  if (result_type == MGP_VALUE_TYPE_LIST) {
    // Multiple roots: yield one record per root
    struct mgp_list *result_list = NULL;
    if (mgp_value_get_list(result_value, &result_list) != MGP_ERROR_NO_ERROR || result_list == NULL) {
      mgp_value_destroy(result_value);
      mgp_result_set_error_msg(result, "Failed to get result list");
      return;
    }

    size_t list_size = 0;
    if (mgp_list_size(result_list, &list_size) != MGP_ERROR_NO_ERROR) {
      mgp_value_destroy(result_value);
      mgp_result_set_error_msg(result, "Failed to get list size");
      return;
    }

    for (size_t i = 0; i < list_size; i++) {
      struct mgp_value *tree_value = NULL;
      if (mgp_list_at(result_list, i, &tree_value) != MGP_ERROR_NO_ERROR || tree_value == NULL) {
        continue;
      }

      struct mgp_result_record *record = NULL;
      if (mgp_result_new_record(result, &record) != MGP_ERROR_NO_ERROR || record == NULL) {
        mgp_value_destroy(result_value);
        mgp_result_set_error_msg(result, "Failed to create result record");
        return;
      }

      if (mgp_result_record_insert(record, kReturnValue, tree_value) != MGP_ERROR_NO_ERROR) {
        mgp_value_destroy(result_value);
        mgp_result_set_error_msg(result, "Failed to insert result value");
        return;
      }
    }
    mgp_value_destroy(result_value);
  } else {
    // Single root: yield one record
    struct mgp_result_record *record = NULL;
    if (mgp_result_new_record(result, &record) != MGP_ERROR_NO_ERROR || record == NULL) {
      mgp_value_destroy(result_value);
      mgp_result_set_error_msg(result, "Failed to create result record");
      return;
    }

    if (mgp_result_record_insert(record, kReturnValue, result_value) != MGP_ERROR_NO_ERROR) {
      mgp_value_destroy(result_value);
      mgp_result_set_error_msg(result, "Failed to insert result value");
      return;
    }
    mgp_value_destroy(result_value);
  }
}

// Module initialization
int mgp_init_module(struct mgp_module *module, struct mgp_memory *memory) {
  // Add the to_tree procedure
  struct mgp_proc *proc = NULL;
  if (mgp_module_add_read_procedure(module, kProcedureToTree, to_tree, &proc) != MGP_ERROR_NO_ERROR || proc == NULL) {
    return 1;
  }

  // Add parameters
  struct mgp_type *any_type = NULL;
  if (mgp_type_any(&any_type) != MGP_ERROR_NO_ERROR || any_type == NULL) {
    return 1;
  }

  struct mgp_type *nullable_any_type = NULL;
  if (mgp_type_nullable(any_type, &nullable_any_type) != MGP_ERROR_NO_ERROR || nullable_any_type == NULL) {
    return 1;
  }

  if (mgp_proc_add_arg(proc, kParameterPaths, nullable_any_type) != MGP_ERROR_NO_ERROR) {
    return 1;
  }

  struct mgp_type *bool_type = NULL;
  if (mgp_type_bool(&bool_type) != MGP_ERROR_NO_ERROR || bool_type == NULL) {
    return 1;
  }

  struct mgp_value *true_value = NULL;
  if (mgp_value_make_bool(1, memory, &true_value) != MGP_ERROR_NO_ERROR || true_value == NULL) {
    return 1;
  }

  if (mgp_proc_add_opt_arg(proc, kParameterLowerCaseRels, bool_type, true_value) != MGP_ERROR_NO_ERROR) {
    mgp_value_destroy(true_value);
    return 1;
  }
  mgp_value_destroy(true_value);

  struct mgp_type *map_type = NULL;
  if (mgp_type_map(&map_type) != MGP_ERROR_NO_ERROR || map_type == NULL) {
    return 1;
  }

  struct mgp_map *empty_map = NULL;
  if (mgp_unordered_map_make_empty(memory, &empty_map) != MGP_ERROR_NO_ERROR || empty_map == NULL) {
    return 1;
  }

  struct mgp_value *empty_map_value = NULL;
  if (mgp_value_make_map(empty_map, &empty_map_value) != MGP_ERROR_NO_ERROR || empty_map_value == NULL) {
    mgp_map_destroy(empty_map);
    return 1;
  }

  if (mgp_proc_add_opt_arg(proc, kParameterConfig, map_type, empty_map_value) != MGP_ERROR_NO_ERROR ||
      empty_map_value == NULL) {
    mgp_value_destroy(empty_map_value);
    return 1;
  }
  mgp_value_destroy(empty_map_value);

  // Add return value
  if (mgp_proc_add_result(proc, kReturnValue, map_type) != MGP_ERROR_NO_ERROR || map_type == NULL) {
    return 1;
  }

  return 0;
}

int mgp_shutdown_module() { return 0; }