neo4j 3.0.1 → 3.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 99403c4083f1e8b94fca74b9a70367a182f51b88
4
- data.tar.gz: 4bf1f031533437c899b09e80f71d130f035d5b88
3
+ metadata.gz: 75e9394211348d0302d6f68dac0f87653eb95f28
4
+ data.tar.gz: 289355ac0ac9c885ae57eb12395046a1b612b5ae
5
5
  SHA512:
6
- metadata.gz: 62d115d92d918698a84f92a5b589409becf59791181a0cf3bc124ea6c736b5bd3d3fa6c63eab3d3f95a9043c15ebd5bce4b2ece0237ff54fc56fe0cadfa2e35d
7
- data.tar.gz: 744d4bf8e8e872cace8c9b316ed3176bdc6a8efea1765fbcb42bf8ae5067a445892a0365eff4ed783ee6359a99efbe99fa29242cbc49ca45bd2872e78151767e
6
+ metadata.gz: 6d85f48c40cb8d3af253ae0eae9ed40d10b351eb0dc993d962987426d54f395495b7a1940f970c8c25b4847693d658fa22a5e6c003ea85cdb36fc04da51b2e04
7
+ data.tar.gz: 8cd671cfc2c868f184388c4b37df8490b7d07b7dcc0421e3bce21ea1ee0caee1626e0b3800128e736f8002497a0868996e2517880b69f076d63d5664d9487096
data/CHANGELOG CHANGED
@@ -1,3 +1,13 @@
1
+ == 3.0.2
2
+ * "Model#all" now evaluates lazily, models no longer include Enumerable
3
+ * Faster, more efficient uniqueness validations
4
+ * Adjusted many common queries to use params, will improve performance
5
+ * ActiveRel fixes: create uses Core Query instead of Core's `rels` method, `{ classname: #{_classname} }` no longer inserted into every query, find related node IDs without loading the nodes
6
+ * Allow inheritance when checking model class on a relation (Andrew Jones)
7
+ * Provided migrations will use Rake.original_dir instead of Rails.env to provide better compatibility with frameworks other than Rails
8
+ * rel_class option in ActiveNode models will now accept string of a model name
9
+ * Additional bug fixes
10
+
1
11
  == 3.0.1
2
12
  * Removed reference to neo4j-core from Gemfile and set neo4j.gemspec to use neo4j-core ~>3.0.0
3
13
 
data/README.md CHANGED
@@ -2,19 +2,20 @@
2
2
 
3
3
  Neo4j.rb is an Active Model compliant Ruby/JRuby wrapper for [the Neo4j graph database](http://www.neo4j.org/). It uses the [neo4j-core](https://github.com/neo4jrb/neo4j-core) and [active_attr](https://github.com/cgriego/active_attr) gems.
4
4
 
5
- ## Documentation version 3.0.0
5
+ ## Modern (3.X) Documentation
6
6
 
7
7
  * [Wiki](https://github.com/neo4jrb/neo4j/wiki/Neo4j.rb-v3-Introduction)
8
8
 
9
- ## Documentation Old stable version 2.x
9
+ ## Legacy (2.x) Documentation
10
10
 
11
11
  * [README](https://github.com/neo4jrb/neo4j/tree/2.x)
12
12
  * [Wiki](https://github.com/neo4jrb/neo4j/wiki/Neo4j%3A%3ARails-Introduction)
13
13
 
14
14
  ## Support
15
15
 
16
+ * Open an [issue](https://github.com/neo4jrb/neo4j/issues), post to [Stack Overflow](http://stackoverflow.com/questions/tagged/neo4j+ruby), or contact one of the developers.
16
17
  * [Neo4j.rb mailing list](https://groups.google.com/forum/#!forum/neo4jrb)
17
- * Consulting support ? ask any of the developers
18
+ * Consulting support? Ask any of the developers, info below.
18
19
 
19
20
  ## Developers
20
21
 
@@ -23,7 +23,6 @@ module Neo4j
23
23
  # Return cypher partial query string for the relationship part of a MATCH (arrow / relationship definition)
24
24
  def arrow_cypher(var = nil, properties = {}, create = false)
25
25
  validate_origin!
26
-
27
26
  relationship_type = relationship_type(create)
28
27
  relationship_name_cypher = ":`#{relationship_type}`" if relationship_type
29
28
  properties_string = get_properties_string(properties)
@@ -55,7 +54,7 @@ module Neo4j
55
54
  def relationship_type(create = false)
56
55
  case
57
56
  when @relationship_class
58
- @relationship_class._type
57
+ relationship_class_type
59
58
  when @relationship_type
60
59
  @relationship_type
61
60
  when @origin
@@ -69,6 +68,16 @@ module Neo4j
69
68
  @relationship_class
70
69
  end
71
70
 
71
+ def relationship_class_type
72
+ @relationship_class = @relationship_class.constantize if @relationship_class.class == String || @relationship_class == Symbol
73
+ @relationship_class._type
74
+ end
75
+
76
+ def inject_classname(properties)
77
+ properties[Neo4j::Config.class_name_property] = @relationship_class.name if @relationship_class
78
+ properties
79
+ end
80
+
72
81
  private
73
82
 
74
83
  def get_direction(relationship_cypher, create)
@@ -88,7 +97,6 @@ module Neo4j
88
97
  end
89
98
 
90
99
  def get_properties_string(properties)
91
- properties[Neo4j::Config.class_name_property] = @relationship_class.name if @relationship_class
92
100
  p = properties.map do |key, value|
93
101
  "#{key}: #{value.inspect}"
94
102
  end.join(', ')
@@ -124,8 +132,8 @@ module Neo4j
124
132
 
125
133
  def validate_option_combinations(options)
126
134
  raise ArgumentError, "Cannot specify both :type and :origin (#{base_declaration})" if options[:type] && options[:origin]
127
- raise ArgumentError, "Cannot specify both :type and :rel_class (#{base_declaration})" if options[:type] && options[:rel_class]
128
- raise ArgumentError, "Cannot specify both :origin and :rel_class (#{base_declaration}" if options[:origin] && options[:rel_class]
135
+ # raise ArgumentError, "Cannot specify both :type and :rel_class (#{base_declaration})" if options[:type] && options[:rel_class] see issue #494
136
+ # raise ArgumentError, "Cannot specify both :origin and :rel_class (#{base_declaration}" if options[:origin] && options[:rel_class]
129
137
  end
130
138
 
131
139
  # Determine if model class as derived from the association name would be different than the one specified via the model_class key
@@ -115,6 +115,10 @@ module Neo4j::ActiveNode
115
115
 
116
116
  module ClassMethods
117
117
 
118
+ def find_by_neo_id(id)
119
+ Neo4j::Node.load(id)
120
+ end
121
+
118
122
  def find_by_id(id)
119
123
  self.where(id_property_name => id).first
120
124
  end
@@ -13,7 +13,16 @@ module Neo4j
13
13
  # @return the labels
14
14
  # @see Neo4j-core
15
15
  def labels
16
- @_persisted_obj.labels
16
+ if @_persisted_obj.labels.nil?
17
+ r = queried_labels
18
+ @_persisted_obj.labels = r
19
+ else
20
+ @_persisted_obj.labels
21
+ end
22
+ end
23
+
24
+ def queried_labels
25
+ self.class.query_as(:result).where("ID(result)" => self.neo_id).return("LABELS(result) as result_labels").first.result_labels.map(&:to_sym)
17
26
  end
18
27
 
19
28
  # adds one or more labels
@@ -63,11 +72,9 @@ module Neo4j
63
72
 
64
73
  # Find all nodes/objects of this class
65
74
  def all
66
- self.query_as(:n).pluck(:n)
75
+ self.as(:n)
67
76
  end
68
77
 
69
-
70
-
71
78
  # Returns the object with the specified neo4j id.
72
79
  # @param [String,Fixnum] id of node to find
73
80
  def find(id)
@@ -3,6 +3,7 @@ class Neo4j::Node
3
3
 
4
4
  # this is a plugin in the neo4j-core so that the Ruby wrapper will be wrapped around the Neo4j::Node objects
5
5
  def wrapper
6
+ self.props.symbolize_keys!
6
7
  most_concrete_class = sorted_wrapper_classes
7
8
  return self unless most_concrete_class
8
9
  wrapped_node = most_concrete_class.new
@@ -17,18 +17,10 @@ module Neo4j
17
17
  # @param var [Symbol, String] The variable name to specify in the query
18
18
  # @return [Neo4j::Core::Query]
19
19
  def query_as(var)
20
- self.class.query_as(var).where("ID(#{var}) = #{self.neo_id}")
20
+ self.class.query_as(var).where("ID(#{var})" => self.neo_id)
21
21
  end
22
22
 
23
23
  module ClassMethods
24
- include Enumerable
25
-
26
- #attr_writer :query_proxy
27
-
28
- def each
29
- self.query_as(:n).pluck(:n).each {|o| yield o }
30
- end
31
-
32
24
  # Returns a Query object with all nodes for the model matched as the specified variable name
33
25
  #
34
26
  # @example Return the registration number of all cars owned by a person over the age of 30
@@ -135,7 +135,7 @@ module Neo4j
135
135
  def create(other_nodes, properties)
136
136
  raise "Can only create associations on associations" unless @association
137
137
  other_nodes = [other_nodes].flatten
138
-
138
+ properties = @association.inject_classname(properties)
139
139
  other_nodes = other_nodes.map do |other_node|
140
140
  case other_node
141
141
  when Integer, String
@@ -145,7 +145,7 @@ module Neo4j
145
145
  end
146
146
  end.compact
147
147
 
148
- raise ArgumentError, "Node must be of the association's class when model is specified" if @model && other_nodes.any? {|other_node| other_node.class != @model }
148
+ raise ArgumentError, "Node must be of the association's class when model is specified" if @model && other_nodes.any? {|other_node| !other_node.is_a?(@model) }
149
149
  other_nodes.each do |other_node|
150
150
  #Neo4j::Transaction.run do
151
151
  other_node.save if not other_node.persisted?
@@ -37,7 +37,7 @@ module Neo4j
37
37
  def include?(other, target=nil)
38
38
  raise(InvalidParameterError, ':include? only accepts nodes') unless other.respond_to?(:neo_id)
39
39
  query_with_target(target) do |target|
40
- self.where("#{target}.#{other.class.primary_key} = {other_node_id}").params(other_node_id: other.id).query.return("count(#{target}) as count").first.count > 0
40
+ self.where("ID(#{target}) = {other_node_id}").params(other_node_id: other.neo_id).query.return("count(#{target}) as count").first.count > 0
41
41
  end
42
42
  end
43
43
 
@@ -59,7 +59,7 @@ module Neo4j
59
59
  def exists_query_start(origin, condition, target)
60
60
  case
61
61
  when condition.class == Fixnum
62
- self.where("ID(#{target}) = #{condition}")
62
+ self.where("ID(#{target}) = {exists_condition}").params(exists_condition: condition)
63
63
  when condition.class == Hash
64
64
  self.where(condition.keys.first => condition.values.first)
65
65
  else
@@ -31,22 +31,17 @@ module Neo4j
31
31
  alias_method :length, :count
32
32
 
33
33
  def empty?
34
- !self.exists?
34
+ !self.all.exists?
35
35
  end
36
36
 
37
37
  alias_method :blank?, :empty?
38
38
 
39
- def include?(other)
40
- raise(InvalidParameterError, ':include? only accepts nodes') unless other.respond_to?(:neo_id)
41
- self.query_as(:n).where(n: {primary_key => other.id}).return("count(n) AS count").first.count > 0
42
- end
43
-
44
39
  private
45
40
 
46
41
  def exists_query_start(node_condition)
47
42
  case node_condition
48
43
  when Fixnum
49
- self.query_as(:n).where("ID(n) = #{node_condition}")
44
+ self.query_as(:n).where("ID(n)" => node_condition)
50
45
  when Hash
51
46
  self.where(node_condition.keys.first => node_condition.values.first)
52
47
  else
@@ -33,10 +33,9 @@ module Neo4j
33
33
 
34
34
  conditions[attribute] = options[:case_sensitive] ? value : /^#{Regexp.escape(value.to_s)}$/i
35
35
 
36
- # prevent that same object is returned
37
- # TODO: add negative condtion to not return current record
38
- found = record.class.where(conditions).to_a.select{|f| f.neo_id != record.neo_id}
39
- record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value)) if found.count > 0
36
+ found = record.class.as(:result).where(conditions)
37
+ found = found.where("NOT ID(result) = {record_neo_id}").params(record_neo_id: record.neo_id) if record.persisted?
38
+ record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value)) if found.exists?
40
39
  end
41
40
 
42
41
  def message(instance)
@@ -24,7 +24,7 @@ module Neo4j::ActiveRel
24
24
  set_timestamps
25
25
  properties = convert_properties_to :db, props
26
26
  rel = _create_rel(from_node, to_node, properties)
27
- init_on_load(rel, from_node, to_node, @rel_type)
27
+ init_on_load(rel._persisted_obj, from_node, to_node, @rel_type)
28
28
  true
29
29
  end
30
30
 
@@ -62,7 +62,7 @@ module Neo4j::ActiveRel
62
62
  props = self.class.default_property_values(self)
63
63
  props.merge!(args[0]) if args[0].is_a?(Hash)
64
64
  set_classname(props)
65
- from_node.create_rel(type, to_node, props)
65
+ _rel_creation_query(from_node, to_node, props)
66
66
  end
67
67
 
68
68
  def class_as_constant(type)
@@ -80,5 +80,19 @@ module Neo4j::ActiveRel
80
80
  def allows_any_class?(type)
81
81
  self.class.send(type) == :any || self.class.send(type) == false
82
82
  end
83
+
84
+ private
85
+
86
+ def _rel_creation_query(from_node, to_node, props)
87
+ from_class = from_node.class
88
+ to_class = to_node.class
89
+ Neo4j::Session.query.match(n1: from_class.mapped_label_name, n2: to_class.mapped_label_name)
90
+ .where("n1.#{from_class.primary_key} = {from_node_id}")
91
+ .where("n2.#{to_class.primary_key} = {to_node_id}")
92
+ .params(from_node_id: from_node.id, to_node_id: to_node.id)
93
+ .create("(n1)-[r:`#{type}`]->(n2)")
94
+ .with('r').set(r: props).return(:r).first.r
95
+ end
96
+
83
97
  end
84
98
  end
@@ -14,7 +14,7 @@ module Neo4j::ActiveRel
14
14
 
15
15
  # Loads the relationship using its neo_id.
16
16
  def find_by_id(key, session = Neo4j::Session.current!)
17
- Neo4j::Relationship.load(key.to_i, session)
17
+ session.query.match("()-[r]-()").where("ID(r)" => key.to_i).limit(1).return(:r).first.r
18
18
  end
19
19
 
20
20
  # Performs a very basic match on the relationship.
@@ -1,22 +1,40 @@
1
1
  module Neo4j::ActiveRel
2
2
  # A container for ActiveRel's :inbound and :outbound methods. It provides lazy loading of nodes.
3
+ # It's important (or maybe not really IMPORTANT, but at least worth mentioning) that calling method_missing
4
+ # will result in a query to load the node if the node is not already loaded.
3
5
  class RelatedNode
4
6
 
5
7
  class InvalidParameterError < StandardError; end
6
8
 
9
+ # ActiveRel's related nodes can be initialized with nothing, an integer, or a fully wrapped node.
10
+ #
11
+ # Initialization with nothing happens when a new, non-persisted ActiveRel object is first initialized.
12
+ #
13
+ # Initialization with an integer happens when a relationship is loaded from the database. It loads using the ID
14
+ # because that is provided by the Cypher response and does not require an extra query.
15
+ #
16
+ # Initialization with a node doesn't appear to happen in the code. TODO: maybe find out why this is an option.
7
17
  def initialize(node = nil)
8
18
  @node = valid_node_param?(node) ? node : (raise InvalidParameterError, 'RelatedNode must be initialized with either a node ID or node' )
9
19
  end
10
20
 
21
+ # Loads the node if needed, then conducts comparison.
11
22
  def == (obj)
12
23
  loaded if @node.is_a?(Fixnum)
13
24
  @node == obj
14
25
  end
15
26
 
27
+ # Returns the neo_id of a given node without loading.
28
+ def neo_id
29
+ loaded? ? @node.neo_id : @node
30
+ end
31
+
32
+ # Loads a node from the database or returns the node if already laoded
16
33
  def loaded
17
34
  @node = @node.respond_to?(:neo_id) ? @node : Neo4j::Node.load(@node)
18
35
  end
19
36
 
37
+ # @return [Boolean] indicates whether a node has or has not been fully loaded from the database
20
38
  def loaded?
21
39
  @node.respond_to?(:neo_id)
22
40
  end
@@ -13,11 +13,19 @@ module Neo4j
13
13
  print string unless !!ENV['silenced']
14
14
  end
15
15
 
16
+ def default_path
17
+ Rails.root if defined? Rails
18
+ end
19
+
20
+ def joined_path(path)
21
+ File.join(path, 'db', 'neo4j-migrate')
22
+ end
23
+
16
24
  class AddIdProperty < Neo4j::Migration
17
25
  attr_reader :models_filename
18
26
 
19
- def initialize
20
- @models_filename = File.join(Rails.root.join('db', 'neo4j-migrate'), 'add_id_property.yml')
27
+ def initialize(path = default_path)
28
+ @models_filename = File.join(joined_path(path), 'add_id_property.yml')
21
29
  end
22
30
 
23
31
  def migrate
@@ -109,9 +117,9 @@ module Neo4j
109
117
 
110
118
  class AddClassnames < Neo4j::Migration
111
119
 
112
- def initialize
120
+ def initialize(path = default_path)
113
121
  @classnames_filename = 'add_classnames.yml'
114
- @classnames_filepath = File.join(Rails.root.join('db', 'neo4j-migrate'), classnames_filename)
122
+ @classnames_filepath = File.join(joined_path(path), classnames_filename)
115
123
  end
116
124
 
117
125
  def migrate
@@ -3,15 +3,16 @@ require 'neo4j/migration'
3
3
  namespace :neo4j do
4
4
  desc "Run a script against the database to perform system-wide changes"
5
5
  task :migrate, [:task_name, :subtask] => :environment do |_, args|
6
+ path = Rake.original_dir
6
7
  migration_task = args[:task_name]
7
8
  task_name_constant = migration_task.split('_').map { |word| word.capitalize }.join('')
8
9
  begin
9
10
  migration_class = "Neo4j::Migration::#{task_name_constant}".constantize
10
11
  rescue NameError
11
- load File.join(Rails.root.join('db', 'neo4j-migrate'), "#{migration_task}.rb")
12
+ load File.join(path, 'db', 'neo4j-migrate', "#{migration_task}.rb")
12
13
  migration_class = "#{task_name_constant}".constantize
13
14
  end
14
- migration = migration_class.new
15
+ migration = migration_class.new(path)
15
16
 
16
17
  subtask = args[:subtask]
17
18
  if subtask
data/lib/neo4j/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Neo4j
2
- VERSION = "3.0.1"
2
+ VERSION = "3.0.2"
3
3
  end
data/neo4j.gemspec CHANGED
@@ -31,7 +31,7 @@ A Neo4j OGM (Object-Graph-Mapper) for use in Ruby on Rails and Rack frameworks,
31
31
  s.add_dependency("activesupport", "~> 4")
32
32
  s.add_dependency("railties", "~> 4")
33
33
  s.add_dependency('active_attr', "~> 0.8")
34
- s.add_dependency("neo4j-core", "~> 3.0.0")
34
+ s.add_dependency("neo4j-core", "~> 3.0.2")
35
35
 
36
36
  if RUBY_PLATFORM =~ /java/
37
37
  s.add_dependency("neo4j-community", '~> 2.0')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: neo4j
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.1
4
+ version: 3.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andreas Ronge
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-29 00:00:00.000000000 Z
11
+ date: 2014-10-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: orm_adapter
@@ -86,14 +86,14 @@ dependencies:
86
86
  requirements:
87
87
  - - "~>"
88
88
  - !ruby/object:Gem::Version
89
- version: 3.0.0
89
+ version: 3.0.2
90
90
  type: :runtime
91
91
  prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
94
  - - "~>"
95
95
  - !ruby/object:Gem::Version
96
- version: 3.0.0
96
+ version: 3.0.2
97
97
  description: |
98
98
  A Neo4j OGM (Object-Graph-Mapper) for use in Ruby on Rails and Rack frameworks, intended as a complete replacement for ActiveRecord.
99
99
  email: andreas.ronge@gmail.com