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,28 @@
1
+ module Neo4j::ActiveRel
2
+ module Initialize
3
+ extend ActiveSupport::Concern
4
+ include Neo4j::Shared::Initialize
5
+
6
+ # called when loading the rel from the database
7
+ # @param [Neo4j::Embedded::EmbeddedRelationship, Neo4j::Server::CypherRelationship] persisted_rel properties of this relationship
8
+ # @param [Neo4j::Relationship] from_node_id The neo_id of the starting node of this rel
9
+ # @param [Neo4j::Relationship] to_node_id The neo_id of the ending node of this rel
10
+ # @param [String] type the relationship type
11
+ def init_on_load(persisted_rel, from_node_id, to_node_id, type)
12
+ @rel_type = type
13
+ @_persisted_obj = persisted_rel
14
+ changed_attributes_clear!
15
+ @attributes = convert_and_assign_attributes(persisted_rel.props)
16
+ load_nodes(from_node_id, to_node_id)
17
+ end
18
+
19
+ def init_on_reload(unwrapped_reloaded)
20
+ @attributes = nil
21
+ init_on_load(unwrapped_reloaded,
22
+ unwrapped_reloaded.start_node_id,
23
+ unwrapped_reloaded.end_node_id,
24
+ unwrapped_reloaded.rel_type)
25
+ self
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,134 @@
1
+ module Neo4j::ActiveRel
2
+ module Persistence
3
+ extend ActiveSupport::Concern
4
+ include Neo4j::Shared::Cypher::RelIdentifiers
5
+ include Neo4j::Shared::Persistence
6
+
7
+ class RelInvalidError < RuntimeError; end
8
+ class ModelClassInvalidError < RuntimeError; end
9
+ class RelCreateFailedError < RuntimeError; end
10
+
11
+ def from_node_identifier
12
+ @from_node_identifier || :from_node
13
+ end
14
+
15
+ def to_node_identifier
16
+ @to_node_identifier || :to_node
17
+ end
18
+
19
+ def from_node_identifier=(id)
20
+ @from_node_identifier = id.to_sym
21
+ end
22
+
23
+ def to_node_identifier=(id)
24
+ @to_node_identifier = id.to_sym
25
+ end
26
+
27
+ def cypher_identifier
28
+ @cypher_identifier || :rel
29
+ end
30
+
31
+ def save(*)
32
+ create_or_update
33
+ end
34
+
35
+ def save!(*args)
36
+ save(*args) or fail(RelInvalidError, inspect) # rubocop:disable Style/AndOr
37
+ end
38
+
39
+ # Increments concurrently a numeric attribute by a centain amount
40
+ # @param [Symbol, String] name of the attribute to increment
41
+ # @param [Integer, Float] amount to increment
42
+ def concurrent_increment!(attribute, by = 1)
43
+ increment_by_query! query_as(:r), attribute, by, :r
44
+ end
45
+
46
+ def create_model
47
+ validate_node_classes!
48
+ validate_has_one_rel
49
+ rel = _create_rel
50
+ return self unless rel.respond_to?(:props)
51
+ init_on_load(rel, from_node, to_node, @rel_type)
52
+ true
53
+ end
54
+
55
+ def validate_has_one_rel
56
+ return unless Neo4j::Config[:enforce_has_one]
57
+ to_node.validate_reverse_has_one_active_rel(self, :in, from_node) if to_node.persisted?
58
+ from_node.validate_reverse_has_one_active_rel(self, :out, to_node) if from_node.persisted?
59
+ end
60
+
61
+ def query_as(var)
62
+ # This should query based on the nodes, not the rel neo_id, I think
63
+ # Also, picky point: Should the var be `n`?
64
+ self.class.query_as(neo_id, var)
65
+ end
66
+
67
+ module ClassMethods
68
+ # Creates a new relationship between objects
69
+ # @param [Hash] props the properties the new relationship should have
70
+ def create(*args)
71
+ new(*args).tap(&:save)
72
+ end
73
+
74
+ # Same as #create, but raises an error if there is a problem during save.
75
+ def create!(*args)
76
+ new(*args).tap(&:save!)
77
+ end
78
+
79
+ def create_method
80
+ creates_unique? ? :create_unique : :create
81
+ end
82
+
83
+ def load_entity(id)
84
+ query_as(id).pluck(:r).first
85
+ end
86
+
87
+ def query_as(neo_id, var = :r)
88
+ Neo4j::ActiveBase.new_query.match("()-[#{var}]->()").where(var => {neo_id: neo_id})
89
+ end
90
+ end
91
+
92
+ def create_method
93
+ self.class.create_method
94
+ end
95
+
96
+ private
97
+
98
+ def destroy_query
99
+ query_as(:r).delete(:r)
100
+ end
101
+
102
+ def validate_node_classes!
103
+ [from_node, to_node].each do |node|
104
+ type = from_node == node ? :_from_class : :_to_class
105
+ type_class = self.class.send(type)
106
+
107
+ unless valid_type?(type_class, node)
108
+ fail ModelClassInvalidError, type_validation_error_message(node, type_class)
109
+ end
110
+ end
111
+ end
112
+
113
+ def valid_type?(type_object, node)
114
+ case type_object
115
+ when false, :any
116
+ true
117
+ when Array
118
+ type_object.any? { |c| valid_type?(c, node) }
119
+ else
120
+ node.class.mapped_label_names.include?(type_object.to_s.constantize.mapped_label_name)
121
+ end
122
+ end
123
+
124
+ def type_validation_error_message(node, type_class)
125
+ "Node class was #{node.class} (#{node.class.object_id}), expected #{type_class} (#{type_class.object_id})"
126
+ end
127
+
128
+ def _create_rel
129
+ factory = QueryFactory.new(from_node, to_node, self)
130
+ factory.build!
131
+ factory.unwrapped_rel
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,95 @@
1
+ module Neo4j::ActiveRel::Persistence
2
+ # This class builds and executes a Cypher query, using information from the graph objects to determine
3
+ # whether they need to be created simultaneously.
4
+ # It keeps the rel instance from being responsible for inspecting the nodes or talking with Shared::QueryFactory.
5
+ class QueryFactory
6
+ NODE_SYMBOLS = [:from_node, :to_node]
7
+ attr_reader :from_node, :to_node, :rel, :unwrapped_rel
8
+
9
+ def initialize(from_node, to_node, rel)
10
+ @from_node = from_node
11
+ @to_node = to_node
12
+ @rel = rel
13
+ end
14
+
15
+ # TODO: This feels like it should also wrap the rel, but that is handled in Neo4j::ActiveRel::Persistence at the moment.
16
+ # Builds and executes the query using the objects giving during init.
17
+ # It holds the process:
18
+ # * Execute node callbacks if needed
19
+ # * Create and execute the query
20
+ # * Mix the query response into the unpersisted objects given during init
21
+ def build!
22
+ node_before_callbacks! do
23
+ res = query_factory(rel, rel_id, iterative_query).query.unwrapped.return(*unpersisted_return_ids).first
24
+ node_symbols.each { |n| wrap!(send(n), res, n) }
25
+ @unwrapped_rel = res.send(rel_id)
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ def rel_id
32
+ @rel_id ||= rel.rel_identifier
33
+ end
34
+
35
+ # Node callbacks only need to be executed if the node is not persisted. We let the `conditional_callback` method do the work,
36
+ # we only have to give it the type of callback we expect to be run and the condition which, if true, will prevent it from executing.
37
+ def node_before_callbacks!
38
+ validate_unpersisted_nodes!
39
+ from_node.conditional_callback(:create, from_node.persisted?) do
40
+ to_node.conditional_callback(:create, to_node.persisted?) do
41
+ yield
42
+ end
43
+ end
44
+ end
45
+
46
+ def validate_unpersisted_nodes!
47
+ node_symbols.each do |s|
48
+ obj = send(s)
49
+ next if obj.persisted?
50
+ fail RelCreateFailedError, "Cannot create rel with unpersisted, invalid #{s}" unless obj.valid?
51
+ end
52
+ end
53
+
54
+ # Each node must be either created or matched before the relationship can be created. This class does not know or care about
55
+ # how that happens, it just knows that it needs a usable Neo4j::Core::Query object to do that.
56
+ # This method is "iterative" because it creates one factory for each node but the second builds upon the first.
57
+ def iterative_query
58
+ node_symbols.inject(false) do |iterative_query, sym|
59
+ obj = send(sym)
60
+ query_factory(obj, sym, iterative_query)
61
+ end
62
+ end
63
+
64
+ # Isolates the dependency to the shared class. This has an awareness of Neo4j::Core::Query and will match or create
65
+ # based on the current state of the object passed in.
66
+ def query_factory(obj, sym, factory = false)
67
+ Neo4j::Shared::QueryFactory.create(obj, sym).tap do |factory_instance|
68
+ factory_instance.base_query = factory.blank? ? false : factory.query
69
+ end
70
+ end
71
+
72
+ # @return [Array<Symbol>] The Cypher identifiers that will be returned from the query.
73
+ # We only need to return objects from our query that were created during it, otherwise we impact performance.
74
+ def unpersisted_return_ids
75
+ [rel_id].tap do |result|
76
+ node_symbols.each { |k| result << k unless send(k).persisted? }
77
+ end
78
+ end
79
+
80
+ # @param [Neo4j::ActiveNode] node A node, persisted or unpersisted
81
+ # @param [Struct] res The result of calling `return` on a Neo4j::Core::Query object. It responds to the same keys
82
+ # as our graph objects. If the object is unpersisted and was created during the query, the unwrapped node is mixed
83
+ # in, making the object reflect as "persisted".
84
+ # @param [Symbol] key :from_node or :to_node, the object to request from the response.
85
+ def wrap!(node, res, key)
86
+ return if node.persisted? || !res.respond_to?(key)
87
+ unwrapped = res.send(key)
88
+ node.init_on_load(unwrapped, unwrapped.props)
89
+ end
90
+
91
+ def node_symbols
92
+ self.class::NODE_SYMBOLS
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,95 @@
1
+ require 'neo4j/class_arguments'
2
+
3
+ module Neo4j::ActiveRel
4
+ module Property
5
+ extend ActiveSupport::Concern
6
+ include Neo4j::Shared::Property
7
+
8
+ %w(to_node from_node).each do |direction|
9
+ define_method(direction.to_s) { instance_variable_get("@#{direction}") }
10
+ define_method("#{direction}=") do |argument|
11
+ fail FrozenRelError, 'Relationship start/end nodes cannot be changed once persisted' if _persisted_obj
12
+ instance_variable_set("@#{direction}", argument)
13
+ end
14
+ end
15
+
16
+ alias start_node from_node
17
+ alias end_node to_node
18
+
19
+ %w(start_node end_node).each do |direction|
20
+ define_method("#{direction}_neo_id") { send(direction).neo_id if direction }
21
+ end
22
+ alias from_node_neo_id start_node_neo_id
23
+ alias to_node_neo_id end_node_neo_id
24
+
25
+ # @return [String] a string representing the relationship type that will be created
26
+ def type
27
+ self.class.type
28
+ end
29
+ alias rel_type type
30
+
31
+ def initialize(attributes = nil)
32
+ super(attributes)
33
+ end
34
+
35
+ def creates_unique_option
36
+ self.class.creates_unique_option
37
+ end
38
+
39
+ module ClassMethods
40
+ include Neo4j::Shared::Cypher::CreateMethod
41
+
42
+ # Extracts keys from attributes hash which are relationships of the model
43
+ # TODO: Validate separately that relationships are getting the right values? Perhaps also store the values and persist relationships on save?
44
+ def extract_association_attributes!(attributes)
45
+ return if attributes.blank?
46
+ {}.tap do |relationship_props|
47
+ attributes.each_key do |key|
48
+ relationship_props[key] = attributes.delete(key) if [:from_node, :to_node].include?(key)
49
+ end
50
+ end
51
+ end
52
+
53
+ def id_property_name
54
+ false
55
+ end
56
+
57
+ %w(to_class from_class).each do |direction|
58
+ define_method(direction.to_s) do |argument = nil|
59
+ if !argument.nil?
60
+ Neo4j::ClassArguments.validate_argument!(argument, direction)
61
+
62
+ instance_variable_set("@#{direction}", argument)
63
+ end
64
+
65
+ self.instance_variable_get("@#{direction}")
66
+ end
67
+
68
+ define_method("_#{direction}") { instance_variable_get "@#{direction}" }
69
+ end
70
+
71
+ def valid_class_argument?(class_argument)
72
+ [String, Symbol, FalseClass].include?(class_argument.class) ||
73
+ (class_argument.is_a?(Array) && class_argument.all? { |c| [String, Symbol].include?(c.class) })
74
+ end
75
+
76
+ alias start_class from_class
77
+ alias end_class to_class
78
+
79
+ def load_entity(id)
80
+ Neo4j::Node.load(id)
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ def load_nodes(from_node = nil, to_node = nil)
87
+ @from_node = RelatedNode.new(from_node)
88
+ @to_node = RelatedNode.new(to_node)
89
+ end
90
+
91
+ def inspect_attributes
92
+ attributes.to_a
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,101 @@
1
+ module Neo4j::ActiveRel
2
+ module Query
3
+ extend ActiveSupport::Concern
4
+
5
+ class RecordNotFound < Neo4j::RecordNotFound; end
6
+
7
+ module ClassMethods
8
+ # Returns the object with the specified neo4j id.
9
+ # @param [String,Integer] id of node to find
10
+ # @param [Neo4j::Session] session optional
11
+ def find(id, session = self.neo4j_session)
12
+ fail "Unknown argument #{id.class} in find method (expected String or Integer)" if !(id.is_a?(String) || id.is_a?(Integer))
13
+ find_by_id(id, session)
14
+ end
15
+
16
+ # Loads the relationship using its neo_id.
17
+ def find_by_id(key, session = nil)
18
+ options = session ? {session: session} : {}
19
+ query ||= Neo4j::ActiveBase.new_query(options)
20
+ result = query.match('()-[r]-()').where('ID(r)' => key.to_i).limit(1).return(:r).first
21
+ fail RecordNotFound.new("Couldn't find #{name} with 'id'=#{key.inspect}", name, key) if result.blank?
22
+ result.r
23
+ end
24
+
25
+ # Performs a very basic match on the relationship.
26
+ # This is not executed lazily, it will immediately return matching objects.
27
+ # To use a string, prefix the property with "r1"
28
+ # @example Match with a string
29
+ # MyRelClass.where('r1.grade > r1')
30
+ def where(args = {})
31
+ where_query.where(where_string(args)).pluck(:r1)
32
+ end
33
+
34
+ # Performs a basic match on the relationship, returning all results.
35
+ # This is not executed lazily, it will immediately return matching objects.
36
+ def all
37
+ all_query.pluck(:r1)
38
+ end
39
+
40
+ def first
41
+ all_query.limit(1).order('ID(r1)').pluck(:r1).first
42
+ end
43
+
44
+ def last
45
+ all_query.limit(1).order('ID(r1) DESC').pluck(:r1).first
46
+ end
47
+
48
+ private
49
+
50
+ def deprecation_warning!
51
+ ActiveSupport::Deprecation.warn 'The Neo4j::ActiveRel::Query module has been deprecated and will be removed in a future version of the gem.', caller
52
+ end
53
+
54
+ def where_query
55
+ deprecation_warning!
56
+ Neo4j::ActiveBase.new_query.match("#{cypher_string(:outbound)}-[r1:`#{self._type}`]->#{cypher_string(:inbound)}")
57
+ end
58
+
59
+ def all_query
60
+ deprecation_warning!
61
+ Neo4j::ActiveBase.new_query.match("#{cypher_string}-[r1:`#{self._type}`]->#{cypher_string(:inbound)}")
62
+ end
63
+
64
+ def cypher_string(dir = :outbound)
65
+ case dir
66
+ when :outbound
67
+ identifier = '(n1'
68
+ identifier + (_from_class == :any ? ')' : cypher_label(:outbound))
69
+ when :inbound
70
+ identifier = '(n2'
71
+ identifier + (_to_class == :any ? ')' : cypher_label(:inbound))
72
+ end
73
+ end
74
+
75
+ def cypher_label(dir = :outbound)
76
+ target_class = dir == :outbound ? as_constant(_from_class) : as_constant(_to_class)
77
+ ":`#{target_class.mapped_label_name}`)"
78
+ end
79
+
80
+ def as_constant(given_class)
81
+ case given_class
82
+ when String, Symbol
83
+ given_class.to_s.constantize
84
+ when Array
85
+ fail "ActiveRel query methods are being deprecated and do not support Array (from|to)_class options. Current value: #{given_class}"
86
+ else
87
+ given_class
88
+ end
89
+ end
90
+
91
+ def where_string(args)
92
+ case args
93
+ when Hash
94
+ args.map { |k, v| v.is_a?(Integer) ? "r1.#{k} = #{v}" : "r1.#{k} = '#{v}'" }.join(', ')
95
+ else
96
+ args
97
+ end
98
+ end
99
+ end
100
+ end
101
+ end