This commit is contained in:
2025-04-21 22:49:24 +04:00
parent de78c229ce
commit 2a1787f28f
26 changed files with 2327 additions and 663 deletions

View File

@@ -1,5 +1,7 @@
# Ternary Search Tree (TST) Implementation Plan
> STILL BROKEN, CAN'T FIND YET
## Overview
A Ternary Search Tree (TST) is a specialized tree structure where each node has three children:

View File

@@ -0,0 +1,140 @@
module tst
import os
// Define a struct for test cases
struct PrefixEdgeCaseTest {
prefix string
expected_keys []string
}
// Test specific edge cases for prefix search that were problematic
fn test_edge_case_prefix_search() {
mut tree := new(path: 'testdata/test_edge_prefix.db', reset: true) or {
assert false, 'Failed to create TST: ${err}'
return
}
// Keys with a common prefix that may cause issues
keys := [
'test', 'testing', 'tea', 'team', 'technology',
'apple', 'application', 'appreciate',
'banana', 'bandage', 'band',
'car', 'carpet', 'carriage'
]
// Insert all keys
for i, key in keys {
value := 'value-${i}'.bytes()
tree.set(key, value) or {
assert false, 'Failed to set key "${key}": ${err}'
return
}
}
// Test cases specifically focusing on the 'te' prefix issue
test_cases := [
// prefix, expected_keys
PrefixEdgeCaseTest{
prefix: 'te'
expected_keys: ['test', 'testing', 'tea', 'team', 'technology']
},
PrefixEdgeCaseTest{
prefix: 'tes'
expected_keys: ['test', 'testing']
},
PrefixEdgeCaseTest{
prefix: 'tea'
expected_keys: ['tea', 'team']
},
PrefixEdgeCaseTest{
prefix: 'a'
expected_keys: ['apple', 'application', 'appreciate']
},
PrefixEdgeCaseTest{
prefix: 'ba'
expected_keys: ['banana', 'bandage', 'band']
},
PrefixEdgeCaseTest{
prefix: 'ban'
expected_keys: ['banana', 'band']
},
PrefixEdgeCaseTest{
prefix: 'c'
expected_keys: ['car', 'carpet', 'carriage']
}
]
for test_case in test_cases {
prefix := test_case.prefix
expected_keys := test_case.expected_keys
result := tree.list(prefix) or {
assert false, 'Failed to list keys with prefix "${prefix}": ${err}'
return
}
// Check count matches
assert result.len == expected_keys.len,
'For prefix "${prefix}": expected ${expected_keys.len} keys, got ${result.len} (keys: ${result})'
// Check all expected keys are present
for key in expected_keys {
assert key in result, 'Key "${key}" missing from results for prefix "${prefix}"'
}
// Verify each result starts with the prefix
for key in result {
assert key.starts_with(prefix), 'Key "${key}" does not start with prefix "${prefix}"'
}
}
println('All edge case prefix tests passed successfully!')
}
// Test prefix search with insert order that might cause issues
fn test_tricky_insertion_order() {
mut tree := new(path: 'testdata/test_tricky_insert.db', reset: true) or {
assert false, 'Failed to create TST: ${err}'
return
}
// Insert keys in a specific order that might trigger the issue
// Insert 'team' first, then 'test', etc. to ensure tree layout is challenging
tricky_keys := [
'team', 'test', 'technology', 'tea', // 'te' prefix cases
'car', 'carriage', 'carpet' // 'ca' prefix cases
]
// Insert all keys
for i, key in tricky_keys {
value := 'value-${i}'.bytes()
tree.set(key, value) or {
assert false, 'Failed to set key "${key}": ${err}'
return
}
}
// Test 'te' prefix
te_results := tree.list('te') or {
assert false, 'Failed to list keys with prefix "te": ${err}'
return
}
assert te_results.len == 4, 'Expected 4 keys with prefix "te", got ${te_results.len} (keys: ${te_results})'
assert 'team' in te_results, 'Expected "team" in results'
assert 'test' in te_results, 'Expected "test" in results'
assert 'technology' in te_results, 'Expected "technology" in results'
assert 'tea' in te_results, 'Expected "tea" in results'
// Test 'ca' prefix
ca_results := tree.list('ca') or {
assert false, 'Failed to list keys with prefix "ca": ${err}'
return
}
assert ca_results.len == 3, 'Expected 3 keys with prefix "ca", got ${ca_results.len} (keys: ${ca_results})'
assert 'car' in ca_results, 'Expected "car" in results'
assert 'carriage' in ca_results, 'Expected "carriage" in results'
assert 'carpet' in ca_results, 'Expected "carpet" in results'
println('All tricky insertion order tests passed successfully!')
}

14
lib/data/tst/texttools.v Normal file
View File

@@ -0,0 +1,14 @@
module tst
// namefix normalizes a string for consistent key handling
// - removes leading/trailing whitespace
// - converts to lowercase
// - replaces special characters with standard ones
pub fn namefix(s string) string {
mut result := s.trim_space().to_lower()
// Replace any problematic characters or sequences if needed
// For this implementation, we'll keep it simple
return result
}

View File

@@ -61,6 +61,7 @@ pub fn new(args NewArgs) !TST {
root_data := db.get(1)! // Get root node with ID 1
root := deserialize_node(root_data)!
println('Root node retrieved: character=${root.character}, is_end=${root.is_end_of_string}, left=${root.left_id}, middle=${root.middle_id}, right=${root.right_id}')
root_id = 1 // Ensure we're using ID 1 for the root
}
return TST{
@@ -71,8 +72,10 @@ pub fn new(args NewArgs) !TST {
// Sets a key-value pair in the tree
pub fn (mut self TST) set(key string, value []u8) ! {
println('Setting key: "${key}"')
if key.len == 0 {
normalized_key := namefix(key)
println('Setting key: "${key}" (normalized: "${normalized_key}")')
if normalized_key.len == 0 {
return error('Empty key not allowed')
}
@@ -91,30 +94,79 @@ pub fn (mut self TST) set(key string, value []u8) ! {
println('Root node created with ID: ${self.root_id}')
}
self.insert_recursive(self.root_id, key, 0, value)!
println('Key "${key}" inserted successfully')
// Insert the key-value pair
mut last_node_id := self.insert_recursive(self.root_id, normalized_key, 0, value)!
println('Key "${normalized_key}" inserted to node ${last_node_id}')
// Make sure the last node is marked as end of string with the value
if last_node_id != 0 {
node_data := self.db.get(last_node_id)!
mut node := deserialize_node(node_data)!
// Ensure this node is marked as the end of a string
if !node.is_end_of_string {
println('Setting node ${last_node_id} as end of string')
node.is_end_of_string = true
node.value = value
self.db.set(id: last_node_id, data: serialize_node(node))!
}
}
println('Key "${normalized_key}" inserted successfully')
}
// Recursive helper function for insertion
fn (mut self TST) insert_recursive(node_id u32, key string, pos int, value []u8) !u32 {
// Check for position out of bounds
if pos >= key.len {
println('Position ${pos} is out of bounds for key "${key}"')
return error('Position out of bounds')
}
// If we've reached the end of the tree, create a new node
if node_id == 0 {
println('Creating new node for character: ${key[pos]} (${key[pos].ascii_str()}) at position ${pos}')
// Create a node for this character
new_node := Node{
character: key[pos]
is_end_of_string: pos == key.len - 1
value: if pos == key.len - 1 { value } else { []u8{} }
value: if pos == key.len - 1 { value.clone() } else { []u8{} }
left_id: 0
middle_id: 0
right_id: 0
}
new_id := self.db.set(data: serialize_node(new_node))!
println('New node created with ID: ${new_id}, character: ${key[pos]} (${key[pos].ascii_str()}), is_end: ${pos == key.len - 1}')
// If this is the last character in the key, we're done
if pos == key.len - 1 {
return new_id
}
// Otherwise, create the next node in the sequence and link to it
next_id := self.insert_recursive(0, key, pos + 1, value)!
// Update the middle link
node_data := self.db.get(new_id)!
mut updated_node := deserialize_node(node_data)!
updated_node.middle_id = next_id
self.db.set(id: new_id, data: serialize_node(updated_node))!
return new_id
}
// Get the current node
mut node := deserialize_node(self.db.get(node_id)!)!
// Get the current node with error handling
node_data := self.db.get(node_id) or {
println('Failed to get node data for ID ${node_id}')
return error('Node retrieval error: ${err}')
}
mut node := deserialize_node(node_data) or {
println('Failed to deserialize node with ID ${node_id}')
return error('Node deserialization error: ${err}')
}
println('Node ${node_id}: character=${node.character} (${node.character.ascii_str()}), is_end=${node.is_end_of_string}, left=${node.left_id}, middle=${node.middle_id}, right=${node.right_id}')
// Compare the current character with the node's character
@@ -149,8 +201,10 @@ fn (mut self TST) insert_recursive(node_id u32, key string, pos int, value []u8)
// Gets a value by key from the tree
pub fn (mut self TST) get(key string) ![]u8 {
println('Getting key: "${key}"')
if key.len == 0 {
normalized_key := namefix(key)
println('Getting key: "${key}" (normalized: "${normalized_key}")')
if normalized_key.len == 0 {
return error('Empty key not allowed')
}
@@ -158,51 +212,92 @@ pub fn (mut self TST) get(key string) ![]u8 {
return error('Tree is empty')
}
return self.search_recursive(self.root_id, key, 0)!
return self.search_recursive(self.root_id, normalized_key, 0)!
}
// Recursive helper function for search
// Very simple recursive search with explicit structure to make the compiler happy
fn (mut self TST) search_recursive(node_id u32, key string, pos int) ![]u8 {
// Base cases
if node_id == 0 {
println('Node ID is 0, key not found')
return error('Key not found')
}
node := deserialize_node(self.db.get(node_id)!)!
println('Searching node ${node_id}: character=${node.character} (${node.character.ascii_str()}), is_end=${node.is_end_of_string}, left=${node.left_id}, middle=${node.middle_id}, right=${node.right_id}, pos=${pos}')
if pos >= key.len {
println('Position ${pos} out of bounds for key "${key}"')
return error('Key not found - position out of bounds')
}
// Get the node
node_data := self.db.get(node_id) or {
println('Failed to get node ${node_id}')
return error('Node not found in database')
}
node := deserialize_node(node_data) or {
println('Failed to deserialize node ${node_id}')
return error('Failed to deserialize node')
}
println('Searching node ${node_id}: char=${node.character}, pos=${pos}, key_char=${key[pos]}')
mut result := []u8{}
// Left branch
if key[pos] < node.character {
println('Going left: ${key[pos]} (${key[pos].ascii_str()}) < ${node.character} (${node.character.ascii_str()})')
// Go left
return self.search_recursive(node.left_id, key, pos)!
} else if key[pos] > node.character {
println('Going right: ${key[pos]} (${key[pos].ascii_str()}) > ${node.character} (${node.character.ascii_str()})')
// Go right
return self.search_recursive(node.right_id, key, pos)!
} else {
// Equal
println('Character matches: ${key[pos]} (${key[pos].ascii_str()}) == ${node.character} (${node.character.ascii_str()})')
if pos == key.len - 1 {
// We've reached the end of the key
if node.is_end_of_string {
println('End of key reached and is_end_of_string=true, returning value')
return node.value
} else {
println('End of key reached but is_end_of_string=false, key not found')
return error('Key not found')
println('Going left')
result = self.search_recursive(node.left_id, key, pos) or {
return error(err.str())
}
return result
}
// Right branch
if key[pos] > node.character {
println('Going right')
result = self.search_recursive(node.right_id, key, pos) or {
return error(err.str())
}
return result
}
// Character matches
println('Character match')
// At end of key
if pos == key.len - 1 {
if node.is_end_of_string {
println('Found key')
// Copy the value
for i in 0 .. node.value.len {
result << node.value[i]
}
return result
} else {
// Move to the next character in the key
println('Moving to next character: ${key[pos+1]} (${key[pos+1].ascii_str()})')
return self.search_recursive(node.middle_id, key, pos + 1)!
println('Character match but not end of string')
return error('Key not found - not marked as end of string')
}
}
// Not at end of key, go to middle
if node.middle_id == 0 {
println('No middle child')
return error('Key not found - no middle child')
}
println('Going to middle child')
result = self.search_recursive(node.middle_id, key, pos + 1) or {
return error(err.str())
}
return result
}
// Deletes a key from the tree
pub fn (mut self TST) delete(key string) ! {
println('Deleting key: "${key}"')
if key.len == 0 {
normalized_key := namefix(key)
println('Deleting key: "${key}" (normalized: "${normalized_key}")')
if normalized_key.len == 0 {
return error('Empty key not allowed')
}
@@ -210,8 +305,8 @@ pub fn (mut self TST) delete(key string) ! {
return error('Tree is empty')
}
self.delete_recursive(self.root_id, key, 0)!
println('Key "${key}" deleted successfully')
self.delete_recursive(self.root_id, normalized_key, 0)!
println('Key "${normalized_key}" deleted successfully')
}
// Recursive helper function for deletion
@@ -220,14 +315,35 @@ fn (mut self TST) delete_recursive(node_id u32, key string, pos int) !bool {
println('Node ID is 0, key not found')
return error('Key not found')
}
// Check for position out of bounds
if pos >= key.len {
println('Position ${pos} is out of bounds for key "${key}"')
return error('Key not found - position out of bounds')
}
mut node := deserialize_node(self.db.get(node_id)!)!
// Get the current node with error handling
node_data := self.db.get(node_id) or {
println('Failed to get node data for ID ${node_id}')
return error('Node retrieval error: ${err}')
}
mut node := deserialize_node(node_data) or {
println('Failed to deserialize node with ID ${node_id}')
return error('Node deserialization error: ${err}')
}
println('Deleting from node ${node_id}: character=${node.character} (${node.character.ascii_str()}), is_end=${node.is_end_of_string}, left=${node.left_id}, middle=${node.middle_id}, right=${node.right_id}, pos=${pos}')
mut deleted := false
if key[pos] < node.character {
println('Going left: ${key[pos]} (${key[pos].ascii_str()}) < ${node.character} (${node.character.ascii_str()})')
// Go left
if node.left_id == 0 {
println('Left child is null, key not found')
return error('Key not found')
}
deleted = self.delete_recursive(node.left_id, key, pos)!
if deleted && node.left_id != 0 {
// Check if the left child has been deleted
@@ -244,6 +360,11 @@ fn (mut self TST) delete_recursive(node_id u32, key string, pos int) !bool {
} else if key[pos] > node.character {
println('Going right: ${key[pos]} (${key[pos].ascii_str()}) > ${node.character} (${node.character.ascii_str()})')
// Go right
if node.right_id == 0 {
println('Right child is null, key not found')
return error('Key not found')
}
deleted = self.delete_recursive(node.right_id, key, pos)!
if deleted && node.right_id != 0 {
// Check if the right child has been deleted
@@ -285,6 +406,11 @@ fn (mut self TST) delete_recursive(node_id u32, key string, pos int) !bool {
} else {
// Move to the next character in the key
println('Moving to next character: ${key[pos+1]} (${key[pos+1].ascii_str()})')
if node.middle_id == 0 {
println('Middle child is null, key not found')
return error('Key not found')
}
deleted = self.delete_recursive(node.middle_id, key, pos + 1)!
if deleted && node.middle_id != 0 {
// Check if the middle child has been deleted
@@ -310,170 +436,3 @@ fn (mut self TST) delete_recursive(node_id u32, key string, pos int) !bool {
return deleted
}
// Lists all keys with a given prefix
pub fn (mut self TST) list(prefix string) ![]string {
println('Listing keys with prefix: "${prefix}"')
mut result := []string{}
// Handle empty prefix case - will return all keys
if prefix.len == 0 {
println('Empty prefix, collecting all keys')
self.collect_all_keys(self.root_id, '', mut result)!
println('Found ${result.len} keys: ${result}')
return result
}
// Find the node corresponding to the prefix
println('Finding node for prefix: "${prefix}"')
// Start from the root and traverse to the node corresponding to the last character of the prefix
mut node_id := self.root_id
mut pos := 0
mut current_path := ''
// Traverse the tree to find the node corresponding to the prefix
for pos < prefix.len && node_id != 0 {
node := deserialize_node(self.db.get(node_id)!)!
println('Examining node ${node_id}: character=${node.character} (${node.character.ascii_str()}), is_end=${node.is_end_of_string}, left=${node.left_id}, middle=${node.middle_id}, right=${node.right_id}, pos=${pos}, current_path="${current_path}"')
if prefix[pos] < node.character {
println('Going left: ${prefix[pos]} (${prefix[pos].ascii_str()}) < ${node.character} (${node.character.ascii_str()})')
node_id = node.left_id
} else if prefix[pos] > node.character {
println('Going right: ${prefix[pos]} (${prefix[pos].ascii_str()}) > ${node.character} (${node.character.ascii_str()})')
node_id = node.right_id
} else {
// Character matches
println('Character matches: ${prefix[pos]} (${prefix[pos].ascii_str()}) == ${node.character} (${node.character.ascii_str()})')
// Update the current path
if node.character != 0 { // Skip the root node character
current_path += node.character.ascii_str()
println('Updated path: "${current_path}"')
}
if pos == prefix.len - 1 {
// We've reached the end of the prefix
println('Reached end of prefix')
// If this node is the end of a string, add it to the result
if node.is_end_of_string {
println('Node is end of string, adding key: "${current_path}"')
result << current_path
}
// Collect all keys from the middle child
if node.middle_id != 0 {
println('Collecting from middle child with path: "${current_path}"')
self.collect_keys_with_prefix(node.middle_id, current_path, prefix, mut result)!
}
break
} else {
// Move to the next character in the prefix
println('Moving to next character in prefix: ${prefix[pos+1]} (${prefix[pos+1].ascii_str()})')
node_id = node.middle_id
pos++
}
}
}
if node_id == 0 || pos < prefix.len - 1 {
// Prefix not found or we didn't reach the end of the prefix
println('Prefix not found or didn\'t reach end of prefix, returning empty result')
return []string{}
}
println('Found ${result.len} keys with prefix "${prefix}": ${result}')
return result
}
// Helper function to collect all keys with a given prefix
fn (mut self TST) collect_keys_with_prefix(node_id u32, current_path string, prefix string, mut result []string) ! {
if node_id == 0 {
return
}
node := deserialize_node(self.db.get(node_id)!)!
println('Collecting keys with prefix from node ${node_id}: character=${node.character} (${node.character.ascii_str()}), is_end=${node.is_end_of_string}, left=${node.left_id}, middle=${node.middle_id}, right=${node.right_id}, current_path="${current_path}"')
// Construct the path for this node
path := current_path + node.character.ascii_str()
println('Path for node: "${path}"')
// If this node is the end of a string, add it to the result
if node.is_end_of_string {
println('Node is end of string, adding key: "${path}"')
result << path
}
// Recursively collect keys from the middle child (keys that extend this prefix)
if node.middle_id != 0 {
println('Collecting from middle child with path: "${path}"')
self.collect_keys_with_prefix(node.middle_id, path, prefix, mut result)!
}
// Also collect keys from left and right children
// This is necessary because multiple keys might share the same prefix
if node.left_id != 0 {
println('Collecting from left child with path: "${current_path}"')
self.collect_keys_with_prefix(node.left_id, current_path, prefix, mut result)!
}
if node.right_id != 0 {
println('Collecting from right child with path: "${current_path}"')
self.collect_keys_with_prefix(node.right_id, current_path, prefix, mut result)!
}
}
// Helper function to recursively collect all keys under a node
fn (mut self TST) collect_all_keys(node_id u32, current_path string, mut result []string) ! {
if node_id == 0 {
return
}
node := deserialize_node(self.db.get(node_id)!)!
println('Collecting all from node ${node_id}: character=${node.character} (${node.character.ascii_str()}), is_end=${node.is_end_of_string}, left=${node.left_id}, middle=${node.middle_id}, right=${node.right_id}, current_path="${current_path}"')
// Construct the path for this node
path := current_path + node.character.ascii_str()
println('Path for node: "${path}"')
// If this node is the end of a string, add it to the result
if node.is_end_of_string {
println('Node is end of string, adding key: "${path}"')
result << path
}
// Recursively collect keys from all children
if node.left_id != 0 {
println('Collecting all from left child with path: "${current_path}"')
self.collect_all_keys(node.left_id, current_path, mut result)!
}
if node.middle_id != 0 {
println('Collecting all from middle child with path: "${path}"')
self.collect_all_keys(node.middle_id, path, mut result)!
}
if node.right_id != 0 {
println('Collecting all from right child with path: "${current_path}"')
self.collect_all_keys(node.right_id, current_path, mut result)!
}
}
// Gets all values for keys with a given prefix
pub fn (mut self TST) getall(prefix string) ![][]u8 {
println('Getting all values with prefix: "${prefix}"')
// Get all matching keys
keys := self.list(prefix)!
// Get values for each key
mut values := [][]u8{}
for key in keys {
if value := self.get(key) {
values << value
}
}
println('Found ${values.len} values with prefix "${prefix}"')
return values
}

204
lib/data/tst/tst_list.v Normal file
View File

@@ -0,0 +1,204 @@
module tst
import freeflowuniverse.herolib.data.ourdb
// Lists all keys with a given prefix
pub fn (mut self TST) list(prefix string) ![]string {
normalized_prefix := namefix(prefix)
println('Listing keys with prefix: "${prefix}" (normalized: "${normalized_prefix}")')
mut result := []string{}
// Handle empty prefix case - will return all keys
if normalized_prefix.len == 0 {
println('Empty prefix, collecting all keys')
self.collect_all_keys(self.root_id, '', mut result)!
println('Found ${result.len} keys: ${result}')
return result
}
// Find the prefix node first
result_info := self.navigate_to_prefix(self.root_id, normalized_prefix, 0)
if !result_info.found {
println('Prefix node not found for "${normalized_prefix}"')
return result // Empty result
}
println('Found node for prefix "${normalized_prefix}" at node ${result_info.node_id}, collecting keys')
// Collect all keys from the subtree rooted at the prefix node
self.collect_keys_with_prefix(result_info.node_id, result_info.prefix, mut result)!
println('Found ${result.len} keys with prefix "${normalized_prefix}": ${result}')
return result
}
// Result struct for prefix navigation
struct PrefixSearchResult {
found bool
node_id u32
prefix string
}
// Navigate to the node corresponding to a prefix
fn (mut self TST) navigate_to_prefix(node_id u32, prefix string, pos int) PrefixSearchResult {
// Base case: no node or out of bounds
if node_id == 0 || pos >= prefix.len {
return PrefixSearchResult{
found: false
node_id: 0
prefix: ''
}
}
// Get node
node_data := self.db.get(node_id) or {
return PrefixSearchResult{found: false, node_id: 0, prefix: ''}
}
node := deserialize_node(node_data) or {
return PrefixSearchResult{found: false, node_id: 0, prefix: ''}
}
println('Navigating node ${node_id}: char=${node.character} (${node.character.ascii_str()}), pos=${pos}, prefix_char=${prefix[pos]} (${prefix[pos].ascii_str()})')
// Character comparison
if prefix[pos] < node.character {
// Go left
println('Going left')
return self.navigate_to_prefix(node.left_id, prefix, pos)
} else if prefix[pos] > node.character {
// Go right
println('Going right')
return self.navigate_to_prefix(node.right_id, prefix, pos)
} else {
// Character match
println('Character match found')
// Check if we're at the end of the prefix
if pos == prefix.len - 1 {
println('Reached end of prefix at node ${node_id}')
// Return the exact prefix string that was passed in
return PrefixSearchResult{
found: true
node_id: node_id
prefix: prefix
}
}
// Not at end of prefix, check middle child
if node.middle_id == 0 {
println('No middle child, prefix not found')
return PrefixSearchResult{found: false, node_id: 0, prefix: ''}
}
// Continue to middle child with next character
return self.navigate_to_prefix(node.middle_id, prefix, pos + 1)
}
}
// Collect all keys in a subtree that have a given prefix
fn (mut self TST) collect_keys_with_prefix(node_id u32, prefix string, mut result []string) ! {
if node_id == 0 {
return
}
// Get node
node_data := self.db.get(node_id) or { return }
node := deserialize_node(node_data) or { return }
println('Collecting from node ${node_id}, char=${node.character} (${node.character.ascii_str()}), prefix="${prefix}"')
// If this node is an end of string and it's not the root, we found a key
if node.is_end_of_string && node.character != 0 {
// The prefix may already contain this node's character
if prefix.len == 0 || prefix[prefix.len-1] != node.character {
println('Found complete key: "${prefix}${node.character.ascii_str()}"')
result << prefix + node.character.ascii_str()
} else {
println('Found complete key: "${prefix}"')
result << prefix
}
}
// Recursively search all children
if node.left_id != 0 {
self.collect_keys_with_prefix(node.left_id, prefix, mut result)!
}
// For middle child, we need to add this node's character to the prefix
if node.middle_id != 0 {
mut next_prefix := prefix
if node.character != 0 { // Skip root node
// Only add the character if it's not already at the end of the prefix
if prefix.len == 0 || prefix[prefix.len-1] != node.character {
next_prefix += node.character.ascii_str()
}
}
self.collect_keys_with_prefix(node.middle_id, next_prefix, mut result)!
}
if node.right_id != 0 {
self.collect_keys_with_prefix(node.right_id, prefix, mut result)!
}
}
// Collect all keys in the tree
fn (mut self TST) collect_all_keys(node_id u32, prefix string, mut result []string) ! {
if node_id == 0 {
return
}
// Get node
node_data := self.db.get(node_id) or { return }
node := deserialize_node(node_data) or { return }
// Calculate current path
mut current_prefix := prefix
// If this is not the root, add the character
if node.character != 0 {
current_prefix += node.character.ascii_str()
}
// If this marks the end of a key, add it to the result
if node.is_end_of_string {
println('Found key: ${current_prefix}')
if current_prefix !in result {
result << current_prefix
}
}
// Visit all children
if node.left_id != 0 {
self.collect_all_keys(node.left_id, prefix, mut result)!
}
if node.middle_id != 0 {
self.collect_all_keys(node.middle_id, current_prefix, mut result)!
}
if node.right_id != 0 {
self.collect_all_keys(node.right_id, prefix, mut result)!
}
}
// Gets all values for keys with a given prefix
pub fn (mut self TST) getall(prefix string) ![][]u8 {
normalized_prefix := namefix(prefix)
println('Getting all values with prefix: "${prefix}" (normalized: "${normalized_prefix}")')
// Get all matching keys
keys := self.list(normalized_prefix)!
// Get values for each key
mut values := [][]u8{}
for key in keys {
if value := self.get(key) {
values << value
}
}
println('Found ${values.len} values with prefix "${normalized_prefix}"')
return values
}