mongory 0.7.3-aarch64-linux-musl

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.

Potentially problematic release.


This version of mongory might be problematic. Click here for more details.

Files changed (115) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/.rubocop.yml +88 -0
  4. data/.yardopts +7 -0
  5. data/CHANGELOG.md +364 -0
  6. data/CODE_OF_CONDUCT.md +84 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +488 -0
  9. data/Rakefile +107 -0
  10. data/SUBMODULE_INTEGRATION.md +325 -0
  11. data/docs/advanced_usage.md +40 -0
  12. data/docs/clang_bridge.md +69 -0
  13. data/docs/field_names.md +30 -0
  14. data/docs/migration.md +30 -0
  15. data/docs/performance.md +61 -0
  16. data/examples/README.md +41 -0
  17. data/examples/benchmark-rails.rb +52 -0
  18. data/examples/benchmark.rb +184 -0
  19. data/ext/mongory_ext/extconf.rb +91 -0
  20. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/array.h +122 -0
  21. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/config.h +161 -0
  22. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/error.h +79 -0
  23. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/memory_pool.h +95 -0
  24. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/table.h +127 -0
  25. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/value.h +175 -0
  26. data/ext/mongory_ext/mongory-core/include/mongory-core/matchers/matcher.h +76 -0
  27. data/ext/mongory_ext/mongory-core/include/mongory-core.h +12 -0
  28. data/ext/mongory_ext/mongory-core/src/foundations/array.c +287 -0
  29. data/ext/mongory_ext/mongory-core/src/foundations/array_private.h +19 -0
  30. data/ext/mongory_ext/mongory-core/src/foundations/config.c +270 -0
  31. data/ext/mongory_ext/mongory-core/src/foundations/config_private.h +48 -0
  32. data/ext/mongory_ext/mongory-core/src/foundations/error.c +38 -0
  33. data/ext/mongory_ext/mongory-core/src/foundations/memory_pool.c +298 -0
  34. data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.c +65 -0
  35. data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.h +49 -0
  36. data/ext/mongory_ext/mongory-core/src/foundations/table.c +498 -0
  37. data/ext/mongory_ext/mongory-core/src/foundations/utils.c +210 -0
  38. data/ext/mongory_ext/mongory-core/src/foundations/utils.h +70 -0
  39. data/ext/mongory_ext/mongory-core/src/foundations/value.c +500 -0
  40. data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.c +164 -0
  41. data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.h +47 -0
  42. data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.c +122 -0
  43. data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.h +100 -0
  44. data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.c +217 -0
  45. data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.h +83 -0
  46. data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.c +573 -0
  47. data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.h +125 -0
  48. data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.c +147 -0
  49. data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.h +48 -0
  50. data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.c +124 -0
  51. data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.h +46 -0
  52. data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.c +126 -0
  53. data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.h +46 -0
  54. data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.c +314 -0
  55. data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.h +97 -0
  56. data/ext/mongory_ext/mongory-core/src/matchers/matcher.c +252 -0
  57. data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.c +79 -0
  58. data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.h +23 -0
  59. data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.c +60 -0
  60. data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.h +23 -0
  61. data/ext/mongory_ext/mongory_ext.c +683 -0
  62. data/lib/generators/mongory/install/install_generator.rb +42 -0
  63. data/lib/generators/mongory/install/templates/initializer.rb.erb +83 -0
  64. data/lib/generators/mongory/matcher/matcher_generator.rb +56 -0
  65. data/lib/generators/mongory/matcher/templates/matcher.rb.erb +92 -0
  66. data/lib/generators/mongory/matcher/templates/matcher_spec.rb.erb +17 -0
  67. data/lib/mongory/c_query_builder.rb +44 -0
  68. data/lib/mongory/converters/abstract_converter.rb +111 -0
  69. data/lib/mongory/converters/condition_converter.rb +64 -0
  70. data/lib/mongory/converters/converted.rb +81 -0
  71. data/lib/mongory/converters/data_converter.rb +37 -0
  72. data/lib/mongory/converters/key_converter.rb +87 -0
  73. data/lib/mongory/converters/value_converter.rb +52 -0
  74. data/lib/mongory/converters.rb +8 -0
  75. data/lib/mongory/matchers/abstract_matcher.rb +219 -0
  76. data/lib/mongory/matchers/abstract_multi_matcher.rb +124 -0
  77. data/lib/mongory/matchers/and_matcher.rb +72 -0
  78. data/lib/mongory/matchers/array_record_matcher.rb +93 -0
  79. data/lib/mongory/matchers/elem_match_matcher.rb +55 -0
  80. data/lib/mongory/matchers/eq_matcher.rb +46 -0
  81. data/lib/mongory/matchers/every_matcher.rb +56 -0
  82. data/lib/mongory/matchers/exists_matcher.rb +53 -0
  83. data/lib/mongory/matchers/field_matcher.rb +147 -0
  84. data/lib/mongory/matchers/gt_matcher.rb +41 -0
  85. data/lib/mongory/matchers/gte_matcher.rb +41 -0
  86. data/lib/mongory/matchers/hash_condition_matcher.rb +62 -0
  87. data/lib/mongory/matchers/in_matcher.rb +68 -0
  88. data/lib/mongory/matchers/literal_matcher.rb +121 -0
  89. data/lib/mongory/matchers/lt_matcher.rb +41 -0
  90. data/lib/mongory/matchers/lte_matcher.rb +41 -0
  91. data/lib/mongory/matchers/ne_matcher.rb +38 -0
  92. data/lib/mongory/matchers/nin_matcher.rb +68 -0
  93. data/lib/mongory/matchers/not_matcher.rb +40 -0
  94. data/lib/mongory/matchers/or_matcher.rb +68 -0
  95. data/lib/mongory/matchers/present_matcher.rb +55 -0
  96. data/lib/mongory/matchers/regex_matcher.rb +80 -0
  97. data/lib/mongory/matchers/size_matcher.rb +54 -0
  98. data/lib/mongory/matchers.rb +176 -0
  99. data/lib/mongory/mongoid.rb +19 -0
  100. data/lib/mongory/query_builder.rb +257 -0
  101. data/lib/mongory/query_matcher.rb +93 -0
  102. data/lib/mongory/query_operator.rb +28 -0
  103. data/lib/mongory/rails.rb +15 -0
  104. data/lib/mongory/utils/context.rb +48 -0
  105. data/lib/mongory/utils/debugger.rb +125 -0
  106. data/lib/mongory/utils/rails_patch.rb +22 -0
  107. data/lib/mongory/utils/singleton_builder.rb +31 -0
  108. data/lib/mongory/utils.rb +76 -0
  109. data/lib/mongory/version.rb +5 -0
  110. data/lib/mongory.rb +123 -0
  111. data/lib/mongory_ext.so +0 -0
  112. data/mongory.gemspec +62 -0
  113. data/scripts/build_with_core.sh +292 -0
  114. data/sig/mongory.rbs +4 -0
  115. metadata +159 -0
@@ -0,0 +1,83 @@
1
+ #ifndef MONGORY_MATCHER_COMPARE_H
2
+ #define MONGORY_MATCHER_COMPARE_H
3
+
4
+ /**
5
+ * @file compare_matcher.h
6
+ * @brief Defines constructors for comparison matchers (e.g., equality, greater
7
+ * than). This is an internal header for the matcher module.
8
+ *
9
+ * These matchers compare an input `mongory_value` against a `condition` value
10
+ * stored within the matcher, using the `comp` function of the input value.
11
+ */
12
+
13
+ #include "base_matcher.h"
14
+ #include "mongory-core/foundations/memory_pool.h"
15
+ #include "mongory-core/foundations/value.h"
16
+ #include "mongory-core/matchers/matcher.h" // For mongory_matcher structure
17
+ #include <stdbool.h>
18
+
19
+ /** @name Comparison Matcher Constructors
20
+ * Functions to create instances of various comparison matchers.
21
+ * Each takes a memory pool and a condition value.
22
+ * @{
23
+ */
24
+
25
+ /**
26
+ * @brief Creates an "equal" ($eq) matcher.
27
+ * Matches if the input value is equal to the matcher's condition value.
28
+ * @param pool Memory pool for allocation.
29
+ * @param condition The `mongory_value` to compare against.
30
+ * @return A new `$eq` matcher, or NULL on failure.
31
+ */
32
+ mongory_matcher *mongory_matcher_equal_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
33
+
34
+ /**
35
+ * @brief Creates a "not equal" ($ne) matcher.
36
+ * Matches if the input value is not equal to the matcher's condition value.
37
+ * Also matches if the types are incompatible for comparison.
38
+ * @param pool Memory pool for allocation.
39
+ * @param condition The `mongory_value` to compare against.
40
+ * @return A new `$ne` matcher, or NULL on failure.
41
+ */
42
+ mongory_matcher *mongory_matcher_not_equal_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
43
+
44
+ /**
45
+ * @brief Creates a "greater than" ($gt) matcher.
46
+ * Matches if the input value is greater than the matcher's condition value.
47
+ * @param pool Memory pool for allocation.
48
+ * @param condition The `mongory_value` to compare against.
49
+ * @return A new `$gt` matcher, or NULL on failure.
50
+ */
51
+ mongory_matcher *mongory_matcher_greater_than_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
52
+
53
+ /**
54
+ * @brief Creates a "less than" ($lt) matcher.
55
+ * Matches if the input value is less than the matcher's condition value.
56
+ * @param pool Memory pool for allocation.
57
+ * @param condition The `mongory_value` to compare against.
58
+ * @return A new `$lt` matcher, or NULL on failure.
59
+ */
60
+ mongory_matcher *mongory_matcher_less_than_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
61
+
62
+ /**
63
+ * @brief Creates a "greater than or equal" ($gte) matcher.
64
+ * Matches if the input value is greater than or equal to the matcher's
65
+ * condition value.
66
+ * @param pool Memory pool for allocation.
67
+ * @param condition The `mongory_value` to compare against.
68
+ * @return A new `$gte` matcher, or NULL on failure.
69
+ */
70
+ mongory_matcher *mongory_matcher_greater_than_or_equal_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
71
+
72
+ /**
73
+ * @brief Creates a "less than or equal" ($lte) matcher.
74
+ * Matches if the input value is less than or equal to the matcher's condition
75
+ * value.
76
+ * @param pool Memory pool for allocation.
77
+ * @param condition The `mongory_value` to compare against.
78
+ * @return A new `$lte` matcher, or NULL on failure.
79
+ */
80
+ mongory_matcher *mongory_matcher_less_than_or_equal_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
81
+ /** @} */
82
+
83
+ #endif /* MONGORY_MATCHER_COMPARE_H */
@@ -0,0 +1,573 @@
1
+ /**
2
+ * @file composite_matcher.c
3
+ * @brief Implements composite matchers like AND, OR, $elemMatch, and the
4
+ * core table condition parser. This is an internal implementation file for the
5
+ * matcher module.
6
+ */
7
+ #include "composite_matcher.h"
8
+ #include "external_matcher.h"
9
+ #include "../foundations/config_private.h" // For mongory_matcher_build_func_get
10
+ #include "../foundations/array_private.h" // For mongory_array_sort_by
11
+ #include "../foundations/string_buffer.h" // For mongory_string_buffer_new
12
+ #include "base_matcher.h" // For mongory_matcher_always_true_new, etc.
13
+ #include "literal_matcher.h" // For mongory_matcher_field_new
14
+ #include "mongory-core/foundations/error.h" // For MONGORY_ERROR_INVALID_ARGUMENT
15
+ #include "mongory-core/foundations/memory_pool.h"
16
+ #include "mongory-core/foundations/table.h" // For mongory_table operations
17
+ #include "mongory-core/foundations/value.h"
18
+ #include "matcher_explainable.h"
19
+ #include "matcher_traversable.h"
20
+ #include "../foundations/utils.h"
21
+ #include <mongory-core.h> // General include
22
+ #include <stdio.h> // For sprintf
23
+
24
+ // Forward declaration.
25
+ double mongory_matcher_calculate_priority(mongory_array *sub_matchers);
26
+ mongory_array *mongory_matcher_sort_matchers(mongory_array *sub_matchers);
27
+
28
+ /**
29
+ * @brief Allocates and initializes a `mongory_composite_matcher` structure.
30
+ *
31
+ * Initializes the base matcher part and sets child pointers (`left`, `right`)
32
+ * to NULL. The specific `match` function and child matchers must be set by the
33
+ * derived composite matcher's constructor.
34
+ *
35
+ * @param pool The memory pool for allocation.
36
+ * @param condition The condition value for this composite matcher.
37
+ * @return mongory_composite_matcher* Pointer to the new matcher, or NULL on
38
+ * failure.
39
+ */
40
+ // ============================================================================
41
+ // Core Composite Matcher Functions
42
+ // ============================================================================
43
+ mongory_composite_matcher *mongory_matcher_composite_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
44
+ if (!pool || !pool->alloc)
45
+ return NULL;
46
+
47
+ mongory_composite_matcher *composite = MG_ALLOC_PTR(pool, mongory_composite_matcher);
48
+ if (composite == NULL) {
49
+ pool->error = &MONGORY_ALLOC_ERROR;
50
+ return NULL; // Allocation failed.
51
+ }
52
+ // Initialize base matcher fields
53
+ composite->base.pool = pool;
54
+ composite->base.name = NULL; // Specific name to be set by derived type if any
55
+ composite->base.match = NULL; // Specific match fn to be set by derived type
56
+ composite->base.explain = mongory_matcher_composite_explain; // Specific explain fn to be set by derived type
57
+ composite->base.original_match = NULL;
58
+ composite->base.sub_count = 0;
59
+ composite->base.condition = condition;
60
+ composite->base.traverse = mongory_matcher_composite_traverse;
61
+ composite->base.extern_ctx = extern_ctx;
62
+ composite->base.priority = 2.0;
63
+ return composite;
64
+ }
65
+
66
+ /**
67
+ * @brief Match function for an AND logical operation.
68
+ *
69
+ * Evaluates to true if both `left` and `right` child matchers (if they exist)
70
+ * evaluate to true. If a child does not exist, it's considered true for this
71
+ * operation.
72
+ *
73
+ * @param matcher Pointer to the composite AND matcher.
74
+ * @param value The value to evaluate.
75
+ * @return True if all child conditions are met, false otherwise.
76
+ */
77
+ // ============================================================================
78
+ // Logical Operator Match Functions (AND, OR)
79
+ // ============================================================================
80
+ static inline bool mongory_matcher_and_match(mongory_matcher *matcher, mongory_value *value) {
81
+ mongory_composite_matcher *composite = (mongory_composite_matcher *)matcher;
82
+ mongory_array *children = composite->children;
83
+ int total = (int)children->count;
84
+ for (int i = 0; i < total; i++) {
85
+ mongory_matcher *child = (mongory_matcher *)children->get(children, i);
86
+ if (!child->match(child, value)) {
87
+ return false;
88
+ }
89
+ }
90
+ return true; // Both matched (or didn't exist, which is fine for AND).
91
+ }
92
+
93
+ /**
94
+ * @brief Match function for an OR logical operation.
95
+ *
96
+ * Evaluates to true if either the `left` or `right` child matcher (if they
97
+ * exist) evaluates to true. If a child does not exist, it's considered false
98
+ * for this operation.
99
+ *
100
+ * @param matcher Pointer to the composite OR matcher.
101
+ * @param value The value to evaluate.
102
+ * @return True if any child condition is met, false otherwise.
103
+ */
104
+ bool mongory_matcher_or_match(mongory_matcher *matcher, mongory_value *value) {
105
+ mongory_composite_matcher *composite = (mongory_composite_matcher *)matcher;
106
+ mongory_array *children = composite->children;
107
+ int total = (int)children->count;
108
+ for (int i = 0; i < total; i++) {
109
+ mongory_matcher *child = (mongory_matcher *)children->get(children, i);
110
+ if (child->match(child, value)) {
111
+ return true;
112
+ }
113
+ }
114
+ return false; // Neither matched (or children didn't exist).
115
+ }
116
+
117
+ /**
118
+ * @brief Context structure for building sub-matchers from a table.
119
+ */
120
+ typedef struct mongory_matcher_table_build_sub_matcher_context {
121
+ mongory_memory_pool *pool; /**< Main pool for allocating created matchers. */
122
+ mongory_array *matchers; /**< Array to store the created sub-matchers. */
123
+ void *extern_ctx; /**< External context for the matcher. */
124
+ } mongory_matcher_table_build_sub_matcher_context;
125
+
126
+ static inline mongory_matcher *mongory_matcher_build_sub_matcher(char *key, mongory_value *value, mongory_matcher_table_build_sub_matcher_context *ctx);
127
+
128
+ /**
129
+ * @brief Callback for iterating over a condition table's key-value pairs.
130
+ *
131
+ * For each pair, it creates an appropriate sub-matcher:
132
+ * - If key starts with '$', it looks up a registered matcher builder.
133
+ * - Otherwise, it creates a field matcher (`mongory_matcher_field_new`).
134
+ * The created sub-matcher is added to the `matchers` array in the context.
135
+ *
136
+ * @param key The key from the condition table.
137
+ * @param value The value associated with the key.
138
+ * @param acc Pointer to `mongory_matcher_table_build_sub_matcher_context`.
139
+ * @return True to continue iteration, false if a sub-matcher creation fails.
140
+ */
141
+ static inline bool mongory_matcher_table_build_sub_matcher(char *key, mongory_value *value, void *acc) {
142
+ mongory_matcher_table_build_sub_matcher_context *ctx = (mongory_matcher_table_build_sub_matcher_context *)acc;
143
+ mongory_array *matchers_array = ctx->matchers;
144
+ mongory_matcher *sub_matcher = mongory_matcher_build_sub_matcher(key, value, ctx);
145
+ if (sub_matcher == NULL) {
146
+ // Failed to create sub-matcher (e.g., allocation error, invalid condition
147
+ // for sub-matcher)
148
+ return false;
149
+ }
150
+
151
+ matchers_array->push(matchers_array, (mongory_value *)sub_matcher);
152
+ return true;
153
+ }
154
+
155
+ /**
156
+ * @brief Builds a sub-matcher from a key-value pair.
157
+ *
158
+ * This function determines the appropriate sub-matcher type based on the key:
159
+ * - If the key starts with '$', it looks up a registered matcher builder.
160
+ * - Otherwise, it creates a field matcher.
161
+ *
162
+ * @param key The key from the condition table.
163
+ * @param value The value associated with the key.
164
+ * @param ctx Pointer to `mongory_matcher_table_build_sub_matcher_context`.
165
+ * @return A new sub-matcher, or NULL on failure.
166
+ */
167
+ // ============================================================================
168
+ // Matcher Construction from Conditions
169
+ // ============================================================================
170
+ static inline mongory_matcher *mongory_matcher_build_sub_matcher(char *key, mongory_value *value, mongory_matcher_table_build_sub_matcher_context *ctx) {
171
+ mongory_memory_pool *pool = ctx->pool;
172
+ mongory_matcher_build_func build_func = NULL;
173
+
174
+ if (key[0] == '$') { // Operator key (e.g., "$eq", "$in")
175
+ build_func = mongory_matcher_build_func_get(key);
176
+ if (build_func != NULL) {
177
+ return build_func(pool, value, ctx->extern_ctx);
178
+ } else if (mongory_custom_matcher_adapter.lookup != NULL && mongory_custom_matcher_adapter.lookup(key)) {
179
+ return mongory_matcher_custom_new(pool, key, value, ctx->extern_ctx);
180
+ }
181
+ }
182
+
183
+ return mongory_matcher_field_new(pool, key, value, ctx->extern_ctx);
184
+ }
185
+
186
+ /**
187
+ * @brief Creates a matcher from a table-based condition.
188
+ *
189
+ * Parses the `condition` table, creating sub-matchers for each key-value pair.
190
+ *
191
+ * This is a core function of the query engine. It takes a query document (a
192
+ * table) and builds a tree of matchers that represents the logic of that query.
193
+ *
194
+ * The process is as follows:
195
+ * 1. Iterate through each key-value pair in the `condition` table.
196
+ * 2. For each pair, create a specific sub-matcher (e.g., a `field_matcher` for
197
+ * a field name, or a `$gt` matcher for a `"$gt"` operator).
198
+ * 3. Store all these sub-matchers in a temporary array.
199
+ * 4. Use `mongory_matcher_binary_construct` to combine all the sub-matchers
200
+ * into a single matcher tree using AND logic.
201
+ *
202
+ * @param pool Memory pool for allocations.
203
+ * @param condition A `mongory_value` of type `MONGORY_TYPE_TABLE`.
204
+ * @return A `mongory_matcher` representing the combined logic of the table, or NULL on failure.
205
+ */
206
+ mongory_matcher *mongory_matcher_table_cond_new(mongory_memory_pool *pool, mongory_value *table_condition, void *extern_ctx) {
207
+ if (!MONGORY_VALIDATE_TABLE(pool, table_condition)) {
208
+ return NULL;
209
+ }
210
+
211
+ mongory_table *table = table_condition->data.t;
212
+ if (table->count == 0) {
213
+ // Empty table condition matches everything.
214
+ return mongory_matcher_always_true_new(pool, table_condition, extern_ctx);
215
+ }
216
+
217
+ mongory_array *sub_matchers = mongory_array_new(pool);
218
+ if (sub_matchers == NULL)
219
+ return NULL; // Failed to create array for sub-matchers.
220
+
221
+ mongory_matcher_table_build_sub_matcher_context build_ctx = {pool, sub_matchers, extern_ctx};
222
+ // Iterate over the condition table, building sub-matchers.
223
+ if (!table->each(table, &build_ctx, mongory_matcher_table_build_sub_matcher)) {
224
+ // Building one of the sub-matchers failed.
225
+ return NULL;
226
+ }
227
+
228
+ if (sub_matchers->count == 1) {
229
+ return (mongory_matcher *)sub_matchers->get(sub_matchers, 0);
230
+ }
231
+
232
+ // Combine sub-matchers using AND logic.
233
+ mongory_composite_matcher *final_matcher = mongory_matcher_composite_new(pool, table_condition, extern_ctx);
234
+ if (final_matcher == NULL)
235
+ return NULL;
236
+ final_matcher->children = mongory_matcher_sort_matchers(sub_matchers);
237
+ final_matcher->base.match = mongory_matcher_and_match;
238
+ final_matcher->base.original_match = mongory_matcher_and_match;
239
+ final_matcher->base.sub_count = sub_matchers->count;
240
+ final_matcher->base.name = mongory_string_cpy(pool, "Condition");
241
+ final_matcher->base.priority += mongory_matcher_calculate_priority(sub_matchers);
242
+ return (mongory_matcher *)final_matcher;
243
+ }
244
+
245
+ /**
246
+ * @brief Callback for $and constructor to build sub-matchers from each table
247
+ * in the condition array. This is a bit complex: each element of the $and array
248
+ * is a table, and each key-value in THAT table becomes a sub-matcher. These
249
+ * are then ANDed together.
250
+ * @param condition_table A `mongory_value` (table) from the $and array.
251
+ * @param acc Pointer to `mongory_matcher_table_build_sub_matcher_context`.
252
+ * @return Result of iterating through `condition_table`.
253
+ */
254
+ static inline bool mongory_matcher_build_and_sub_matcher(mongory_value *and_sub_condition, void *acc) {
255
+ // The 'and_sub_condition' is one of the tables in the $and:[{}, {}, ...] array.
256
+ // We need to build all matchers from this table and add them to the list.
257
+ // The list in 'acc' (ctx->matchers) will then be ANDed together.
258
+ if (!MONGORY_VALIDATE_TABLE(and_sub_condition->pool, and_sub_condition)) {
259
+ return false; // Element in $and array is not a table.
260
+ }
261
+ return and_sub_condition->data.t->each(and_sub_condition->data.t, acc, mongory_matcher_table_build_sub_matcher);
262
+ }
263
+
264
+ /**
265
+ * @brief Creates an "AND" ($and) matcher from an array of condition tables.
266
+ * @param pool Memory pool for allocations.
267
+ *
268
+ * The `$and` operator takes an array of query documents. This function builds
269
+ * a single, flat list of all the sub-matchers from all the query documents,
270
+ * and then combines them into one large AND-connected matcher tree.
271
+ *
272
+ * @param pool Memory pool for allocations.
273
+ * @param condition A `mongory_value` array of table conditions.
274
+ * @return A new $and matcher, or NULL on failure.
275
+ */
276
+ mongory_matcher *mongory_matcher_and_new(mongory_memory_pool *pool, mongory_value *and_condition, void *extern_ctx) {
277
+ if (!MONGORY_VALIDATE_ARRAY(pool, and_condition)) {
278
+ return NULL;
279
+ }
280
+
281
+ mongory_array *array_of_tables = and_condition->data.a;
282
+ if (array_of_tables->count == 0) {
283
+ return mongory_matcher_always_true_new(pool, and_condition, extern_ctx); // $and:[] is true
284
+ }
285
+ mongory_value *sub_condition_of_and_condition = array_of_tables->get(array_of_tables, 0);
286
+
287
+ if (!MONGORY_VALIDATE_TABLE(pool, sub_condition_of_and_condition)) {
288
+ return NULL;
289
+ }
290
+
291
+ mongory_array *sub_matchers = mongory_array_new(pool);
292
+ if (sub_matchers == NULL) {
293
+ return NULL;
294
+ }
295
+
296
+ // Context for building matchers from EACH table within the $and array.
297
+ mongory_matcher_table_build_sub_matcher_context build_ctx = {pool, sub_matchers, extern_ctx};
298
+ // Iterate through the array of tables provided in the $and condition.
299
+ // mongory_matcher_build_and_sub_matcher will then iterate keys of EACH table.
300
+ int total = (int)array_of_tables->count;
301
+ for (int i = 0; i < total; i++) {
302
+ mongory_value *table = array_of_tables->get(array_of_tables, i);
303
+ if (!mongory_matcher_build_and_sub_matcher(table, &build_ctx)) {
304
+ return NULL; // Failure during sub-matcher construction.
305
+ }
306
+ }
307
+
308
+ if (sub_matchers->count == 0) {
309
+ return mongory_matcher_always_true_new(pool, and_condition, extern_ctx);
310
+ }
311
+
312
+ if (sub_matchers->count == 1) {
313
+ return (mongory_matcher *)sub_matchers->get(sub_matchers, 0);
314
+ }
315
+
316
+ mongory_composite_matcher *final_matcher = mongory_matcher_composite_new(pool, and_condition, extern_ctx);
317
+ if (final_matcher == NULL)
318
+ return NULL;
319
+ final_matcher->children = mongory_matcher_sort_matchers(sub_matchers);
320
+ final_matcher->base.match = mongory_matcher_and_match;
321
+ final_matcher->base.original_match = mongory_matcher_and_match;
322
+ final_matcher->base.sub_count = sub_matchers->count;
323
+ final_matcher->base.name = mongory_string_cpy(pool, "And");
324
+ final_matcher->base.priority += mongory_matcher_calculate_priority(sub_matchers);
325
+ return (mongory_matcher *)final_matcher;
326
+ }
327
+
328
+ /**
329
+ * @brief Callback for $or constructor. Each element in the $or array is a
330
+ * complete table condition, which is turned into a single matcher. These
331
+ * "table condition matchers" are then ORed.
332
+ * @param condition_table A `mongory_value` (table) from the $or array.
333
+ * @param acc Pointer to `mongory_matcher_table_build_sub_matcher_context`.
334
+ * The `matchers` array in context will store the result of
335
+ * `mongory_matcher_table_cond_new`.
336
+ * @return True if successful, false otherwise.
337
+ */
338
+ static inline bool mongory_matcher_build_or_sub_matcher(mongory_value *condition_table, void *acc) {
339
+ mongory_matcher_table_build_sub_matcher_context *ctx = (mongory_matcher_table_build_sub_matcher_context *)acc;
340
+ mongory_memory_pool *pool_for_new_matchers = ctx->pool;
341
+ mongory_array *array_to_store_table_matchers = ctx->matchers;
342
+
343
+ // Each 'condition_table' is a complete query object for one branch of the OR.
344
+ // So, create a full table_cond_new matcher for it.
345
+ mongory_matcher *table_level_matcher = mongory_matcher_table_cond_new(pool_for_new_matchers, condition_table, ctx->extern_ctx);
346
+ if (table_level_matcher == NULL) {
347
+ return false; // Failed to create a matcher for this OR branch.
348
+ }
349
+ array_to_store_table_matchers->push(array_to_store_table_matchers, (mongory_value *)table_level_matcher);
350
+ return true;
351
+ }
352
+
353
+ /**
354
+ * @brief Creates an "OR" ($or) matcher from an array of condition tables.
355
+ * @param pool Memory pool for allocations.
356
+ *
357
+ * The `$or` operator takes an array of query documents. For each document in
358
+ * the array, this function creates a complete sub-matcher (using
359
+ * `table_cond_new`). It then combines these top-level sub-matchers into an
360
+ * OR-connected tree. This is different from `$and`, which flattens the list.
361
+ *
362
+ * @param pool Memory pool for allocations.
363
+ * @param condition A `mongory_value` array of table conditions.
364
+ * @return A new $or matcher, or NULL on failure.
365
+ */
366
+ mongory_matcher *mongory_matcher_or_new(mongory_memory_pool *pool, mongory_value *or_condition, void *extern_ctx) {
367
+ if (!MONGORY_VALIDATE_ARRAY(pool, or_condition)) {
368
+ return NULL;
369
+ }
370
+ mongory_array *array_of_tables = or_condition->data.a;
371
+ if (array_of_tables->count == 0) {
372
+ return mongory_matcher_always_false_new(pool, or_condition, extern_ctx); // $or:[] is false
373
+ }
374
+ mongory_value *sub_condition_of_or_condition = array_of_tables->get(array_of_tables, 0);
375
+ if (!MONGORY_VALIDATE_TABLE(pool, sub_condition_of_or_condition)) {
376
+ return NULL;
377
+ }
378
+
379
+ mongory_array *sub_matchers = mongory_array_new(pool);
380
+ if (sub_matchers == NULL) {
381
+ return NULL;
382
+ }
383
+
384
+ mongory_matcher_table_build_sub_matcher_context build_ctx = {pool, sub_matchers, extern_ctx};
385
+ int total = (int)array_of_tables->count;
386
+ for (int i = 0; i < total; i++) {
387
+ mongory_value *table = array_of_tables->get(array_of_tables, i);
388
+ if (!mongory_matcher_build_or_sub_matcher(table, &build_ctx)) {
389
+ return NULL; // Failure building one of the OR branches
390
+ }
391
+ }
392
+
393
+ if (sub_matchers->count == 1) {
394
+ return (mongory_matcher *)sub_matchers->get(sub_matchers, 0);
395
+ }
396
+
397
+ mongory_composite_matcher *final_matcher = mongory_matcher_composite_new(pool, or_condition, extern_ctx);
398
+ if (final_matcher == NULL)
399
+ return NULL;
400
+ final_matcher->children = mongory_matcher_sort_matchers(sub_matchers);
401
+ final_matcher->base.match = mongory_matcher_or_match;
402
+ final_matcher->base.original_match = mongory_matcher_or_match;
403
+ final_matcher->base.sub_count = sub_matchers->count;
404
+ final_matcher->base.name = mongory_string_cpy(pool, "Or");
405
+ final_matcher->base.priority += mongory_matcher_calculate_priority(sub_matchers);
406
+ return (mongory_matcher *)final_matcher;
407
+ }
408
+
409
+ /**
410
+ * @brief Match function for $elemMatch.
411
+ * Checks if any element in the input array `value` matches the condition
412
+ * stored in `composite->children`.
413
+ * @param matcher The $elemMatch composite matcher.
414
+ * @param value_to_check The input value, expected to be an array.
415
+ * @return True if `value_to_check` is an array and at least one of its elements
416
+ * matches.
417
+ */
418
+ // ============================================================================
419
+ // Array-based Match Functions ($elemMatch, $every)
420
+ // ============================================================================
421
+ static inline bool mongory_matcher_elem_match_match(mongory_matcher *matcher, mongory_value *elem_match_target) {
422
+ if (elem_match_target == NULL || elem_match_target->type != MONGORY_TYPE_ARRAY) {
423
+ return false; // $elemMatch applies to arrays.
424
+ }
425
+ mongory_array *target_array = elem_match_target->data.a;
426
+ if (target_array->count == 0)
427
+ return false; // Empty array cannot have a matching element.
428
+
429
+ int total = (int)target_array->count;
430
+ for (int i = 0; i < total; i++) {
431
+ mongory_value *value = target_array->get(target_array, i);
432
+ if (mongory_matcher_and_match(matcher, value)) {
433
+ return true;
434
+ }
435
+ }
436
+ return false;
437
+ }
438
+
439
+ /**
440
+ * @brief Creates an $elemMatch matcher.
441
+ * The `condition` (a table) is used to create a sub-matcher
442
+ * (`composite->left`) which is then applied to each element of an input array.
443
+ * @param pool Memory pool for allocations.
444
+ * @param condition The table condition for matching array elements.
445
+ * @return A new $elemMatch matcher, or NULL on failure.
446
+ */
447
+ mongory_matcher *mongory_matcher_elem_match_new(mongory_memory_pool *pool, mongory_value *elem_match_condition, void *extern_ctx) {
448
+ if (!MONGORY_VALIDATE_TABLE(pool, elem_match_condition)) {
449
+ return NULL;
450
+ }
451
+ mongory_array *sub_matchers = mongory_array_new(pool);
452
+ if (sub_matchers == NULL)
453
+ return NULL;
454
+ mongory_matcher_table_build_sub_matcher_context build_ctx = {pool, sub_matchers, extern_ctx};
455
+ if (!mongory_matcher_build_and_sub_matcher(elem_match_condition, &build_ctx))
456
+ return NULL;
457
+
458
+ if (sub_matchers->count == 0)
459
+ return mongory_matcher_always_false_new(pool, elem_match_condition, extern_ctx);
460
+
461
+ mongory_composite_matcher *composite = mongory_matcher_composite_new(pool, elem_match_condition, extern_ctx);
462
+ if (composite == NULL)
463
+ return NULL;
464
+
465
+ composite->children = mongory_matcher_sort_matchers(sub_matchers);
466
+ composite->base.match = mongory_matcher_elem_match_match;
467
+ composite->base.original_match = mongory_matcher_elem_match_match;
468
+ composite->base.sub_count = sub_matchers->count;
469
+ composite->base.name = mongory_string_cpy(pool, "ElemMatch");
470
+ composite->base.priority = 3.0 + mongory_matcher_calculate_priority(sub_matchers);
471
+ return (mongory_matcher *)composite;
472
+ }
473
+
474
+ /**
475
+ * @brief Match function for $every.
476
+ * Checks if all elements in the input array `value` match the condition
477
+ * stored in `composite->children`.
478
+ * @param matcher The $every composite matcher.
479
+ * @param value_to_check The input value, expected to be an array.
480
+ * @return True if `value_to_check` is an array and all its elements match (or
481
+ * if array is empty).
482
+ */
483
+ static inline bool mongory_matcher_every_match(mongory_matcher *matcher, mongory_value *every_match_target) {
484
+ if (every_match_target == NULL || every_match_target->type != MONGORY_TYPE_ARRAY) {
485
+ return false;
486
+ }
487
+ mongory_array *target_array = every_match_target->data.a;
488
+ if (target_array->count == 0)
489
+ return false; // Non-empty array must have at least one element.
490
+
491
+ int total = (int)target_array->count;
492
+ for (int i = 0; i < total; i++) {
493
+ mongory_value *value = target_array->get(target_array, i);
494
+ if (!mongory_matcher_and_match(matcher, value)) {
495
+ return false;
496
+ }
497
+ }
498
+ return true;
499
+ }
500
+
501
+ /**
502
+ * @brief Creates an $every matcher.
503
+ * The `condition` (a table) is used to create a sub-matcher
504
+ * (`composite->left`) which is then applied to each element of an input array.
505
+ * @param pool Memory pool for allocations.
506
+ * @param condition The table condition for matching array elements.
507
+ * @return A new $every matcher, or NULL on failure.
508
+ */
509
+ mongory_matcher *mongory_matcher_every_new(mongory_memory_pool *pool, mongory_value *every_condition, void *extern_ctx) {
510
+ if (!MONGORY_VALIDATE_TABLE(pool, every_condition)) {
511
+ return NULL;
512
+ }
513
+ mongory_array *sub_matchers = mongory_array_new(pool);
514
+ if (sub_matchers == NULL)
515
+ return NULL;
516
+ mongory_matcher_table_build_sub_matcher_context build_ctx = {pool, sub_matchers, extern_ctx};
517
+ if (!mongory_matcher_build_and_sub_matcher(every_condition, &build_ctx))
518
+ return NULL;
519
+
520
+ if (sub_matchers->count == 0)
521
+ return mongory_matcher_always_true_new(pool, every_condition, extern_ctx);
522
+
523
+ mongory_composite_matcher *composite = mongory_matcher_composite_new(pool, every_condition, extern_ctx);
524
+ if (composite == NULL)
525
+ return NULL;
526
+
527
+ composite->children = mongory_matcher_sort_matchers(sub_matchers);
528
+ composite->base.match = mongory_matcher_every_match;
529
+ composite->base.original_match = mongory_matcher_every_match;
530
+ composite->base.sub_count = sub_matchers->count;
531
+ composite->base.name = mongory_string_cpy(pool, "Every");
532
+ composite->base.priority = 3.0 + mongory_matcher_calculate_priority(sub_matchers);
533
+ return (mongory_matcher *)composite;
534
+ }
535
+
536
+ // ============================================================================
537
+ // Helper Functions
538
+ // ============================================================================
539
+
540
+ /**
541
+ * @brief Calculates the priority of a composite matcher.
542
+ * @param sub_matchers The array of sub-matchers.
543
+ * @return The priority of the composite matcher.
544
+ */
545
+ double mongory_matcher_calculate_priority(mongory_array *sub_matchers) {
546
+ double priority = 0.0;
547
+ for (size_t i = 0; i < sub_matchers->count; i++) {
548
+ priority += ((mongory_matcher *)sub_matchers->get(sub_matchers, i))->priority;
549
+ }
550
+ return priority;
551
+ }
552
+
553
+ static inline size_t mongory_matcher_calc_priority(mongory_value *matcher_value, void *ctx) {
554
+ (void)ctx;
555
+ mongory_matcher *matcher = (mongory_matcher *)matcher_value;
556
+ return (size_t)(matcher->priority * 10000);
557
+ }
558
+
559
+ /**
560
+ * @brief Sorts the sub-matchers by priority.
561
+ * @param sub_matchers The array of sub-matchers.
562
+ * @return The sorted array of sub-matchers.
563
+ */
564
+ mongory_array *mongory_matcher_sort_matchers(mongory_array *sub_matchers) {
565
+ mongory_memory_pool *temp_pool = mongory_memory_pool_new();
566
+ if (temp_pool == NULL) {
567
+ sub_matchers->pool->error = &MONGORY_ALLOC_ERROR;
568
+ return NULL;
569
+ }
570
+ mongory_array *sorted_matchers = mongory_array_sort_by(sub_matchers, temp_pool, NULL, mongory_matcher_calc_priority);
571
+ temp_pool->free(temp_pool);
572
+ return sorted_matchers;
573
+ }