mongory 0.7.2-x86_64-linux
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.
- checksums.yaml +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +88 -0
- data/.yardopts +7 -0
- data/CHANGELOG.md +364 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/LICENSE.txt +21 -0
- data/README.md +488 -0
- data/Rakefile +107 -0
- data/SUBMODULE_INTEGRATION.md +325 -0
- data/docs/advanced_usage.md +40 -0
- data/docs/clang_bridge.md +69 -0
- data/docs/field_names.md +30 -0
- data/docs/migration.md +30 -0
- data/docs/performance.md +61 -0
- data/examples/README.md +41 -0
- data/examples/benchmark-rails.rb +52 -0
- data/examples/benchmark.rb +184 -0
- data/ext/mongory_ext/extconf.rb +91 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/array.h +122 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/config.h +161 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/error.h +79 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/memory_pool.h +95 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/table.h +127 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/value.h +175 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core/matchers/matcher.h +76 -0
- data/ext/mongory_ext/mongory-core/include/mongory-core.h +12 -0
- data/ext/mongory_ext/mongory-core/src/foundations/array.c +287 -0
- data/ext/mongory_ext/mongory-core/src/foundations/array_private.h +19 -0
- data/ext/mongory_ext/mongory-core/src/foundations/config.c +270 -0
- data/ext/mongory_ext/mongory-core/src/foundations/config_private.h +48 -0
- data/ext/mongory_ext/mongory-core/src/foundations/error.c +38 -0
- data/ext/mongory_ext/mongory-core/src/foundations/memory_pool.c +298 -0
- data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.c +65 -0
- data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.h +49 -0
- data/ext/mongory_ext/mongory-core/src/foundations/table.c +498 -0
- data/ext/mongory_ext/mongory-core/src/foundations/utils.c +210 -0
- data/ext/mongory_ext/mongory-core/src/foundations/utils.h +70 -0
- data/ext/mongory_ext/mongory-core/src/foundations/value.c +500 -0
- data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.c +164 -0
- data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.h +47 -0
- data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.c +122 -0
- data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.h +100 -0
- data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.c +217 -0
- data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.h +83 -0
- data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.c +573 -0
- data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.h +125 -0
- data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.c +147 -0
- data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.h +48 -0
- data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.c +124 -0
- data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.h +46 -0
- data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.c +126 -0
- data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.h +46 -0
- data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.c +314 -0
- data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.h +97 -0
- data/ext/mongory_ext/mongory-core/src/matchers/matcher.c +252 -0
- data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.c +79 -0
- data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.h +23 -0
- data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.c +60 -0
- data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.h +23 -0
- data/ext/mongory_ext/mongory_ext.c +683 -0
- data/lib/generators/mongory/install/install_generator.rb +42 -0
- data/lib/generators/mongory/install/templates/initializer.rb.erb +83 -0
- data/lib/generators/mongory/matcher/matcher_generator.rb +56 -0
- data/lib/generators/mongory/matcher/templates/matcher.rb.erb +92 -0
- data/lib/generators/mongory/matcher/templates/matcher_spec.rb.erb +17 -0
- data/lib/mongory/c_query_builder.rb +44 -0
- data/lib/mongory/converters/abstract_converter.rb +111 -0
- data/lib/mongory/converters/condition_converter.rb +64 -0
- data/lib/mongory/converters/converted.rb +81 -0
- data/lib/mongory/converters/data_converter.rb +37 -0
- data/lib/mongory/converters/key_converter.rb +87 -0
- data/lib/mongory/converters/value_converter.rb +52 -0
- data/lib/mongory/converters.rb +8 -0
- data/lib/mongory/matchers/abstract_matcher.rb +219 -0
- data/lib/mongory/matchers/abstract_multi_matcher.rb +124 -0
- data/lib/mongory/matchers/and_matcher.rb +72 -0
- data/lib/mongory/matchers/array_record_matcher.rb +93 -0
- data/lib/mongory/matchers/elem_match_matcher.rb +55 -0
- data/lib/mongory/matchers/eq_matcher.rb +46 -0
- data/lib/mongory/matchers/every_matcher.rb +56 -0
- data/lib/mongory/matchers/exists_matcher.rb +53 -0
- data/lib/mongory/matchers/field_matcher.rb +147 -0
- data/lib/mongory/matchers/gt_matcher.rb +41 -0
- data/lib/mongory/matchers/gte_matcher.rb +41 -0
- data/lib/mongory/matchers/hash_condition_matcher.rb +62 -0
- data/lib/mongory/matchers/in_matcher.rb +68 -0
- data/lib/mongory/matchers/literal_matcher.rb +121 -0
- data/lib/mongory/matchers/lt_matcher.rb +41 -0
- data/lib/mongory/matchers/lte_matcher.rb +41 -0
- data/lib/mongory/matchers/ne_matcher.rb +38 -0
- data/lib/mongory/matchers/nin_matcher.rb +68 -0
- data/lib/mongory/matchers/not_matcher.rb +40 -0
- data/lib/mongory/matchers/or_matcher.rb +68 -0
- data/lib/mongory/matchers/present_matcher.rb +55 -0
- data/lib/mongory/matchers/regex_matcher.rb +80 -0
- data/lib/mongory/matchers/size_matcher.rb +54 -0
- data/lib/mongory/matchers.rb +176 -0
- data/lib/mongory/mongoid.rb +19 -0
- data/lib/mongory/query_builder.rb +257 -0
- data/lib/mongory/query_matcher.rb +93 -0
- data/lib/mongory/query_operator.rb +28 -0
- data/lib/mongory/rails.rb +15 -0
- data/lib/mongory/utils/context.rb +48 -0
- data/lib/mongory/utils/debugger.rb +125 -0
- data/lib/mongory/utils/rails_patch.rb +22 -0
- data/lib/mongory/utils/singleton_builder.rb +31 -0
- data/lib/mongory/utils.rb +76 -0
- data/lib/mongory/version.rb +5 -0
- data/lib/mongory.rb +123 -0
- data/lib/mongory_ext.so +0 -0
- data/mongory.gemspec +50 -0
- data/scripts/build_with_core.sh +292 -0
- data/sig/mongory.rbs +4 -0
- metadata +159 -0
@@ -0,0 +1,164 @@
|
|
1
|
+
/**
|
2
|
+
* @file array_record_matcher.c
|
3
|
+
* @brief Implements a versatile matcher for arrays, handling various condition
|
4
|
+
* types. This is an internal implementation file for the matcher module.
|
5
|
+
*
|
6
|
+
* This matcher can interpret conditions as:
|
7
|
+
* - A table: Parsed for operators like $elemMatch or implicit field conditions
|
8
|
+
* on array elements.
|
9
|
+
* - A regex: Creates an $elemMatch to match array elements against the regex.
|
10
|
+
* - A literal: Creates an $elemMatch to check for equality with elements.
|
11
|
+
* - Another array: Checks for whole-array equality.
|
12
|
+
* It often constructs a composite OR matcher to combine these possibilities.
|
13
|
+
*/
|
14
|
+
#include "array_record_matcher.h"
|
15
|
+
#include "../foundations/config_private.h"
|
16
|
+
#include "../foundations/utils.h" // For mongory_try_parse_int
|
17
|
+
#include "base_matcher.h" // For mongory_matcher_base_new
|
18
|
+
#include "compare_matcher.h" // For mongory_matcher_equal_new
|
19
|
+
#include "composite_matcher.h" // For mongory_matcher_composite_new, $elemMatch, table_cond_new, or_match
|
20
|
+
#include "literal_matcher.h" // Potentially used by table_cond_new
|
21
|
+
#include "mongory-core/foundations/table.h"
|
22
|
+
#include "mongory-core/foundations/value.h"
|
23
|
+
#include <mongory-core.h> // General include
|
24
|
+
#include <stdio.h> // For NULL
|
25
|
+
#include <string.h> // For strcmp
|
26
|
+
|
27
|
+
/**
|
28
|
+
* @struct mongory_matcher_array_record_parse_table_context
|
29
|
+
* @brief Context used when parsing a table condition for array record matching.
|
30
|
+
*
|
31
|
+
* Helps separate parts of the condition table:
|
32
|
+
* - `parsed_table`: For explicit operators (e.g., $size, $all if implemented)
|
33
|
+
* or indexed conditions.
|
34
|
+
* - `elem_match_table`: For conditions that should apply to individual elements
|
35
|
+
* via an implicit or explicit $elemMatch.
|
36
|
+
*/
|
37
|
+
typedef struct mongory_matcher_array_record_parse_table_context {
|
38
|
+
mongory_table *parsed_table; /**< Stores operator or indexed conditions. */
|
39
|
+
mongory_table *elem_match_table; /**< Stores conditions for element matching. */
|
40
|
+
} mongory_matcher_array_record_parse_table_context;
|
41
|
+
|
42
|
+
/**
|
43
|
+
* @brief Callback to parse a condition table for array matching.
|
44
|
+
*
|
45
|
+
* Iterates through the main condition table:
|
46
|
+
* - If `key` is "$elemMatch" and `value` is a table, its contents are added to
|
47
|
+
* `context->elem_match_table`.
|
48
|
+
* - If `key` starts with '$' (another operator) or is numeric (array index),
|
49
|
+
* it's added to `context->parsed_table`.
|
50
|
+
* - Otherwise (plain field name), it's considered part of an implicit element
|
51
|
+
* match and added to `context->elem_match_table`.
|
52
|
+
*
|
53
|
+
* @param key Current key in the condition table.
|
54
|
+
* @param value Current value for the key.
|
55
|
+
* @param acc Pointer to `mongory_matcher_array_record_parse_table_context`.
|
56
|
+
* @return Always true to continue.
|
57
|
+
*/
|
58
|
+
static inline bool mongory_matcher_array_record_parse_table_foreach(char *key, mongory_value *value, void *acc) {
|
59
|
+
mongory_matcher_array_record_parse_table_context *context = (mongory_matcher_array_record_parse_table_context *)acc;
|
60
|
+
mongory_table *parsed_table_for_operators = context->parsed_table;
|
61
|
+
mongory_table *table_for_elem_match_conditions = context->elem_match_table;
|
62
|
+
|
63
|
+
if (strcmp(key, "$elemMatch") == 0 && value->type == MONGORY_TYPE_TABLE && value->data.t != NULL) {
|
64
|
+
// Explicit $elemMatch: iterate its sub-table and add to elem_match_table
|
65
|
+
mongory_table_merge(table_for_elem_match_conditions, value->data.t);
|
66
|
+
} else if (*key == '$' || mongory_try_parse_int(key, NULL)) {
|
67
|
+
// Operator (like $size) or numeric index: goes to parsed_table
|
68
|
+
parsed_table_for_operators->set(parsed_table_for_operators, key, value);
|
69
|
+
} else {
|
70
|
+
// Regular field name: implies a condition on elements, goes to
|
71
|
+
// elem_match_table
|
72
|
+
table_for_elem_match_conditions->set(table_for_elem_match_conditions, key, value);
|
73
|
+
}
|
74
|
+
return true;
|
75
|
+
}
|
76
|
+
|
77
|
+
/**
|
78
|
+
* @brief Parses a table `condition` intended for array matching.
|
79
|
+
*
|
80
|
+
* Separates the condition into parts for direct array operations (like $size)
|
81
|
+
* and parts for matching individual elements (implicit or explicit $elemMatch).
|
82
|
+
* If any element-matching conditions are found, they are grouped under an
|
83
|
+
* "$elemMatch" key in the returned `parsed_table`.
|
84
|
+
*
|
85
|
+
* @param condition The `mongory_value` (must be a table) to parse.
|
86
|
+
* @return A new `mongory_value` (table) containing the parsed and restructured
|
87
|
+
* condition. Returns NULL if input is not a table or on allocation failure.
|
88
|
+
*/
|
89
|
+
static inline mongory_value *mongory_matcher_array_record_parse_table(mongory_value *condition) {
|
90
|
+
if (!condition->data.t || !condition->pool) {
|
91
|
+
mongory_error *error = MG_ALLOC_PTR(condition->pool, mongory_error);
|
92
|
+
if (!error) {
|
93
|
+
condition->pool->error = &MONGORY_ALLOC_ERROR;
|
94
|
+
return NULL;
|
95
|
+
}
|
96
|
+
error->type = MONGORY_ERROR_INVALID_TYPE;
|
97
|
+
error->message = "Expected condition to be a table, got a non-table value";
|
98
|
+
condition->pool->error = error; // TODO: This is a hack, we should use a better error handling mechanism
|
99
|
+
return NULL; // Invalid input
|
100
|
+
}
|
101
|
+
mongory_memory_pool *pool = condition->pool;
|
102
|
+
mongory_table *parsed_table = mongory_table_new(pool);
|
103
|
+
mongory_table *elem_match_sub_table = mongory_table_new(pool);
|
104
|
+
|
105
|
+
if (!parsed_table || !elem_match_sub_table) {
|
106
|
+
// Cleanup if one allocation succeeded but other failed? Pool should handle.
|
107
|
+
return NULL;
|
108
|
+
}
|
109
|
+
|
110
|
+
mongory_matcher_array_record_parse_table_context parse_ctx = {parsed_table, elem_match_sub_table};
|
111
|
+
mongory_table *original_condition_table = condition->data.t;
|
112
|
+
|
113
|
+
original_condition_table->each(original_condition_table, &parse_ctx,
|
114
|
+
mongory_matcher_array_record_parse_table_foreach);
|
115
|
+
|
116
|
+
if (elem_match_sub_table->count > 0) {
|
117
|
+
// If there were conditions for element matching, add them as an $elemMatch
|
118
|
+
// clause to the main parsed_table.
|
119
|
+
parsed_table->set(parsed_table, "$elemMatch", mongory_value_wrap_t(pool, elem_match_sub_table));
|
120
|
+
}
|
121
|
+
// If elem_match_sub_table is empty, it's not added. The original parsed_table
|
122
|
+
// (which might contain $size etc.) is returned.
|
123
|
+
return mongory_value_wrap_t(pool, parsed_table);
|
124
|
+
}
|
125
|
+
|
126
|
+
/**
|
127
|
+
* @brief Main constructor for `mongory_matcher_array_record_new`.
|
128
|
+
*
|
129
|
+
* Constructs a two-part matcher (often combined with OR):
|
130
|
+
* - `left`: Handles element-wise conditions (like $elemMatch from various
|
131
|
+
* condition types).
|
132
|
+
* - `right`: Handles whole-array equality if the original `condition` was an
|
133
|
+
* array.
|
134
|
+
*
|
135
|
+
* If `right` is NULL (i.e., original `condition` was not an array), only the
|
136
|
+
* `left` matcher is returned. Otherwise, a composite OR matcher is created
|
137
|
+
* with `left` and `right` as children.
|
138
|
+
*
|
139
|
+
* @param pool Memory pool.
|
140
|
+
* @param condition The condition to apply to arrays.
|
141
|
+
* @return The constructed array_record_matcher, or NULL on failure.
|
142
|
+
*/
|
143
|
+
mongory_matcher *mongory_matcher_array_record_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
|
144
|
+
if (!condition)
|
145
|
+
return NULL;
|
146
|
+
switch (condition->type) {
|
147
|
+
case MONGORY_TYPE_TABLE:
|
148
|
+
return mongory_matcher_table_cond_new(pool,
|
149
|
+
mongory_matcher_array_record_parse_table(condition),
|
150
|
+
extern_ctx
|
151
|
+
);
|
152
|
+
case MONGORY_TYPE_ARRAY:
|
153
|
+
return mongory_matcher_or_new(pool, MG_ARRAY_WRAP(pool, 2,
|
154
|
+
MG_TABLE_WRAP(pool, 1, "$eq", condition),
|
155
|
+
MG_TABLE_WRAP(pool, 1, "$elemMatch",
|
156
|
+
MG_TABLE_WRAP(pool, 1, "$eq", condition)
|
157
|
+
)
|
158
|
+
), extern_ctx);
|
159
|
+
case MONGORY_TYPE_REGEX:
|
160
|
+
return mongory_matcher_elem_match_new(pool, MG_TABLE_WRAP(pool, 1, "$regex", condition), extern_ctx);
|
161
|
+
default: // Literals (string, int, bool, etc.)
|
162
|
+
return mongory_matcher_elem_match_new(pool, MG_TABLE_WRAP(pool, 1, "$eq", condition), extern_ctx);
|
163
|
+
}
|
164
|
+
}
|
@@ -0,0 +1,47 @@
|
|
1
|
+
#ifndef MONGORY_MATCHER_ARRAY_RECORD_H
|
2
|
+
#define MONGORY_MATCHER_ARRAY_RECORD_H
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @file array_record_matcher.h
|
6
|
+
* @brief Defines the constructor for a matcher that handles complex conditions
|
7
|
+
* against arrays. This is an internal header for the matcher module.
|
8
|
+
*
|
9
|
+
* This matcher is more specialized than simple `$elemMatch` or `$every`. It can
|
10
|
+
* interpret a condition that might be a direct value to find, a regex to match
|
11
|
+
* elements, or a table defining multiple criteria for array elements (similar
|
12
|
+
* to an implicit `$elemMatch` with that table). It can also handle checking if
|
13
|
+
* an input array is equal to a condition array.
|
14
|
+
*/
|
15
|
+
|
16
|
+
#include "base_matcher.h"
|
17
|
+
#include "mongory-core/foundations/memory_pool.h"
|
18
|
+
#include "mongory-core/foundations/value.h"
|
19
|
+
#include "mongory-core/matchers/matcher.h" // For mongory_matcher structure
|
20
|
+
|
21
|
+
/**
|
22
|
+
* @brief Creates an "array record" matcher.
|
23
|
+
*
|
24
|
+
* This matcher is designed to apply various types of conditions to an input
|
25
|
+
* array or its elements. The behavior depends on the `condition`'s type:
|
26
|
+
* - If `condition` is a table: It's parsed. Keys like `$elemMatch` are handled
|
27
|
+
* specifically. Other field-value pairs in the table are typically wrapped
|
28
|
+
* into an implicit `$elemMatch` condition that applies to elements of the
|
29
|
+
* target array.
|
30
|
+
* - If `condition` is a regex: It effectively creates an `$elemMatch` where
|
31
|
+
* each element of the target array is matched against this regex.
|
32
|
+
* - If `condition` is a simple literal (string, number, bool): It creates an
|
33
|
+
* `$elemMatch` where elements are checked for equality against this literal.
|
34
|
+
* - If `condition` itself is an array: It creates a matcher that checks if the
|
35
|
+
* target array is equal to this condition array.
|
36
|
+
*
|
37
|
+
* The resulting matcher might be a composite of several internal matchers (e.g.,
|
38
|
+
* an OR between element matching and whole-array equality).
|
39
|
+
*
|
40
|
+
* @param pool Memory pool for allocation.
|
41
|
+
* @param condition A `mongory_value` representing the condition to apply to an
|
42
|
+
* array or its elements.
|
43
|
+
* @return A new array record matcher, or NULL on failure.
|
44
|
+
*/
|
45
|
+
mongory_matcher *mongory_matcher_array_record_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
|
46
|
+
|
47
|
+
#endif /* MONGORY_MATCHER_ARRAY_RECORD_H */
|
@@ -0,0 +1,122 @@
|
|
1
|
+
/**
|
2
|
+
* @file base_matcher.c
|
3
|
+
* @brief Implements the base matcher constructor and related utility functions.
|
4
|
+
* This is an internal implementation file for the matcher module.
|
5
|
+
*/
|
6
|
+
#include "base_matcher.h"
|
7
|
+
#include "../foundations/string_buffer.h"
|
8
|
+
#include <errno.h> // For errno, ERANGE
|
9
|
+
#include <limits.h> // For INT_MIN, INT_MAX
|
10
|
+
#include <mongory-core.h> // General include, for mongory_matcher types
|
11
|
+
#include <stdbool.h>
|
12
|
+
#include <stdio.h> // For printf
|
13
|
+
#include <stdlib.h> // For strtol
|
14
|
+
#include "matcher_explainable.h"
|
15
|
+
#include "matcher_traversable.h"
|
16
|
+
#include "../foundations/utils.h"
|
17
|
+
|
18
|
+
/**
|
19
|
+
* @brief Allocates and initializes common fields of a `mongory_matcher`.
|
20
|
+
*
|
21
|
+
* This function serves as a common initializer for all specific matcher types.
|
22
|
+
* It allocates memory for the `mongory_matcher` structure itself from the
|
23
|
+
* provided `pool`, sets the `pool` and `condition` members.
|
24
|
+
* The `matcher->match` function pointer specific to the matcher type must be
|
25
|
+
* set by the caller. `original_match` and `trace` in the context are set to
|
26
|
+
* NULL.
|
27
|
+
*
|
28
|
+
* @param pool The memory pool to use for allocation. Must be non-NULL and
|
29
|
+
* valid.
|
30
|
+
* @param condition The condition value for this matcher.
|
31
|
+
* @return mongory_matcher* Pointer to the newly allocated and partially
|
32
|
+
* initialized matcher, or NULL if allocation fails.
|
33
|
+
*/
|
34
|
+
mongory_matcher *mongory_matcher_base_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
|
35
|
+
if (!pool || !pool->alloc) {
|
36
|
+
return NULL; // Invalid memory pool.
|
37
|
+
}
|
38
|
+
mongory_matcher *matcher = MG_ALLOC_PTR(pool, mongory_matcher);
|
39
|
+
if (matcher == NULL) {
|
40
|
+
// Allocation failed, pool->alloc might set pool->error.
|
41
|
+
pool->error = &MONGORY_ALLOC_ERROR;
|
42
|
+
return NULL;
|
43
|
+
}
|
44
|
+
|
45
|
+
// Initialize common fields
|
46
|
+
matcher->original_match = NULL;
|
47
|
+
matcher->sub_count = 0;
|
48
|
+
matcher->pool = pool;
|
49
|
+
matcher->condition = condition;
|
50
|
+
matcher->name = NULL; // Name is not set by base_new.
|
51
|
+
matcher->match = NULL; // Specific match function must be set by derived type.
|
52
|
+
matcher->explain = mongory_matcher_base_explain; // Specific explain function must be set by derived type.
|
53
|
+
matcher->traverse = mongory_matcher_leaf_traverse;
|
54
|
+
matcher->extern_ctx = extern_ctx; // Set the external context.
|
55
|
+
matcher->priority = 1.0; // Set the priority to 1.0.
|
56
|
+
return matcher;
|
57
|
+
}
|
58
|
+
|
59
|
+
/**
|
60
|
+
* @brief The match function for a matcher that always returns true.
|
61
|
+
* @param matcher Unused.
|
62
|
+
* @param value Unused.
|
63
|
+
* @return Always true.
|
64
|
+
*/
|
65
|
+
static inline bool mongory_matcher_always_true_match(mongory_matcher *matcher, mongory_value *value) {
|
66
|
+
(void)matcher; // Mark as unused to prevent compiler warnings.
|
67
|
+
(void)value; // Mark as unused.
|
68
|
+
return true; // This matcher always indicates a match.
|
69
|
+
}
|
70
|
+
|
71
|
+
/**
|
72
|
+
* @brief Creates a matcher instance that will always evaluate to true.
|
73
|
+
* Useful as a placeholder or for default cases.
|
74
|
+
* @param pool Memory pool for allocation.
|
75
|
+
* @param condition Condition (typically ignored by this matcher).
|
76
|
+
* @return A new `mongory_matcher` or NULL on failure.
|
77
|
+
*/
|
78
|
+
mongory_matcher *mongory_matcher_always_true_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
|
79
|
+
mongory_matcher *matcher = mongory_matcher_base_new(pool, condition, extern_ctx);
|
80
|
+
if (!matcher) {
|
81
|
+
return NULL;
|
82
|
+
}
|
83
|
+
matcher->match = mongory_matcher_always_true_match;
|
84
|
+
matcher->original_match = mongory_matcher_always_true_match;
|
85
|
+
matcher->name = mongory_string_cpy(pool, "Always True");
|
86
|
+
matcher->explain = mongory_matcher_base_explain;
|
87
|
+
// Optionally set original_match as well if it's a strict policy
|
88
|
+
// matcher->context.original_match = mongory_matcher_always_true_match;
|
89
|
+
return matcher;
|
90
|
+
}
|
91
|
+
|
92
|
+
/**
|
93
|
+
* @brief The match function for a matcher that always returns false.
|
94
|
+
* @param matcher Unused.
|
95
|
+
* @param value Unused.
|
96
|
+
* @return Always false.
|
97
|
+
*/
|
98
|
+
static inline bool mongory_matcher_always_false_match(mongory_matcher *matcher, mongory_value *value) {
|
99
|
+
(void)matcher; // Mark as unused.
|
100
|
+
(void)value; // Mark as unused.
|
101
|
+
return false; // This matcher never indicates a match.
|
102
|
+
}
|
103
|
+
|
104
|
+
/**
|
105
|
+
* @brief Creates a matcher instance that will always evaluate to false.
|
106
|
+
* Useful for conditions that should never match.
|
107
|
+
* @param pool Memory pool for allocation.
|
108
|
+
* @param condition Condition (typically ignored by this matcher).
|
109
|
+
* @return A new `mongory_matcher` or NULL on failure.
|
110
|
+
*/
|
111
|
+
mongory_matcher *mongory_matcher_always_false_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
|
112
|
+
mongory_matcher *matcher = mongory_matcher_base_new(pool, condition, extern_ctx);
|
113
|
+
if (!matcher) {
|
114
|
+
return NULL;
|
115
|
+
}
|
116
|
+
matcher->match = mongory_matcher_always_false_match;
|
117
|
+
matcher->original_match = mongory_matcher_always_false_match;
|
118
|
+
matcher->name = mongory_string_cpy(pool, "Always False");
|
119
|
+
matcher->explain = mongory_matcher_base_explain;
|
120
|
+
// matcher->context.original_match = mongory_matcher_always_false_match;
|
121
|
+
return matcher;
|
122
|
+
}
|
@@ -0,0 +1,100 @@
|
|
1
|
+
#ifndef MONGORY_MATCHER_BASE_H
|
2
|
+
#define MONGORY_MATCHER_BASE_H
|
3
|
+
|
4
|
+
/**
|
5
|
+
* @file base_matcher.h
|
6
|
+
* @brief Defines the base matcher constructor and utility functions for
|
7
|
+
* matchers. This is an internal header for the matcher module.
|
8
|
+
*
|
9
|
+
* This includes a constructor for the fundamental `mongory_matcher` structure,
|
10
|
+
* constructors for trivial matchers (always true/false), and a utility
|
11
|
+
* for parsing integers from strings.
|
12
|
+
*/
|
13
|
+
|
14
|
+
#include "mongory-core/foundations/array.h" // For mongory_array (context trace)
|
15
|
+
#include "mongory-core/foundations/memory_pool.h"
|
16
|
+
#include "mongory-core/foundations/value.h"
|
17
|
+
#include "mongory-core/matchers/matcher.h" // For mongory_matcher structure
|
18
|
+
#include "matcher_explainable.h"
|
19
|
+
#include "matcher_traversable.h"
|
20
|
+
#include <stdbool.h>
|
21
|
+
|
22
|
+
/**
|
23
|
+
* @brief Function pointer type for a matcher's core matching logic.
|
24
|
+
*
|
25
|
+
* @param matcher A pointer to the `mongory_matcher` instance itself.
|
26
|
+
* @param value A pointer to the `mongory_value` to be evaluated against the
|
27
|
+
* matcher's condition.
|
28
|
+
* @return bool True if the `value` matches the condition, false otherwise.
|
29
|
+
*/
|
30
|
+
typedef bool (*mongory_matcher_match_func)(mongory_matcher *matcher, mongory_value *value);
|
31
|
+
|
32
|
+
/**
|
33
|
+
* @struct mongory_matcher
|
34
|
+
* @brief Represents a generic matcher in the Mongory system.
|
35
|
+
*
|
36
|
+
* Each matcher has a name (optional, for identification), a condition value
|
37
|
+
* that defines its criteria, a function pointer to its matching logic,
|
38
|
+
* a memory pool for its allocations, and a context.
|
39
|
+
*/
|
40
|
+
struct mongory_matcher {
|
41
|
+
char *name; /**< Optional name for the matcher (e.g., "$eq").
|
42
|
+
String is typically allocated from the pool. */
|
43
|
+
mongory_value *condition; /**< The condition (a `mongory_value`) that this
|
44
|
+
matcher evaluates against. */
|
45
|
+
mongory_matcher_match_func match; /**< Function pointer to the specific matching
|
46
|
+
logic for this matcher type. */
|
47
|
+
mongory_memory_pool *pool; /**< The memory pool used for allocations related
|
48
|
+
to this matcher instance. */
|
49
|
+
mongory_matcher_traverse_func explain; /**< Function pointer to the explanation
|
50
|
+
logic for this matcher type. */
|
51
|
+
mongory_matcher_match_func original_match; /**< Stores the original match function, potentially for
|
52
|
+
restoration or delegation. */
|
53
|
+
size_t sub_count; /**< The number of sub-matchers. */
|
54
|
+
mongory_matcher_traverse_func traverse; /**< Function pointer to the traversal logic for this matcher type. */
|
55
|
+
mongory_array *trace_stack; /**< The trace stack for this matcher. */
|
56
|
+
int trace_level; /**< The trace level for this matcher. */
|
57
|
+
double priority; /**< The priority for this matcher. */
|
58
|
+
void *extern_ctx; /**< External context for the matcher. */
|
59
|
+
};
|
60
|
+
|
61
|
+
/**
|
62
|
+
* @brief Creates a new base `mongory_matcher` instance and initializes its
|
63
|
+
* common fields.
|
64
|
+
*
|
65
|
+
* This function allocates a `mongory_matcher` structure from the provided pool
|
66
|
+
* and sets its `pool` and `condition` fields. The `match` function and other
|
67
|
+
* specific fields must be set by the caller or derived matcher constructors.
|
68
|
+
* The context's original_match and trace are initialized to NULL.
|
69
|
+
*
|
70
|
+
* @param pool The memory pool to use for allocating the matcher.
|
71
|
+
* @param condition The `mongory_value` representing the condition for this
|
72
|
+
* matcher.
|
73
|
+
* @return mongory_matcher* A pointer to the newly allocated base matcher, or
|
74
|
+
* NULL on allocation failure.
|
75
|
+
*/
|
76
|
+
mongory_matcher *mongory_matcher_base_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
|
77
|
+
|
78
|
+
/**
|
79
|
+
* @brief Creates a new matcher that always evaluates to true.
|
80
|
+
*
|
81
|
+
* @param pool The memory pool for allocation.
|
82
|
+
* @param condition The condition value (often unused by this matcher but stored
|
83
|
+
* for consistency).
|
84
|
+
* @return mongory_matcher* A pointer to the "always true" matcher, or NULL on
|
85
|
+
* failure.
|
86
|
+
*/
|
87
|
+
mongory_matcher *mongory_matcher_always_true_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
|
88
|
+
|
89
|
+
/**
|
90
|
+
* @brief Creates a new matcher that always evaluates to false.
|
91
|
+
*
|
92
|
+
* @param pool The memory pool for allocation.
|
93
|
+
* @param condition The condition value (often unused by this matcher but stored
|
94
|
+
* for consistency).
|
95
|
+
* @return mongory_matcher* A pointer to the "always false" matcher, or NULL on
|
96
|
+
* failure.
|
97
|
+
*/
|
98
|
+
mongory_matcher *mongory_matcher_always_false_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
|
99
|
+
|
100
|
+
#endif /* MONGORY_MATCHER_BASE_H */
|
@@ -0,0 +1,217 @@
|
|
1
|
+
/**
|
2
|
+
* @file compare_matcher.c
|
3
|
+
* @brief Implements comparison matchers like `$eq`, `$gt`, `$lt`, etc.
|
4
|
+
*
|
5
|
+
* This file follows a factory pattern. A generic private constructor,
|
6
|
+
* `mongory_matcher_compare_new`, is used to create a base matcher.
|
7
|
+
* Each specific comparison operator (e.g., `$eq`, `$gt`) has its own public
|
8
|
+
* constructor (e.g., `mongory_matcher_equal_new`) that provides a specialized
|
9
|
+
* matching function to the generic constructor.
|
10
|
+
*/
|
11
|
+
#include "compare_matcher.h"
|
12
|
+
#include "base_matcher.h" // For mongory_matcher_base_new
|
13
|
+
#include <mongory-core.h> // For mongory_value, mongory_matcher types
|
14
|
+
#include "../foundations/utils.h"
|
15
|
+
|
16
|
+
/**
|
17
|
+
* @brief Generic constructor for comparison matchers.
|
18
|
+
*
|
19
|
+
* Initializes a base matcher and sets its `match` function and
|
20
|
+
* `original_match` context field to the provided `match_func`.
|
21
|
+
*
|
22
|
+
* @param pool The memory pool for allocation.
|
23
|
+
* @param condition The `mongory_value` to be stored as the comparison target.
|
24
|
+
* @param match_func The specific comparison logic function (e.g., for equality,
|
25
|
+
* greater than).
|
26
|
+
* @return mongory_matcher* A pointer to the newly created comparison matcher,
|
27
|
+
* or NULL on failure.
|
28
|
+
*/
|
29
|
+
static inline mongory_matcher *mongory_matcher_compare_new(mongory_memory_pool *pool, mongory_value *condition,
|
30
|
+
mongory_matcher_match_func match_func, void *extern_ctx) {
|
31
|
+
mongory_matcher *matcher = mongory_matcher_base_new(pool, condition, extern_ctx);
|
32
|
+
if (matcher == NULL) {
|
33
|
+
return NULL; // Base matcher allocation failed.
|
34
|
+
}
|
35
|
+
matcher->match = match_func;
|
36
|
+
matcher->original_match = match_func; // Store original match function
|
37
|
+
return matcher;
|
38
|
+
}
|
39
|
+
|
40
|
+
// ============================================================================
|
41
|
+
// Static Match Functions
|
42
|
+
//
|
43
|
+
// These functions contain the actual logic for each comparison operator.
|
44
|
+
// They all follow the `mongory_matcher_match_func` signature and are passed
|
45
|
+
// to the generic constructor.
|
46
|
+
// ============================================================================
|
47
|
+
|
48
|
+
/**
|
49
|
+
* @brief Match function for equality ($eq).
|
50
|
+
*
|
51
|
+
* Compares the input `value` with the matcher's `condition` using the
|
52
|
+
* polymorphic `comp` function of the value.
|
53
|
+
*
|
54
|
+
* @param matcher The equality matcher instance.
|
55
|
+
* @param value The value to check.
|
56
|
+
* @return True if `value` is equal to `matcher->condition`; false otherwise or
|
57
|
+
* if the types are not comparable.
|
58
|
+
*/
|
59
|
+
static inline bool mongory_matcher_equal_match(mongory_matcher *matcher, mongory_value *value) {
|
60
|
+
if (!value || !value->comp || !matcher->condition)
|
61
|
+
return false; // Invalid inputs
|
62
|
+
int result = value->comp(value, matcher->condition);
|
63
|
+
if (result == mongory_value_compare_fail) {
|
64
|
+
return false; // Types are not comparable or other comparison error.
|
65
|
+
}
|
66
|
+
return result == 0; // 0 indicates equality.
|
67
|
+
}
|
68
|
+
|
69
|
+
mongory_matcher *mongory_matcher_equal_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
|
70
|
+
mongory_matcher *matcher = mongory_matcher_compare_new(pool, condition, mongory_matcher_equal_match, extern_ctx);
|
71
|
+
if (!matcher) {
|
72
|
+
return NULL;
|
73
|
+
}
|
74
|
+
matcher->name = mongory_string_cpy(pool, "Eq");
|
75
|
+
matcher->priority = 1.0;
|
76
|
+
return matcher;
|
77
|
+
}
|
78
|
+
|
79
|
+
/**
|
80
|
+
* @brief Match function for inequality ($ne).
|
81
|
+
*
|
82
|
+
* This is the logical inverse of the `equal_match` function.
|
83
|
+
* If the types are not comparable (comparison fails), it is considered "not
|
84
|
+
* equal", so this function returns true in that case.
|
85
|
+
*
|
86
|
+
* @param matcher The inequality matcher instance.
|
87
|
+
* @param value The value to check.
|
88
|
+
* @return True if `value` is not equal to `matcher->condition` or if they
|
89
|
+
* are not comparable.
|
90
|
+
*/
|
91
|
+
static inline bool mongory_matcher_not_equal_match(mongory_matcher *matcher, mongory_value *value) {
|
92
|
+
if (!value || !value->comp || !matcher->condition)
|
93
|
+
return true; // Invalid inputs, treat as "not equal"
|
94
|
+
int result = value->comp(value, matcher->condition);
|
95
|
+
if (result == mongory_value_compare_fail) {
|
96
|
+
return true; // Incomparable types are considered "not equal".
|
97
|
+
}
|
98
|
+
return result != 0; // Non-zero indicates inequality.
|
99
|
+
}
|
100
|
+
|
101
|
+
mongory_matcher *mongory_matcher_not_equal_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
|
102
|
+
mongory_matcher *matcher = mongory_matcher_compare_new(pool, condition, mongory_matcher_not_equal_match, extern_ctx);
|
103
|
+
if (!matcher) {
|
104
|
+
return NULL;
|
105
|
+
}
|
106
|
+
matcher->name = mongory_string_cpy(pool, "Ne");
|
107
|
+
matcher->priority = 1.0;
|
108
|
+
return matcher;
|
109
|
+
}
|
110
|
+
|
111
|
+
/**
|
112
|
+
* @brief Match function for "greater than" ($gt).
|
113
|
+
* @param matcher The $gt matcher instance.
|
114
|
+
* @param value The value to check.
|
115
|
+
* @return True if `value` is greater than `matcher->condition`, false
|
116
|
+
* otherwise or on comparison failure.
|
117
|
+
*/
|
118
|
+
static inline bool mongory_matcher_greater_than_match(mongory_matcher *matcher, mongory_value *value) {
|
119
|
+
if (!value || !value->comp || !matcher->condition)
|
120
|
+
return false;
|
121
|
+
int result = value->comp(value, matcher->condition);
|
122
|
+
if (result == mongory_value_compare_fail) {
|
123
|
+
return false;
|
124
|
+
}
|
125
|
+
return result == 1; // 1 indicates value > condition.
|
126
|
+
}
|
127
|
+
|
128
|
+
mongory_matcher *mongory_matcher_greater_than_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
|
129
|
+
mongory_matcher *matcher = mongory_matcher_compare_new(pool, condition, mongory_matcher_greater_than_match, extern_ctx);
|
130
|
+
if (!matcher) {
|
131
|
+
return NULL;
|
132
|
+
}
|
133
|
+
matcher->name = mongory_string_cpy(pool, "Gt");
|
134
|
+
matcher->priority = 2.0;
|
135
|
+
return matcher;
|
136
|
+
}
|
137
|
+
|
138
|
+
/**
|
139
|
+
* @brief Match function for "less than" ($lt).
|
140
|
+
* @param matcher The $lt matcher instance.
|
141
|
+
* @param value The value to check.
|
142
|
+
* @return True if `value` is less than `matcher->condition`, false otherwise or
|
143
|
+
* on comparison failure.
|
144
|
+
*/
|
145
|
+
static inline bool mongory_matcher_less_than_match(mongory_matcher *matcher, mongory_value *value) {
|
146
|
+
if (!value || !value->comp || !matcher->condition)
|
147
|
+
return false;
|
148
|
+
int result = value->comp(value, matcher->condition);
|
149
|
+
if (result == mongory_value_compare_fail) {
|
150
|
+
return false;
|
151
|
+
}
|
152
|
+
return result == -1; // -1 indicates value < condition.
|
153
|
+
}
|
154
|
+
|
155
|
+
mongory_matcher *mongory_matcher_less_than_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
|
156
|
+
mongory_matcher *matcher = mongory_matcher_compare_new(pool, condition, mongory_matcher_less_than_match, extern_ctx);
|
157
|
+
if (!matcher) {
|
158
|
+
return NULL;
|
159
|
+
}
|
160
|
+
matcher->name = mongory_string_cpy(pool, "Lt");
|
161
|
+
matcher->priority = 2.0;
|
162
|
+
return matcher;
|
163
|
+
}
|
164
|
+
|
165
|
+
/**
|
166
|
+
* @brief Match function for "greater than or equal" ($gte).
|
167
|
+
* @param matcher The $gte matcher instance.
|
168
|
+
* @param value The value to check.
|
169
|
+
* @return True if `value` is >= `matcher->condition`, false otherwise or on
|
170
|
+
* comparison failure.
|
171
|
+
*/
|
172
|
+
static inline bool mongory_matcher_greater_than_or_equal_match(mongory_matcher *matcher, mongory_value *value) {
|
173
|
+
if (!value || !value->comp || !matcher->condition)
|
174
|
+
return false;
|
175
|
+
int result = value->comp(value, matcher->condition);
|
176
|
+
if (result == mongory_value_compare_fail) {
|
177
|
+
return false;
|
178
|
+
}
|
179
|
+
return result >= 0; // 0 or 1 indicates value >= condition.
|
180
|
+
}
|
181
|
+
|
182
|
+
mongory_matcher *mongory_matcher_greater_than_or_equal_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
|
183
|
+
mongory_matcher *matcher = mongory_matcher_compare_new(pool, condition, mongory_matcher_greater_than_or_equal_match, extern_ctx);
|
184
|
+
if (!matcher) {
|
185
|
+
return NULL;
|
186
|
+
}
|
187
|
+
matcher->name = mongory_string_cpy(pool, "Gte");
|
188
|
+
matcher->priority = 2.0;
|
189
|
+
return matcher;
|
190
|
+
}
|
191
|
+
|
192
|
+
/**
|
193
|
+
* @brief Match function for "less than or equal" ($lte).
|
194
|
+
* @param matcher The $lte matcher instance.
|
195
|
+
* @param value The value to check.
|
196
|
+
* @return True if `value` is <= `matcher->condition`, false otherwise or on
|
197
|
+
* comparison failure.
|
198
|
+
*/
|
199
|
+
static inline bool mongory_matcher_less_than_or_equal_match(mongory_matcher *matcher, mongory_value *value) {
|
200
|
+
if (!value || !value->comp || !matcher->condition)
|
201
|
+
return false;
|
202
|
+
int result = value->comp(value, matcher->condition);
|
203
|
+
if (result == mongory_value_compare_fail) {
|
204
|
+
return false;
|
205
|
+
}
|
206
|
+
return result <= 0; // 0 or -1 indicates value <= condition.
|
207
|
+
}
|
208
|
+
|
209
|
+
mongory_matcher *mongory_matcher_less_than_or_equal_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
|
210
|
+
mongory_matcher *matcher = mongory_matcher_compare_new(pool, condition, mongory_matcher_less_than_or_equal_match, extern_ctx);
|
211
|
+
if (!matcher) {
|
212
|
+
return NULL;
|
213
|
+
}
|
214
|
+
matcher->name = mongory_string_cpy(pool, "Lte");
|
215
|
+
matcher->priority = 2.0;
|
216
|
+
return matcher;
|
217
|
+
}
|