mongory 0.4.0 → 0.6.1
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 +4 -4
- data/CHANGELOG.md +51 -0
- data/README.md +37 -7
- data/examples/benchmark-rails.rb +52 -0
- data/examples/benchmark.rb +79 -18
- data/lib/generators/mongory/matcher/matcher_generator.rb +1 -1
- data/lib/mongory/converters/abstract_converter.rb +22 -32
- data/lib/mongory/converters/condition_converter.rb +9 -19
- data/lib/mongory/converters/converted.rb +81 -0
- data/lib/mongory/converters/data_converter.rb +18 -7
- data/lib/mongory/converters/key_converter.rb +43 -19
- data/lib/mongory/converters/value_converter.rb +24 -19
- data/lib/mongory/converters.rb +1 -0
- data/lib/mongory/matchers/abstract_matcher.rb +94 -32
- data/lib/mongory/matchers/abstract_multi_matcher.rb +16 -45
- data/lib/mongory/matchers/and_matcher.rb +38 -10
- data/lib/mongory/matchers/array_record_matcher.rb +54 -28
- data/lib/mongory/matchers/elem_match_matcher.rb +13 -9
- data/lib/mongory/matchers/eq_matcher.rb +12 -7
- data/lib/mongory/matchers/every_matcher.rb +20 -9
- data/lib/mongory/matchers/exists_matcher.rb +15 -14
- data/lib/mongory/matchers/field_matcher.rb +58 -38
- data/lib/mongory/matchers/gt_matcher.rb +15 -7
- data/lib/mongory/matchers/gte_matcher.rb +15 -7
- data/lib/mongory/matchers/hash_condition_matcher.rb +54 -26
- data/lib/mongory/matchers/in_matcher.rb +20 -13
- data/lib/mongory/matchers/literal_matcher.rb +42 -48
- data/lib/mongory/matchers/lt_matcher.rb +15 -7
- data/lib/mongory/matchers/lte_matcher.rb +15 -7
- data/lib/mongory/matchers/ne_matcher.rb +12 -7
- data/lib/mongory/matchers/nin_matcher.rb +20 -12
- data/lib/mongory/matchers/not_matcher.rb +9 -5
- data/lib/mongory/matchers/or_matcher.rb +42 -13
- data/lib/mongory/matchers/present_matcher.rb +14 -15
- data/lib/mongory/matchers/regex_matcher.rb +37 -22
- data/lib/mongory/matchers/size_matcher.rb +50 -0
- data/lib/mongory/matchers.rb +1 -1
- data/lib/mongory/query_builder.rb +89 -27
- data/lib/mongory/query_matcher.rb +40 -13
- data/lib/mongory/query_operator.rb +1 -1
- data/lib/mongory/utils/context.rb +41 -0
- data/lib/mongory/utils/debugger.rb +6 -4
- data/lib/mongory/utils.rb +1 -0
- data/lib/mongory/version.rb +1 -1
- data/lib/mongory.rb +3 -3
- data/mongory.gemspec +3 -3
- metadata +11 -9
- data/lib/mongory/matchers/README.md +0 -57
- data/lib/mongory/matchers/abstract_operator_matcher.rb +0 -46
@@ -10,12 +10,19 @@ module Mongory
|
|
10
10
|
#
|
11
11
|
# Internally it compiles all conditions and invokes `QueryMatcher`.
|
12
12
|
#
|
13
|
-
# @example
|
13
|
+
# @example Basic query
|
14
14
|
# records.mongory
|
15
15
|
# .where(:age.gte => 18)
|
16
16
|
# .or({ :name => /J/ }, { :name.eq => 'Bob' })
|
17
17
|
# .limit(2)
|
18
18
|
# .to_a
|
19
|
+
#
|
20
|
+
# @example Complex query
|
21
|
+
# records.mongory
|
22
|
+
# .where(:status => 'active')
|
23
|
+
# .not(:age.lt => 18)
|
24
|
+
# .any_of({ :role => 'admin' }, { :role => 'moderator' })
|
25
|
+
# .pluck(:name, :email)
|
19
26
|
class QueryBuilder
|
20
27
|
include ::Enumerable
|
21
28
|
include Utils
|
@@ -23,44 +30,68 @@ module Mongory
|
|
23
30
|
# Initializes a new query builder with the given record set.
|
24
31
|
#
|
25
32
|
# @param records [Enumerable] the collection to query against
|
26
|
-
def initialize(records)
|
33
|
+
def initialize(records, context: Utils::Context.new)
|
27
34
|
@records = records
|
35
|
+
@context = context
|
28
36
|
set_matcher
|
29
37
|
end
|
30
38
|
|
31
39
|
# Iterates through all records that match the current matcher.
|
40
|
+
# Uses the standard matcher implementation.
|
32
41
|
#
|
33
|
-
# @yieldparam record [Object]
|
34
|
-
# @return [Enumerator]
|
42
|
+
# @yieldparam record [Object] each matching record
|
43
|
+
# @return [Enumerator] if no block given
|
44
|
+
# @return [void] if block given
|
35
45
|
def each
|
36
46
|
return to_enum(:each) unless block_given?
|
37
47
|
|
38
48
|
@matcher.prepare_query
|
39
49
|
@records.each do |record|
|
50
|
+
@context.current_record = record
|
40
51
|
yield record if @matcher.match?(record)
|
41
52
|
end
|
42
53
|
end
|
43
54
|
|
55
|
+
# Iterates through all records that match the current matcher.
|
56
|
+
# Uses a compiled Proc for faster matching.
|
57
|
+
#
|
58
|
+
# @yieldparam record [Object] each matching record
|
59
|
+
# @return [Enumerator] if no block given
|
60
|
+
# @return [void] if block given
|
61
|
+
def fast
|
62
|
+
return to_enum(:fast) unless block_given?
|
63
|
+
|
64
|
+
@context.need_convert = false
|
65
|
+
@matcher.prepare_query
|
66
|
+
matcher_block = @matcher.to_proc
|
67
|
+
@records.each do |record|
|
68
|
+
yield record if matcher_block.call(record)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
44
72
|
# Adds a condition to filter records using the given condition.
|
73
|
+
# This is an alias for `and`.
|
45
74
|
#
|
46
|
-
# @param condition [Hash]
|
75
|
+
# @param condition [Hash] the condition to add
|
47
76
|
# @return [QueryBuilder] a new builder instance
|
48
77
|
def where(condition)
|
49
78
|
self.and(condition)
|
50
79
|
end
|
51
80
|
|
52
81
|
# Adds a negated condition to the current query.
|
82
|
+
# Wraps the condition in a `$not` operator.
|
53
83
|
#
|
54
|
-
# @param condition [Hash]
|
55
|
-
# @return [QueryBuilder]
|
84
|
+
# @param condition [Hash] the condition to negate
|
85
|
+
# @return [QueryBuilder] a new builder instance
|
56
86
|
def not(condition)
|
57
87
|
self.and('$not' => condition)
|
58
88
|
end
|
59
89
|
|
60
90
|
# Adds one or more conditions combined with `$and`.
|
91
|
+
# All conditions must match for a record to be included.
|
61
92
|
#
|
62
|
-
# @param conditions [Array<Hash>]
|
63
|
-
# @return [QueryBuilder]
|
93
|
+
# @param conditions [Array<Hash>] the conditions to add
|
94
|
+
# @return [QueryBuilder] a new builder instance
|
64
95
|
def and(*conditions)
|
65
96
|
dup_instance_exec do
|
66
97
|
add_conditions('$and', conditions)
|
@@ -68,9 +99,10 @@ module Mongory
|
|
68
99
|
end
|
69
100
|
|
70
101
|
# Adds one or more conditions combined with `$or`.
|
102
|
+
# Any condition can match for a record to be included.
|
71
103
|
#
|
72
|
-
# @param conditions [Array<Hash>]
|
73
|
-
# @return [QueryBuilder]
|
104
|
+
# @param conditions [Array<Hash>] the conditions to add
|
105
|
+
# @return [QueryBuilder] a new builder instance
|
74
106
|
def or(*conditions)
|
75
107
|
operator = '$or'
|
76
108
|
dup_instance_exec do
|
@@ -85,24 +117,34 @@ module Mongory
|
|
85
117
|
# Adds a `$or` query combined inside an `$and` block.
|
86
118
|
# This is a semantic alias for `.and('$or' => [...])`
|
87
119
|
#
|
88
|
-
# @param conditions [Array<Hash>]
|
89
|
-
# @return [QueryBuilder]
|
120
|
+
# @param conditions [Array<Hash>] the conditions to add
|
121
|
+
# @return [QueryBuilder] a new builder instance
|
90
122
|
def any_of(*conditions)
|
91
123
|
self.and('$or' => conditions)
|
92
124
|
end
|
93
125
|
|
126
|
+
# Adds an `$in` condition to the query.
|
127
|
+
# Matches records where the field value is in the given array.
|
128
|
+
#
|
129
|
+
# @param condition [Hash] the field and values to match
|
130
|
+
# @return [QueryBuilder] a new builder instance
|
94
131
|
def in(condition)
|
95
132
|
self.and(wrap_values_with_key(condition, '$in'))
|
96
133
|
end
|
97
134
|
|
135
|
+
# Adds a `$nin` condition to the query.
|
136
|
+
# Matches records where the field value is not in the given array.
|
137
|
+
#
|
138
|
+
# @param condition [Hash] the field and values to exclude
|
139
|
+
# @return [QueryBuilder] a new builder instance
|
98
140
|
def nin(condition)
|
99
141
|
self.and(wrap_values_with_key(condition, '$nin'))
|
100
142
|
end
|
101
143
|
|
102
144
|
# Limits the number of records returned by the query.
|
103
145
|
#
|
104
|
-
# @param count [Integer]
|
105
|
-
# @return [QueryBuilder]
|
146
|
+
# @param count [Integer] the maximum number of records to return
|
147
|
+
# @return [QueryBuilder] a new builder instance
|
106
148
|
def limit(count)
|
107
149
|
dup_instance_exec do
|
108
150
|
@records = take(count)
|
@@ -111,9 +153,10 @@ module Mongory
|
|
111
153
|
|
112
154
|
# Extracts selected fields from matching records.
|
113
155
|
#
|
114
|
-
# @param field [Symbol, String]
|
115
|
-
# @param fields [Array<Symbol, String>]
|
116
|
-
# @return [Array<Object
|
156
|
+
# @param field [Symbol, String] the first field to extract
|
157
|
+
# @param fields [Array<Symbol, String>] additional fields to extract
|
158
|
+
# @return [Array<Object>] array of single field values if one field given
|
159
|
+
# @return [Array<Array<Object>>] array of field value arrays if multiple fields given
|
117
160
|
def pluck(field, *fields)
|
118
161
|
if fields.empty?
|
119
162
|
map { |record| record[field] }
|
@@ -126,11 +169,24 @@ module Mongory
|
|
126
169
|
# Returns the raw parsed condition for this query.
|
127
170
|
#
|
128
171
|
# @return [Hash] the raw compiled condition
|
129
|
-
def
|
172
|
+
def raw_condition
|
130
173
|
@matcher.condition
|
131
174
|
end
|
132
175
|
|
133
|
-
|
176
|
+
# Creates a new query builder with additional context configuration.
|
177
|
+
#
|
178
|
+
# @param addon_context [Hash] Additional context configuration to merge
|
179
|
+
# @return [QueryBuilder] A new query builder instance with merged context
|
180
|
+
# @note Creates a new query builder with the current matcher's condition and merged context
|
181
|
+
# @example
|
182
|
+
# query.with_context(need_convert: false) #=> Returns a new query builder with conversion disabled
|
183
|
+
def with_context(addon_context = {})
|
184
|
+
dup_instance_exec do
|
185
|
+
@context = @context.dup
|
186
|
+
@context.config.merge!(addon_context)
|
187
|
+
set_matcher(@matcher.condition)
|
188
|
+
end
|
189
|
+
end
|
134
190
|
|
135
191
|
# Prints the internal matcher tree structure for the current query.
|
136
192
|
# Will output a human-readable visual tree of matchers.
|
@@ -148,8 +204,7 @@ module Mongory
|
|
148
204
|
# @private
|
149
205
|
# Duplicates the query and executes the block in context.
|
150
206
|
#
|
151
|
-
# @
|
152
|
-
# @return [QueryBuilder]
|
207
|
+
# @return [QueryBuilder] the modified duplicate
|
153
208
|
def dup_instance_exec(&block)
|
154
209
|
dup.tap do |obj|
|
155
210
|
obj.instance_exec(&block)
|
@@ -160,24 +215,31 @@ module Mongory
|
|
160
215
|
# Builds the internal matcher tree from a condition hash.
|
161
216
|
# Used to eagerly parse conditions to improve inspect/debug visibility.
|
162
217
|
#
|
163
|
-
# @param condition [Hash]
|
218
|
+
# @param condition [Hash] the condition to build the matcher from
|
164
219
|
# @return [void]
|
165
220
|
def set_matcher(condition = {})
|
166
|
-
@matcher = QueryMatcher.new(condition)
|
221
|
+
@matcher = QueryMatcher.new(condition, context: @context)
|
167
222
|
end
|
168
223
|
|
169
224
|
# @private
|
170
225
|
# Merges additional conditions into the matcher.
|
171
226
|
#
|
172
|
-
# @param key [String, Symbol]
|
173
|
-
# @param conditions [Array<Hash>]
|
227
|
+
# @param key [String, Symbol] the operator key (e.g. '$and', '$or')
|
228
|
+
# @param conditions [Array<Hash>] the conditions to add
|
229
|
+
# @return [void]
|
174
230
|
def add_conditions(key, conditions)
|
175
|
-
condition_dup = @matcher.condition
|
231
|
+
condition_dup = {}.merge!(@matcher.condition)
|
176
232
|
condition_dup[key] ||= []
|
177
233
|
condition_dup[key] += conditions
|
178
234
|
set_matcher(condition_dup)
|
179
235
|
end
|
180
236
|
|
237
|
+
# @private
|
238
|
+
# Wraps values in a condition hash with a given operator key.
|
239
|
+
#
|
240
|
+
# @param condition [Hash] the condition to transform
|
241
|
+
# @param key [String] the operator key to wrap with
|
242
|
+
# @return [Hash] the transformed condition
|
181
243
|
def wrap_values_with_key(condition, key)
|
182
244
|
condition.transform_values do |sub_condition|
|
183
245
|
{ key => sub_condition }
|
@@ -11,31 +11,58 @@ module Mongory
|
|
11
11
|
# Typically used internally by `QueryBuilder`.
|
12
12
|
#
|
13
13
|
# Conversion via Mongory.data_converter is applied to the record
|
14
|
+
# before matching to ensure consistent data types.
|
14
15
|
#
|
15
|
-
# @example
|
16
|
-
# matcher = QueryMatcher.
|
17
|
-
# matcher.match?(record)
|
16
|
+
# @example Basic matching
|
17
|
+
# matcher = QueryMatcher.new({ :age.gte => 18 })
|
18
|
+
# matcher.match?(record) # => true/false
|
19
|
+
#
|
20
|
+
# @example Complex condition
|
21
|
+
# matcher = QueryMatcher.new({
|
22
|
+
# :age.gte => 18,
|
23
|
+
# :$or => [
|
24
|
+
# { :name => /J/ },
|
25
|
+
# { :name.eq => 'Bob' }
|
26
|
+
# ]
|
27
|
+
# })
|
18
28
|
#
|
19
29
|
# @see Matchers::LiteralMatcher
|
20
30
|
# @see Converters::ConditionConverter
|
21
|
-
class QueryMatcher < Matchers::
|
31
|
+
class QueryMatcher < Matchers::HashConditionMatcher
|
32
|
+
# Initializes a new query matcher with the given condition.
|
33
|
+
# The condition is converted using Mongory.condition_converter
|
34
|
+
# before being passed to the parent matcher.
|
35
|
+
#
|
22
36
|
# @param condition [Hash<Symbol, Object>] a query condition using operator-style symbol keys,
|
23
37
|
# e.g. { :age.gt => 18 }, which will be parsed by `Mongory.condition_converter`.
|
24
|
-
|
25
|
-
|
38
|
+
# @param context [Context] The query context containing configuration and current record
|
39
|
+
# @option context [Hash] :config The query configuration
|
40
|
+
# @option context [Object] :current_record The current record being processed
|
41
|
+
# @option context [Boolean] :need_convert Whether the record needs to be converted
|
42
|
+
def initialize(condition, context: Utils::Context.new)
|
43
|
+
super(Mongory.condition_converter.convert(condition), context: context)
|
26
44
|
end
|
27
45
|
|
28
|
-
#
|
46
|
+
# Returns a Proc that can be used for fast matching.
|
47
|
+
# The Proc converts the record using Mongory.data_converter
|
48
|
+
# and delegates to the superclass's raw_proc.
|
29
49
|
#
|
30
|
-
# @
|
31
|
-
#
|
32
|
-
|
33
|
-
|
34
|
-
|
50
|
+
# @return [Proc] A proc that performs query matching with context awareness
|
51
|
+
# @note The proc includes error handling and context-based record conversion
|
52
|
+
def raw_proc
|
53
|
+
super_proc = super
|
54
|
+
need_convert = @context.need_convert
|
55
|
+
data_converter = Mongory.data_converter
|
56
|
+
|
57
|
+
Proc.new do |record|
|
58
|
+
record = data_converter.convert(record) if need_convert
|
59
|
+
super_proc.call(record)
|
60
|
+
rescue StandardError
|
61
|
+
false
|
62
|
+
end
|
35
63
|
end
|
36
64
|
|
37
65
|
# Renders the full matcher tree for the current query.
|
38
|
-
#
|
39
66
|
# This method is intended to be the public entry point for rendering
|
40
67
|
# the matcher tree. It does not accept any arguments and internally
|
41
68
|
# handles rendering via the configured pretty-print logic.
|
@@ -22,7 +22,7 @@ module Mongory
|
|
22
22
|
# @param other [Object] the value to match against
|
23
23
|
# @return [Hash] converted query condition
|
24
24
|
def __expr_part__(other, *)
|
25
|
-
|
25
|
+
{ @name => { @operator => other } }
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Mongory
|
4
|
+
module Utils
|
5
|
+
# Context is a utility class that provides a stable but mutatable
|
6
|
+
# shared context for the Mongory query builder. It holds the configuration
|
7
|
+
# and the current record being matcher tree processed.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# context = Mongory::Utils::Context.new(config)
|
11
|
+
# context.current_record = record
|
12
|
+
# context.config = new_config
|
13
|
+
#
|
14
|
+
# @attr [Config] config The configuration object for the context
|
15
|
+
# @attr [Record] current_record The current record being processed in the matcher tree
|
16
|
+
# @attr [Boolean] need_convert Whether the record needs to be converted before matching
|
17
|
+
class Context
|
18
|
+
attr_accessor :config, :current_record, :need_convert
|
19
|
+
|
20
|
+
# Initializes a new Context instance with the given configuration.
|
21
|
+
#
|
22
|
+
# @param config [Config] The configuration object for the context.
|
23
|
+
# @return [Context] A new Context instance.
|
24
|
+
def initialize(config = {})
|
25
|
+
@config = config
|
26
|
+
@current_record = nil
|
27
|
+
@need_convert = true
|
28
|
+
end
|
29
|
+
|
30
|
+
# Creates a duplicate of the context with its own configuration.
|
31
|
+
#
|
32
|
+
# @return [Context] A new context instance with duplicated configuration
|
33
|
+
# @note The new context shares the same configuration object but has its own state
|
34
|
+
def dup
|
35
|
+
new_context = super
|
36
|
+
new_context.config = @config.dup
|
37
|
+
new_context
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -32,16 +32,18 @@ module Mongory
|
|
32
32
|
@trace_entries = []
|
33
33
|
end
|
34
34
|
|
35
|
-
# Enables debug mode by aliasing `
|
35
|
+
# Enables debug mode by aliasing `to_proc` to `debug_proc`.
|
36
36
|
# @return [void]
|
37
|
+
# @note Changes the behavior of to_proc to use debug_proc instead of cached_proc
|
37
38
|
def enable
|
38
|
-
Matchers::AbstractMatcher.alias_method :
|
39
|
+
Matchers::AbstractMatcher.alias_method :to_proc, :debug_proc
|
39
40
|
end
|
40
41
|
|
41
|
-
# Disables debug mode by restoring `
|
42
|
+
# Disables debug mode by restoring `to_proc` to `cached_proc`.
|
42
43
|
# @return [void]
|
44
|
+
# @note Restores the original behavior of to_proc
|
43
45
|
def disable
|
44
|
-
Matchers::AbstractMatcher.alias_method :
|
46
|
+
Matchers::AbstractMatcher.alias_method :to_proc, :cached_proc
|
45
47
|
end
|
46
48
|
|
47
49
|
# Wraps a matcher evaluation block with indentation control.
|
data/lib/mongory/utils.rb
CHANGED
data/lib/mongory/version.rb
CHANGED
data/lib/mongory.rb
CHANGED
@@ -58,21 +58,21 @@ module Mongory
|
|
58
58
|
#
|
59
59
|
# @return [Converters::DataConverter]
|
60
60
|
def self.data_converter
|
61
|
-
Converters::DataConverter.instance
|
61
|
+
@data_converter ||= Converters::DataConverter.instance
|
62
62
|
end
|
63
63
|
|
64
64
|
# Returns the condition converter instance.
|
65
65
|
#
|
66
66
|
# @return [Converters::ConditionConverter]
|
67
67
|
def self.condition_converter
|
68
|
-
Converters::ConditionConverter.instance
|
68
|
+
@condition_converter ||= Converters::ConditionConverter.instance
|
69
69
|
end
|
70
70
|
|
71
71
|
# Returns the debugger instance.
|
72
72
|
#
|
73
73
|
# @return [Utils::Debugger]
|
74
74
|
def self.debugger
|
75
|
-
Utils::Debugger.instance
|
75
|
+
@debugger ||= Utils::Debugger.instance
|
76
76
|
end
|
77
77
|
|
78
78
|
# Builds a new query over the given record set.
|
data/mongory.gemspec
CHANGED
@@ -10,15 +10,15 @@ Gem::Specification.new do |spec|
|
|
10
10
|
|
11
11
|
spec.summary = 'MongoDB-like in-memory query DSL for Ruby'
|
12
12
|
spec.description = 'A Mongo-like in-memory query DSL for Ruby'
|
13
|
-
spec.homepage = 'https://
|
13
|
+
spec.homepage = 'https://mongoryhq.github.io/mongory-rb/'
|
14
14
|
spec.license = 'MIT'
|
15
15
|
spec.required_ruby_version = '>= 2.6.0'
|
16
16
|
|
17
17
|
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
18
18
|
spec.metadata['rubygems_mfa_required'] = 'true'
|
19
19
|
spec.metadata['homepage_uri'] = spec.homepage
|
20
|
-
spec.metadata['source_code_uri'] = 'https://github.com/
|
21
|
-
spec.metadata['changelog_uri'] = 'https://github.com/
|
20
|
+
spec.metadata['source_code_uri'] = 'https://github.com/mongoryhq/mongory-rb'
|
21
|
+
spec.metadata['changelog_uri'] = 'https://github.com/mongoryhq/mongory-rb/blob/main/CHANGELOG.md'
|
22
22
|
|
23
23
|
# Specify which files should be added to the gem when it is released.
|
24
24
|
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mongory
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- koten0224
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-04-
|
11
|
+
date: 2025-04-24 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A Mongo-like in-memory query DSL for Ruby
|
14
14
|
email:
|
@@ -26,6 +26,7 @@ files:
|
|
26
26
|
- README.md
|
27
27
|
- Rakefile
|
28
28
|
- examples/README.md
|
29
|
+
- examples/benchmark-rails.rb
|
29
30
|
- examples/benchmark.rb
|
30
31
|
- lib/generators/mongory/install/install_generator.rb
|
31
32
|
- lib/generators/mongory/install/templates/initializer.rb.erb
|
@@ -36,14 +37,13 @@ files:
|
|
36
37
|
- lib/mongory/converters.rb
|
37
38
|
- lib/mongory/converters/abstract_converter.rb
|
38
39
|
- lib/mongory/converters/condition_converter.rb
|
40
|
+
- lib/mongory/converters/converted.rb
|
39
41
|
- lib/mongory/converters/data_converter.rb
|
40
42
|
- lib/mongory/converters/key_converter.rb
|
41
43
|
- lib/mongory/converters/value_converter.rb
|
42
44
|
- lib/mongory/matchers.rb
|
43
|
-
- lib/mongory/matchers/README.md
|
44
45
|
- lib/mongory/matchers/abstract_matcher.rb
|
45
46
|
- lib/mongory/matchers/abstract_multi_matcher.rb
|
46
|
-
- lib/mongory/matchers/abstract_operator_matcher.rb
|
47
47
|
- lib/mongory/matchers/and_matcher.rb
|
48
48
|
- lib/mongory/matchers/array_record_matcher.rb
|
49
49
|
- lib/mongory/matchers/elem_match_matcher.rb
|
@@ -64,27 +64,29 @@ files:
|
|
64
64
|
- lib/mongory/matchers/or_matcher.rb
|
65
65
|
- lib/mongory/matchers/present_matcher.rb
|
66
66
|
- lib/mongory/matchers/regex_matcher.rb
|
67
|
+
- lib/mongory/matchers/size_matcher.rb
|
67
68
|
- lib/mongory/mongoid.rb
|
68
69
|
- lib/mongory/query_builder.rb
|
69
70
|
- lib/mongory/query_matcher.rb
|
70
71
|
- lib/mongory/query_operator.rb
|
71
72
|
- lib/mongory/rails.rb
|
72
73
|
- lib/mongory/utils.rb
|
74
|
+
- lib/mongory/utils/context.rb
|
73
75
|
- lib/mongory/utils/debugger.rb
|
74
76
|
- lib/mongory/utils/rails_patch.rb
|
75
77
|
- lib/mongory/utils/singleton_builder.rb
|
76
78
|
- lib/mongory/version.rb
|
77
79
|
- mongory.gemspec
|
78
80
|
- sig/mongory.rbs
|
79
|
-
homepage: https://
|
81
|
+
homepage: https://mongoryhq.github.io/mongory-rb/
|
80
82
|
licenses:
|
81
83
|
- MIT
|
82
84
|
metadata:
|
83
85
|
allowed_push_host: https://rubygems.org
|
84
86
|
rubygems_mfa_required: 'true'
|
85
|
-
homepage_uri: https://
|
86
|
-
source_code_uri: https://github.com/
|
87
|
-
changelog_uri: https://github.com/
|
87
|
+
homepage_uri: https://mongoryhq.github.io/mongory-rb/
|
88
|
+
source_code_uri: https://github.com/mongoryhq/mongory-rb
|
89
|
+
changelog_uri: https://github.com/mongoryhq/mongory-rb/blob/main/CHANGELOG.md
|
88
90
|
post_install_message:
|
89
91
|
rdoc_options: []
|
90
92
|
require_paths:
|
@@ -100,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
100
102
|
- !ruby/object:Gem::Version
|
101
103
|
version: '0'
|
102
104
|
requirements: []
|
103
|
-
rubygems_version: 3.
|
105
|
+
rubygems_version: 3.0.8
|
104
106
|
signing_key:
|
105
107
|
specification_version: 4
|
106
108
|
summary: MongoDB-like in-memory query DSL for Ruby
|
@@ -1,57 +0,0 @@
|
|
1
|
-
# Matcher classes inheritance diagram
|
2
|
-
|
3
|
-
```mermaid
|
4
|
-
---
|
5
|
-
config:
|
6
|
-
theme: neutral
|
7
|
-
look: classic
|
8
|
-
---
|
9
|
-
|
10
|
-
graph TD
|
11
|
-
%% style blocks
|
12
|
-
classDef abstract fill:#ddd,stroke:#333,stroke-width:1px,color:#000;
|
13
|
-
classDef main fill:#cce5ff,stroke:#339,stroke-width:1px;
|
14
|
-
classDef multi fill:#d4edda,stroke:#282,stroke-width:1px;
|
15
|
-
classDef operator fill:#fff3cd,stroke:#aa8800,stroke-width:1px;
|
16
|
-
classDef leaf fill:#f8f9fa,stroke:#999,stroke-width:1px;
|
17
|
-
|
18
|
-
%% Abstract base classes
|
19
|
-
A[AbstractMatcher]
|
20
|
-
C[AbstractMultiMatcher]
|
21
|
-
D[AbstractOperatorMatcher]
|
22
|
-
|
23
|
-
subgraph MultipleConditions
|
24
|
-
F[HashConditionMatcher]
|
25
|
-
I[AndMatcher]
|
26
|
-
J[OrMatcher]
|
27
|
-
W[ArrayRecordMatcher]
|
28
|
-
end
|
29
|
-
|
30
|
-
subgraph SimpleCompare
|
31
|
-
E[EqMatcher]
|
32
|
-
K[RegexMatcher]
|
33
|
-
L[PresentMatcher]
|
34
|
-
M[ExistsMatcher]
|
35
|
-
O[NeMatcher]
|
36
|
-
Q[GtMatcher]
|
37
|
-
R[GteMatcher]
|
38
|
-
S[LtMatcher]
|
39
|
-
T[LteMatcher]
|
40
|
-
end
|
41
|
-
|
42
|
-
A --> B[LiteralMatcher]
|
43
|
-
A --> U[InMatcher]
|
44
|
-
A --> V[NinMatcher]
|
45
|
-
A --> C --> MultipleConditions
|
46
|
-
A --> D --> SimpleCompare
|
47
|
-
B --> G[FieldMatcher]
|
48
|
-
B --> H[ElemMatchMatcher]
|
49
|
-
B --> N[NotMatcher]
|
50
|
-
|
51
|
-
%% Apply classes
|
52
|
-
class A,C,D abstract;
|
53
|
-
class B main;
|
54
|
-
class F,I,J,W multi;
|
55
|
-
class E,K,L,M,O,Q,R,S,T operator;
|
56
|
-
class G,U,V,H,N leaf;
|
57
|
-
```
|
@@ -1,46 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Mongory
|
4
|
-
module Matchers
|
5
|
-
# AbstractOperatorMatcher is a base class for matchers that apply a binary
|
6
|
-
# operator (e.g., `==`, `<`, `>` etc.) between the record and the condition.
|
7
|
-
#
|
8
|
-
# This class assumes that the match logic consists of:
|
9
|
-
# preprocess(record).send(operator, condition)
|
10
|
-
# and provides a fallback behavior for invalid comparisons.
|
11
|
-
#
|
12
|
-
# Subclasses must implement `#operator` and may override `#preprocess`
|
13
|
-
# to normalize or cast the record before comparison.
|
14
|
-
#
|
15
|
-
# @abstract
|
16
|
-
# @see AbstractMatcher
|
17
|
-
class AbstractOperatorMatcher < AbstractMatcher
|
18
|
-
# A list of Boolean values used for type guarding in some subclasses.
|
19
|
-
BOOLEAN_VALUES = [true, false].freeze
|
20
|
-
|
21
|
-
# Applies the binary operator to the preprocessed record and condition.
|
22
|
-
# If an error is raised (e.g., undefined comparison), the match fails.
|
23
|
-
#
|
24
|
-
# @param record [Object] the input record to test
|
25
|
-
# @return [Boolean] the result of record <operator> condition
|
26
|
-
def match(record)
|
27
|
-
preprocess(record).send(operator, @condition)
|
28
|
-
end
|
29
|
-
|
30
|
-
# Hook for subclasses to transform the record before comparison.
|
31
|
-
# Default behavior normalizes KEY_NOT_FOUND to nil.
|
32
|
-
#
|
33
|
-
# @param record [Object] the raw record value
|
34
|
-
# @return [Object] the transformed value
|
35
|
-
def preprocess(record)
|
36
|
-
normalize(record)
|
37
|
-
end
|
38
|
-
|
39
|
-
# Returns the Ruby operator symbol to be used in comparison.
|
40
|
-
# Must be implemented by subclasses (e.g., :==, :<, :>=)
|
41
|
-
#
|
42
|
-
# @return [Symbol] the comparison operator
|
43
|
-
def operator; end
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|