elasticsearch-persistence-queryable 0.1.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/CHANGELOG.md +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +13 -0
- data/README.md +678 -0
- data/Rakefile +57 -0
- data/elasticsearch-persistence.gemspec +57 -0
- data/examples/music/album.rb +34 -0
- data/examples/music/artist.rb +50 -0
- data/examples/music/artists/_form.html.erb +8 -0
- data/examples/music/artists/artists_controller.rb +67 -0
- data/examples/music/artists/artists_controller_test.rb +53 -0
- data/examples/music/artists/index.html.erb +57 -0
- data/examples/music/artists/show.html.erb +51 -0
- data/examples/music/assets/application.css +226 -0
- data/examples/music/assets/autocomplete.css +48 -0
- data/examples/music/assets/blank_cover.png +0 -0
- data/examples/music/assets/form.css +113 -0
- data/examples/music/index_manager.rb +60 -0
- data/examples/music/search/index.html.erb +93 -0
- data/examples/music/search/search_controller.rb +41 -0
- data/examples/music/search/search_controller_test.rb +9 -0
- data/examples/music/search/search_helper.rb +15 -0
- data/examples/music/suggester.rb +45 -0
- data/examples/music/template.rb +392 -0
- data/examples/music/vendor/assets/jquery-ui-1.10.4.custom.min.css +7 -0
- data/examples/music/vendor/assets/jquery-ui-1.10.4.custom.min.js +6 -0
- data/examples/notes/.gitignore +7 -0
- data/examples/notes/Gemfile +28 -0
- data/examples/notes/README.markdown +36 -0
- data/examples/notes/application.rb +238 -0
- data/examples/notes/config.ru +7 -0
- data/examples/notes/test.rb +118 -0
- data/lib/elasticsearch/per_thread_registry.rb +53 -0
- data/lib/elasticsearch/persistence/client.rb +51 -0
- data/lib/elasticsearch/persistence/inheritence.rb +9 -0
- data/lib/elasticsearch/persistence/model/base.rb +95 -0
- data/lib/elasticsearch/persistence/model/callbacks.rb +37 -0
- data/lib/elasticsearch/persistence/model/errors.rb +9 -0
- data/lib/elasticsearch/persistence/model/find.rb +155 -0
- data/lib/elasticsearch/persistence/model/gateway_delegation.rb +23 -0
- data/lib/elasticsearch/persistence/model/hash_wrapper.rb +17 -0
- data/lib/elasticsearch/persistence/model/rails.rb +39 -0
- data/lib/elasticsearch/persistence/model/store.rb +271 -0
- data/lib/elasticsearch/persistence/model.rb +148 -0
- data/lib/elasticsearch/persistence/null_relation.rb +56 -0
- data/lib/elasticsearch/persistence/query_cache.rb +68 -0
- data/lib/elasticsearch/persistence/querying.rb +21 -0
- data/lib/elasticsearch/persistence/relation/delegation.rb +130 -0
- data/lib/elasticsearch/persistence/relation/finder_methods.rb +39 -0
- data/lib/elasticsearch/persistence/relation/merger.rb +179 -0
- data/lib/elasticsearch/persistence/relation/query_builder.rb +279 -0
- data/lib/elasticsearch/persistence/relation/query_methods.rb +362 -0
- data/lib/elasticsearch/persistence/relation/search_option_methods.rb +44 -0
- data/lib/elasticsearch/persistence/relation/spawn_methods.rb +61 -0
- data/lib/elasticsearch/persistence/relation.rb +110 -0
- data/lib/elasticsearch/persistence/repository/class.rb +71 -0
- data/lib/elasticsearch/persistence/repository/find.rb +73 -0
- data/lib/elasticsearch/persistence/repository/naming.rb +115 -0
- data/lib/elasticsearch/persistence/repository/response/results.rb +105 -0
- data/lib/elasticsearch/persistence/repository/search.rb +156 -0
- data/lib/elasticsearch/persistence/repository/serialize.rb +31 -0
- data/lib/elasticsearch/persistence/repository/store.rb +94 -0
- data/lib/elasticsearch/persistence/repository.rb +77 -0
- data/lib/elasticsearch/persistence/scoping/default.rb +137 -0
- data/lib/elasticsearch/persistence/scoping/named.rb +70 -0
- data/lib/elasticsearch/persistence/scoping.rb +52 -0
- data/lib/elasticsearch/persistence/version.rb +5 -0
- data/lib/elasticsearch/persistence.rb +157 -0
- data/lib/elasticsearch/rails_compatibility.rb +17 -0
- data/lib/rails/generators/elasticsearch/model/model_generator.rb +21 -0
- data/lib/rails/generators/elasticsearch/model/templates/model.rb.tt +9 -0
- data/lib/rails/generators/elasticsearch_generator.rb +2 -0
- data/lib/rails/instrumentation/railtie.rb +31 -0
- data/lib/rails/instrumentation.rb +10 -0
- data/test/integration/model/model_basic_test.rb +157 -0
- data/test/integration/repository/custom_class_test.rb +85 -0
- data/test/integration/repository/customized_class_test.rb +82 -0
- data/test/integration/repository/default_class_test.rb +114 -0
- data/test/integration/repository/virtus_model_test.rb +114 -0
- data/test/test_helper.rb +53 -0
- data/test/unit/model_base_test.rb +48 -0
- data/test/unit/model_find_test.rb +148 -0
- data/test/unit/model_gateway_test.rb +99 -0
- data/test/unit/model_rails_test.rb +88 -0
- data/test/unit/model_store_test.rb +514 -0
- data/test/unit/persistence_test.rb +32 -0
- data/test/unit/repository_class_test.rb +51 -0
- data/test/unit/repository_client_test.rb +32 -0
- data/test/unit/repository_find_test.rb +388 -0
- data/test/unit/repository_indexing_test.rb +37 -0
- data/test/unit/repository_module_test.rb +146 -0
- data/test/unit/repository_naming_test.rb +146 -0
- data/test/unit/repository_response_results_test.rb +98 -0
- data/test/unit/repository_search_test.rb +117 -0
- data/test/unit/repository_serialize_test.rb +57 -0
- data/test/unit/repository_store_test.rb +303 -0
- metadata +487 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Elasticsearch
|
4
|
+
module Persistence
|
5
|
+
module NullRelation # :nodoc:
|
6
|
+
def pluck(*column_names)
|
7
|
+
[]
|
8
|
+
end
|
9
|
+
|
10
|
+
def delete_all
|
11
|
+
0
|
12
|
+
end
|
13
|
+
|
14
|
+
def update_all(_updates)
|
15
|
+
0
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete(_id_or_array)
|
19
|
+
0
|
20
|
+
end
|
21
|
+
|
22
|
+
def empty?
|
23
|
+
true
|
24
|
+
end
|
25
|
+
|
26
|
+
def none?
|
27
|
+
true
|
28
|
+
end
|
29
|
+
|
30
|
+
def any?
|
31
|
+
false
|
32
|
+
end
|
33
|
+
|
34
|
+
def one?
|
35
|
+
false
|
36
|
+
end
|
37
|
+
|
38
|
+
def many?
|
39
|
+
false
|
40
|
+
end
|
41
|
+
|
42
|
+
def exists?(_conditions = :none)
|
43
|
+
false
|
44
|
+
end
|
45
|
+
|
46
|
+
def or(other)
|
47
|
+
other.spawn
|
48
|
+
end
|
49
|
+
|
50
|
+
|
51
|
+
def exec_queries
|
52
|
+
@records = OpenStruct.new(klass: Elasticsearch::Persistence::Repository::Class, total: 0, results: []).freeze
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require "active_support/core_ext/module"
|
2
|
+
|
3
|
+
module Elasticsearch
|
4
|
+
module Persistence
|
5
|
+
module QueryCache
|
6
|
+
module CacheMethods
|
7
|
+
mattr_accessor :force_cache
|
8
|
+
mattr_accessor :cache_store
|
9
|
+
mattr_accessor :cache_store_expire_in
|
10
|
+
|
11
|
+
@@cache_store_expire_in = 300
|
12
|
+
|
13
|
+
@@force_cache = false
|
14
|
+
|
15
|
+
def cache
|
16
|
+
Elasticsearch::Persistence.force_cache = true
|
17
|
+
lm = yield
|
18
|
+
Elasticsearch::Persistence.force_cache = false
|
19
|
+
lm
|
20
|
+
end
|
21
|
+
|
22
|
+
def setup_store!
|
23
|
+
case Elasticsearch::Persistence.cache_store
|
24
|
+
when :redis_store
|
25
|
+
ActiveSupport::Cache::RedisStore
|
26
|
+
when :memory_store
|
27
|
+
ActiveSupport::Cache::MemoryStore
|
28
|
+
else
|
29
|
+
ActiveSupport::Cache::MemoryStore
|
30
|
+
end.new(namespace: "elasticsearch", expires_in: Elasticsearch::Persistence.cache_store_expire_in)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def store
|
35
|
+
@query_cache ||= Elasticsearch::Persistence.setup_store!
|
36
|
+
end
|
37
|
+
|
38
|
+
def cache_query(query, klass)
|
39
|
+
cache_key = sha(query)
|
40
|
+
Elasticsearch::Persistence.force_cache
|
41
|
+
result = if store.exist?(cache_key) && Elasticsearch::Persistence.force_cache
|
42
|
+
ActiveSupport::Notifications.instrument "cache.query.elasticsearch",
|
43
|
+
name: klass.name,
|
44
|
+
query: query
|
45
|
+
|
46
|
+
store.fetch cache_key
|
47
|
+
else
|
48
|
+
res = []
|
49
|
+
ActiveSupport::Notifications.instrument "query.elasticsearch",
|
50
|
+
name: klass.name,
|
51
|
+
query: query do
|
52
|
+
res = yield
|
53
|
+
end
|
54
|
+
|
55
|
+
store.write(cache_key, res) if Elasticsearch::Persistence.force_cache
|
56
|
+
res
|
57
|
+
end
|
58
|
+
result.dup
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
def sha(str)
|
64
|
+
Digest::SHA256.new.hexdigest(str)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module Persistence
|
3
|
+
module Querying
|
4
|
+
delegate :first, :first!, :last, :last!, :exists?, :has_field?, :any?, :many?, to: :all
|
5
|
+
delegate :order, :limit, :size, :sort, :where, :rewhere, :eager_load, :includes, :create_with, :none, :unscope, to: :all
|
6
|
+
delegate :or_filter, :filter, :fields, :source, :highlight, :aggregation, to: :all
|
7
|
+
delegate :skip_callbacks, :routing, to: :all
|
8
|
+
delegate :search_options, :routing, to: :all
|
9
|
+
delegate :must, :must_not, :should, :where_not, :query_string, to: :all
|
10
|
+
|
11
|
+
def fetch_results(es)
|
12
|
+
unless es.count?
|
13
|
+
gateway.search(es.to_elastic, es.search_options)
|
14
|
+
else
|
15
|
+
gateway.count(es.to_elastic, es.search_options)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module Persistence
|
3
|
+
module Delegation # :nodoc:
|
4
|
+
module DelegateCache
|
5
|
+
def relation_delegate_class(klass) # :nodoc:
|
6
|
+
@relation_delegate_cache[klass]
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize_relation_delegate_cache # :nodoc:
|
10
|
+
@relation_delegate_cache = cache = {}
|
11
|
+
[
|
12
|
+
Elasticsearch::Persistence::Relation,
|
13
|
+
].each do |klass|
|
14
|
+
delegate = Class.new(klass) {
|
15
|
+
include ClassSpecificRelation
|
16
|
+
}
|
17
|
+
const_set klass.name.gsub("::", "_"), delegate
|
18
|
+
cache[klass] = delegate
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.extended(child_class)
|
23
|
+
child_class.initialize_relation_delegate_cache
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
extend ActiveSupport::Concern
|
29
|
+
|
30
|
+
# This module creates compiled delegation methods dynamically at runtime, which makes
|
31
|
+
# subsequent calls to that method faster by avoiding method_missing. The delegations
|
32
|
+
# may vary depending on the klass of a relation, so we create a subclass of Relation
|
33
|
+
# for each different klass, and the delegations are compiled into that subclass only.
|
34
|
+
|
35
|
+
BLACKLISTED_ARRAY_METHODS = [
|
36
|
+
:compact!, :flatten!, :reject!, :reverse!, :rotate!, :map!,
|
37
|
+
:shuffle!, :slice!, :sort!, :sort_by!, :delete_if,
|
38
|
+
:keep_if, :pop, :shift, :delete_at, :compact, :select!,
|
39
|
+
].to_set # :nodoc:
|
40
|
+
|
41
|
+
delegate :to_xml, :to_yaml, :length, :collect, :map, :each, :all?, :include?, :to_ary, :join, to: :to_a
|
42
|
+
delegate :inner_hits, :aggregations, :highlights, :total, to: :to_a
|
43
|
+
|
44
|
+
delegate :mapping, :index_name, :document_type, :to => :klass
|
45
|
+
|
46
|
+
module ClassSpecificRelation # :nodoc:
|
47
|
+
extend ActiveSupport::Concern
|
48
|
+
|
49
|
+
included do
|
50
|
+
@delegation_mutex = Mutex.new
|
51
|
+
end
|
52
|
+
|
53
|
+
module ClassMethods # :nodoc:
|
54
|
+
def name
|
55
|
+
superclass.name
|
56
|
+
end
|
57
|
+
|
58
|
+
def delegate_to_scoped_klass(method)
|
59
|
+
@delegation_mutex.synchronize do
|
60
|
+
return if method_defined?(method)
|
61
|
+
|
62
|
+
if method.to_s =~ /\A[a-zA-Z_]\w*[!?]?\z/
|
63
|
+
module_eval <<-RUBY, __FILE__, __LINE__ + 1
|
64
|
+
def #{method}(*args, &block)
|
65
|
+
scoping { @klass.#{method}(*args, &block) }
|
66
|
+
end
|
67
|
+
RUBY
|
68
|
+
else
|
69
|
+
define_method method do |*args, &block|
|
70
|
+
scoping { @klass.public_send(method, *args, &block) }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def delegate(method, opts = {})
|
77
|
+
@delegation_mutex.synchronize do
|
78
|
+
return if method_defined?(method)
|
79
|
+
super
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
def method_missing(method, *args, &block)
|
87
|
+
if @klass.respond_to?(method)
|
88
|
+
self.class.delegate_to_scoped_klass(method)
|
89
|
+
scoping { @klass.public_send(method, *args, &block) }
|
90
|
+
else
|
91
|
+
super
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
module ClassMethods # :nodoc:
|
97
|
+
def create(klass, *args)
|
98
|
+
relation_class_for(klass).new(klass, *args)
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
def relation_class_for(klass)
|
104
|
+
klass.relation_delegate_class(self)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def respond_to?(method, include_private = false)
|
109
|
+
super || @klass.respond_to?(method, include_private) ||
|
110
|
+
array_delegable?(method)
|
111
|
+
end
|
112
|
+
|
113
|
+
protected
|
114
|
+
|
115
|
+
def array_delegable?(method)
|
116
|
+
Array.method_defined?(method) && BLACKLISTED_ARRAY_METHODS.exclude?(method)
|
117
|
+
end
|
118
|
+
|
119
|
+
def method_missing(method, *args, &block)
|
120
|
+
if @klass.respond_to?(method)
|
121
|
+
scoping { @klass.public_send(method, *args, &block) }
|
122
|
+
elsif array_delegable?(method)
|
123
|
+
to_a.public_send(method, *args, &block)
|
124
|
+
else
|
125
|
+
super
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Elasticsearch
|
2
|
+
module Persistence
|
3
|
+
|
4
|
+
module FinderMethods
|
5
|
+
|
6
|
+
def first
|
7
|
+
return results.first if @loaded
|
8
|
+
spawn.first!.results.first
|
9
|
+
end
|
10
|
+
|
11
|
+
def first!
|
12
|
+
spawn.sort(Hash[default_sort_key, :asc]).spawn.size(1)
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def last
|
17
|
+
return results.last if @loaded
|
18
|
+
spawn.last!.results.first
|
19
|
+
end
|
20
|
+
|
21
|
+
def last!
|
22
|
+
spawn.sort(Hash[default_sort_key, :desc]).spawn.size(1)
|
23
|
+
self
|
24
|
+
end
|
25
|
+
|
26
|
+
def count
|
27
|
+
return results.count if @loaded
|
28
|
+
spawn.count!
|
29
|
+
end
|
30
|
+
|
31
|
+
def count!
|
32
|
+
@values[:count] = true
|
33
|
+
@values.delete(:size)
|
34
|
+
spawn.to_a["count"]
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'active_support/core_ext/hash/keys'
|
2
|
+
require "set"
|
3
|
+
|
4
|
+
module Elasticsearch
|
5
|
+
module Persistence
|
6
|
+
class Relation
|
7
|
+
class HashMerger # :nodoc:
|
8
|
+
attr_reader :relation, :hash
|
9
|
+
|
10
|
+
def initialize(relation, hash)
|
11
|
+
hash.assert_valid_keys(*Relation::VALUE_METHODS)
|
12
|
+
|
13
|
+
@relation = relation
|
14
|
+
@hash = hash
|
15
|
+
end
|
16
|
+
|
17
|
+
def merge
|
18
|
+
Merger.new(relation, other).merge
|
19
|
+
end
|
20
|
+
|
21
|
+
# Applying values to a relation has some side effects. E.g.
|
22
|
+
# interpolation might take place for where values. So we should
|
23
|
+
# build a relation to merge in rather than directly merging
|
24
|
+
# the values.
|
25
|
+
def other
|
26
|
+
other = Relation.create(relation.klass)
|
27
|
+
hash.each { |k, v|
|
28
|
+
if k == :joins
|
29
|
+
if Hash === v
|
30
|
+
other.joins!(v)
|
31
|
+
else
|
32
|
+
other.joins!(*v)
|
33
|
+
end
|
34
|
+
elsif k == :select
|
35
|
+
other._select!(v)
|
36
|
+
else
|
37
|
+
other.send("#{k}!", v)
|
38
|
+
end
|
39
|
+
}
|
40
|
+
other
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Merger # :nodoc:
|
45
|
+
attr_reader :relation, :values, :other
|
46
|
+
|
47
|
+
def initialize(relation, other)
|
48
|
+
@relation = relation
|
49
|
+
@values = other.values
|
50
|
+
@other = other
|
51
|
+
end
|
52
|
+
|
53
|
+
NORMAL_VALUES = [:where, :first, :last, :filter]
|
54
|
+
|
55
|
+
def normal_values
|
56
|
+
NORMAL_VALUES
|
57
|
+
end
|
58
|
+
|
59
|
+
def merge
|
60
|
+
normal_values.each do |name|
|
61
|
+
value = values[name]
|
62
|
+
# The unless clause is here mostly for performance reasons (since the `send` call might be moderately
|
63
|
+
# expensive), most of the time the value is going to be `nil` or `.blank?`, the only catch is that
|
64
|
+
# `false.blank?` returns `true`, so there needs to be an extra check so that explicit `false` values
|
65
|
+
# don't fall through the cracks.
|
66
|
+
|
67
|
+
unless value.nil? || (value.blank? && false != value)
|
68
|
+
if name == :select
|
69
|
+
relation._select!(*value)
|
70
|
+
elsif name == :filter
|
71
|
+
values.each do |v|
|
72
|
+
relation.send("#{name}!", v.first, v.last)
|
73
|
+
end
|
74
|
+
else
|
75
|
+
relation.send("#{name}!", *value)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
merge_multi_values
|
81
|
+
merge_single_values
|
82
|
+
#merge_joins
|
83
|
+
|
84
|
+
relation
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
|
89
|
+
def merge_joins
|
90
|
+
return if values[:joins].blank?
|
91
|
+
|
92
|
+
if other.klass == relation.klass
|
93
|
+
relation.joins!(*values[:joins])
|
94
|
+
else
|
95
|
+
joins_dependency, rest = values[:joins].partition do |join|
|
96
|
+
case join
|
97
|
+
when Hash, Symbol, Array
|
98
|
+
true
|
99
|
+
else
|
100
|
+
false
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
join_dependency = ActiveRecord::Associations::JoinDependency.new(other.klass,
|
105
|
+
joins_dependency,
|
106
|
+
[])
|
107
|
+
relation.joins! rest
|
108
|
+
|
109
|
+
@relation = relation.joins join_dependency
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def merge_multi_values
|
114
|
+
lhs_wheres = relation.where_values
|
115
|
+
rhs_wheres = values[:where] || []
|
116
|
+
|
117
|
+
lhs_filters = relation.filter_values
|
118
|
+
rhs_filters = values[:filter] || []
|
119
|
+
|
120
|
+
removed, kept = partition_overwrites(lhs_wheres, rhs_wheres)
|
121
|
+
|
122
|
+
where_values = kept + rhs_wheres
|
123
|
+
|
124
|
+
filters_removed, filters_kept = partition_overwrites(lhs_wheres, rhs_wheres)
|
125
|
+
filter_values = rhs_filters
|
126
|
+
|
127
|
+
|
128
|
+
relation.where_values = where_values.empty? ? nil : where_values
|
129
|
+
relation.filter_values = filter_values.empty? ? nil : filter_values
|
130
|
+
|
131
|
+
if values[:reordering]
|
132
|
+
# override any order specified in the original relation
|
133
|
+
relation.reorder! values[:order]
|
134
|
+
elsif values[:order]
|
135
|
+
# merge in order_values from relation
|
136
|
+
relation.order! values[:order]
|
137
|
+
end
|
138
|
+
|
139
|
+
relation.extend(*values[:extending]) unless values[:extending].blank?
|
140
|
+
end
|
141
|
+
|
142
|
+
def merge_single_values
|
143
|
+
#relation.from_value = values[:from] unless relation.from_value
|
144
|
+
#relation.lock_value = values[:lock] unless relation.lock_value
|
145
|
+
|
146
|
+
unless values[:create_with].blank?
|
147
|
+
relation.create_with_value = (relation.create_with_value || {}).merge(values[:create_with])
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def filter_binds(lhs_binds, removed_wheres)
|
152
|
+
return lhs_binds if removed_wheres.empty?
|
153
|
+
|
154
|
+
set = Set.new removed_wheres.map { |x| x.left.name.to_s }
|
155
|
+
lhs_binds.dup.delete_if { |col,_| set.include? col.name }
|
156
|
+
end
|
157
|
+
|
158
|
+
# Remove equalities from the existing relation with a LHS which is
|
159
|
+
# present in the relation being merged in.
|
160
|
+
# returns [things_to_remove, things_to_keep]
|
161
|
+
def partition_overwrites(lhs_wheres, rhs_wheres)
|
162
|
+
if lhs_wheres.empty? || rhs_wheres.empty?
|
163
|
+
return [[], lhs_wheres]
|
164
|
+
end
|
165
|
+
|
166
|
+
nodes = rhs_wheres.find_all do |w|
|
167
|
+
w.respond_to?(:operator) && w.operator == :==
|
168
|
+
end
|
169
|
+
seen = Set.new(nodes) { |node| node.left }
|
170
|
+
|
171
|
+
lhs_wheres.partition do |w|
|
172
|
+
w.respond_to?(:operator) && w.operator == :== && seen.include?(w.left)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|