create-feltdb 0.4.11 → 0.4.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/dist/cli.js +16 -1
  2. package/dist/create.js +30 -16
  3. package/dist/docker-compose-generator.js +48 -9
  4. package/dist/package-versions.js +1 -1
  5. package/dist/server-source/Cargo.lock +2238 -0
  6. package/dist/server-source/Cargo.toml +7 -0
  7. package/dist/server-source/crates/feltdb/Cargo.lock +175 -0
  8. package/dist/server-source/crates/feltdb/Cargo.toml +79 -0
  9. package/dist/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +370 -0
  10. package/dist/server-source/crates/feltdb/benches/gate13_baseline.rs +589 -0
  11. package/dist/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +259 -0
  12. package/dist/server-source/crates/feltdb/benches/gate_13_redux.rs +446 -0
  13. package/dist/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +272 -0
  14. package/dist/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +378 -0
  15. package/dist/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +414 -0
  16. package/dist/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +486 -0
  17. package/dist/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +273 -0
  18. package/dist/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +587 -0
  19. package/dist/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +513 -0
  20. package/dist/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +395 -0
  21. package/dist/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +469 -0
  22. package/dist/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +409 -0
  23. package/dist/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +627 -0
  24. package/dist/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +383 -0
  25. package/dist/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +298 -0
  26. package/dist/server-source/crates/feltdb/src/acceptance_tests.rs +1698 -0
  27. package/dist/server-source/crates/feltdb/src/acquisition.rs +286 -0
  28. package/dist/server-source/crates/feltdb/src/admission.rs +192 -0
  29. package/dist/server-source/crates/feltdb/src/admission_contract_tests.rs +477 -0
  30. package/dist/server-source/crates/feltdb/src/adversarial_transport.rs +566 -0
  31. package/dist/server-source/crates/feltdb/src/analytics.rs +475 -0
  32. package/dist/server-source/crates/feltdb/src/application.rs +2244 -0
  33. package/dist/server-source/crates/feltdb/src/application_runtime.rs +1070 -0
  34. package/dist/server-source/crates/feltdb/src/authorization.rs +1030 -0
  35. package/dist/server-source/crates/feltdb/src/bin/feltdb_node.rs +266 -0
  36. package/dist/server-source/crates/feltdb/src/capabilities/mod.rs +8 -0
  37. package/dist/server-source/crates/feltdb/src/capabilities/search.rs +418 -0
  38. package/dist/server-source/crates/feltdb/src/capabilities/vector.rs +482 -0
  39. package/dist/server-source/crates/feltdb/src/capability.rs +903 -0
  40. package/dist/server-source/crates/feltdb/src/cardinality_diagnostics.rs +261 -0
  41. package/dist/server-source/crates/feltdb/src/cardinality_endpoint.rs +78 -0
  42. package/dist/server-source/crates/feltdb/src/causal_dependency_barrier.rs +2214 -0
  43. package/dist/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +194 -0
  44. package/dist/server-source/crates/feltdb/src/concurrency_fuzzing.rs +427 -0
  45. package/dist/server-source/crates/feltdb/src/consistency_contract.rs +453 -0
  46. package/dist/server-source/crates/feltdb/src/content_distribution.rs +465 -0
  47. package/dist/server-source/crates/feltdb/src/convergence.rs +618 -0
  48. package/dist/server-source/crates/feltdb/src/crash_atomic_boundary.rs +418 -0
  49. package/dist/server-source/crates/feltdb/src/crash_injection.rs +380 -0
  50. package/dist/server-source/crates/feltdb/src/crash_recovery_tests.rs +362 -0
  51. package/dist/server-source/crates/feltdb/src/cron.rs +294 -0
  52. package/dist/server-source/crates/feltdb/src/distributed_indexing.rs +474 -0
  53. package/dist/server-source/crates/feltdb/src/distributed_tests.rs +1003 -0
  54. package/dist/server-source/crates/feltdb/src/distributed_transactions.rs +536 -0
  55. package/dist/server-source/crates/feltdb/src/durability_guarantees.rs +364 -0
  56. package/dist/server-source/crates/feltdb/src/durable_dedup_set.rs +235 -0
  57. package/dist/server-source/crates/feltdb/src/durable_operation_log.rs +207 -0
  58. package/dist/server-source/crates/feltdb/src/durable_sync.rs +316 -0
  59. package/dist/server-source/crates/feltdb/src/execution.rs +426 -0
  60. package/dist/server-source/crates/feltdb/src/in_process_transport.rs +219 -0
  61. package/dist/server-source/crates/feltdb/src/indexing.rs +779 -0
  62. package/dist/server-source/crates/feltdb/src/lib.rs +2838 -0
  63. package/dist/server-source/crates/feltdb/src/materialization.rs +184 -0
  64. package/dist/server-source/crates/feltdb/src/metrics.rs +267 -0
  65. package/dist/server-source/crates/feltdb/src/multi_node_convergence.rs +311 -0
  66. package/dist/server-source/crates/feltdb/src/observability.rs +366 -0
  67. package/dist/server-source/crates/feltdb/src/operation.rs +251 -0
  68. package/dist/server-source/crates/feltdb/src/operation_algebra.rs +438 -0
  69. package/dist/server-source/crates/feltdb/src/operation_log.rs +344 -0
  70. package/dist/server-source/crates/feltdb/src/partition_reconciliation.rs +477 -0
  71. package/dist/server-source/crates/feltdb/src/peer_registry.rs +166 -0
  72. package/dist/server-source/crates/feltdb/src/permutation_scheduler.rs +261 -0
  73. package/dist/server-source/crates/feltdb/src/persistence_reality.rs +560 -0
  74. package/dist/server-source/crates/feltdb/src/phase1b_acceptance.rs +3226 -0
  75. package/dist/server-source/crates/feltdb/src/phase1c1_acceptance.rs +201 -0
  76. package/dist/server-source/crates/feltdb/src/phase1c2_acceptance.rs +263 -0
  77. package/dist/server-source/crates/feltdb/src/phase1c3_acceptance.rs +484 -0
  78. package/dist/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +216 -0
  79. package/dist/server-source/crates/feltdb/src/phase5_integration.rs +281 -0
  80. package/dist/server-source/crates/feltdb/src/phase5_scenarios.rs +323 -0
  81. package/dist/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +573 -0
  82. package/dist/server-source/crates/feltdb/src/phase6_convergence_validator.rs +404 -0
  83. package/dist/server-source/crates/feltdb/src/phase6_persistence.rs +418 -0
  84. package/dist/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +381 -0
  85. package/dist/server-source/crates/feltdb/src/phase_1c_three_node.rs +523 -0
  86. package/dist/server-source/crates/feltdb/src/phase_2a_failures.rs +334 -0
  87. package/dist/server-source/crates/feltdb/src/phase_2b_network.rs +306 -0
  88. package/dist/server-source/crates/feltdb/src/phase_2c_cascading.rs +355 -0
  89. package/dist/server-source/crates/feltdb/src/phase_3_durability.rs +395 -0
  90. package/dist/server-source/crates/feltdb/src/phase_4_baseline.rs +346 -0
  91. package/dist/server-source/crates/feltdb/src/phase_5_soak.rs +430 -0
  92. package/dist/server-source/crates/feltdb/src/production_api.rs +400 -0
  93. package/dist/server-source/crates/feltdb/src/provenance.rs +207 -0
  94. package/dist/server-source/crates/feltdb/src/query_performance.rs +217 -0
  95. package/dist/server-source/crates/feltdb/src/references.rs +404 -0
  96. package/dist/server-source/crates/feltdb/src/replay_fuzzing.rs +401 -0
  97. package/dist/server-source/crates/feltdb/src/replication_manager.rs +160 -0
  98. package/dist/server-source/crates/feltdb/src/replication_protocol.rs +132 -0
  99. package/dist/server-source/crates/feltdb/src/routing.rs +409 -0
  100. package/dist/server-source/crates/feltdb/src/sharding.rs +523 -0
  101. package/dist/server-source/crates/feltdb/src/state_contract.rs +3118 -0
  102. package/dist/server-source/crates/feltdb/src/state_hash.rs +291 -0
  103. package/dist/server-source/crates/feltdb/src/state_transition_store.rs +376 -0
  104. package/dist/server-source/crates/feltdb/src/storage.rs +267 -0
  105. package/dist/server-source/crates/feltdb/src/submission.rs +477 -0
  106. package/dist/server-source/crates/feltdb/src/sync.rs +483 -0
  107. package/dist/server-source/crates/feltdb/src/sync_contract.rs +822 -0
  108. package/dist/server-source/crates/feltdb/src/tcp_transport.rs +366 -0
  109. package/dist/server-source/crates/feltdb/src/transaction_api.rs +473 -0
  110. package/dist/server-source/crates/feltdb/src/transaction_invariants.rs +949 -0
  111. package/dist/server-source/crates/feltdb/src/transactions.rs +646 -0
  112. package/dist/server-source/crates/feltdb/src/trigger.rs +390 -0
  113. package/dist/server-source/crates/feltdb/src/worker_mesh.rs +928 -0
  114. package/dist/server-source/crates/feltdb/src/workflow.rs +713 -0
  115. package/dist/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +510 -0
  116. package/dist/server-source/crates/feltdb/src/workflow_integration.rs +354 -0
  117. package/dist/server-source/crates/feltdb/src/workflow_runtime.rs +594 -0
  118. package/dist/server-source/crates/feltdb/src/workload.rs +1310 -0
  119. package/dist/server-source/crates/feltdb-server/Cargo.toml +23 -0
  120. package/dist/server-source/crates/feltdb-server/README.md +79 -0
  121. package/dist/server-source/crates/feltdb-server/src/app_state.rs +73 -0
  122. package/dist/server-source/crates/feltdb-server/src/application_contract.rs +425 -0
  123. package/dist/server-source/crates/feltdb-server/src/artifacts.rs +449 -0
  124. package/dist/server-source/crates/feltdb-server/src/audit.rs +59 -0
  125. package/dist/server-source/crates/feltdb-server/src/auth.rs +308 -0
  126. package/dist/server-source/crates/feltdb-server/src/authorization.rs +228 -0
  127. package/dist/server-source/crates/feltdb-server/src/backup.rs +416 -0
  128. package/dist/server-source/crates/feltdb-server/src/causal.rs +218 -0
  129. package/dist/server-source/crates/feltdb-server/src/certification.rs +223 -0
  130. package/dist/server-source/crates/feltdb-server/src/clock.rs +132 -0
  131. package/dist/server-source/crates/feltdb-server/src/cluster.rs +165 -0
  132. package/dist/server-source/crates/feltdb-server/src/connections.rs +1044 -0
  133. package/dist/server-source/crates/feltdb-server/src/content.rs +111 -0
  134. package/dist/server-source/crates/feltdb-server/src/identity.rs +250 -0
  135. package/dist/server-source/crates/feltdb-server/src/key_management.rs +78 -0
  136. package/dist/server-source/crates/feltdb-server/src/key_provider.rs +247 -0
  137. package/dist/server-source/crates/feltdb-server/src/leases.rs +123 -0
  138. package/dist/server-source/crates/feltdb-server/src/lib.rs +25 -0
  139. package/dist/server-source/crates/feltdb-server/src/main.rs +8715 -0
  140. package/dist/server-source/crates/feltdb-server/src/metrics.rs +97 -0
  141. package/dist/server-source/crates/feltdb-server/src/portable_bundle.rs +350 -0
  142. package/dist/server-source/crates/feltdb-server/src/principals.rs +510 -0
  143. package/dist/server-source/crates/feltdb-server/src/providers.rs +269 -0
  144. package/dist/server-source/crates/feltdb-server/src/releases.rs +2563 -0
  145. package/dist/server-source/crates/feltdb-server/src/sessions.rs +156 -0
  146. package/dist/server-source/crates/feltdb-server/src/tenancy.rs +1097 -0
  147. package/dist/server-source/crates/feltdb-server/src/versions.rs +264 -0
  148. package/dist/server-source/crates/feltdb-wasm/Cargo.toml +31 -0
  149. package/dist/server-source/crates/feltdb-wasm/src/lib.rs +1086 -0
  150. package/dist/server-source/crates/feltdb-wasm/test.db +0 -0
  151. package/package.json +3 -3
@@ -0,0 +1,779 @@
1
+ use serde::{Deserialize, Serialize};
2
+ use std::collections::{BTreeMap, HashMap, HashSet};
3
+
4
+ #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
5
+ pub enum IndexType {
6
+ Hash,
7
+ Sorted,
8
+ Compound,
9
+ Text,
10
+ }
11
+
12
+ #[derive(Debug, Clone, Serialize, Deserialize)]
13
+ pub struct CompoundKey {
14
+ pub parts: Vec<String>,
15
+ }
16
+
17
+ impl CompoundKey {
18
+ fn from_values(values: &[&str]) -> Self {
19
+ CompoundKey {
20
+ parts: values.iter().map(|v| v.to_string()).collect(),
21
+ }
22
+ }
23
+
24
+ fn to_key(&self) -> String {
25
+ self.parts.join("\0")
26
+ }
27
+ }
28
+
29
+ #[derive(Debug, Clone, Serialize, Deserialize)]
30
+ pub struct IndexConfig {
31
+ pub name: String,
32
+ pub index_type: IndexType,
33
+ pub field: Option<String>,
34
+ pub fields: Option<Vec<String>>,
35
+ pub text_fields: Option<Vec<String>>,
36
+ pub inmemory: Option<bool>,
37
+ pub persistent: Option<bool>,
38
+ pub description: Option<String>,
39
+ }
40
+
41
+ #[derive(Debug, Clone, Serialize, Deserialize)]
42
+ pub struct IndexStats {
43
+ pub name: String,
44
+ pub index_type: IndexType,
45
+ pub entries: usize,
46
+ pub size: usize,
47
+ pub last_updated: u64,
48
+ pub reads: u64,
49
+ pub writes: u64,
50
+ pub selectivity: f64,
51
+ }
52
+
53
+ #[derive(Debug, Clone, Serialize, Deserialize)]
54
+ pub struct QueryPlan {
55
+ pub index_name: String,
56
+ pub operator: String,
57
+ pub selectivity: f64,
58
+ pub estimated_rows: usize,
59
+ pub cost: u64,
60
+ }
61
+
62
+ #[derive(Debug, Clone, Serialize, Deserialize)]
63
+ pub struct PaginationParams {
64
+ pub limit: usize,
65
+ pub offset: usize,
66
+ }
67
+
68
+ #[derive(Debug, Clone, Serialize, Deserialize)]
69
+ pub struct QueryResult {
70
+ pub ids: Vec<String>,
71
+ pub total_count: usize,
72
+ pub limit: usize,
73
+ pub offset: usize,
74
+ pub has_more: bool,
75
+ }
76
+
77
+ #[derive(Debug, Clone)]
78
+ struct HashIndexInner {
79
+ entries: HashMap<String, HashSet<String>>,
80
+ }
81
+
82
+ impl HashIndexInner {
83
+ fn new() -> Self {
84
+ HashIndexInner {
85
+ entries: HashMap::new(),
86
+ }
87
+ }
88
+
89
+ fn add(&mut self, record_id: &str, value: &str) {
90
+ let key = value.to_string();
91
+ self.entries
92
+ .entry(key)
93
+ .or_insert_with(HashSet::new)
94
+ .insert(record_id.to_string());
95
+ }
96
+
97
+ fn remove(&mut self, record_id: &str, value: &str) {
98
+ if let Some(ids) = self.entries.get_mut(value) {
99
+ ids.remove(record_id);
100
+ if ids.is_empty() {
101
+ self.entries.remove(value);
102
+ }
103
+ }
104
+ }
105
+
106
+ fn query(&self, value: &str) -> Vec<String> {
107
+ self.entries
108
+ .get(value)
109
+ .map(|ids| ids.iter().cloned().collect())
110
+ .unwrap_or_default()
111
+ }
112
+
113
+ fn size(&self) -> usize {
114
+ self.entries.len()
115
+ }
116
+ }
117
+
118
+ #[derive(Debug, Clone)]
119
+ struct SortedIndexInner {
120
+ entries: BTreeMap<String, HashSet<String>>,
121
+ }
122
+
123
+ impl SortedIndexInner {
124
+ fn new() -> Self {
125
+ SortedIndexInner {
126
+ entries: BTreeMap::new(),
127
+ }
128
+ }
129
+
130
+ fn add(&mut self, record_id: &str, value: &str) {
131
+ self.entries
132
+ .entry(value.to_string())
133
+ .or_insert_with(HashSet::new)
134
+ .insert(record_id.to_string());
135
+ }
136
+
137
+ fn remove(&mut self, record_id: &str, value: &str) {
138
+ if let Some(ids) = self.entries.get_mut(value) {
139
+ ids.remove(record_id);
140
+ if ids.is_empty() {
141
+ self.entries.remove(value);
142
+ }
143
+ }
144
+ }
145
+
146
+ fn query_equal(&self, value: &str) -> Vec<String> {
147
+ self.entries
148
+ .get(value)
149
+ .map(|ids| ids.iter().cloned().collect())
150
+ .unwrap_or_default()
151
+ }
152
+
153
+ fn query_range(&self, min: &str, max: &str) -> Vec<String> {
154
+ let mut result = HashSet::new();
155
+ for (_key, ids) in self.entries.range(min.to_string()..=max.to_string()) {
156
+ for id in ids {
157
+ result.insert(id.clone());
158
+ }
159
+ }
160
+ result.into_iter().collect()
161
+ }
162
+
163
+ fn size(&self) -> usize {
164
+ self.entries.len()
165
+ }
166
+ }
167
+
168
+ #[derive(Debug, Clone)]
169
+ struct TextIndexInner {
170
+ token_index: HashMap<String, HashSet<String>>,
171
+ }
172
+
173
+ impl TextIndexInner {
174
+ fn new() -> Self {
175
+ TextIndexInner {
176
+ token_index: HashMap::new(),
177
+ }
178
+ }
179
+
180
+ fn add(&mut self, record_id: &str, text: &str) {
181
+ let tokens = Self::tokenize(text);
182
+ for token in tokens {
183
+ self.token_index
184
+ .entry(token)
185
+ .or_insert_with(HashSet::new)
186
+ .insert(record_id.to_string());
187
+ }
188
+ }
189
+
190
+ fn remove(&mut self, record_id: &str, text: &str) {
191
+ let tokens = Self::tokenize(text);
192
+ for token in tokens {
193
+ if let Some(ids) = self.token_index.get_mut(&token) {
194
+ ids.remove(record_id);
195
+ if ids.is_empty() {
196
+ self.token_index.remove(&token);
197
+ }
198
+ }
199
+ }
200
+ }
201
+
202
+ fn query(&self, search_text: &str) -> Vec<String> {
203
+ let tokens = Self::tokenize(search_text);
204
+ if tokens.is_empty() {
205
+ return Vec::new();
206
+ }
207
+
208
+ let mut result = self
209
+ .token_index
210
+ .get(&tokens[0])
211
+ .cloned()
212
+ .unwrap_or_default();
213
+
214
+ for token in tokens.iter().skip(1) {
215
+ if let Some(token_ids) = self.token_index.get(token) {
216
+ result.retain(|id| token_ids.contains(id));
217
+ } else {
218
+ return Vec::new();
219
+ }
220
+ }
221
+
222
+ result.into_iter().collect()
223
+ }
224
+
225
+ fn size(&self) -> usize {
226
+ self.token_index.len()
227
+ }
228
+
229
+ fn tokenize(text: &str) -> Vec<String> {
230
+ text.to_lowercase()
231
+ .split_whitespace()
232
+ .map(|t| t.to_string())
233
+ .collect()
234
+ }
235
+ }
236
+
237
+ #[derive(Debug, Clone)]
238
+ struct CompoundIndexInner {
239
+ entries: BTreeMap<String, HashSet<String>>,
240
+ fields: Vec<String>,
241
+ }
242
+
243
+ impl CompoundIndexInner {
244
+ fn new(fields: Vec<String>) -> Self {
245
+ CompoundIndexInner {
246
+ entries: BTreeMap::new(),
247
+ fields,
248
+ }
249
+ }
250
+
251
+ fn add(&mut self, record_id: &str, values: Vec<&str>) {
252
+ let key = CompoundKey::from_values(&values).to_key();
253
+ self.entries
254
+ .entry(key)
255
+ .or_insert_with(HashSet::new)
256
+ .insert(record_id.to_string());
257
+ }
258
+
259
+ fn remove(&mut self, record_id: &str, values: Vec<&str>) {
260
+ let key = CompoundKey::from_values(&values).to_key();
261
+ if let Some(ids) = self.entries.get_mut(&key) {
262
+ ids.remove(record_id);
263
+ if ids.is_empty() {
264
+ self.entries.remove(&key);
265
+ }
266
+ }
267
+ }
268
+
269
+ fn query_exact(&self, values: Vec<&str>) -> Vec<String> {
270
+ let key = CompoundKey::from_values(&values).to_key();
271
+ self.entries
272
+ .get(&key)
273
+ .map(|ids| ids.iter().cloned().collect())
274
+ .unwrap_or_default()
275
+ }
276
+
277
+ fn query_prefix(&self, prefix_values: Vec<&str>) -> Vec<String> {
278
+ let prefix = CompoundKey::from_values(&prefix_values).to_key();
279
+ let mut result = HashSet::new();
280
+ for (key, ids) in self.entries.range(prefix.clone()..) {
281
+ if !key.starts_with(&prefix) {
282
+ break;
283
+ }
284
+ for id in ids {
285
+ result.insert(id.clone());
286
+ }
287
+ }
288
+ result.into_iter().collect()
289
+ }
290
+
291
+ fn size(&self) -> usize {
292
+ self.entries.len()
293
+ }
294
+ }
295
+
296
+ #[derive(Debug, Clone)]
297
+ enum IndexInner {
298
+ Hash(HashIndexInner),
299
+ Sorted(SortedIndexInner),
300
+ Compound(CompoundIndexInner),
301
+ Text(TextIndexInner),
302
+ }
303
+
304
+ #[derive(Debug, Clone)]
305
+ struct IndexEntry {
306
+ inner: IndexInner,
307
+ config: IndexConfig,
308
+ reads: u64,
309
+ writes: u64,
310
+ }
311
+
312
+ pub struct IndexManager {
313
+ indexes: HashMap<String, IndexEntry>,
314
+ }
315
+
316
+ impl IndexManager {
317
+ pub fn new() -> Self {
318
+ IndexManager {
319
+ indexes: HashMap::new(),
320
+ }
321
+ }
322
+
323
+ pub fn create_index(&mut self, config: IndexConfig) -> Result<(), String> {
324
+ if self.indexes.contains_key(&config.name) {
325
+ return Err(format!("Index {} already exists", config.name));
326
+ }
327
+
328
+ let inner = match config.index_type {
329
+ IndexType::Hash => IndexInner::Hash(HashIndexInner::new()),
330
+ IndexType::Sorted => IndexInner::Sorted(SortedIndexInner::new()),
331
+ IndexType::Compound => {
332
+ if let Some(fields) = &config.fields {
333
+ IndexInner::Compound(CompoundIndexInner::new(fields.clone()))
334
+ } else {
335
+ return Err("Compound index requires 'fields' configuration".to_string());
336
+ }
337
+ }
338
+ IndexType::Text => IndexInner::Text(TextIndexInner::new()),
339
+ };
340
+
341
+ self.indexes.insert(
342
+ config.name.clone(),
343
+ IndexEntry {
344
+ inner,
345
+ config,
346
+ reads: 0,
347
+ writes: 0,
348
+ },
349
+ );
350
+
351
+ Ok(())
352
+ }
353
+
354
+ pub fn update_record(
355
+ &mut self,
356
+ record_id: &str,
357
+ old_value: Option<&str>,
358
+ new_value: &str,
359
+ ) -> Result<(), String> {
360
+ // Parse new_value as JSON to extract fields for compound indexes
361
+ let new_obj: Option<serde_json::Value> = serde_json::from_str(new_value).ok();
362
+ let old_obj: Option<serde_json::Value> =
363
+ old_value.and_then(|v| serde_json::from_str(v).ok());
364
+
365
+ for entry in self.indexes.values_mut() {
366
+ if let Some(old) = old_value {
367
+ match &mut entry.inner {
368
+ IndexInner::Hash(idx) => idx.remove(record_id, old),
369
+ IndexInner::Sorted(idx) => idx.remove(record_id, old),
370
+ IndexInner::Compound(idx) => {
371
+ if let Some(ref obj) = old_obj {
372
+ let values: Vec<&str> = idx
373
+ .fields
374
+ .iter()
375
+ .filter_map(|field| {
376
+ obj.get(field).and_then(|v| v.as_str())
377
+ })
378
+ .collect();
379
+ if values.len() == idx.fields.len() {
380
+ idx.remove(record_id, values);
381
+ }
382
+ }
383
+ }
384
+ IndexInner::Text(idx) => idx.remove(record_id, old),
385
+ }
386
+ }
387
+
388
+ match &mut entry.inner {
389
+ IndexInner::Hash(idx) => idx.add(record_id, new_value),
390
+ IndexInner::Sorted(idx) => idx.add(record_id, new_value),
391
+ IndexInner::Compound(idx) => {
392
+ if let Some(ref obj) = new_obj {
393
+ let values: Vec<&str> = idx
394
+ .fields
395
+ .iter()
396
+ .filter_map(|field| {
397
+ obj.get(field).and_then(|v| v.as_str())
398
+ })
399
+ .collect();
400
+ if values.len() == idx.fields.len() {
401
+ idx.add(record_id, values);
402
+ }
403
+ }
404
+ }
405
+ IndexInner::Text(idx) => idx.add(record_id, new_value),
406
+ }
407
+
408
+ entry.writes += 1;
409
+ }
410
+
411
+ Ok(())
412
+ }
413
+
414
+ pub fn query_index(
415
+ &mut self,
416
+ index_name: &str,
417
+ operator: &str,
418
+ value: &str,
419
+ ) -> Result<Vec<String>, String> {
420
+ let entry = self
421
+ .indexes
422
+ .get_mut(index_name)
423
+ .ok_or_else(|| format!("Index {} not found", index_name))?;
424
+
425
+ entry.reads += 1;
426
+
427
+ let result = match &entry.inner {
428
+ IndexInner::Hash(_) => {
429
+ if operator == "eq" {
430
+ match &entry.inner {
431
+ IndexInner::Hash(idx) => idx.query(value),
432
+ _ => unreachable!(),
433
+ }
434
+ } else {
435
+ return Err("Hash index only supports eq operator".to_string());
436
+ }
437
+ }
438
+ IndexInner::Sorted(_) => {
439
+ match operator {
440
+ "eq" => match &entry.inner {
441
+ IndexInner::Sorted(idx) => idx.query_equal(value),
442
+ _ => unreachable!(),
443
+ },
444
+ "range" => {
445
+ // For range, value should be "min,max"
446
+ let parts: Vec<&str> = value.split(',').collect();
447
+ if parts.len() != 2 {
448
+ return Err("Range query requires min,max format".to_string());
449
+ }
450
+ match &entry.inner {
451
+ IndexInner::Sorted(idx) => idx.query_range(parts[0], parts[1]),
452
+ _ => unreachable!(),
453
+ }
454
+ }
455
+ _ => return Err(format!("Sorted index does not support {} operator", operator)),
456
+ }
457
+ }
458
+ IndexInner::Compound(_) => {
459
+ match operator {
460
+ "eq" => {
461
+ // For exact match, value contains comma-separated field values
462
+ let values: Vec<&str> = value.split('\0').collect();
463
+ match &entry.inner {
464
+ IndexInner::Compound(idx) => idx.query_exact(values),
465
+ _ => unreachable!(),
466
+ }
467
+ }
468
+ "prefix" => {
469
+ // For prefix match (e.g., status=active,*)
470
+ let values: Vec<&str> = value.split('\0').collect();
471
+ match &entry.inner {
472
+ IndexInner::Compound(idx) => idx.query_prefix(values),
473
+ _ => unreachable!(),
474
+ }
475
+ }
476
+ _ => {
477
+ return Err(format!(
478
+ "Compound index does not support {} operator",
479
+ operator
480
+ ))
481
+ }
482
+ }
483
+ }
484
+ IndexInner::Text(_) => {
485
+ if operator == "contains" {
486
+ match &entry.inner {
487
+ IndexInner::Text(idx) => idx.query(value),
488
+ _ => unreachable!(),
489
+ }
490
+ } else {
491
+ return Err("Text index only supports contains operator".to_string());
492
+ }
493
+ }
494
+ };
495
+
496
+ Ok(result)
497
+ }
498
+
499
+ pub fn list_indexes(&self) -> Vec<IndexConfig> {
500
+ self.indexes.values().map(|e| e.config.clone()).collect()
501
+ }
502
+
503
+ pub fn get_stats(&self, name: &str) -> Option<IndexStats> {
504
+ self.indexes.get(name).map(|e| IndexStats {
505
+ name: e.config.name.clone(),
506
+ index_type: e.config.index_type.clone(),
507
+ entries: match &e.inner {
508
+ IndexInner::Hash(idx) => idx.size(),
509
+ IndexInner::Sorted(idx) => idx.size(),
510
+ IndexInner::Compound(idx) => idx.size(),
511
+ IndexInner::Text(idx) => idx.size(),
512
+ },
513
+ size: 0, // TODO: implement size calculation
514
+ last_updated: 0, // TODO: track timestamp
515
+ reads: e.reads,
516
+ writes: e.writes,
517
+ selectivity: 0.5, // TODO: implement selectivity calculation
518
+ })
519
+ }
520
+ }
521
+
522
+ impl IndexManager {
523
+ pub fn query_with_pagination(
524
+ &mut self,
525
+ index_name: &str,
526
+ operator: &str,
527
+ value: &str,
528
+ params: PaginationParams,
529
+ ) -> Result<QueryResult, String> {
530
+ let all_ids = self.query_index(index_name, operator, value)?;
531
+ let total_count = all_ids.len();
532
+
533
+ let start = params.offset.min(total_count);
534
+ let end = (params.offset + params.limit).min(total_count);
535
+
536
+ let ids = all_ids[start..end].to_vec();
537
+ let has_more = end < total_count;
538
+
539
+ Ok(QueryResult {
540
+ ids,
541
+ total_count,
542
+ limit: params.limit,
543
+ offset: params.offset,
544
+ has_more,
545
+ })
546
+ }
547
+
548
+ pub fn plan_query(
549
+ &self,
550
+ index_name: &str,
551
+ operator: &str,
552
+ ) -> Result<QueryPlan, String> {
553
+ let entry = self
554
+ .indexes
555
+ .get(index_name)
556
+ .ok_or_else(|| format!("Index {} not found", index_name))?;
557
+
558
+ let total_entries = match &entry.inner {
559
+ IndexInner::Hash(idx) => idx.size(),
560
+ IndexInner::Sorted(idx) => idx.size(),
561
+ IndexInner::Compound(idx) => idx.size(),
562
+ IndexInner::Text(idx) => idx.size(),
563
+ };
564
+
565
+ let selectivity = match operator {
566
+ "eq" => 0.1,
567
+ "range" => 0.3,
568
+ "prefix" => 0.25,
569
+ "contains" => 0.15,
570
+ _ => 0.5,
571
+ };
572
+
573
+ let estimated_rows = ((total_entries as f64) * selectivity).ceil() as usize;
574
+ let cost = (1.0 / selectivity).ceil() as u64;
575
+
576
+ Ok(QueryPlan {
577
+ index_name: index_name.to_string(),
578
+ operator: operator.to_string(),
579
+ selectivity,
580
+ estimated_rows,
581
+ cost,
582
+ })
583
+ }
584
+
585
+ pub fn count_indexed(&mut self, index_name: &str, operator: &str, value: &str) -> Result<usize, String> {
586
+ let results = self.query_index(index_name, operator, value)?;
587
+ Ok(results.len())
588
+ }
589
+
590
+ pub fn exists_indexed(&mut self, index_name: &str, operator: &str, value: &str) -> Result<bool, String> {
591
+ let count = self.count_indexed(index_name, operator, value)?;
592
+ Ok(count > 0)
593
+ }
594
+ }
595
+
596
+ impl Default for IndexManager {
597
+ fn default() -> Self {
598
+ Self::new()
599
+ }
600
+ }
601
+
602
+ #[cfg(test)]
603
+ mod tests {
604
+ use super::*;
605
+
606
+ #[test]
607
+ fn test_hash_index() {
608
+ let mut manager = IndexManager::new();
609
+ manager
610
+ .create_index(IndexConfig {
611
+ name: "status_idx".to_string(),
612
+ index_type: IndexType::Hash,
613
+ field: Some("status".to_string()),
614
+ fields: None,
615
+ text_fields: None,
616
+ inmemory: Some(true),
617
+ persistent: Some(false),
618
+ description: None,
619
+ })
620
+ .unwrap();
621
+
622
+ manager
623
+ .update_record("1", None, "active")
624
+ .unwrap();
625
+ manager
626
+ .update_record("2", None, "inactive")
627
+ .unwrap();
628
+ manager
629
+ .update_record("3", None, "active")
630
+ .unwrap();
631
+
632
+ let results = manager
633
+ .query_index("status_idx", "eq", "active")
634
+ .unwrap();
635
+ assert_eq!(results.len(), 2);
636
+ }
637
+
638
+ #[test]
639
+ fn test_sorted_index() {
640
+ let mut manager = IndexManager::new();
641
+ manager
642
+ .create_index(IndexConfig {
643
+ name: "created_idx".to_string(),
644
+ index_type: IndexType::Sorted,
645
+ field: Some("created_at".to_string()),
646
+ fields: None,
647
+ text_fields: None,
648
+ inmemory: Some(true),
649
+ persistent: Some(false),
650
+ description: None,
651
+ })
652
+ .unwrap();
653
+
654
+ manager
655
+ .update_record("1", None, "2024-01-01")
656
+ .unwrap();
657
+ manager
658
+ .update_record("2", None, "2024-02-01")
659
+ .unwrap();
660
+ manager
661
+ .update_record("3", None, "2024-03-01")
662
+ .unwrap();
663
+
664
+ let results = manager
665
+ .query_index("created_idx", "range", "2024-01-01,2024-02-01")
666
+ .unwrap();
667
+ assert_eq!(results.len(), 2);
668
+ }
669
+
670
+ #[test]
671
+ fn test_pagination() {
672
+ let mut manager = IndexManager::new();
673
+ manager
674
+ .create_index(IndexConfig {
675
+ name: "status_idx".to_string(),
676
+ index_type: IndexType::Hash,
677
+ field: Some("status".to_string()),
678
+ fields: None,
679
+ text_fields: None,
680
+ inmemory: Some(true),
681
+ persistent: Some(false),
682
+ description: None,
683
+ })
684
+ .unwrap();
685
+
686
+ for i in 1..=10 {
687
+ manager
688
+ .update_record(&i.to_string(), None, "active")
689
+ .unwrap();
690
+ }
691
+
692
+ let result = manager
693
+ .query_with_pagination(
694
+ "status_idx",
695
+ "eq",
696
+ "active",
697
+ PaginationParams {
698
+ limit: 3,
699
+ offset: 0,
700
+ },
701
+ )
702
+ .unwrap();
703
+
704
+ assert_eq!(result.ids.len(), 3);
705
+ assert_eq!(result.total_count, 10);
706
+ assert!(result.has_more);
707
+ }
708
+
709
+ #[test]
710
+ fn test_count_indexed() {
711
+ let mut manager = IndexManager::new();
712
+ manager
713
+ .create_index(IndexConfig {
714
+ name: "status_idx".to_string(),
715
+ index_type: IndexType::Hash,
716
+ field: Some("status".to_string()),
717
+ fields: None,
718
+ text_fields: None,
719
+ inmemory: Some(true),
720
+ persistent: Some(false),
721
+ description: None,
722
+ })
723
+ .unwrap();
724
+
725
+ for i in 1..=5 {
726
+ manager
727
+ .update_record(&i.to_string(), None, "active")
728
+ .unwrap();
729
+ }
730
+
731
+ let count = manager.count_indexed("status_idx", "eq", "active").unwrap();
732
+ assert_eq!(count, 5);
733
+ }
734
+
735
+ #[test]
736
+ fn test_exists_indexed() {
737
+ let mut manager = IndexManager::new();
738
+ manager
739
+ .create_index(IndexConfig {
740
+ name: "status_idx".to_string(),
741
+ index_type: IndexType::Hash,
742
+ field: Some("status".to_string()),
743
+ fields: None,
744
+ text_fields: None,
745
+ inmemory: Some(true),
746
+ persistent: Some(false),
747
+ description: None,
748
+ })
749
+ .unwrap();
750
+
751
+ manager.update_record("1", None, "active").unwrap();
752
+
753
+ assert!(manager.exists_indexed("status_idx", "eq", "active").unwrap());
754
+ assert!(!manager.exists_indexed("status_idx", "eq", "inactive").unwrap());
755
+ }
756
+
757
+ #[test]
758
+ fn test_query_plan() {
759
+ let mut manager = IndexManager::new();
760
+ manager
761
+ .create_index(IndexConfig {
762
+ name: "status_idx".to_string(),
763
+ index_type: IndexType::Hash,
764
+ field: Some("status".to_string()),
765
+ fields: None,
766
+ text_fields: None,
767
+ inmemory: Some(true),
768
+ persistent: Some(false),
769
+ description: None,
770
+ })
771
+ .unwrap();
772
+
773
+ let plan = manager.plan_query("status_idx", "eq").unwrap();
774
+ assert_eq!(plan.index_name, "status_idx");
775
+ assert_eq!(plan.operator, "eq");
776
+ assert!(plan.selectivity > 0.0);
777
+ assert!(plan.cost > 0);
778
+ }
779
+ }