@feltdb/core 0.4.12 → 0.4.14

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 (165) hide show
  1. package/dist/cli/commands.js +42 -25
  2. package/dist/cli/index.js +1 -1
  3. package/dist/create/cli.js +39 -4
  4. package/dist/create/create.js +30 -18
  5. package/dist/create/docker-compose-generator.js +68 -11
  6. package/dist/create/package-versions.js +1 -1
  7. package/dist/create/server-source/Cargo.lock +2238 -0
  8. package/dist/create/server-source/Cargo.toml +7 -0
  9. package/dist/create/server-source/crates/feltdb/Cargo.lock +175 -0
  10. package/dist/create/server-source/crates/feltdb/Cargo.toml +79 -0
  11. package/dist/create/server-source/crates/feltdb/benches/baselines/gate-13-redux.json +370 -0
  12. package/dist/create/server-source/crates/feltdb/benches/gate13_baseline.rs +589 -0
  13. package/dist/create/server-source/crates/feltdb/benches/gate13_phase_7_1_release_economics.rs +259 -0
  14. package/dist/create/server-source/crates/feltdb/benches/gate_13_redux.rs +446 -0
  15. package/dist/create/server-source/crates/feltdb/benches/gate_13_regression_runner.rs +272 -0
  16. package/dist/create/server-source/crates/feltdb/benches/gate_14a_concurrent_writer_scaling.rs +378 -0
  17. package/dist/create/server-source/crates/feltdb/benches/gate_14a_production_admission_revalidation.rs +414 -0
  18. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc2_admission_contract.rs +486 -0
  19. package/dist/create/server-source/crates/feltdb/benches/gate_14a_rc_root_cause.rs +273 -0
  20. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync1_queued_prototype.rs +587 -0
  21. package/dist/create/server-source/crates/feltdb/benches/gate_14a_sync_economics.rs +513 -0
  22. package/dist/create/server-source/crates/feltdb/benches/gate_14b_causal_backlog_scaling.rs +395 -0
  23. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_contract_test.rs +469 -0
  24. package/dist/create/server-source/crates/feltdb/benches/gate_14c_replication_scaling.rs +409 -0
  25. package/dist/create/server-source/crates/feltdb/benches/gate_14d_combined_dimension_scaling.rs +627 -0
  26. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_2_optimization_benchmark.rs +383 -0
  27. package/dist/create/server-source/crates/feltdb/benches/phase_7_1_3_crossover_analysis.rs +298 -0
  28. package/dist/create/server-source/crates/feltdb/src/acceptance_tests.rs +1698 -0
  29. package/dist/create/server-source/crates/feltdb/src/acquisition.rs +286 -0
  30. package/dist/create/server-source/crates/feltdb/src/admission.rs +192 -0
  31. package/dist/create/server-source/crates/feltdb/src/admission_contract_tests.rs +477 -0
  32. package/dist/create/server-source/crates/feltdb/src/adversarial_transport.rs +566 -0
  33. package/dist/create/server-source/crates/feltdb/src/analytics.rs +475 -0
  34. package/dist/create/server-source/crates/feltdb/src/application.rs +2244 -0
  35. package/dist/create/server-source/crates/feltdb/src/application_runtime.rs +1070 -0
  36. package/dist/create/server-source/crates/feltdb/src/authorization.rs +1030 -0
  37. package/dist/create/server-source/crates/feltdb/src/bin/feltdb_node.rs +266 -0
  38. package/dist/create/server-source/crates/feltdb/src/capabilities/mod.rs +8 -0
  39. package/dist/create/server-source/crates/feltdb/src/capabilities/search.rs +418 -0
  40. package/dist/create/server-source/crates/feltdb/src/capabilities/vector.rs +482 -0
  41. package/dist/create/server-source/crates/feltdb/src/capability.rs +903 -0
  42. package/dist/create/server-source/crates/feltdb/src/cardinality_diagnostics.rs +261 -0
  43. package/dist/create/server-source/crates/feltdb/src/cardinality_endpoint.rs +78 -0
  44. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier.rs +2214 -0
  45. package/dist/create/server-source/crates/feltdb/src/causal_dependency_barrier_phase_7_1.rs +194 -0
  46. package/dist/create/server-source/crates/feltdb/src/concurrency_fuzzing.rs +427 -0
  47. package/dist/create/server-source/crates/feltdb/src/consistency_contract.rs +453 -0
  48. package/dist/create/server-source/crates/feltdb/src/content_distribution.rs +465 -0
  49. package/dist/create/server-source/crates/feltdb/src/convergence.rs +618 -0
  50. package/dist/create/server-source/crates/feltdb/src/crash_atomic_boundary.rs +418 -0
  51. package/dist/create/server-source/crates/feltdb/src/crash_injection.rs +380 -0
  52. package/dist/create/server-source/crates/feltdb/src/crash_recovery_tests.rs +362 -0
  53. package/dist/create/server-source/crates/feltdb/src/cron.rs +294 -0
  54. package/dist/create/server-source/crates/feltdb/src/distributed_indexing.rs +474 -0
  55. package/dist/create/server-source/crates/feltdb/src/distributed_tests.rs +1003 -0
  56. package/dist/create/server-source/crates/feltdb/src/distributed_transactions.rs +536 -0
  57. package/dist/create/server-source/crates/feltdb/src/durability_guarantees.rs +364 -0
  58. package/dist/create/server-source/crates/feltdb/src/durable_dedup_set.rs +235 -0
  59. package/dist/create/server-source/crates/feltdb/src/durable_operation_log.rs +207 -0
  60. package/dist/create/server-source/crates/feltdb/src/durable_sync.rs +316 -0
  61. package/dist/create/server-source/crates/feltdb/src/execution.rs +426 -0
  62. package/dist/create/server-source/crates/feltdb/src/in_process_transport.rs +219 -0
  63. package/dist/create/server-source/crates/feltdb/src/indexing.rs +779 -0
  64. package/dist/create/server-source/crates/feltdb/src/lib.rs +2838 -0
  65. package/dist/create/server-source/crates/feltdb/src/materialization.rs +184 -0
  66. package/dist/create/server-source/crates/feltdb/src/metrics.rs +267 -0
  67. package/dist/create/server-source/crates/feltdb/src/multi_node_convergence.rs +311 -0
  68. package/dist/create/server-source/crates/feltdb/src/observability.rs +366 -0
  69. package/dist/create/server-source/crates/feltdb/src/operation.rs +251 -0
  70. package/dist/create/server-source/crates/feltdb/src/operation_algebra.rs +438 -0
  71. package/dist/create/server-source/crates/feltdb/src/operation_log.rs +344 -0
  72. package/dist/create/server-source/crates/feltdb/src/partition_reconciliation.rs +477 -0
  73. package/dist/create/server-source/crates/feltdb/src/peer_registry.rs +166 -0
  74. package/dist/create/server-source/crates/feltdb/src/permutation_scheduler.rs +261 -0
  75. package/dist/create/server-source/crates/feltdb/src/persistence_reality.rs +560 -0
  76. package/dist/create/server-source/crates/feltdb/src/phase1b_acceptance.rs +3226 -0
  77. package/dist/create/server-source/crates/feltdb/src/phase1c1_acceptance.rs +201 -0
  78. package/dist/create/server-source/crates/feltdb/src/phase1c2_acceptance.rs +263 -0
  79. package/dist/create/server-source/crates/feltdb/src/phase1c3_acceptance.rs +484 -0
  80. package/dist/create/server-source/crates/feltdb/src/phase1c_atomicity_proof.rs +216 -0
  81. package/dist/create/server-source/crates/feltdb/src/phase5_integration.rs +281 -0
  82. package/dist/create/server-source/crates/feltdb/src/phase5_scenarios.rs +323 -0
  83. package/dist/create/server-source/crates/feltdb/src/phase6_adversarial_scenarios.rs +573 -0
  84. package/dist/create/server-source/crates/feltdb/src/phase6_convergence_validator.rs +404 -0
  85. package/dist/create/server-source/crates/feltdb/src/phase6_persistence.rs +418 -0
  86. package/dist/create/server-source/crates/feltdb/src/phase_1c_real_tcp.rs +381 -0
  87. package/dist/create/server-source/crates/feltdb/src/phase_1c_three_node.rs +523 -0
  88. package/dist/create/server-source/crates/feltdb/src/phase_2a_failures.rs +334 -0
  89. package/dist/create/server-source/crates/feltdb/src/phase_2b_network.rs +306 -0
  90. package/dist/create/server-source/crates/feltdb/src/phase_2c_cascading.rs +355 -0
  91. package/dist/create/server-source/crates/feltdb/src/phase_3_durability.rs +395 -0
  92. package/dist/create/server-source/crates/feltdb/src/phase_4_baseline.rs +346 -0
  93. package/dist/create/server-source/crates/feltdb/src/phase_5_soak.rs +430 -0
  94. package/dist/create/server-source/crates/feltdb/src/production_api.rs +400 -0
  95. package/dist/create/server-source/crates/feltdb/src/provenance.rs +207 -0
  96. package/dist/create/server-source/crates/feltdb/src/query_performance.rs +217 -0
  97. package/dist/create/server-source/crates/feltdb/src/references.rs +404 -0
  98. package/dist/create/server-source/crates/feltdb/src/replay_fuzzing.rs +401 -0
  99. package/dist/create/server-source/crates/feltdb/src/replication_manager.rs +160 -0
  100. package/dist/create/server-source/crates/feltdb/src/replication_protocol.rs +132 -0
  101. package/dist/create/server-source/crates/feltdb/src/routing.rs +409 -0
  102. package/dist/create/server-source/crates/feltdb/src/sharding.rs +523 -0
  103. package/dist/create/server-source/crates/feltdb/src/state_contract.rs +3118 -0
  104. package/dist/create/server-source/crates/feltdb/src/state_hash.rs +291 -0
  105. package/dist/create/server-source/crates/feltdb/src/state_transition_store.rs +376 -0
  106. package/dist/create/server-source/crates/feltdb/src/storage.rs +267 -0
  107. package/dist/create/server-source/crates/feltdb/src/submission.rs +477 -0
  108. package/dist/create/server-source/crates/feltdb/src/sync.rs +483 -0
  109. package/dist/create/server-source/crates/feltdb/src/sync_contract.rs +822 -0
  110. package/dist/create/server-source/crates/feltdb/src/tcp_transport.rs +366 -0
  111. package/dist/create/server-source/crates/feltdb/src/transaction_api.rs +473 -0
  112. package/dist/create/server-source/crates/feltdb/src/transaction_invariants.rs +949 -0
  113. package/dist/create/server-source/crates/feltdb/src/transactions.rs +646 -0
  114. package/dist/create/server-source/crates/feltdb/src/trigger.rs +390 -0
  115. package/dist/create/server-source/crates/feltdb/src/worker_mesh.rs +928 -0
  116. package/dist/create/server-source/crates/feltdb/src/workflow.rs +713 -0
  117. package/dist/create/server-source/crates/feltdb/src/workflow_acceptance_tests.rs +510 -0
  118. package/dist/create/server-source/crates/feltdb/src/workflow_integration.rs +354 -0
  119. package/dist/create/server-source/crates/feltdb/src/workflow_runtime.rs +594 -0
  120. package/dist/create/server-source/crates/feltdb/src/workload.rs +1310 -0
  121. package/dist/create/server-source/crates/feltdb-server/Cargo.toml +23 -0
  122. package/dist/create/server-source/crates/feltdb-server/README.md +79 -0
  123. package/dist/create/server-source/crates/feltdb-server/src/app_state.rs +73 -0
  124. package/dist/create/server-source/crates/feltdb-server/src/application_contract.rs +425 -0
  125. package/dist/create/server-source/crates/feltdb-server/src/artifacts.rs +449 -0
  126. package/dist/create/server-source/crates/feltdb-server/src/audit.rs +59 -0
  127. package/dist/create/server-source/crates/feltdb-server/src/auth.rs +308 -0
  128. package/dist/create/server-source/crates/feltdb-server/src/authorization.rs +228 -0
  129. package/dist/create/server-source/crates/feltdb-server/src/backup.rs +416 -0
  130. package/dist/create/server-source/crates/feltdb-server/src/causal.rs +218 -0
  131. package/dist/create/server-source/crates/feltdb-server/src/certification.rs +223 -0
  132. package/dist/create/server-source/crates/feltdb-server/src/clock.rs +132 -0
  133. package/dist/create/server-source/crates/feltdb-server/src/cluster.rs +165 -0
  134. package/dist/create/server-source/crates/feltdb-server/src/connections.rs +1044 -0
  135. package/dist/create/server-source/crates/feltdb-server/src/content.rs +111 -0
  136. package/dist/create/server-source/crates/feltdb-server/src/identity.rs +250 -0
  137. package/dist/create/server-source/crates/feltdb-server/src/key_management.rs +78 -0
  138. package/dist/create/server-source/crates/feltdb-server/src/key_provider.rs +247 -0
  139. package/dist/create/server-source/crates/feltdb-server/src/leases.rs +123 -0
  140. package/dist/create/server-source/crates/feltdb-server/src/lib.rs +25 -0
  141. package/dist/create/server-source/crates/feltdb-server/src/main.rs +8715 -0
  142. package/dist/create/server-source/crates/feltdb-server/src/metrics.rs +97 -0
  143. package/dist/create/server-source/crates/feltdb-server/src/portable_bundle.rs +350 -0
  144. package/dist/create/server-source/crates/feltdb-server/src/principals.rs +510 -0
  145. package/dist/create/server-source/crates/feltdb-server/src/providers.rs +269 -0
  146. package/dist/create/server-source/crates/feltdb-server/src/releases.rs +2563 -0
  147. package/dist/create/server-source/crates/feltdb-server/src/sessions.rs +156 -0
  148. package/dist/create/server-source/crates/feltdb-server/src/tenancy.rs +1097 -0
  149. package/dist/create/server-source/crates/feltdb-server/src/versions.rs +264 -0
  150. package/dist/create/server-source/crates/feltdb-wasm/Cargo.toml +31 -0
  151. package/dist/create/server-source/crates/feltdb-wasm/src/lib.rs +1086 -0
  152. package/dist/create/server-source/crates/feltdb-wasm/test.db +0 -0
  153. package/dist/flowspec.d.ts +2 -0
  154. package/dist/flowspec.d.ts.map +1 -1
  155. package/dist/flowspec.js +28 -4
  156. package/dist/state-contract.d.ts +7 -1
  157. package/dist/state-contract.d.ts.map +1 -1
  158. package/dist/studio/components/ApplicationDesigner.d.ts.map +1 -1
  159. package/dist/studio/components/ConflictExplorer.d.ts.map +1 -1
  160. package/dist/studio/components/index.js +1 -1
  161. package/dist/studio/{components-Q0Nx6gLE.js → components-9kDSWiGL.js} +163 -47
  162. package/dist/studio/index.js +31 -28
  163. package/dist/studio-app/assets/{index-BHOmLZF-.js → index-DVdvmf69.js} +9 -9
  164. package/dist/studio-app/index.html +1 -1
  165. package/package.json +1 -1
@@ -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
+ }