nobrainer 0.16.0 → 0.18.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/lib/no_brainer/config.rb +53 -28
- data/lib/no_brainer/connection.rb +1 -0
- data/lib/no_brainer/connection_manager.rb +60 -0
- data/lib/no_brainer/criteria/after_find.rb +3 -11
- data/lib/no_brainer/criteria/aggregate.rb +8 -5
- data/lib/no_brainer/criteria/cache.rb +7 -7
- data/lib/no_brainer/criteria/core.rb +56 -16
- data/lib/no_brainer/criteria/count.rb +1 -1
- data/lib/no_brainer/criteria/delete.rb +1 -1
- data/lib/no_brainer/criteria/extend.rb +19 -0
- data/lib/no_brainer/criteria/first.rb +1 -1
- data/lib/no_brainer/criteria/index.rb +7 -13
- data/lib/no_brainer/criteria/limit.rb +5 -13
- data/lib/no_brainer/criteria/order_by.rb +22 -41
- data/lib/no_brainer/criteria/pluck.rb +17 -23
- data/lib/no_brainer/criteria/preload.rb +9 -15
- data/lib/no_brainer/criteria/raw.rb +5 -11
- data/lib/no_brainer/criteria/scope.rb +9 -15
- data/lib/no_brainer/criteria/update.rb +3 -3
- data/lib/no_brainer/criteria/where.rb +33 -56
- data/lib/no_brainer/criteria.rb +1 -1
- data/lib/no_brainer/document/aliases.rb +1 -1
- data/lib/no_brainer/document/association/belongs_to.rb +6 -6
- data/lib/no_brainer/document/association/core.rb +11 -11
- data/lib/no_brainer/document/association/eager_loader.rb +4 -3
- data/lib/no_brainer/document/association/has_many.rb +7 -7
- data/lib/no_brainer/document/association/has_many_through.rb +1 -1
- data/lib/no_brainer/document/association/has_one.rb +1 -1
- data/lib/no_brainer/document/association.rb +5 -5
- data/lib/no_brainer/document/atomic_ops.rb +213 -0
- data/lib/no_brainer/document/attributes.rb +19 -10
- data/lib/no_brainer/document/callbacks.rb +1 -1
- data/lib/no_brainer/document/core.rb +1 -1
- data/lib/no_brainer/document/criteria.rb +9 -2
- data/lib/no_brainer/document/dirty.rb +22 -22
- data/lib/no_brainer/document/dynamic_attributes.rb +2 -14
- data/lib/no_brainer/document/id.rb +1 -0
- data/lib/no_brainer/document/index/index.rb +83 -0
- data/lib/no_brainer/document/index/meta_store.rb +31 -0
- data/lib/no_brainer/document/index/synchronizer.rb +68 -0
- data/lib/no_brainer/document/index.rb +13 -72
- data/lib/no_brainer/document/lazy_fetch.rb +5 -5
- data/lib/no_brainer/document/missing_attributes.rb +6 -23
- data/lib/no_brainer/document/persistance.rb +38 -21
- data/lib/no_brainer/document/polymorphic.rb +1 -1
- data/lib/no_brainer/document/types/set.rb +23 -0
- data/lib/no_brainer/document/types.rb +8 -6
- data/lib/no_brainer/document/uniqueness.rb +3 -3
- data/lib/no_brainer/document/validation.rb +13 -4
- data/lib/no_brainer/document.rb +1 -1
- data/lib/no_brainer/error.rb +14 -0
- data/lib/no_brainer/fork.rb +1 -0
- data/lib/no_brainer/query_runner/connection_lock.rb +5 -1
- data/lib/no_brainer/query_runner/database_on_demand.rb +6 -5
- data/lib/no_brainer/query_runner/logger.rb +10 -6
- data/lib/no_brainer/query_runner/missing_index.rb +5 -4
- data/lib/no_brainer/query_runner/reconnect.rb +20 -17
- data/lib/no_brainer/query_runner/run_options.rb +3 -0
- data/lib/no_brainer/query_runner/table_on_demand.rb +11 -8
- data/lib/no_brainer/railtie/database.rake +12 -12
- data/lib/no_brainer/railtie.rb +5 -5
- data/lib/no_brainer/rql.rb +9 -0
- data/lib/nobrainer.rb +9 -23
- metadata +10 -4
- data/lib/no_brainer/index_manager.rb +0 -9
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
module NoBrainer::Criteria::OrderBy
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def initialize(options={})
|
|
7
|
-
super
|
|
8
|
-
self.order = {}
|
|
9
|
-
end
|
|
4
|
+
# The latest order_by() wins
|
|
5
|
+
included { criteria_option :order_by, :ordering_mode, :merge_with => :set_scalar }
|
|
10
6
|
|
|
11
7
|
def order_by(*rules, &block)
|
|
12
8
|
# Note: We are relying on the fact that Hashes are ordered (since 1.9)
|
|
13
|
-
rules = [*rules, block].compact.map do |rule|
|
|
9
|
+
rules = [*rules, block].flatten.compact.map do |rule|
|
|
14
10
|
case rule
|
|
15
11
|
when Hash then
|
|
16
12
|
bad_rule = rule.values.reject { |v| v.in? [:asc, :desc] }.first
|
|
@@ -21,34 +17,20 @@ module NoBrainer::Criteria::OrderBy
|
|
|
21
17
|
end
|
|
22
18
|
end.reduce({}, :merge)
|
|
23
19
|
|
|
24
|
-
chain
|
|
25
|
-
criteria.order = rules
|
|
26
|
-
criteria.ordering_mode = :normal
|
|
27
|
-
end
|
|
20
|
+
chain(:order_by => rules, :ordering_mode => :normal)
|
|
28
21
|
end
|
|
29
22
|
|
|
30
23
|
def without_ordering
|
|
31
|
-
chain
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
def merge!(criteria, options={})
|
|
35
|
-
super
|
|
36
|
-
# The latest order_by() wins
|
|
37
|
-
self.order = criteria.order if criteria.order.present?
|
|
38
|
-
self.ordering_mode = criteria.ordering_mode unless criteria.ordering_mode.nil?
|
|
39
|
-
self
|
|
24
|
+
chain(:ordering_mode => :disabled)
|
|
40
25
|
end
|
|
41
26
|
|
|
42
27
|
def reverse_order
|
|
43
|
-
chain
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
when :disabled then :disabled
|
|
50
|
-
end
|
|
51
|
-
end
|
|
28
|
+
chain(:ordering_mode => case @options[:ordering_mode]
|
|
29
|
+
when nil then :reversed
|
|
30
|
+
when :normal then :reversed
|
|
31
|
+
when :reversed then :normal
|
|
32
|
+
when :disabled then :disabled
|
|
33
|
+
end)
|
|
52
34
|
end
|
|
53
35
|
|
|
54
36
|
def order_by_indexed?
|
|
@@ -62,15 +44,15 @@ module NoBrainer::Criteria::OrderBy
|
|
|
62
44
|
private
|
|
63
45
|
|
|
64
46
|
def effective_order
|
|
65
|
-
|
|
47
|
+
@options[:order_by].presence || (model ? {model.pk_name => :asc} : {})
|
|
66
48
|
end
|
|
67
49
|
|
|
68
50
|
def reverse_order?
|
|
69
|
-
|
|
51
|
+
@options[:ordering_mode] == :reversed
|
|
70
52
|
end
|
|
71
53
|
|
|
72
54
|
def should_order?
|
|
73
|
-
|
|
55
|
+
@options[:ordering_mode] != :disabled
|
|
74
56
|
end
|
|
75
57
|
|
|
76
58
|
class IndexFinder < Struct.new(:criteria, :index_name, :rql_proc)
|
|
@@ -83,15 +65,15 @@ module NoBrainer::Criteria::OrderBy
|
|
|
83
65
|
end
|
|
84
66
|
|
|
85
67
|
def first_key_indexable?
|
|
86
|
-
(first_key.is_a?(Symbol) || first_key.is_a?(String)) && criteria.
|
|
68
|
+
(first_key.is_a?(Symbol) || first_key.is_a?(String)) && criteria.model.has_index?(first_key)
|
|
87
69
|
end
|
|
88
70
|
|
|
89
71
|
def find_index
|
|
90
72
|
return if criteria.without_index?
|
|
91
73
|
return unless first_key_indexable?
|
|
92
74
|
|
|
93
|
-
if criteria.
|
|
94
|
-
return unless first_key.to_s == criteria.
|
|
75
|
+
if criteria.options[:use_index] && criteria.options[:use_index] != true
|
|
76
|
+
return unless first_key.to_s == criteria.options[:use_index].to_s
|
|
95
77
|
end
|
|
96
78
|
|
|
97
79
|
# We need make sure that the where index finder has been invoked, it has priority.
|
|
@@ -116,9 +98,9 @@ module NoBrainer::Criteria::OrderBy
|
|
|
116
98
|
|
|
117
99
|
rql_rules = _effective_order.map do |k,v|
|
|
118
100
|
if order_by_index_finder.index_name == k
|
|
119
|
-
k =
|
|
101
|
+
k = model.lookup_index_alias(k)
|
|
120
102
|
else
|
|
121
|
-
k =
|
|
103
|
+
k = model.lookup_field_alias(k)
|
|
122
104
|
end
|
|
123
105
|
|
|
124
106
|
case v
|
|
@@ -127,14 +109,13 @@ module NoBrainer::Criteria::OrderBy
|
|
|
127
109
|
end
|
|
128
110
|
end
|
|
129
111
|
|
|
130
|
-
# We can only apply an
|
|
131
|
-
#
|
|
132
|
-
#
|
|
112
|
+
# We can only apply an indexed order_by on a table() RQL term.
|
|
113
|
+
# If we can, great. Otherwise, the ordering is applied in pass2, which will
|
|
114
|
+
# happen after a potential filter(), which is better for perfs.
|
|
133
115
|
if order_by_index_finder.could_find_index?
|
|
134
116
|
options = { :index => rql_rules.shift }
|
|
135
117
|
rql = rql.order_by(*rql_rules, options)
|
|
136
118
|
else
|
|
137
|
-
# Stashing @rql_rules for pass2
|
|
138
119
|
@rql_rules_pass2 = rql_rules
|
|
139
120
|
end
|
|
140
121
|
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
module NoBrainer::Criteria::Pluck
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
|
-
included {
|
|
4
|
+
included { criteria_option :missing_attributes, :merge_with =>
|
|
5
|
+
NoBrainer::Criteria::Pluck.method(:merge_missing_attributes) }
|
|
5
6
|
|
|
6
7
|
def pluck(*attrs)
|
|
7
8
|
_missing_attributes_criteria(:pluck, attrs)
|
|
@@ -16,24 +17,19 @@ module NoBrainer::Criteria::Pluck
|
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
def without_plucking
|
|
19
|
-
chain
|
|
20
|
+
chain(:missing_attributes => :remove)
|
|
20
21
|
end
|
|
21
22
|
|
|
22
|
-
def
|
|
23
|
-
return
|
|
23
|
+
def self.merge_missing_attributes(a, b)
|
|
24
|
+
return nil if b.nil? || b == :remove
|
|
24
25
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
old_attrs = self.missing_attributes[type] || {}.with_indifferent_access
|
|
31
|
-
new_attrs = old_attrs.deep_merge(attrs)
|
|
32
|
-
self.missing_attributes[type] = new_attrs
|
|
33
|
-
end
|
|
26
|
+
a = a ? a.dup : {}
|
|
27
|
+
b.each do |type, attrs|
|
|
28
|
+
old_attrs = a[type] || {}.with_indifferent_access
|
|
29
|
+
new_attrs = old_attrs.deep_merge(attrs)
|
|
30
|
+
a[type] = new_attrs
|
|
34
31
|
end
|
|
35
|
-
|
|
36
|
-
self
|
|
32
|
+
a
|
|
37
33
|
end
|
|
38
34
|
|
|
39
35
|
private
|
|
@@ -41,16 +37,14 @@ module NoBrainer::Criteria::Pluck
|
|
|
41
37
|
def _missing_attributes_criteria(type, args)
|
|
42
38
|
raise ArgumentError if args.size.zero?
|
|
43
39
|
args = [Hash[args.flatten.map { |k| [k, true] }]] unless args.size == 1 && args.first.is_a?(Hash)
|
|
44
|
-
chain
|
|
45
|
-
|
|
40
|
+
chain(:missing_attributes => {type => args.first})
|
|
46
41
|
end
|
|
47
42
|
|
|
48
43
|
def effective_missing_attributes
|
|
49
|
-
return nil if
|
|
44
|
+
return nil if @options[:missing_attributes].nil?
|
|
50
45
|
@effective_missing_attributes ||= begin
|
|
51
46
|
# pluck gets priority
|
|
52
|
-
|
|
53
|
-
missing_attributes = Hash[self.missing_attributes.map do |type, attrs|
|
|
47
|
+
missing_attributes = Hash[@options[:missing_attributes].map do |type, attrs|
|
|
54
48
|
attrs = attrs.select { |k,v| v } # TODO recursive
|
|
55
49
|
attrs.empty? ? nil : [type, attrs]
|
|
56
50
|
end.compact]
|
|
@@ -65,16 +59,16 @@ module NoBrainer::Criteria::Pluck
|
|
|
65
59
|
end
|
|
66
60
|
|
|
67
61
|
def _instantiate_model(attrs, options={})
|
|
68
|
-
return super if missing_attributes.nil?
|
|
62
|
+
return super if @options[:missing_attributes].nil?
|
|
69
63
|
super(attrs, options.merge(:missing_attributes => effective_missing_attributes,
|
|
70
|
-
:lazy_fetch => missing_attributes[:lazy_fetch]))
|
|
64
|
+
:lazy_fetch => @options[:missing_attributes][:lazy_fetch]))
|
|
71
65
|
end
|
|
72
66
|
|
|
73
67
|
def compile_rql_pass2
|
|
74
68
|
rql = super
|
|
75
69
|
if effective_missing_attributes
|
|
76
70
|
type, attrs = effective_missing_attributes.first
|
|
77
|
-
rql = rql.__send__(type,
|
|
71
|
+
rql = rql.__send__(type, model.with_fields_aliased(attrs))
|
|
78
72
|
end
|
|
79
73
|
rql
|
|
80
74
|
end
|
|
@@ -1,24 +1,18 @@
|
|
|
1
1
|
module NoBrainer::Criteria::Preload
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
|
-
included {
|
|
5
|
-
|
|
6
|
-
def initialize(options={})
|
|
7
|
-
super
|
|
8
|
-
self._preloads = []
|
|
9
|
-
end
|
|
4
|
+
included { criteria_option :preload, :merge_with => :append_array }
|
|
10
5
|
|
|
11
6
|
def preload(*values)
|
|
12
|
-
chain(:
|
|
7
|
+
chain({:preload => values}, :copy_cache_from => self)
|
|
13
8
|
end
|
|
14
9
|
|
|
15
10
|
def merge!(criteria, options={})
|
|
16
|
-
super
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
perform_preloads(@cache)
|
|
11
|
+
super.tap do
|
|
12
|
+
# XXX Not pretty hack
|
|
13
|
+
if criteria.options[:preload].present? && criteria.cached?
|
|
14
|
+
perform_preloads(@cache)
|
|
15
|
+
end
|
|
22
16
|
end
|
|
23
17
|
end
|
|
24
18
|
|
|
@@ -35,7 +29,7 @@ module NoBrainer::Criteria::Preload
|
|
|
35
29
|
private
|
|
36
30
|
|
|
37
31
|
def should_preloads?
|
|
38
|
-
|
|
32
|
+
@options[:preload].present? && !raw?
|
|
39
33
|
end
|
|
40
34
|
|
|
41
35
|
def get_one(criteria)
|
|
@@ -44,7 +38,7 @@ module NoBrainer::Criteria::Preload
|
|
|
44
38
|
|
|
45
39
|
def perform_preloads(docs)
|
|
46
40
|
if should_preloads? && docs.present?
|
|
47
|
-
NoBrainer::Document::Association::EagerLoader.new.eager_load(docs,
|
|
41
|
+
NoBrainer::Document::Association::EagerLoader.new.eager_load(docs, @options[:preload])
|
|
48
42
|
end
|
|
49
43
|
end
|
|
50
44
|
end
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
module NoBrainer::Criteria::Raw
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
|
-
included {
|
|
4
|
+
included { criteria_option :raw, :merge_with => :set_scalar }
|
|
5
5
|
|
|
6
|
-
def raw
|
|
7
|
-
chain
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def merge!(criteria, options={})
|
|
11
|
-
super
|
|
12
|
-
self._raw = criteria._raw unless criteria._raw.nil?
|
|
13
|
-
self
|
|
6
|
+
def raw(value = true)
|
|
7
|
+
chain(:raw => value)
|
|
14
8
|
end
|
|
15
9
|
|
|
16
10
|
def raw?
|
|
17
|
-
!!finalized_criteria.
|
|
11
|
+
!!finalized_criteria.options[:raw]
|
|
18
12
|
end
|
|
19
13
|
|
|
20
14
|
private
|
|
@@ -28,6 +22,6 @@ module NoBrainer::Criteria::Raw
|
|
|
28
22
|
end
|
|
29
23
|
|
|
30
24
|
def _instantiate_model(attrs, options={})
|
|
31
|
-
|
|
25
|
+
model.new_from_db(attrs, options)
|
|
32
26
|
end
|
|
33
27
|
end
|
|
@@ -1,29 +1,23 @@
|
|
|
1
1
|
module NoBrainer::Criteria::Scope
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
|
-
included {
|
|
4
|
+
included { criteria_option :use_default_scope, :merge_with => :set_scalar }
|
|
5
5
|
|
|
6
6
|
def scoped
|
|
7
|
-
chain
|
|
7
|
+
chain(:use_default_scope => true)
|
|
8
8
|
end
|
|
9
9
|
|
|
10
10
|
def unscoped
|
|
11
|
-
chain
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def merge!(criteria, options={})
|
|
15
|
-
super
|
|
16
|
-
self.use_default_scope = criteria.use_default_scope unless criteria.use_default_scope.nil?
|
|
17
|
-
self
|
|
11
|
+
chain(:use_default_scope => false)
|
|
18
12
|
end
|
|
19
13
|
|
|
20
14
|
def respond_to?(name, include_private = false)
|
|
21
|
-
super || self.
|
|
15
|
+
super || self.model.respond_to?(name)
|
|
22
16
|
end
|
|
23
17
|
|
|
24
18
|
def method_missing(name, *args, &block)
|
|
25
|
-
return super unless self.
|
|
26
|
-
criteria = self.
|
|
19
|
+
return super unless self.model.respond_to?(name)
|
|
20
|
+
criteria = self.model.method(name).call(*args, &block)
|
|
27
21
|
raise "#{name} did not return a criteria" unless criteria.is_a?(NoBrainer::Criteria)
|
|
28
22
|
merge(criteria)
|
|
29
23
|
end
|
|
@@ -31,13 +25,13 @@ module NoBrainer::Criteria::Scope
|
|
|
31
25
|
private
|
|
32
26
|
|
|
33
27
|
def should_apply_default_scope?
|
|
34
|
-
|
|
28
|
+
model.default_scope_proc && @options[:use_default_scope] != false
|
|
35
29
|
end
|
|
36
30
|
|
|
37
31
|
def _apply_default_scope
|
|
38
32
|
return unless should_apply_default_scope?
|
|
39
|
-
criteria =
|
|
40
|
-
raise "Mixing model issue. Contact developer." if [criteria.
|
|
33
|
+
criteria = model.default_scope_proc.call
|
|
34
|
+
raise "Mixing model issue. Contact developer." if [criteria.model, self.model].compact.uniq.size == 2
|
|
41
35
|
criteria.merge(self)
|
|
42
36
|
end
|
|
43
37
|
|
|
@@ -3,17 +3,17 @@ module NoBrainer::Criteria::Update
|
|
|
3
3
|
|
|
4
4
|
def update_all(*a, &b)
|
|
5
5
|
prepare_args_for_update!(a)
|
|
6
|
-
run
|
|
6
|
+
run { without_ordering.without_plucking.to_rql.update(*a, &b) }
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def replace_all(*a, &b)
|
|
10
10
|
prepare_args_for_update!(a)
|
|
11
|
-
run
|
|
11
|
+
run { without_ordering.without_plucking.to_rql.replace(*a, &b) }
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
private
|
|
15
15
|
|
|
16
16
|
def prepare_args_for_update!(a)
|
|
17
|
-
a[0] =
|
|
17
|
+
a[0] = model.persistable_attributes(a[0]) if !a.empty? && a.first.is_a?(Hash)
|
|
18
18
|
end
|
|
19
19
|
end
|
|
@@ -1,35 +1,20 @@
|
|
|
1
1
|
module NoBrainer::Criteria::Where
|
|
2
2
|
extend ActiveSupport::Concern
|
|
3
3
|
|
|
4
|
-
included
|
|
5
|
-
|
|
6
|
-
def initialize(options={})
|
|
7
|
-
super
|
|
4
|
+
included do
|
|
5
|
+
criteria_option :where_ast, :merge_with => NoBrainer::Criteria::Where.method(:merge_where_ast)
|
|
8
6
|
end
|
|
9
7
|
|
|
10
8
|
def where(*args, &block)
|
|
11
|
-
chain
|
|
9
|
+
chain(:where_ast => parse_clause([*args, block].compact))
|
|
12
10
|
end
|
|
13
11
|
|
|
14
|
-
def
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
if criteria.where_ast
|
|
18
|
-
if self.where_ast
|
|
19
|
-
self.where_ast = MultiOperator.new(:and, [self.where_ast, criteria.where_ast])
|
|
20
|
-
else
|
|
21
|
-
self.where_ast = criteria.where_ast
|
|
22
|
-
end
|
|
23
|
-
self.where_ast = self.where_ast.simplify
|
|
24
|
-
raise unless criteria.where_ast.is_a?(MultiOperator)
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
self.with_index_name = criteria.with_index_name unless criteria.with_index_name.nil?
|
|
28
|
-
self
|
|
12
|
+
def self.merge_where_ast(a, b)
|
|
13
|
+
(a ? MultiOperator.new(:and, [a, b]) : b).simplify
|
|
29
14
|
end
|
|
30
15
|
|
|
31
16
|
def where_present?
|
|
32
|
-
finalized_criteria.where_ast.try(:clauses).present?
|
|
17
|
+
finalized_criteria.options[:where_ast].try(:clauses).present?
|
|
33
18
|
end
|
|
34
19
|
|
|
35
20
|
def where_indexed?
|
|
@@ -134,9 +119,9 @@ module NoBrainer::Criteria::Where
|
|
|
134
119
|
|
|
135
120
|
case association
|
|
136
121
|
when NoBrainer::Document::Association::BelongsTo::Metadata
|
|
137
|
-
|
|
138
|
-
opts = { :attr_name => key, :value => value, :type =>
|
|
139
|
-
raise NoBrainer::Error::InvalidType.new(opts) unless value.is_a?(
|
|
122
|
+
target_model = association.target_model
|
|
123
|
+
opts = { :attr_name => key, :value => value, :type => target_model}
|
|
124
|
+
raise NoBrainer::Error::InvalidType.new(opts) unless value.is_a?(target_model)
|
|
140
125
|
value.pk_value
|
|
141
126
|
else
|
|
142
127
|
model.cast_user_to_db_for(key, value)
|
|
@@ -200,7 +185,7 @@ module NoBrainer::Criteria::Where
|
|
|
200
185
|
when :nin then parse_clause(:not => { key.symbol.in => value })
|
|
201
186
|
when :ne then parse_clause(:not => { key.symbol.eq => value })
|
|
202
187
|
when :eq then parse_clause_stub_eq(key.symbol, value)
|
|
203
|
-
else BinaryOperator.new(key.symbol, key.modifier, value, self.
|
|
188
|
+
else BinaryOperator.new(key.symbol, key.modifier, value, self.model)
|
|
204
189
|
end
|
|
205
190
|
else raise "Invalid key: #{key}"
|
|
206
191
|
end
|
|
@@ -208,9 +193,9 @@ module NoBrainer::Criteria::Where
|
|
|
208
193
|
|
|
209
194
|
def parse_clause_stub_eq(key, value)
|
|
210
195
|
case value
|
|
211
|
-
when Range then BinaryOperator.new(key, :between, value, self.
|
|
212
|
-
when Regexp then BinaryOperator.new(key, :match, value.inspect[1..-2], self.
|
|
213
|
-
else BinaryOperator.new(key, :eq, value, self.
|
|
196
|
+
when Range then BinaryOperator.new(key, :between, value, self.model)
|
|
197
|
+
when Regexp then BinaryOperator.new(key, :match, value.inspect[1..-2], self.model)
|
|
198
|
+
else BinaryOperator.new(key, :eq, value, self.model)
|
|
214
199
|
end
|
|
215
200
|
end
|
|
216
201
|
|
|
@@ -230,10 +215,10 @@ module NoBrainer::Criteria::Where
|
|
|
230
215
|
def get_usable_indexes(*types)
|
|
231
216
|
@usable_indexes = {}
|
|
232
217
|
@usable_indexes[types] ||= begin
|
|
233
|
-
indexes = criteria.
|
|
234
|
-
indexes = indexes.select { |
|
|
235
|
-
if criteria.
|
|
236
|
-
indexes = indexes.select { |
|
|
218
|
+
indexes = criteria.model.indexes.values
|
|
219
|
+
indexes = indexes.select { |i| types.include?(i.kind) } if types.present?
|
|
220
|
+
if criteria.options[:use_index] && criteria.options[:use_index] != true
|
|
221
|
+
indexes = indexes.select { |i| i.name == criteria.options[:use_index].to_sym }
|
|
237
222
|
end
|
|
238
223
|
indexes
|
|
239
224
|
end
|
|
@@ -248,16 +233,15 @@ module NoBrainer::Criteria::Where
|
|
|
248
233
|
clauses = Hash[get_candidate_clauses(:eq, :in, :between).map { |c| [c.key, c] }]
|
|
249
234
|
return unless clauses.present?
|
|
250
235
|
|
|
251
|
-
if
|
|
252
|
-
clause = clauses[
|
|
253
|
-
|
|
254
|
-
self.index_name = index_name
|
|
236
|
+
if index = get_usable_indexes.select { |i| clauses[i.name] }.first
|
|
237
|
+
clause = clauses[index.name]
|
|
238
|
+
self.index_name = index.name
|
|
255
239
|
self.ast = remove_from_ast([clause])
|
|
256
240
|
self.index_type = clause.op == :between ? :between : :get_all
|
|
257
241
|
self.rql_proc = case clause.op
|
|
258
|
-
when :eq then ->(rql){ rql.get_all(clause.value, :index =>
|
|
259
|
-
when :in then ->(rql){ rql.get_all(*clause.value, :index =>
|
|
260
|
-
when :between then ->(rql){ rql.between(clause.value.min, clause.value.max, :index =>
|
|
242
|
+
when :eq then ->(rql){ rql.get_all(clause.value, :index => index.aliased_name) }
|
|
243
|
+
when :in then ->(rql){ rql.get_all(*clause.value, :index => index.aliased_name) }
|
|
244
|
+
when :between then ->(rql){ rql.between(clause.value.min, clause.value.max, :index => index.aliased_name,
|
|
261
245
|
:left_bound => :closed, :right_bound => :closed) }
|
|
262
246
|
end
|
|
263
247
|
end
|
|
@@ -267,18 +251,12 @@ module NoBrainer::Criteria::Where
|
|
|
267
251
|
clauses = Hash[get_candidate_clauses(:eq).map { |c| [c.key, c] }]
|
|
268
252
|
return unless clauses.present?
|
|
269
253
|
|
|
270
|
-
|
|
271
|
-
.map
|
|
272
|
-
.
|
|
273
|
-
.first
|
|
274
|
-
|
|
275
|
-
if index_name
|
|
276
|
-
indexed_clauses = index_values.map { |field| clauses[field] }
|
|
277
|
-
aliased_index = criteria.klass.indexes[index_name][:as]
|
|
278
|
-
self.index_name = index_name
|
|
254
|
+
if index = get_usable_indexes(:compound).select { |i| i.what & clauses.keys == i.what }.first
|
|
255
|
+
indexed_clauses = index.what.map { |field| clauses[field] }
|
|
256
|
+
self.index_name = index.name
|
|
279
257
|
self.ast = remove_from_ast(indexed_clauses)
|
|
280
258
|
self.index_type = :get_all
|
|
281
|
-
self.rql_proc = ->(rql){ rql.get_all(indexed_clauses.map { |c| c.value }, :index =>
|
|
259
|
+
self.rql_proc = ->(rql){ rql.get_all(indexed_clauses.map { |c| c.value }, :index => index.aliased_name) }
|
|
282
260
|
end
|
|
283
261
|
end
|
|
284
262
|
|
|
@@ -286,17 +264,16 @@ module NoBrainer::Criteria::Where
|
|
|
286
264
|
clauses = get_candidate_clauses(:gt, :ge, :lt, :le).group_by(&:key)
|
|
287
265
|
return unless clauses.present?
|
|
288
266
|
|
|
289
|
-
if
|
|
290
|
-
op_clauses = Hash[clauses[
|
|
267
|
+
if index = get_usable_indexes.select { |i| clauses[i.name] }.first
|
|
268
|
+
op_clauses = Hash[clauses[index.name].map { |c| [c.op, c] }]
|
|
291
269
|
left_bound = op_clauses[:gt] || op_clauses[:ge]
|
|
292
270
|
right_bound = op_clauses[:lt] || op_clauses[:le]
|
|
293
271
|
|
|
294
|
-
|
|
295
|
-
self.index_name = index_name
|
|
272
|
+
self.index_name = index.name
|
|
296
273
|
self.ast = remove_from_ast([left_bound, right_bound].compact)
|
|
297
274
|
|
|
298
275
|
options = {}
|
|
299
|
-
options[:index] =
|
|
276
|
+
options[:index] = index.aliased_name
|
|
300
277
|
options[:left_bound] = {:gt => :open, :ge => :closed}[left_bound.op] if left_bound
|
|
301
278
|
options[:right_bound] = {:lt => :open, :le => :closed}[right_bound.op] if right_bound
|
|
302
279
|
self.index_type = :between
|
|
@@ -335,7 +312,7 @@ module NoBrainer::Criteria::Where
|
|
|
335
312
|
|
|
336
313
|
def where_index_finder
|
|
337
314
|
return finalized_criteria.__send__(:where_index_finder) unless finalized?
|
|
338
|
-
@where_index_finder ||= IndexFinder.new(self, where_ast).tap { |index_finder| index_finder.find_index }
|
|
315
|
+
@where_index_finder ||= IndexFinder.new(self, @options[:where_ast]).tap { |index_finder| index_finder.find_index }
|
|
339
316
|
end
|
|
340
317
|
|
|
341
318
|
def compile_rql_pass1
|
|
@@ -346,7 +323,7 @@ module NoBrainer::Criteria::Where
|
|
|
346
323
|
|
|
347
324
|
def compile_rql_pass2
|
|
348
325
|
rql = super
|
|
349
|
-
ast = where_index_finder.could_find_index? ? where_index_finder.ast :
|
|
326
|
+
ast = where_index_finder.could_find_index? ? where_index_finder.ast : @options[:where_ast]
|
|
350
327
|
rql = rql.filter { |doc| ast.to_rql(doc) } if ast.try(:clauses).present?
|
|
351
328
|
rql
|
|
352
329
|
end
|
data/lib/no_brainer/criteria.rb
CHANGED
|
@@ -4,5 +4,5 @@ class NoBrainer::Criteria
|
|
|
4
4
|
extend NoBrainer::Autoload
|
|
5
5
|
autoload_and_include :Core, :Raw, :AfterFind, :Where, :OrderBy, :Limit,
|
|
6
6
|
:Pluck, :Count, :Delete, :Enumerable, :First, :Aggregate,
|
|
7
|
-
:Preload, :Update, :Cache, :Index, :Scope
|
|
7
|
+
:Preload, :Update, :Cache, :Index, :Extend, :Scope
|
|
8
8
|
end
|
|
@@ -13,10 +13,10 @@ class NoBrainer::Document::Association::BelongsTo
|
|
|
13
13
|
|
|
14
14
|
def primary_key
|
|
15
15
|
# TODO test :primary_key
|
|
16
|
-
options[:primary_key].try(:to_sym) ||
|
|
16
|
+
options[:primary_key].try(:to_sym) || target_model.pk_name
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
def
|
|
19
|
+
def target_model
|
|
20
20
|
# TODO test :class_name
|
|
21
21
|
(options[:class_name] || target_name.to_s.camelize).constantize
|
|
22
22
|
end
|
|
@@ -29,9 +29,9 @@ class NoBrainer::Document::Association::BelongsTo
|
|
|
29
29
|
# This would have the effect of loading all the models because they
|
|
30
30
|
# are likely to be related to each other. So we don't know the type
|
|
31
31
|
# of the primary key of the target.
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
32
|
+
owner_model.field(foreign_key, :as => options[:foreign_key_as], :index => options[:index])
|
|
33
|
+
owner_model.validates(target_name, { :presence => true }) if options[:required]
|
|
34
|
+
owner_model.validates(target_name, options[:validates]) if options[:validates]
|
|
35
35
|
|
|
36
36
|
delegate("#{foreign_key}=", :assign_foreign_key, :call_super => true)
|
|
37
37
|
add_callback_for(:after_validation)
|
|
@@ -54,7 +54,7 @@ class NoBrainer::Document::Association::BelongsTo
|
|
|
54
54
|
return target if loaded?
|
|
55
55
|
|
|
56
56
|
if fk = owner.read_attribute(foreign_key)
|
|
57
|
-
preload(
|
|
57
|
+
preload(target_model.unscoped.where(primary_key => fk).first)
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
|
|
@@ -4,25 +4,25 @@ module NoBrainer::Document::Association::Core
|
|
|
4
4
|
module Metadata
|
|
5
5
|
extend ActiveSupport::Concern
|
|
6
6
|
|
|
7
|
-
attr_accessor :
|
|
7
|
+
attr_accessor :owner_model, :target_name, :options
|
|
8
8
|
|
|
9
|
-
def initialize(
|
|
10
|
-
@
|
|
9
|
+
def initialize(owner_model, target_name, options={})
|
|
10
|
+
@owner_model = owner_model
|
|
11
11
|
@target_name = target_name
|
|
12
12
|
@options = options
|
|
13
13
|
end
|
|
14
14
|
|
|
15
|
-
def
|
|
16
|
-
@
|
|
15
|
+
def association_model
|
|
16
|
+
@association_model ||= self.class.name.deconstantize.constantize
|
|
17
17
|
end
|
|
18
18
|
|
|
19
19
|
def new(owner)
|
|
20
|
-
|
|
20
|
+
association_model.new(self, owner)
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def delegate(method_name, target, options={})
|
|
24
24
|
metadata = self
|
|
25
|
-
|
|
25
|
+
owner_model.inject_in_layer :associations do
|
|
26
26
|
define_method(method_name) do |*args, &block|
|
|
27
27
|
super(*args, &block) if options[:call_super]
|
|
28
28
|
associations[metadata].__send__(target, *args, &block)
|
|
@@ -40,7 +40,7 @@ module NoBrainer::Document::Association::Core
|
|
|
40
40
|
instance_eval <<-RUBY, __FILE__, __LINE__+1
|
|
41
41
|
if !@added_#{what}
|
|
42
42
|
metadata = self
|
|
43
|
-
|
|
43
|
+
owner_model.#{what} { associations[metadata].#{what}_callback }
|
|
44
44
|
@added_#{what} = true
|
|
45
45
|
end
|
|
46
46
|
RUBY
|
|
@@ -49,15 +49,15 @@ module NoBrainer::Document::Association::Core
|
|
|
49
49
|
|
|
50
50
|
included { attr_accessor :metadata, :owner }
|
|
51
51
|
|
|
52
|
-
delegate :primary_key, :foreign_key, :target_name, :
|
|
52
|
+
delegate :primary_key, :foreign_key, :target_name, :target_model, :to => :metadata
|
|
53
53
|
|
|
54
54
|
def initialize(metadata, owner)
|
|
55
55
|
@metadata, @owner = metadata, owner
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def assert_target_type(value)
|
|
59
|
-
unless value.is_a?(
|
|
60
|
-
options = { :attr_name => target_name, :value => value, :type =>
|
|
59
|
+
unless value.is_a?(target_model) || value.nil?
|
|
60
|
+
options = { :attr_name => target_name, :value => value, :type => target_model }
|
|
61
61
|
raise NoBrainer::Error::InvalidType.new(options)
|
|
62
62
|
end
|
|
63
63
|
end
|