elasticsearch-persistence-queryable 0.1.8
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|