activegraph 10.0.0.pre.alpha.6

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.
Files changed (142) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1989 -0
  3. data/CONTRIBUTORS +12 -0
  4. data/Gemfile +24 -0
  5. data/README.md +107 -0
  6. data/bin/rake +17 -0
  7. data/config/locales/en.yml +5 -0
  8. data/config/neo4j/add_classnames.yml +1 -0
  9. data/config/neo4j/config.yml +38 -0
  10. data/lib/neo4j.rb +116 -0
  11. data/lib/neo4j/active_base.rb +89 -0
  12. data/lib/neo4j/active_node.rb +108 -0
  13. data/lib/neo4j/active_node/callbacks.rb +8 -0
  14. data/lib/neo4j/active_node/dependent.rb +11 -0
  15. data/lib/neo4j/active_node/dependent/association_methods.rb +49 -0
  16. data/lib/neo4j/active_node/dependent/query_proxy_methods.rb +51 -0
  17. data/lib/neo4j/active_node/enum.rb +26 -0
  18. data/lib/neo4j/active_node/has_n.rb +612 -0
  19. data/lib/neo4j/active_node/has_n/association.rb +278 -0
  20. data/lib/neo4j/active_node/has_n/association/rel_factory.rb +61 -0
  21. data/lib/neo4j/active_node/has_n/association/rel_wrapper.rb +23 -0
  22. data/lib/neo4j/active_node/has_n/association_cypher_methods.rb +108 -0
  23. data/lib/neo4j/active_node/id_property.rb +224 -0
  24. data/lib/neo4j/active_node/id_property/accessor.rb +62 -0
  25. data/lib/neo4j/active_node/initialize.rb +21 -0
  26. data/lib/neo4j/active_node/labels.rb +207 -0
  27. data/lib/neo4j/active_node/labels/index.rb +37 -0
  28. data/lib/neo4j/active_node/labels/reloading.rb +21 -0
  29. data/lib/neo4j/active_node/node_list_formatter.rb +13 -0
  30. data/lib/neo4j/active_node/node_wrapper.rb +54 -0
  31. data/lib/neo4j/active_node/orm_adapter.rb +82 -0
  32. data/lib/neo4j/active_node/persistence.rb +187 -0
  33. data/lib/neo4j/active_node/property.rb +60 -0
  34. data/lib/neo4j/active_node/query.rb +76 -0
  35. data/lib/neo4j/active_node/query/query_proxy.rb +374 -0
  36. data/lib/neo4j/active_node/query/query_proxy_eager_loading.rb +177 -0
  37. data/lib/neo4j/active_node/query/query_proxy_eager_loading/association_tree.rb +75 -0
  38. data/lib/neo4j/active_node/query/query_proxy_enumerable.rb +110 -0
  39. data/lib/neo4j/active_node/query/query_proxy_find_in_batches.rb +19 -0
  40. data/lib/neo4j/active_node/query/query_proxy_link.rb +139 -0
  41. data/lib/neo4j/active_node/query/query_proxy_methods.rb +302 -0
  42. data/lib/neo4j/active_node/query/query_proxy_methods_of_mass_updating.rb +86 -0
  43. data/lib/neo4j/active_node/query_methods.rb +68 -0
  44. data/lib/neo4j/active_node/reflection.rb +86 -0
  45. data/lib/neo4j/active_node/rels.rb +11 -0
  46. data/lib/neo4j/active_node/scope.rb +166 -0
  47. data/lib/neo4j/active_node/unpersisted.rb +48 -0
  48. data/lib/neo4j/active_node/validations.rb +59 -0
  49. data/lib/neo4j/active_rel.rb +67 -0
  50. data/lib/neo4j/active_rel/callbacks.rb +15 -0
  51. data/lib/neo4j/active_rel/initialize.rb +28 -0
  52. data/lib/neo4j/active_rel/persistence.rb +134 -0
  53. data/lib/neo4j/active_rel/persistence/query_factory.rb +95 -0
  54. data/lib/neo4j/active_rel/property.rb +95 -0
  55. data/lib/neo4j/active_rel/query.rb +101 -0
  56. data/lib/neo4j/active_rel/rel_wrapper.rb +31 -0
  57. data/lib/neo4j/active_rel/related_node.rb +87 -0
  58. data/lib/neo4j/active_rel/types.rb +82 -0
  59. data/lib/neo4j/active_rel/validations.rb +8 -0
  60. data/lib/neo4j/ansi.rb +14 -0
  61. data/lib/neo4j/class_arguments.rb +39 -0
  62. data/lib/neo4j/config.rb +135 -0
  63. data/lib/neo4j/core.rb +14 -0
  64. data/lib/neo4j/core/connection_failed_error.rb +6 -0
  65. data/lib/neo4j/core/cypher_error.rb +37 -0
  66. data/lib/neo4j/core/driver.rb +66 -0
  67. data/lib/neo4j/core/has_uri.rb +63 -0
  68. data/lib/neo4j/core/instrumentable.rb +36 -0
  69. data/lib/neo4j/core/label.rb +158 -0
  70. data/lib/neo4j/core/logging.rb +44 -0
  71. data/lib/neo4j/core/node.rb +23 -0
  72. data/lib/neo4j/core/querable.rb +88 -0
  73. data/lib/neo4j/core/query.rb +487 -0
  74. data/lib/neo4j/core/query_builder.rb +32 -0
  75. data/lib/neo4j/core/query_clauses.rb +727 -0
  76. data/lib/neo4j/core/query_ext.rb +24 -0
  77. data/lib/neo4j/core/query_find_in_batches.rb +49 -0
  78. data/lib/neo4j/core/relationship.rb +13 -0
  79. data/lib/neo4j/core/responses.rb +50 -0
  80. data/lib/neo4j/core/result.rb +33 -0
  81. data/lib/neo4j/core/schema.rb +30 -0
  82. data/lib/neo4j/core/schema_errors.rb +12 -0
  83. data/lib/neo4j/core/wrappable.rb +30 -0
  84. data/lib/neo4j/errors.rb +57 -0
  85. data/lib/neo4j/migration.rb +148 -0
  86. data/lib/neo4j/migrations.rb +27 -0
  87. data/lib/neo4j/migrations/base.rb +77 -0
  88. data/lib/neo4j/migrations/check_pending.rb +20 -0
  89. data/lib/neo4j/migrations/helpers.rb +105 -0
  90. data/lib/neo4j/migrations/helpers/id_property.rb +75 -0
  91. data/lib/neo4j/migrations/helpers/relationships.rb +66 -0
  92. data/lib/neo4j/migrations/helpers/schema.rb +51 -0
  93. data/lib/neo4j/migrations/migration_file.rb +24 -0
  94. data/lib/neo4j/migrations/runner.rb +195 -0
  95. data/lib/neo4j/migrations/schema.rb +44 -0
  96. data/lib/neo4j/migrations/schema_migration.rb +14 -0
  97. data/lib/neo4j/model_schema.rb +139 -0
  98. data/lib/neo4j/paginated.rb +27 -0
  99. data/lib/neo4j/railtie.rb +105 -0
  100. data/lib/neo4j/schema/operation.rb +102 -0
  101. data/lib/neo4j/shared.rb +60 -0
  102. data/lib/neo4j/shared/attributes.rb +216 -0
  103. data/lib/neo4j/shared/callbacks.rb +68 -0
  104. data/lib/neo4j/shared/cypher.rb +37 -0
  105. data/lib/neo4j/shared/declared_properties.rb +204 -0
  106. data/lib/neo4j/shared/declared_property.rb +109 -0
  107. data/lib/neo4j/shared/declared_property/index.rb +37 -0
  108. data/lib/neo4j/shared/enum.rb +167 -0
  109. data/lib/neo4j/shared/filtered_hash.rb +79 -0
  110. data/lib/neo4j/shared/identity.rb +34 -0
  111. data/lib/neo4j/shared/initialize.rb +64 -0
  112. data/lib/neo4j/shared/marshal.rb +23 -0
  113. data/lib/neo4j/shared/mass_assignment.rb +64 -0
  114. data/lib/neo4j/shared/permitted_attributes.rb +28 -0
  115. data/lib/neo4j/shared/persistence.rb +282 -0
  116. data/lib/neo4j/shared/property.rb +240 -0
  117. data/lib/neo4j/shared/query_factory.rb +102 -0
  118. data/lib/neo4j/shared/rel_type_converters.rb +43 -0
  119. data/lib/neo4j/shared/serialized_properties.rb +30 -0
  120. data/lib/neo4j/shared/type_converters.rb +433 -0
  121. data/lib/neo4j/shared/typecasted_attributes.rb +98 -0
  122. data/lib/neo4j/shared/typecaster.rb +53 -0
  123. data/lib/neo4j/shared/validations.rb +44 -0
  124. data/lib/neo4j/tasks/migration.rake +202 -0
  125. data/lib/neo4j/timestamps.rb +11 -0
  126. data/lib/neo4j/timestamps/created.rb +9 -0
  127. data/lib/neo4j/timestamps/updated.rb +9 -0
  128. data/lib/neo4j/transaction.rb +139 -0
  129. data/lib/neo4j/type_converters.rb +7 -0
  130. data/lib/neo4j/undeclared_properties.rb +53 -0
  131. data/lib/neo4j/version.rb +3 -0
  132. data/lib/neo4j/wrapper.rb +4 -0
  133. data/lib/rails/generators/neo4j/migration/migration_generator.rb +14 -0
  134. data/lib/rails/generators/neo4j/migration/templates/migration.erb +9 -0
  135. data/lib/rails/generators/neo4j/model/model_generator.rb +88 -0
  136. data/lib/rails/generators/neo4j/model/templates/migration.erb +9 -0
  137. data/lib/rails/generators/neo4j/model/templates/model.erb +15 -0
  138. data/lib/rails/generators/neo4j/upgrade_v8/templates/migration.erb +17 -0
  139. data/lib/rails/generators/neo4j/upgrade_v8/upgrade_v8_generator.rb +32 -0
  140. data/lib/rails/generators/neo4j_generator.rb +119 -0
  141. data/neo4j.gemspec +51 -0
  142. metadata +421 -0
@@ -0,0 +1,177 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ module Query
4
+ module QueryProxyEagerLoading
5
+ class IdentityMap < Hash
6
+ def add(node)
7
+ self[node.neo_id] ||= node
8
+ end
9
+ end
10
+
11
+ def pluck_vars(node, rel)
12
+ with_associations_tree.empty? ? super : perform_query
13
+ end
14
+
15
+ def perform_query
16
+ @_cache = IdentityMap.new
17
+ build_query
18
+ .map do |record, eager_data|
19
+ cache_and_init(record, with_associations_tree)
20
+ eager_data.zip(with_associations_tree.paths.map(&:last)).each do |eager_records, element|
21
+ eager_records.first.zip(eager_records.last).each do |eager_record|
22
+ add_to_cache(*eager_record, element)
23
+ end
24
+ end
25
+ record
26
+ end
27
+ end
28
+
29
+ def with_associations(*spec)
30
+ new_link.tap do |new_query_proxy|
31
+ new_query_proxy.with_associations_tree = with_associations_tree.clone
32
+ new_query_proxy.with_associations_tree.add_spec(spec)
33
+ end
34
+ end
35
+
36
+ def propagate_context(query_proxy)
37
+ super
38
+ query_proxy.instance_variable_set('@with_associations_tree', @with_associations_tree)
39
+ end
40
+
41
+ def with_associations_tree
42
+ @with_associations_tree ||= association_tree_class.new(model)
43
+ end
44
+
45
+ def association_tree_class
46
+ AssociationTree
47
+ end
48
+
49
+ def with_associations_tree=(tree)
50
+ @with_associations_tree = tree
51
+ end
52
+
53
+ def first
54
+ (query.clause?(:order) ? self : order(order_property)).limit(1).to_a.first
55
+ end
56
+
57
+ private
58
+
59
+ def add_to_cache(rel, node, element)
60
+ direction = element.association.direction
61
+ node = cache_and_init(node, element)
62
+ if rel.is_a?(Neo4j::ActiveRel)
63
+ rel.instance_variable_set(direction == :in ? '@from_node' : '@to_node', node)
64
+ end
65
+ @_cache[direction == :out ? rel.start_node_neo_id : rel.end_node_neo_id]
66
+ .association_proxy(element.name).add_to_cache(node, rel)
67
+ end
68
+
69
+ def init_associations(node, element)
70
+ element.each_key { |key| node.association_proxy(key).init_cache }
71
+ node.association_proxy(element.name).init_cache if element.rel_length == ''
72
+ end
73
+
74
+ def cache_and_init(node, element)
75
+ @_cache.add(node).tap { |n| init_associations(n, element) }
76
+ end
77
+
78
+ def with_associations_return_clause
79
+ path_names.map { |n| var(n, :collection, &:itself) }.join(',')
80
+ end
81
+
82
+ def var(*parts)
83
+ yield(escape(parts.compact.join('_')))
84
+ end
85
+
86
+ # In neo4j version 2.1.8 this fails due to a bug:
87
+ # MATCH (`n`) WITH `n` RETURN `n`
88
+ # but this
89
+ # MATCH (`n`) WITH n RETURN `n`
90
+ # and this
91
+ # MATCH (`n`) WITH `n` AS `n` RETURN `n`
92
+ # does not
93
+ def var_fix(*var)
94
+ var(*var, &method(:as_alias))
95
+ end
96
+
97
+ def as_alias(var)
98
+ "#{var} AS #{var}"
99
+ end
100
+
101
+ def escape(s)
102
+ "`#{s}`"
103
+ end
104
+
105
+ def path_name(path)
106
+ path.map(&:name).join('.')
107
+ end
108
+
109
+ def path_names
110
+ with_associations_tree.paths.map { |path| path_name(path) }
111
+ end
112
+
113
+ def build_query
114
+ before_pluck(query_from_association_tree).pluck(identity, "[#{with_associations_return_clause}]")
115
+ end
116
+
117
+ def before_pluck(query)
118
+ query_from_chain(@order_chain, query, identity)
119
+ end
120
+
121
+ def query_from_association_tree
122
+ previous_with_vars = []
123
+ with_associations_tree.paths.inject(query_as(identity).with(ensure_distinct(identity))) do |query, path|
124
+ with_association_query_part(query, path, previous_with_vars).tap do
125
+ previous_with_vars << var_fix(path_name(path), :collection)
126
+ end
127
+ end
128
+ end
129
+
130
+ def with_association_query_part(base_query, path, previous_with_vars)
131
+ optional_match_with_where(base_query, path, previous_with_vars)
132
+ .with(identity,
133
+ "[#{relationship_collection(path)}, collect(#{escape path_name(path)})] "\
134
+ "AS #{escape("#{path_name(path)}_collection")}",
135
+ *previous_with_vars)
136
+ end
137
+
138
+ def relationship_collection(path)
139
+ path.last.rel_length ? "collect(last(relationships(#{escape("#{path_name(path)}_path")})))" : "collect(#{escape("#{path_name(path)}_rel")})"
140
+ end
141
+
142
+ def optional_match_with_where(base_query, path, _)
143
+ path
144
+ .each_with_index.map { |_, index| path[0..index] }
145
+ .inject(optional_match(base_query, path)) do |query, path_prefix|
146
+ query.where(path_prefix.last.association.target_where_clause(escape(path_name(path_prefix))))
147
+ end
148
+ end
149
+
150
+ def optional_match(base_query, path)
151
+ start_path = "#{escape("#{path_name(path)}_path")}=(#{identity})"
152
+ base_query.optional_match(
153
+ "#{start_path}#{path.each_with_index.map do |element, index|
154
+ relationship_part(element.association, path_name(path[0..index]), element.rel_length)
155
+ end.join}"
156
+ )
157
+ end
158
+
159
+ def relationship_part(association, path_name, rel_length)
160
+ if rel_length
161
+ rel_name = nil
162
+ length = {max: rel_length}
163
+ else
164
+ rel_name = escape("#{path_name}_rel")
165
+ length = nil
166
+ end
167
+ "#{association.arrow_cypher(rel_name, {}, false, false, length)}(#{escape(path_name)})"
168
+ end
169
+
170
+ def chain
171
+ @order_chain = @chain.select { |link| link.clause == :order } unless with_associations_tree.empty?
172
+ @chain
173
+ end
174
+ end
175
+ end
176
+ end
177
+ end
@@ -0,0 +1,75 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ module Query
4
+ module QueryProxyEagerLoading
5
+ class AssociationTree < Hash
6
+ attr_accessor :model, :name, :association, :path, :rel_length
7
+
8
+ def initialize(model, name = nil, rel_length = nil)
9
+ super()
10
+ self.model = name ? target_class(model, name) : model
11
+ self.name = name
12
+ self.association = name ? model.associations[name] : nil
13
+ self.rel_length = rel_length
14
+ end
15
+
16
+ def clone
17
+ super.tap { |copy| copy.each { |key, value| copy[key] = value.clone } }
18
+ end
19
+
20
+ def add_spec(spec)
21
+ fail_spec(spec) unless model
22
+
23
+ case spec
24
+ when nil
25
+ nil
26
+ when Array
27
+ spec.each(&method(:add_spec))
28
+ when Hash
29
+ process_hash(spec)
30
+ when String
31
+ process_string(spec)
32
+ else
33
+ add_key(spec)
34
+ end
35
+ end
36
+
37
+ def fail_spec(spec)
38
+ fail "Cannot eager load \"past\" a polymorphic association. \
39
+ (Since the association can return multiple models, we don't how to handle the \"#{spec}\" association.)"
40
+ end
41
+
42
+ def paths(*prefix)
43
+ values.flat_map { |v| [[*prefix, v]] + v.paths(*prefix, v) }
44
+ end
45
+
46
+ def process_hash(spec)
47
+ spec.each { |key, value| add_nested(key, value) }
48
+ end
49
+
50
+ def add_key(key, length = nil)
51
+ self[key] ||= self.class.new(model, key, length)
52
+ end
53
+
54
+ def add_nested(key, value, length = nil)
55
+ add_key(key, length).add_spec(value)
56
+ end
57
+
58
+ def process_string(str)
59
+ head, rest = str.split('.', 2)
60
+ k, length = head.split('*', -2)
61
+ add_nested(k.to_sym, rest, length)
62
+ end
63
+
64
+ private
65
+
66
+ def target_class(model, key)
67
+ association = model.associations[key]
68
+ fail "Invalid association: #{[*path, key].join('.')}" unless association
69
+ model.associations[key].target_class
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,110 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ module Query
4
+ # Methods related to returning nodes and rels from QueryProxy
5
+ module QueryProxyEnumerable
6
+ include Enumerable
7
+
8
+ # Just like every other <tt>each</tt> but it allows for optional params to support the versions that also return relationships.
9
+ # The <tt>node</tt> and <tt>rel</tt> params are typically used by those other methods but there's nothing stopping you from
10
+ # using `your_node.each(true, true)` instead of `your_node.each_with_rel`.
11
+ # @return [Enumerable] An enumerable containing some combination of nodes and rels.
12
+ def each(node = true, rel = nil, &block)
13
+ result(node, rel).each(&block)
14
+ end
15
+
16
+ def result(node = true, rel = nil)
17
+ return [].freeze if unpersisted_start_object?
18
+
19
+ @result_cache ||= {}
20
+ return result_cache_for(node, rel) if result_cache?(node, rel)
21
+
22
+ result = pluck_vars(node, rel)
23
+ set_instance_caches(result, node, rel)
24
+
25
+ @result_cache[[node, rel]] ||= result
26
+ end
27
+
28
+ def result_cache?(node = true, rel = nil)
29
+ !!result_cache_for(node, rel)
30
+ end
31
+
32
+ def result_cache_for(node = true, rel = nil)
33
+ (@result_cache || {})[[node, rel]]
34
+ end
35
+
36
+ def fetch_result_cache
37
+ @result_cache ||= yield
38
+ end
39
+
40
+ # When called at the end of a QueryProxy chain, it will return the resultant relationship objects intead of nodes.
41
+ # For example, to return the relationship between a given student and their lessons:
42
+ #
43
+ # .. code-block:: ruby
44
+ #
45
+ # student.lessons.each_rel do |rel|
46
+ #
47
+ # @return [Enumerable] An enumerable containing any number of applicable relationship objects.
48
+ def each_rel(&block)
49
+ block_given? ? each(false, true, &block) : to_enum(:each, false, true)
50
+ end
51
+
52
+ # When called at the end of a QueryProxy chain, it will return the nodes and relationships of the last link.
53
+ # For example, to return a lesson and each relationship to a given student:
54
+ #
55
+ # .. code-block:: ruby
56
+ #
57
+ # student.lessons.each_with_rel do |lesson, rel|
58
+ def each_with_rel(&block)
59
+ block_given? ? each(true, true, &block) : to_enum(:each, true, true)
60
+ end
61
+
62
+ # Does exactly what you would hope. Without it, comparing `bobby.lessons == sandy.lessons` would evaluate to false because it
63
+ # would be comparing the QueryProxy objects, not the lessons themselves.
64
+ def ==(other)
65
+ self.to_a == other
66
+ end
67
+
68
+ # For getting variables which have been defined as part of the association chain
69
+ def pluck(*args)
70
+ transformable_attributes = (model ? model.attribute_names : []) + %w(uuid neo_id)
71
+ arg_list = args.map do |arg|
72
+ arg = Neo4j::ActiveNode::Query::QueryProxy::Link.converted_key(model, arg)
73
+ if transformable_attributes.include?(arg.to_s)
74
+ {identity => arg}
75
+ else
76
+ arg
77
+ end
78
+ end
79
+
80
+ self.query.pluck(*arg_list)
81
+ end
82
+
83
+ protected
84
+
85
+ def ensure_distinct(node, force = false)
86
+ @distinct || force ? "DISTINCT(#{node})" : node
87
+ end
88
+
89
+ private
90
+
91
+ def pluck_vars(node, rel)
92
+ vars = []
93
+ vars << ensure_distinct(identity) if node
94
+ vars << @rel_var if rel
95
+ pluck(*vars)
96
+ end
97
+
98
+ def set_instance_caches(instance, node, rel)
99
+ instance.each do |object|
100
+ object.instance_variable_set('@source_query_proxy', self)
101
+ object.instance_variable_set('@source_proxy_result_cache', instance)
102
+ if node && rel && object.last.is_a?(Neo4j::ActiveRel)
103
+ object.last.instance_variable_set(association.direction == :in ? '@from_node' : '@to_node', object.first)
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,19 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ module Query
4
+ module QueryProxyFindInBatches
5
+ def find_in_batches(options = {})
6
+ query.return(identity).find_in_batches(identity, @model.primary_key, options) do |batch|
7
+ yield batch.map(&identity)
8
+ end
9
+ end
10
+
11
+ def find_each(options = {})
12
+ query.return(identity).find_each(identity, @model.primary_key, options) do |result|
13
+ yield result.send(identity)
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,139 @@
1
+ module Neo4j
2
+ module ActiveNode
3
+ module Query
4
+ class QueryProxy
5
+ class Link
6
+ attr_reader :clause
7
+
8
+ def initialize(clause, arg, args = [])
9
+ @clause = clause
10
+ @arg = arg
11
+ @args = args
12
+ end
13
+
14
+ def args(var, rel_var)
15
+ if @arg.respond_to?(:call)
16
+ @arg.call(var, rel_var)
17
+ else
18
+ [@arg] + @args
19
+ end
20
+ end
21
+
22
+ class << self
23
+ def for_clause(clause, arg, model, *args)
24
+ method_to_call = "for_#{clause}_clause"
25
+
26
+ send(method_to_call, arg, model, *args)
27
+ end
28
+
29
+ def for_where_clause(arg, model, *args)
30
+ node_num = 1
31
+ result = []
32
+ if arg.is_a?(Hash)
33
+ arg.each do |key, value|
34
+ if model && model.association?(key)
35
+ result += for_association(key, value, "n#{node_num}", model)
36
+ node_num += 1
37
+ else
38
+ result << new_for_key_and_value(model, key, value)
39
+ end
40
+ end
41
+ elsif arg.is_a?(String)
42
+ result << new(:where, arg, args)
43
+ end
44
+ result
45
+ end
46
+ alias for_node_where_clause for_where_clause
47
+
48
+ def for_where_not_clause(*args)
49
+ for_where_clause(*args).each do |link|
50
+ link.instance_variable_set('@clause', :where_not)
51
+ end
52
+ end
53
+
54
+ def new_for_key_and_value(model, key, value)
55
+ key = converted_key(model, key)
56
+
57
+ val = if !model
58
+ value
59
+ elsif key == model.id_property_name && value.is_a?(Neo4j::ActiveNode)
60
+ value.id
61
+ else
62
+ converted_value(model, key, value)
63
+ end
64
+
65
+ new(:where, ->(v, _) { {v => {key => val}} })
66
+ end
67
+
68
+ def for_association(name, value, n_string, model)
69
+ neo_id = value.try(:neo_id) || value
70
+ fail ArgumentError, "Invalid value for '#{name}' condition" if not neo_id.is_a?(Integer)
71
+
72
+ [
73
+ new(:match, ->(v, _) { "(#{v})#{model.associations[name].arrow_cypher}(#{n_string})" }),
74
+ new(:where, ->(_, _) { {"ID(#{n_string})" => neo_id.to_i} })
75
+ ]
76
+ end
77
+
78
+ # We don't accept strings here. If you want to use a string, just use where.
79
+ def for_rel_where_clause(arg, _, association)
80
+ arg.each_with_object([]) do |(key, value), result|
81
+ rel_class = association.relationship_class if association.relationship_class
82
+ val = rel_class ? converted_value(rel_class, key, value) : value
83
+ result << new(:where, ->(_, rel_var) { {rel_var => {key => val}} })
84
+ end
85
+ end
86
+
87
+ def for_rel_where_not_clause(*args)
88
+ for_rel_where_clause(*args).each do |link|
89
+ link.instance_variable_set('@clause', :where_not)
90
+ end
91
+ end
92
+
93
+ def for_rel_order_clause(arg, _)
94
+ [new(:order, ->(_, v) { arg.is_a?(String) ? arg : {v => arg} })]
95
+ end
96
+
97
+ def for_order_clause(arg, model)
98
+ [new(:order, ->(v, _) { arg.is_a?(String) ? arg : {v => converted_keys(model, arg)} })]
99
+ end
100
+
101
+ def for_args(model, clause, args, association = nil)
102
+ if [:where, :where_not].include?(clause) && args[0].is_a?(String) # Better way?
103
+ [for_arg(model, clause, args[0], *args[1..-1])]
104
+ elsif [:rel_where, :rel_where_not].include?(clause)
105
+ args.map { |arg| for_arg(model, clause, arg, association) }
106
+ else
107
+ args.map { |arg| for_arg(model, clause, arg) }
108
+ end
109
+ end
110
+
111
+ def for_arg(model, clause, arg, *args)
112
+ default = [Link.new(clause, arg, *args)]
113
+
114
+ Link.for_clause(clause, arg, model, *args) || default
115
+ rescue NoMethodError
116
+ default
117
+ end
118
+
119
+ def converted_keys(model, arg)
120
+ arg.is_a?(Hash) ? Hash[arg.map { |key, value| [converted_key(model, key), value] }] : arg
121
+ end
122
+
123
+ def converted_key(model, key)
124
+ if key.to_sym == :id
125
+ model ? model.id_property_name : :uuid
126
+ else
127
+ key
128
+ end
129
+ end
130
+
131
+ def converted_value(model, key, value)
132
+ model.declared_properties.value_for_where(key, value)
133
+ end
134
+ end
135
+ end
136
+ end
137
+ end
138
+ end
139
+ end