mongory 0.6.2 → 0.7.0

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 (87) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/CHANGELOG.md +42 -0
  4. data/README.md +82 -176
  5. data/Rakefile +77 -0
  6. data/SUBMODULE_INTEGRATION.md +325 -0
  7. data/docs/advanced_usage.md +40 -0
  8. data/docs/clang_bridge.md +69 -0
  9. data/docs/field_names.md +30 -0
  10. data/docs/migration.md +30 -0
  11. data/docs/performance.md +61 -0
  12. data/examples/benchmark.rb +98 -19
  13. data/ext/mongory_ext/extconf.rb +91 -0
  14. data/ext/mongory_ext/mongory-core/LICENSE +3 -0
  15. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/array.h +105 -0
  16. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/config.h +206 -0
  17. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/error.h +82 -0
  18. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/memory_pool.h +95 -0
  19. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/table.h +108 -0
  20. data/ext/mongory_ext/mongory-core/include/mongory-core/foundations/value.h +175 -0
  21. data/ext/mongory_ext/mongory-core/include/mongory-core/matchers/matcher.h +76 -0
  22. data/ext/mongory_ext/mongory-core/include/mongory-core.h +12 -0
  23. data/ext/mongory_ext/mongory-core/src/foundations/array.c +246 -0
  24. data/ext/mongory_ext/mongory-core/src/foundations/array_private.h +18 -0
  25. data/ext/mongory_ext/mongory-core/src/foundations/config.c +406 -0
  26. data/ext/mongory_ext/mongory-core/src/foundations/config_private.h +30 -0
  27. data/ext/mongory_ext/mongory-core/src/foundations/error.c +30 -0
  28. data/ext/mongory_ext/mongory-core/src/foundations/memory_pool.c +298 -0
  29. data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.c +65 -0
  30. data/ext/mongory_ext/mongory-core/src/foundations/string_buffer.h +49 -0
  31. data/ext/mongory_ext/mongory-core/src/foundations/table.c +458 -0
  32. data/ext/mongory_ext/mongory-core/src/foundations/value.c +459 -0
  33. data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.c +309 -0
  34. data/ext/mongory_ext/mongory-core/src/matchers/array_record_matcher.h +47 -0
  35. data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.c +161 -0
  36. data/ext/mongory_ext/mongory-core/src/matchers/base_matcher.h +115 -0
  37. data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.c +210 -0
  38. data/ext/mongory_ext/mongory-core/src/matchers/compare_matcher.h +83 -0
  39. data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.c +539 -0
  40. data/ext/mongory_ext/mongory-core/src/matchers/composite_matcher.h +125 -0
  41. data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.c +144 -0
  42. data/ext/mongory_ext/mongory-core/src/matchers/existance_matcher.h +48 -0
  43. data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.c +121 -0
  44. data/ext/mongory_ext/mongory-core/src/matchers/external_matcher.h +46 -0
  45. data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.c +199 -0
  46. data/ext/mongory_ext/mongory-core/src/matchers/inclusion_matcher.h +46 -0
  47. data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.c +334 -0
  48. data/ext/mongory_ext/mongory-core/src/matchers/literal_matcher.h +97 -0
  49. data/ext/mongory_ext/mongory-core/src/matchers/matcher.c +196 -0
  50. data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.c +107 -0
  51. data/ext/mongory_ext/mongory-core/src/matchers/matcher_explainable.h +50 -0
  52. data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.c +55 -0
  53. data/ext/mongory_ext/mongory-core/src/matchers/matcher_traversable.h +23 -0
  54. data/ext/mongory_ext/mongory_ext.c +635 -0
  55. data/lib/mongory/c_query_builder.rb +44 -0
  56. data/lib/mongory/converters/converted.rb +2 -2
  57. data/lib/mongory/matchers/abstract_matcher.rb +4 -0
  58. data/lib/mongory/matchers/abstract_multi_matcher.rb +44 -0
  59. data/lib/mongory/matchers/and_matcher.rb +2 -23
  60. data/lib/mongory/matchers/array_record_matcher.rb +2 -23
  61. data/lib/mongory/matchers/elem_match_matcher.rb +4 -0
  62. data/lib/mongory/matchers/eq_matcher.rb +4 -0
  63. data/lib/mongory/matchers/every_matcher.rb +4 -0
  64. data/lib/mongory/matchers/exists_matcher.rb +4 -0
  65. data/lib/mongory/matchers/field_matcher.rb +4 -0
  66. data/lib/mongory/matchers/gt_matcher.rb +4 -0
  67. data/lib/mongory/matchers/gte_matcher.rb +4 -0
  68. data/lib/mongory/matchers/hash_condition_matcher.rb +2 -23
  69. data/lib/mongory/matchers/in_matcher.rb +13 -4
  70. data/lib/mongory/matchers/literal_matcher.rb +4 -0
  71. data/lib/mongory/matchers/lt_matcher.rb +4 -0
  72. data/lib/mongory/matchers/lte_matcher.rb +4 -0
  73. data/lib/mongory/matchers/ne_matcher.rb +4 -0
  74. data/lib/mongory/matchers/nin_matcher.rb +14 -5
  75. data/lib/mongory/matchers/not_matcher.rb +4 -0
  76. data/lib/mongory/matchers/or_matcher.rb +2 -23
  77. data/lib/mongory/matchers/present_matcher.rb +4 -0
  78. data/lib/mongory/matchers/regex_matcher.rb +4 -0
  79. data/lib/mongory/matchers/size_matcher.rb +4 -0
  80. data/lib/mongory/query_builder.rb +8 -0
  81. data/lib/mongory/utils/context.rb +7 -0
  82. data/lib/mongory/utils.rb +1 -1
  83. data/lib/mongory/version.rb +1 -1
  84. data/lib/mongory.rb +7 -0
  85. data/mongory.gemspec +10 -4
  86. data/scripts/build_with_core.sh +292 -0
  87. metadata +70 -5
@@ -0,0 +1,144 @@
1
+ /**
2
+ * @file existance_matcher.c
3
+ * @brief Implements $exists and $present matchers.
4
+ * This is an internal implementation file for the matcher module.
5
+ */
6
+ #include "existance_matcher.h"
7
+ #include "base_matcher.h" // For mongory_matcher_base_new
8
+ #include "mongory-core/foundations/array.h" // For value->data.a->count
9
+ #include "mongory-core/foundations/error.h" // For mongory_error types
10
+ #include "mongory-core/foundations/table.h" // For value->data.t->count
11
+ #include "mongory-core/foundations/value.h" // For mongory_value types
12
+ #include <mongory-core.h> // General include
13
+
14
+ /**
15
+ * @brief Validates that the condition for an existence/presence matcher is a
16
+ * boolean.
17
+ * @param condition The `mongory_value` condition to validate.
18
+ * @return True if `condition` is not NULL and is of type `MONGORY_TYPE_BOOL`,
19
+ * false otherwise.
20
+ */
21
+ static bool mongory_matcher_validate_bool_condition(mongory_value *condition) {
22
+ if (condition == NULL) {
23
+ return false; // Condition itself must exist.
24
+ }
25
+ if (condition->type != MONGORY_TYPE_BOOL) {
26
+ return false; // Condition must be a boolean value.
27
+ }
28
+ return true;
29
+ }
30
+
31
+ /**
32
+ * @brief Match function for the $exists matcher.
33
+ *
34
+ * Compares the existence of the input `value` (i.e., `value != NULL`)
35
+ * with the boolean `matcher->condition->data.b`.
36
+ * - If `condition` is true, matches if `value` is not NULL.
37
+ * - If `condition` is false, matches if `value` is NULL.
38
+ *
39
+ * @param matcher The $exists matcher instance. Its condition must be boolean.
40
+ * @param value The value whose existence is being checked. This is typically
41
+ * the result of a field lookup.
42
+ * @return True if the existence matches the condition, false otherwise.
43
+ */
44
+ static inline bool mongory_matcher_exists_match(mongory_matcher *matcher, mongory_value *value) {
45
+ // The condition for $exists is a boolean (e.g., field: {$exists: true})
46
+ bool condition_expects_existence = matcher->condition->data.b;
47
+ bool actual_value_exists = (value != NULL);
48
+
49
+ return condition_expects_existence == actual_value_exists;
50
+ }
51
+
52
+ mongory_matcher *mongory_matcher_exists_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
53
+ if (!mongory_matcher_validate_bool_condition(condition)) {
54
+ pool->error = MG_ALLOC_PTR(pool, mongory_error);
55
+ if (pool->error) {
56
+ pool->error->type = MONGORY_ERROR_INVALID_ARGUMENT;
57
+ pool->error->message = "$exists condition must be a boolean value.";
58
+ }
59
+ return NULL;
60
+ }
61
+ mongory_matcher *matcher = mongory_matcher_base_new(pool, condition, extern_ctx);
62
+ if (!matcher) {
63
+ return NULL;
64
+ }
65
+ matcher->match = mongory_matcher_exists_match;
66
+ matcher->original_match = mongory_matcher_exists_match;
67
+ matcher->name = mongory_string_cpy(pool, "Exists");
68
+ return matcher;
69
+ }
70
+
71
+ /**
72
+ * @brief Match function for the $present matcher.
73
+ *
74
+ * Determines if a `value` is "present" based on its type and content,
75
+ * and compares this with the boolean `matcher->condition->data.b`.
76
+ * - NULL value: Present if condition is false.
77
+ * - Array: Present if non-empty and condition is true.
78
+ * - Table: Present if non-empty and condition is true.
79
+ * - String: Present if non-NULL, non-empty, and condition is true.
80
+ * - MONGORY_TYPE_NULL: Not present (matches if condition is false).
81
+ * - Boolean: Present if its value matches the condition.
82
+ * - Other non-NULL types: Considered present if condition is true.
83
+ *
84
+ * @param matcher The $present matcher. Its condition must be boolean.
85
+ * @param value The value to check for presence.
86
+ * @return True if the presence status matches the condition, false otherwise.
87
+ */
88
+ static inline bool mongory_matcher_present_match(mongory_matcher *matcher, mongory_value *value) {
89
+ bool condition_expects_presence = matcher->condition->data.b;
90
+
91
+ if (value == NULL) {
92
+ // A NULL value (field does not exist) is "present" if condition_expects_presence is false.
93
+ return !condition_expects_presence;
94
+ }
95
+
96
+ bool actual_value_is_present;
97
+ switch (value->type) {
98
+ case MONGORY_TYPE_ARRAY:
99
+ actual_value_is_present = (value->data.a != NULL && value->data.a->count > 0);
100
+ break;
101
+ case MONGORY_TYPE_TABLE:
102
+ actual_value_is_present = (value->data.t != NULL && value->data.t->count > 0);
103
+ break;
104
+ case MONGORY_TYPE_STRING:
105
+ actual_value_is_present = (value->data.s != NULL && *(value->data.s) != '\0');
106
+ break;
107
+ case MONGORY_TYPE_NULL:
108
+ actual_value_is_present = false; // An explicit BSON-style Null is not "present".
109
+ break;
110
+ case MONGORY_TYPE_BOOL:
111
+ // For a boolean value, "present" means its own value matches the condition.
112
+ // e.g. {$present: true} on a true boolean is true.
113
+ // {$present: false} on a true boolean is false.
114
+ // {$present: true} on a false boolean is false.
115
+ // {$present: false} on a false boolean is true.
116
+ return value->data.b == condition_expects_presence;
117
+ default:
118
+ // For other types (int, double, pointer, regex, unsupported), if they are
119
+ // not NULL (checked above), they are considered "present".
120
+ actual_value_is_present = true;
121
+ break;
122
+ }
123
+
124
+ return condition_expects_presence == actual_value_is_present;
125
+ }
126
+
127
+ mongory_matcher *mongory_matcher_present_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
128
+ if (!mongory_matcher_validate_bool_condition(condition)) {
129
+ pool->error = MG_ALLOC_PTR(pool, mongory_error);
130
+ if (pool->error) {
131
+ pool->error->type = MONGORY_ERROR_INVALID_ARGUMENT;
132
+ pool->error->message = "$present condition must be a boolean value.";
133
+ }
134
+ return NULL;
135
+ }
136
+ mongory_matcher *matcher = mongory_matcher_base_new(pool, condition, extern_ctx);
137
+ if (!matcher) {
138
+ return NULL;
139
+ }
140
+ matcher->match = mongory_matcher_present_match;
141
+ matcher->original_match = mongory_matcher_present_match;
142
+ matcher->name = mongory_string_cpy(pool, "Present");
143
+ return matcher;
144
+ }
@@ -0,0 +1,48 @@
1
+ #ifndef MONGORY_MATCHER_EXISTANCE_H
2
+ #define MONGORY_MATCHER_EXISTANCE_H
3
+
4
+ /**
5
+ * @file existance_matcher.h
6
+ * @brief Defines constructors for existence and presence matchers ($exists,
7
+ * $present). This is an internal header for the matcher module.
8
+ */
9
+
10
+ #include "base_matcher.h"
11
+ #include "mongory-core/foundations/memory_pool.h"
12
+ #include "mongory-core/foundations/value.h"
13
+ #include "mongory-core/matchers/matcher.h" // For mongory_matcher structure
14
+
15
+ /**
16
+ * @brief Creates an "exists" ($exists) matcher.
17
+ *
18
+ * This matcher checks for the existence of a field (i.e., if a value is
19
+ * non-NULL when retrieved from a table or array). The `condition` for this
20
+ * matcher must be a boolean `mongory_value`.
21
+ * - If `condition` is true, it matches if the field exists (value is not NULL).
22
+ * - If `condition` is false, it matches if the field does not exist (value is
23
+ * NULL).
24
+ *
25
+ * @param pool Memory pool for allocation.
26
+ * @param condition A `mongory_value` of type `MONGORY_TYPE_BOOL`.
27
+ * @return A new `$exists` matcher, or NULL on failure or invalid condition.
28
+ */
29
+ mongory_matcher *mongory_matcher_exists_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
30
+
31
+ /**
32
+ * @brief Creates a "present" ($present) matcher.
33
+ *
34
+ * This matcher provides a more nuanced check than `$exists`. It considers a
35
+ * value "present" based on its type and content (e.g., non-empty array/table,
36
+ * non-empty string). The `condition` for this matcher must be a boolean
37
+ * `mongory_value`.
38
+ * - If `condition` is true, it matches if the value is considered "present".
39
+ * - If `condition` is false, it matches if the value is not "present" (e.g.,
40
+ * NULL, empty array/table/string).
41
+ *
42
+ * @param pool Memory pool for allocation.
43
+ * @param condition A `mongory_value` of type `MONGORY_TYPE_BOOL`.
44
+ * @return A new `$present` matcher, or NULL on failure or invalid condition.
45
+ */
46
+ mongory_matcher *mongory_matcher_present_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
47
+
48
+ #endif /* MONGORY_MATCHER_EXISTANCE_H */
@@ -0,0 +1,121 @@
1
+ /**
2
+ * @file external_matcher.c
3
+ * @brief Implements the $regex matcher.
4
+ * This is an internal implementation file for the matcher module.
5
+ */
6
+ #include "external_matcher.h"
7
+ #include "../foundations/config_private.h" // For mongory_internal_regex_adapter
8
+ #include "base_matcher.h" // For mongory_matcher_base_new
9
+ #include "mongory-core/foundations/config.h" // For mongory_string_cpy
10
+ #include "mongory-core/foundations/error.h" // For MONGORY_ERROR_INVALID_ARGUMENT
11
+ #include "mongory-core/foundations/memory_pool.h"
12
+ #include "mongory-core/foundations/value.h"
13
+ #include "matcher_explainable.h"
14
+ #include "matcher_traversable.h"
15
+
16
+ /**
17
+ * @brief Match function for the $regex matcher.
18
+ *
19
+ * Checks if the input `value` (which must be a string) matches the regex
20
+ * pattern stored in `matcher->condition`. The actual regex matching is
21
+ * performed by the function pointed to by
22
+ * `mongory_internal_regex_adapter->match_func`.
23
+ *
24
+ * @param matcher The $regex matcher instance.
25
+ * @param value The `mongory_value` to test; must be of type
26
+ * `MONGORY_TYPE_STRING`.
27
+ * @return True if the string value matches the regex, false otherwise or if
28
+ * input is not a string.
29
+ */
30
+ static inline bool mongory_matcher_regex_match(mongory_matcher *matcher, mongory_value *value) {
31
+ if (!value || value->type != MONGORY_TYPE_STRING) {
32
+ return false; // Regex matching applies only to strings.
33
+ }
34
+ if (!mongory_internal_regex_adapter || !mongory_internal_regex_adapter->match_func) {
35
+ return false; // Regex adapter or function not configured.
36
+ }
37
+
38
+ // Delegate to the configured regex function.
39
+ return mongory_internal_regex_adapter->match_func(matcher->pool, matcher->condition, value);
40
+ }
41
+
42
+ /**
43
+ * @brief Validates that the condition for a $regex matcher is valid.
44
+ * The condition must be non-NULL and either a `MONGORY_TYPE_STRING` (pattern)
45
+ * or `MONGORY_TYPE_REGEX` (pre-compiled regex object).
46
+ * @param condition The `mongory_value` condition to validate.
47
+ * @return True if the condition is valid for a regex matcher, false otherwise.
48
+ */
49
+ static inline bool mongory_matcher_regex_condition_validate(mongory_value *condition) {
50
+ return condition != NULL && (condition->type == MONGORY_TYPE_STRING || condition->type == MONGORY_TYPE_REGEX);
51
+ }
52
+
53
+ mongory_matcher *mongory_matcher_regex_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
54
+ if (!mongory_matcher_regex_condition_validate(condition)) {
55
+ pool->error = MG_ALLOC_PTR(pool, mongory_error);
56
+ if (pool->error) {
57
+ pool->error->type = MONGORY_ERROR_INVALID_ARGUMENT;
58
+ pool->error->message = "$regex condition must be a string or a regex object.";
59
+ }
60
+ return NULL;
61
+ }
62
+
63
+ mongory_matcher *matcher = mongory_matcher_base_new(pool, condition, extern_ctx);
64
+ if (!matcher) {
65
+ return NULL;
66
+ }
67
+ matcher->match = mongory_matcher_regex_match;
68
+ matcher->original_match = mongory_matcher_regex_match;
69
+ matcher->name = mongory_string_cpy(pool, "Regex");
70
+ return matcher;
71
+ }
72
+
73
+ typedef struct mongory_custom_matcher {
74
+ mongory_matcher base;
75
+ void *external_matcher;
76
+ } mongory_custom_matcher;
77
+
78
+ /**
79
+ * @brief The match function for a custom matcher.
80
+ * @param matcher The matcher to match against.
81
+ * @param value The value to match.
82
+ * @return True if the value matches the matcher, false otherwise.
83
+ */
84
+ bool mongory_matcher_custom_match(mongory_matcher *matcher, mongory_value *value) {
85
+ if (mongory_custom_matcher_adapter == NULL || mongory_custom_matcher_adapter->match == NULL) {
86
+ return false; // Custom matcher adapter not initialized.
87
+ }
88
+ mongory_custom_matcher *custom_matcher = (mongory_custom_matcher *)matcher;
89
+ return mongory_custom_matcher_adapter->match(custom_matcher->external_matcher, value);
90
+ }
91
+
92
+ /**
93
+ * @brief Creates a new custom matcher instance.
94
+ *
95
+ * @param pool The memory pool to use for the matcher's allocations.
96
+ * @param key The key for the custom matcher.
97
+ * @param condition The `mongory_value` representing the condition for this
98
+ * matcher.
99
+ * @return mongory_matcher* A pointer to the newly created custom matcher, or
100
+ * NULL on failure.
101
+ */
102
+ mongory_matcher *mongory_matcher_custom_new(mongory_memory_pool *pool, char *key, mongory_value *condition, void *extern_ctx) {
103
+ if (mongory_custom_matcher_adapter == NULL || mongory_custom_matcher_adapter->build == NULL)
104
+ return NULL; // Custom matcher adapter not initialized.
105
+ mongory_custom_matcher *matcher = MG_ALLOC_PTR(pool, mongory_custom_matcher);
106
+ if (matcher == NULL)
107
+ return NULL;
108
+ mongory_matcher_custom_context *context = mongory_custom_matcher_adapter->build(key, condition, extern_ctx);
109
+ if (context == NULL)
110
+ return NULL;
111
+ matcher->base.pool = pool;
112
+ matcher->base.condition = condition;
113
+ matcher->base.name = context->name;
114
+ matcher->base.match = mongory_matcher_custom_match;
115
+ matcher->base.original_match = mongory_matcher_custom_match;
116
+ matcher->base.explain = mongory_matcher_base_explain;
117
+ matcher->base.traverse = mongory_matcher_leaf_traverse;
118
+ matcher->base.extern_ctx = extern_ctx;
119
+ matcher->external_matcher = context->external_matcher;
120
+ return (mongory_matcher *)matcher;
121
+ }
@@ -0,0 +1,46 @@
1
+ #ifndef MONGORY_MATCHER_EXTERNAL_H
2
+ #define MONGORY_MATCHER_EXTERNAL_H
3
+
4
+ /**
5
+ * @file external_matcher.h
6
+ * @brief Defines the constructor for matchers that are not built-in.
7
+ * This is an internal header for the matcher module.
8
+ */
9
+
10
+ #include "base_matcher.h"
11
+ #include "mongory-core/foundations/memory_pool.h"
12
+ #include "mongory-core/foundations/value.h"
13
+ #include "mongory-core/matchers/matcher.h" // For mongory_matcher structure
14
+
15
+ /**
16
+ * @brief Creates a regular expression ($regex) matcher.
17
+ *
18
+ * This matcher tests if an input string `mongory_value` matches a given regular
19
+ * expression. The regular expression itself is provided in the `condition`
20
+ * value, which can be either a `MONGORY_TYPE_STRING` (the pattern) or a
21
+ * `MONGORY_TYPE_REGEX` (a pre-compiled regex object, if the underlying regex
22
+ * engine supports it and it's wrapped). The actual regex matching logic is
23
+ * delegated to a function provided via `mongory_regex_func_set` (see
24
+ * `foundations/config.h`).
25
+ *
26
+ * @param pool Memory pool for allocation.
27
+ * @param condition A `mongory_value` representing the regex pattern. This
28
+ * should be of type `MONGORY_TYPE_STRING` or `MONGORY_TYPE_REGEX`.
29
+ * @return A new $regex matcher, or NULL on failure or if the condition is
30
+ * invalid.
31
+ */
32
+ mongory_matcher *mongory_matcher_regex_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
33
+
34
+ /**
35
+ * @brief Creates a new custom matcher instance.
36
+ *
37
+ * @param pool The memory pool to use for the matcher's allocations.
38
+ * @param key The key for the custom matcher.
39
+ * @param condition The `mongory_value` representing the condition for this
40
+ * matcher.
41
+ * @return mongory_matcher* A pointer to the newly created custom matcher, or
42
+ * NULL on failure.
43
+ */
44
+ mongory_matcher *mongory_matcher_custom_new(mongory_memory_pool *pool, char *key, mongory_value *condition, void *extern_ctx);
45
+
46
+ #endif /* MONGORY_MATCHER_EXTERNAL_H */
@@ -0,0 +1,199 @@
1
+ /**
2
+ * @file inclusion_matcher.c
3
+ * @brief Implements $in and $nin matchers for checking value inclusion in an
4
+ * array. This is an internal implementation file for the matcher module.
5
+ */
6
+ #include "inclusion_matcher.h"
7
+ #include "base_matcher.h" // For mongory_matcher_base_new
8
+ #include "mongory-core/foundations/array.h" // For mongory_array operations
9
+ #include "mongory-core/foundations/error.h" // For mongory_error
10
+ #include "mongory-core/foundations/value.h" // For mongory_value
11
+ #include <mongory-core.h> // General include
12
+
13
+ /**
14
+ * @struct mongory_matcher_inclusion_context
15
+ * @brief Context structure used during the inclusion matching process.
16
+ *
17
+ * Stores the result of the comparison and the value being searched for.
18
+ */
19
+ typedef struct mongory_matcher_inclusion_context {
20
+ bool result; /**< Stores the outcome (true if found, false otherwise). */
21
+ mongory_value *value; /**< The value to search for within the condition array or
22
+ target array. */
23
+ } mongory_matcher_inclusion_context;
24
+
25
+ /**
26
+ * @brief Validates that the condition for an inclusion matcher is a valid
27
+ * array.
28
+ * @param condition The `mongory_value` condition to validate.
29
+ * @return True if `condition` is not NULL, is of type `MONGORY_TYPE_ARRAY`,
30
+ * and its internal array data `condition->data.a` is not NULL. False otherwise.
31
+ */
32
+ static inline bool mongory_matcher_validate_array_condition(mongory_value *condition) {
33
+ if (condition == NULL) {
34
+ return false; // Condition must exist.
35
+ }
36
+ if (condition->type != MONGORY_TYPE_ARRAY) {
37
+ return false; // Condition must be an array.
38
+ }
39
+ if (condition->data.a == NULL) {
40
+ return false; // The array data within the condition must be valid.
41
+ }
42
+ return true;
43
+ }
44
+
45
+ /**
46
+ * @brief Callback function for `mongory_array_each` to compare a single value
47
+ * (`b` from context) against an element (`a`) from the condition array.
48
+ *
49
+ * This is used when the value being matched against $in is a scalar.
50
+ *
51
+ * @param item_in_condition_array The current item (`a`) from the condition array.
52
+ * @param acc Pointer to `mongory_matcher_inclusion_context`. `context->value`
53
+ * is the scalar value (`b`) being searched for.
54
+ * @return `false` (stop iteration) if `item_in_condition_array` equals
55
+ * `context->value`, `true` (continue iteration) otherwise.
56
+ */
57
+ static inline bool mongory_matcher_inclusion_value_compare(mongory_value *item_in_condition_array, void *acc) {
58
+ mongory_matcher_inclusion_context *context = (mongory_matcher_inclusion_context *)acc;
59
+ mongory_value *scalar_value_to_find = context->value;
60
+
61
+ if (!item_in_condition_array || !scalar_value_to_find || !item_in_condition_array->comp) {
62
+ // Cannot compare if item or value is NULL, or if item has no compare function.
63
+ // Treat as not equal for safety, continue search.
64
+ return true;
65
+ }
66
+
67
+ // Check if the item from condition array equals the scalar value we're looking for.
68
+ context->result = (item_in_condition_array->comp(item_in_condition_array, scalar_value_to_find) == 0);
69
+
70
+ return !context->result; // Stop if found (result is true), continue if not found.
71
+ }
72
+
73
+ /**
74
+ * @brief Callback function for `mongory_array_each` to compare an array (`a`
75
+ * from context->value) against an element (`b_scalar_in_condition_array`) from
76
+ * the condition array.
77
+ *
78
+ * This is used when the value being matched against $in is an array itself.
79
+ * It checks if `b_scalar_in_condition_array` exists in the input array `a`.
80
+ *
81
+ * @param b_scalar_in_condition_array The current scalar item (`b`) from the condition array.
82
+ * @param acc Pointer to `mongory_matcher_inclusion_context`. `context->value`
83
+ * is the input array (`a`) being checked.
84
+ * @return `false` (stop iteration) if `b_scalar_in_condition_array` is found
85
+ * in `context->value` (the input array), `true` (continue) otherwise.
86
+ */
87
+ static inline bool mongory_matcher_inclusion_array_compare(mongory_value *b_scalar_in_condition_array, void *acc) {
88
+ mongory_matcher_inclusion_context *outer_context = (mongory_matcher_inclusion_context *)acc;
89
+ // outer_context->value is the input array we are checking.
90
+ mongory_array *input_array_a = outer_context->value->data.a;
91
+
92
+ // We want to find if b_scalar_in_condition_array is present in input_array_a.
93
+ // Create a new context for searching b_scalar_in_condition_array within input_array_a.
94
+ mongory_matcher_inclusion_context inner_search_ctx = {false, b_scalar_in_condition_array};
95
+
96
+ // Iterate through input_array_a to find b_scalar_in_condition_array.
97
+ // mongory_matcher_inclusion_value_compare will set inner_search_ctx.result to true if found.
98
+ // It returns !result, so if found (result=true), it returns false (stop).
99
+ bool iteration_stopped =
100
+ !input_array_a->each(input_array_a, &inner_search_ctx, mongory_matcher_inclusion_value_compare);
101
+
102
+ if (iteration_stopped && inner_search_ctx.result) {
103
+ // b_scalar_in_condition_array was found in input_array_a.
104
+ outer_context->result = true; // Mark overall $in as successful.
105
+ return false; // Stop iterating the outer (condition) array.
106
+ }
107
+
108
+ return true; // b_scalar_in_condition_array was not in input_array_a, continue.
109
+ }
110
+
111
+ /**
112
+ * @brief Match function for the $in matcher.
113
+ *
114
+ * If `value_to_check` is a scalar, it checks if `value_to_check` is present in
115
+ * `matcher->condition` (which must be an array).
116
+ * If `value_to_check` is an array, it checks if any element of `value_to_check`
117
+ * is present in `matcher->condition` (array intersection).
118
+ *
119
+ * @param matcher The $in matcher instance.
120
+ * @param value_to_check The value to check for inclusion.
121
+ * @return True if `value_to_check` (or one of its elements) is found in the
122
+ * condition array, false otherwise.
123
+ */
124
+ static inline bool mongory_matcher_in_match(mongory_matcher *matcher, mongory_value *value_to_check) {
125
+ if (!value_to_check || !matcher->condition || !matcher->condition->data.a) {
126
+ // Invalid inputs or condition is not a proper array.
127
+ return false;
128
+ }
129
+
130
+ mongory_matcher_inclusion_context ctx = {false, value_to_check};
131
+ mongory_array *condition_array = matcher->condition->data.a;
132
+
133
+ if (value_to_check->type == MONGORY_TYPE_ARRAY) {
134
+ if (value_to_check->data.a == NULL)
135
+ return false; // Invalid input array
136
+ // Case: $in: [c1, c2], input: [a1, a2]
137
+ // We need to check if any c_i is in [a1, a2] OR if any a_i is in [c1, c2].
138
+ // The current mongory_matcher_inclusion_array_compare checks if an element from
139
+ // condition_array is present in value_to_check (the input array).
140
+ // So, we iterate the condition_array, and for each element, check if it's in value_to_check.
141
+ condition_array->each(condition_array, &ctx, mongory_matcher_inclusion_array_compare);
142
+ } else {
143
+ // Case: $in: [c1, c2], input: scalar_value
144
+ // Check if scalar_value is any of c_i.
145
+ condition_array->each(condition_array, &ctx, mongory_matcher_inclusion_value_compare);
146
+ }
147
+ return ctx.result; // True if any comparison led to result=true.
148
+ }
149
+
150
+ mongory_matcher *mongory_matcher_in_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
151
+ if (!mongory_matcher_validate_array_condition(condition)) {
152
+ pool->error = MG_ALLOC_PTR(pool, mongory_error);
153
+ if (pool->error) {
154
+ pool->error->type = MONGORY_ERROR_INVALID_ARGUMENT;
155
+ pool->error->message = "$in condition must be a valid array.";
156
+ }
157
+ return NULL;
158
+ }
159
+ mongory_matcher *matcher = mongory_matcher_base_new(pool, condition, extern_ctx);
160
+ if (!matcher) {
161
+ return NULL;
162
+ }
163
+ matcher->match = mongory_matcher_in_match;
164
+ matcher->original_match = mongory_matcher_in_match;
165
+ matcher->name = mongory_string_cpy(pool, "In");
166
+ return matcher;
167
+ }
168
+
169
+ /**
170
+ * @brief Match function for the $nin (not in) matcher.
171
+ * Simply negates the result of the $in logic.
172
+ * @param matcher The $nin matcher instance.
173
+ * @param value The value to check.
174
+ * @return True if the value is NOT found according to $in logic, false
175
+ * otherwise.
176
+ */
177
+ static inline bool mongory_matcher_not_in_match(mongory_matcher *matcher, mongory_value *value) {
178
+ // $nin is true if $in is false.
179
+ return !mongory_matcher_in_match(matcher, value);
180
+ }
181
+
182
+ mongory_matcher *mongory_matcher_not_in_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx) {
183
+ if (!mongory_matcher_validate_array_condition(condition)) {
184
+ pool->error = MG_ALLOC_PTR(pool, mongory_error);
185
+ if (pool->error) {
186
+ pool->error->type = MONGORY_ERROR_INVALID_ARGUMENT;
187
+ pool->error->message = "$nin condition must be a valid array.";
188
+ }
189
+ return NULL;
190
+ }
191
+ mongory_matcher *matcher = mongory_matcher_base_new(pool, condition, extern_ctx);
192
+ if (!matcher) {
193
+ return NULL;
194
+ }
195
+ matcher->match = mongory_matcher_not_in_match;
196
+ matcher->original_match = mongory_matcher_not_in_match;
197
+ matcher->name = mongory_string_cpy(pool, "Nin");
198
+ return matcher;
199
+ }
@@ -0,0 +1,46 @@
1
+ #ifndef MONGORY_MATCHER_INCLUSION_H
2
+ #define MONGORY_MATCHER_INCLUSION_H
3
+
4
+ /**
5
+ * @file inclusion_matcher.h
6
+ * @brief Defines constructors for inclusion matchers ($in, $nin).
7
+ * This is an internal header for the matcher module.
8
+ *
9
+ * These matchers check if a value is present or not present in a given array
10
+ * of values.
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
+
18
+ /**
19
+ * @brief Creates an "in" ($in) matcher.
20
+ *
21
+ * Matches if the input value is equal to any of the values in the `condition`
22
+ * array. If the input value itself is an array, it matches if any element of
23
+ * the input array is found in the `condition` array (set intersection).
24
+ *
25
+ * @param pool Memory pool for allocation.
26
+ * @param condition A `mongory_value` of type `MONGORY_TYPE_ARRAY` containing
27
+ * the set of values to check against.
28
+ * @return A new `$in` matcher, or NULL on failure or invalid condition.
29
+ */
30
+ mongory_matcher *mongory_matcher_in_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
31
+
32
+ /**
33
+ * @brief Creates a "not in" ($nin) matcher.
34
+ *
35
+ * Matches if the input value is not equal to any of the values in the
36
+ * `condition` array. If the input value itself is an array, it matches if no
37
+ * element of the input array is found in the `condition` array.
38
+ *
39
+ * @param pool Memory pool for allocation.
40
+ * @param condition A `mongory_value` of type `MONGORY_TYPE_ARRAY` containing
41
+ * the set of values to check against.
42
+ * @return A new `$nin` matcher, or NULL on failure or invalid condition.
43
+ */
44
+ mongory_matcher *mongory_matcher_not_in_new(mongory_memory_pool *pool, mongory_value *condition, void *extern_ctx);
45
+
46
+ #endif /* MONGORY_MATCHER_INCLUSION_H */