mongory 0.6.2 → 0.6.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3e912f1cb9b9d1c87259e837d71c1b7034359d360d2c2f9d4f09ab30b740270c
4
- data.tar.gz: 6d007afc7889af43f94b4d704d8512ba3cc39ceafd974433168bfa57c0e1ba87
3
+ metadata.gz: 78a3d73e3c078b41aeeeb727445229e4edce85ff089de2d7412c9e64bfc613c0
4
+ data.tar.gz: d7a9191a78a75b00f7a3ef7aa5d0b8e5ed8e8cb53e6a839e0873548da341849f
5
5
  SHA512:
6
- metadata.gz: ff4f410a6d7fb0da08f6fe2dae1701d3d740c0f318982283d639a0a23b5f5798f048e9b483439e157ffcfb954f83f9985886534ec44b55da1dde1a810eb35e15
7
- data.tar.gz: 41ff1d53a5f06ef0c2912b2d5e5a81b31b5639b95f84b2c5d932fd5ce350fafb2cdbb8058716a16b88a5d59d65bac0c74ce8d51cecb4a8e911e2926e7652fe8c
6
+ metadata.gz: 6cc1387d67ccf0559a5f12d2140b2f0349a16b8461ae3e6992a3ba199a1824b178236d91cd8fe31bdbd73a85d056074efac32440ef8827bb31e9aebb6fd544bf
7
+ data.tar.gz: 8f34cab007b5c692b48634686aa2931036b3f928da72004512b7e0de744dbe009656cac108f9f2d6d3d59b8d6d7e997b1b1b3f3a5865998bc83168c2dce60ea0
@@ -14,9 +14,9 @@ module Mongory
14
14
  return other if other.is_a?(Converted)
15
15
 
16
16
  case other
17
- when Hash
17
+ when ::Hash
18
18
  Converted::Hash.new(other)
19
- when Array
19
+ when ::Array
20
20
  Converted::Array.new(other)
21
21
  else
22
22
  other
@@ -161,6 +161,10 @@ module Mongory
161
161
  puts "#{prefix}#{is_last ? '└─ ' : '├─ '}#{tree_title}\n"
162
162
  end
163
163
 
164
+ def priority
165
+ 20
166
+ end
167
+
164
168
  private
165
169
 
166
170
  # Returns a single-line string representing this matcher in the tree output.
@@ -75,6 +75,50 @@ module Mongory
75
75
  matcher.render_tree(new_prefix, is_last: index == last_index)
76
76
  end
77
77
  end
78
+
79
+ def priority
80
+ 1 + matchers.sum(&:priority)
81
+ end
82
+
83
+ private
84
+
85
+ # Recursively combines multiple matcher procs with AND logic.
86
+ # This method optimizes the combination of multiple matchers by building
87
+ # a balanced tree of AND operations.
88
+ #
89
+ # @param left [Proc] The left matcher proc to combine
90
+ # @param rest [Array<Proc>] The remaining matcher procs to combine
91
+ # @return [Proc] A new proc that combines all matchers with AND logic
92
+ # @example
93
+ # combine_procs_with_and(proc1, proc2, proc3)
94
+ # #=> proc { |record| proc1.call(record) && proc2.call(record) && proc3.call(record) }
95
+ def combine_procs_with_and(left = TRUE_PROC, *rest)
96
+ return left if rest.empty?
97
+
98
+ right = combine_procs_with_and(*rest)
99
+ Proc.new do |record|
100
+ left.call(record) && right.call(record)
101
+ end
102
+ end
103
+
104
+ # Recursively combines multiple matcher procs with OR logic.
105
+ # This method optimizes the combination of multiple matchers by building
106
+ # a balanced tree of OR operations.
107
+ #
108
+ # @param left [Proc] The left matcher proc to combine
109
+ # @param rest [Array<Proc>] The remaining matcher procs to combine
110
+ # @return [Proc] A new proc that combines all matchers with OR logic
111
+ # @example
112
+ # combine_procs_with_or(proc1, proc2, proc3)
113
+ # #=> proc { |record| proc1.call(record) || proc2.call(record) || proc3.call(record) }
114
+ def combine_procs_with_or(left = FALSE_PROC, *rest)
115
+ return left if rest.empty?
116
+
117
+ right = combine_procs_with_or(*rest)
118
+ Proc.new do |record|
119
+ left.call(record) || right.call(record)
120
+ end
121
+ end
78
122
  end
79
123
  end
80
124
  end
@@ -36,28 +36,7 @@ module Mongory
36
36
  #
37
37
  # @return [Proc] a Proc that performs the AND operation
38
38
  def raw_proc
39
- return TRUE_PROC if matchers.empty?
40
-
41
- combine_procs(*matchers.map(&:to_proc))
42
- end
43
-
44
- # Recursively combines multiple matcher procs with AND logic.
45
- # This method optimizes the combination of multiple matchers by building
46
- # a balanced tree of AND operations.
47
- #
48
- # @param left [Proc] The left matcher proc to combine
49
- # @param rest [Array<Proc>] The remaining matcher procs to combine
50
- # @return [Proc] A new proc that combines all matchers with AND logic
51
- # @example
52
- # combine_procs(proc1, proc2, proc3)
53
- # #=> proc { |record| proc1.call(record) && proc2.call(record) && proc3.call(record) }
54
- def combine_procs(left, *rest)
55
- return left if rest.empty?
56
-
57
- right = combine_procs(*rest)
58
- Proc.new do |record|
59
- left.call(record) && right.call(record)
60
- end
39
+ combine_procs_with_and(*matchers.map(&:to_proc))
61
40
  end
62
41
 
63
42
  # Returns the flattened list of all matchers from each subcondition.
@@ -70,7 +49,7 @@ module Mongory
70
49
  define_instance_cache_method(:matchers) do
71
50
  @condition.flat_map do |condition|
72
51
  HashConditionMatcher.new(condition, context: @context).matchers
73
- end.uniq(&:uniq_key)
52
+ end.uniq(&:uniq_key).sort_by(&:priority)
74
53
  end
75
54
 
76
55
  # Ensures the condition is an array of hashes.
@@ -34,28 +34,7 @@ module Mongory
34
34
  #
35
35
  # @return [Proc] a Proc that performs the array matching operation
36
36
  def raw_proc
37
- return FALSE_PROC if matchers.empty?
38
-
39
- combine_procs(*matchers.map(&:to_proc))
40
- end
41
-
42
- # Recursively combines multiple matcher procs with OR logic.
43
- # This method optimizes the combination of multiple matchers by building
44
- # a balanced tree of OR operations.
45
- #
46
- # @param left [Proc] The left matcher proc to combine
47
- # @param rest [Array<Proc>] The remaining matcher procs to combine
48
- # @return [Proc] A new proc that combines all matchers with OR logic
49
- # @example
50
- # combine_procs(proc1, proc2, proc3)
51
- # #=> proc { |record| proc1.call(record) || proc2.call(record) || proc3.call(record) }
52
- def combine_procs(left, *rest)
53
- return left if rest.empty?
54
-
55
- right = combine_procs(*rest)
56
- Proc.new do |record|
57
- left.call(record) || right.call(record)
58
- end
37
+ combine_procs_with_or(*matchers.map(&:to_proc))
59
38
  end
60
39
 
61
40
  # Builds an array of matchers to evaluate the given condition against an array record.
@@ -77,7 +56,7 @@ module Mongory
77
56
  else
78
57
  ElemMatchMatcher.build({ '$eq' => @condition }, context: @context)
79
58
  end
80
- result
59
+ result.sort_by(&:priority)
81
60
  end
82
61
 
83
62
  private
@@ -35,6 +35,10 @@ module Mongory
35
35
  end
36
36
  end
37
37
 
38
+ def priority
39
+ 3 + super
40
+ end
41
+
38
42
  # Ensures the condition is a Hash.
39
43
  #
40
44
  # @raise [Mongory::TypeError] if the condition is not a Hash
@@ -35,6 +35,10 @@ module Mongory
35
35
  record == condition
36
36
  end
37
37
  end
38
+
39
+ def priority
40
+ 1
41
+ end
38
42
  end
39
43
 
40
44
  register(:eq, '$eq', EqMatcher)
@@ -36,6 +36,10 @@ module Mongory
36
36
  end
37
37
  end
38
38
 
39
+ def priority
40
+ 3 + super
41
+ end
42
+
39
43
  # Ensures the condition is a Hash.
40
44
  #
41
45
  # @raise [Mongory::TypeError] if the condition is not a Hash
@@ -33,6 +33,10 @@ module Mongory
33
33
  end
34
34
  end
35
35
 
36
+ def priority
37
+ 2
38
+ end
39
+
36
40
  # Ensures that the condition value is a valid boolean.
37
41
  #
38
42
  # @raise [TypeError] if condition is not true or false
@@ -109,6 +109,10 @@ module Mongory
109
109
  end
110
110
  end
111
111
 
112
+ def priority
113
+ 1 + super
114
+ end
115
+
112
116
  # Returns a unique key for this matcher, including the field name.
113
117
  # Used for deduplication in multi-matchers.
114
118
  #
@@ -30,6 +30,10 @@ module Mongory
30
30
  false
31
31
  end
32
32
  end
33
+
34
+ def priority
35
+ 3
36
+ end
33
37
  end
34
38
 
35
39
  register(:gt, '$gt', GtMatcher)
@@ -30,6 +30,10 @@ module Mongory
30
30
  false
31
31
  end
32
32
  end
33
+
34
+ def priority
35
+ 3
36
+ end
33
37
  end
34
38
 
35
39
  register(:gte, '$gte', GteMatcher)
@@ -32,28 +32,7 @@ module Mongory
32
32
  #
33
33
  # @return [Proc] a Proc that performs the hash condition matching operation
34
34
  def raw_proc
35
- return TRUE_PROC if matchers.empty?
36
-
37
- combine_procs(*matchers.map(&:to_proc))
38
- end
39
-
40
- # Recursively combines multiple matcher procs with AND logic.
41
- # This method optimizes the combination of multiple matchers by building
42
- # a balanced tree of AND operations.
43
- #
44
- # @param left [Proc] The left matcher proc to combine
45
- # @param rest [Array<Proc>] The remaining matcher procs to combine
46
- # @return [Proc] A new proc that combines all matchers with AND logic
47
- # @example
48
- # combine_procs(proc1, proc2, proc3)
49
- # #=> proc { |record| proc1.call(record) && proc2.call(record) && proc3.call(record) }
50
- def combine_procs(left, *rest)
51
- return left if rest.empty?
52
-
53
- right = combine_procs(*rest)
54
- Proc.new do |record|
55
- left.call(record) && right.call(record)
56
- end
35
+ combine_procs_with_and(*matchers.map(&:to_proc))
57
36
  end
58
37
 
59
38
  # Returns the list of matchers for each key-value pair in the condition.
@@ -70,7 +49,7 @@ module Mongory
70
49
  else
71
50
  FieldMatcher.build(key, value, context: @context)
72
51
  end
73
- end
52
+ end.sort_by(&:priority)
74
53
  end
75
54
 
76
55
  def check_validity!
@@ -24,17 +24,23 @@ module Mongory
24
24
  #
25
25
  # @see AbstractMatcher
26
26
  class InMatcher < AbstractMatcher
27
+ def self.build(condition, *args)
28
+ return super unless condition.is_a?(Range)
29
+
30
+ end_op = condition.exclude_end? ? '$lt' : '$lte'
31
+ head, tail = [condition.first, condition.last].sort
32
+ AndMatcher.build([{ '$gte' => head }, { end_op => tail }], *args)
33
+ end
34
+
27
35
  # Creates a raw Proc that performs the in-matching operation.
28
36
  # The Proc checks if any element of the record is in the condition array.
29
37
  #
30
38
  # @return [Proc] a Proc that performs the in-matching operation
31
39
  def raw_proc
32
- condition = @condition
40
+ condition = Set.new(@condition)
33
41
 
34
42
  Proc.new do |record|
35
43
  if record.is_a?(Array)
36
- return false if condition.is_a?(Range)
37
-
38
44
  is_present?(condition & record)
39
45
  else
40
46
  condition.include?(record)
@@ -42,13 +48,16 @@ module Mongory
42
48
  end
43
49
  end
44
50
 
51
+ def priority
52
+ 1 + Math.log(@condition.size + 1, 1.5)
53
+ end
54
+
45
55
  # Ensures the condition is an array or range.
46
56
  #
47
57
  # @raise [TypeError] if condition is not an array nor a range
48
58
  # @return [void]
49
59
  def check_validity!
50
60
  return if @condition.is_a?(Array)
51
- return if @condition.is_a?(Range)
52
61
 
53
62
  raise TypeError, '$in needs an array or range'
54
63
  end
@@ -48,6 +48,10 @@ module Mongory
48
48
  end
49
49
  end
50
50
 
51
+ def priority
52
+ 1 + dispatched_matcher.priority
53
+ end
54
+
51
55
  # Selects and returns the appropriate matcher instance for a given literal condition.
52
56
  #
53
57
  # This method analyzes the type of the raw condition (e.g., Hash, Regexp, nil)
@@ -30,6 +30,10 @@ module Mongory
30
30
  false
31
31
  end
32
32
  end
33
+
34
+ def priority
35
+ 3
36
+ end
33
37
  end
34
38
 
35
39
  register(:lt, '$lt', LtMatcher)
@@ -30,6 +30,10 @@ module Mongory
30
30
  false
31
31
  end
32
32
  end
33
+
34
+ def priority
35
+ 3
36
+ end
33
37
  end
34
38
 
35
39
  register(:lte, '$lte', LteMatcher)
@@ -27,6 +27,10 @@ module Mongory
27
27
  record != condition
28
28
  end
29
29
  end
30
+
31
+ def priority
32
+ 1
33
+ end
30
34
  end
31
35
 
32
36
  register(:ne, '$ne', NeMatcher)
@@ -24,17 +24,23 @@ module Mongory
24
24
  #
25
25
  # @see AbstractMatcher
26
26
  class NinMatcher < AbstractMatcher
27
+ def self.build(condition, *args)
28
+ return super unless condition.is_a?(Range)
29
+
30
+ end_op = condition.exclude_end? ? '$gte' : '$gt'
31
+ head, tail = [condition.first, condition.last].sort
32
+ OrMatcher.build([{ '$lt' => head }, { end_op => tail }], *args)
33
+ end
34
+
27
35
  # Creates a raw Proc that performs the not-in matching operation.
28
36
  # The Proc checks if the record has no elements in common with the condition array.
29
37
  #
30
38
  # @return [Proc] A proc that performs not-in matching
31
39
  def raw_proc
32
- condition = @condition
40
+ condition = Set.new(@condition)
33
41
 
34
42
  Proc.new do |record|
35
43
  if record.is_a?(Array)
36
- return true if condition.is_a?(Range)
37
-
38
44
  is_blank?(condition & record)
39
45
  else
40
46
  !condition.include?(record)
@@ -42,15 +48,18 @@ module Mongory
42
48
  end
43
49
  end
44
50
 
51
+ def priority
52
+ 1 + Math.log(@condition.size + 1, 1.5)
53
+ end
54
+
45
55
  # Ensures the condition is a valid array or range.
46
56
  #
47
57
  # @raise [TypeError] if the condition is not an array nor a range
48
58
  # @return [void]
49
59
  def check_validity!
50
60
  return if @condition.is_a?(Array)
51
- return if @condition.is_a?(Range)
52
61
 
53
- raise TypeError, '$nin needs an array'
62
+ raise TypeError, '$nin needs an array or range'
54
63
  end
55
64
  end
56
65
 
@@ -29,6 +29,10 @@ module Mongory
29
29
  !super_proc.call(record)
30
30
  end
31
31
  end
32
+
33
+ def priority
34
+ 1 + super
35
+ end
32
36
  end
33
37
 
34
38
  register(:not, '$not', NotMatcher)
@@ -35,28 +35,7 @@ module Mongory
35
35
  #
36
36
  # @return [Proc] a Proc that performs the or-matching operation
37
37
  def raw_proc
38
- return FALSE_PROC if matchers.empty?
39
-
40
- combine_procs(*matchers.map(&:to_proc))
41
- end
42
-
43
- # Recursively combines multiple matcher procs with OR logic.
44
- # This method optimizes the combination of multiple matchers by building
45
- # a balanced tree of OR operations.
46
- #
47
- # @param left [Proc] The left matcher proc to combine
48
- # @param rest [Array<Proc>] The remaining matcher procs to combine
49
- # @return [Proc] A new proc that combines all matchers with OR logic
50
- # @example
51
- # combine_procs(proc1, proc2, proc3)
52
- # #=> proc { |record| proc1.call(record) || proc2.call(record) || proc3.call(record) }
53
- def combine_procs(left, *rest)
54
- return left if rest.empty?
55
-
56
- right = combine_procs(*rest)
57
- Proc.new do |record|
58
- left.call(record) || right.call(record)
59
- end
38
+ combine_procs_with_or(*matchers.map(&:to_proc))
60
39
  end
61
40
 
62
41
  # Builds an array of matchers from the subconditions.
@@ -66,7 +45,7 @@ module Mongory
66
45
  define_instance_cache_method(:matchers) do
67
46
  @condition.map do |condition|
68
47
  HashConditionMatcher.build(condition, context: @context)
69
- end
48
+ end.sort_by(&:priority)
70
49
  end
71
50
 
72
51
  # Ensures the condition is an array of hashes.
@@ -35,6 +35,10 @@ module Mongory
35
35
  end
36
36
  end
37
37
 
38
+ def priority
39
+ 2
40
+ end
41
+
38
42
  # Ensures that the condition value is a boolean.
39
43
  #
40
44
  # @raise [TypeError] if condition is not true or false
@@ -59,6 +59,10 @@ module Mongory
59
59
  end
60
60
  end
61
61
 
62
+ def priority
63
+ @condition.source.start_with?('^') ? 8 : 20
64
+ end
65
+
62
66
  # Ensures the condition is a valid regex pattern (Regexp or String).
63
67
  #
64
68
  # @raise [TypeError] if condition is not a string or Regexp
@@ -43,6 +43,10 @@ module Mongory
43
43
  super_proc.call(record.size)
44
44
  end
45
45
  end
46
+
47
+ def priority
48
+ 2 + super
49
+ end
46
50
  end
47
51
 
48
52
  register(:size, '$size', SizeMatcher)
data/lib/mongory/utils.rb CHANGED
@@ -46,7 +46,7 @@ module Mongory
46
46
  case obj
47
47
  when false, nil
48
48
  true
49
- when Hash, Array, String
49
+ when Hash, Array, String, Set
50
50
  obj.empty?
51
51
  else
52
52
  false
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mongory
4
- VERSION = '0.6.2'
4
+ VERSION = '0.6.3'
5
5
  end
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.6.2
4
+ version: 0.6.3
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-24 00:00:00.000000000 Z
11
+ date: 2025-05-27 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: A Mongo-like in-memory query DSL for Ruby
14
14
  email:
@@ -102,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
102
102
  - !ruby/object:Gem::Version
103
103
  version: '0'
104
104
  requirements: []
105
- rubygems_version: 3.0.8
105
+ rubygems_version: 3.1.6
106
106
  signing_key:
107
107
  specification_version: 4
108
108
  summary: MongoDB-like in-memory query DSL for Ruby