neo4j 3.0.1 → 3.0.2

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