neo4j 1.0.0.beta.13 → 1.0.0.beta.14
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +9 -0
- data/README.rdoc +92 -28
- data/lib/neo4j.rb +9 -3
- data/lib/neo4j/database.rb +12 -2
- data/lib/neo4j/equal.rb +0 -4
- data/lib/neo4j/event_handler.rb +9 -9
- data/lib/neo4j/index/class_methods.rb +61 -0
- data/lib/neo4j/index/index.rb +33 -0
- data/lib/neo4j/index/index_registry.rb +67 -0
- data/lib/neo4j/index/indexer.rb +193 -0
- data/lib/neo4j/index/wrapped_query.rb +59 -0
- data/lib/neo4j/load.rb +6 -3
- data/lib/neo4j/mapping/class_methods/relationship.rb +7 -13
- data/lib/neo4j/mapping/class_methods/rule.rb +6 -7
- data/lib/neo4j/mapping/decl_relationship_dsl.rb +24 -12
- data/lib/neo4j/mapping/has_n.rb +1 -11
- data/lib/neo4j/mapping/node_mixin.rb +2 -2
- data/lib/neo4j/neo4j.rb +5 -0
- data/lib/neo4j/node.rb +54 -5
- data/lib/neo4j/node_relationship.rb +92 -3
- data/lib/neo4j/property.rb +19 -3
- data/lib/neo4j/rails/model.rb +0 -1
- data/lib/neo4j/rails/value.rb +2 -2
- data/lib/neo4j/relationship.rb +16 -0
- data/lib/neo4j/relationship_traverser.rb +10 -0
- data/lib/neo4j/to_java.rb +15 -1
- data/lib/neo4j/version.rb +1 -1
- data/neo4j.gemspec +8 -8
- metadata +13 -51
- data/lib/neo4j/index.rb +0 -248
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -1,40 +1,104 @@
|
|
1
|
-
|
1
|
+
== Welcome to Neo4j.rb
|
2
2
|
|
3
3
|
Neo4j.rb is a graph database for JRuby.
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
It provides:
|
8
|
-
* Mapping of ruby objects to nodes in networks rather than in tables.
|
9
|
-
* Dynamic, and schema-free - no need to declare nodes/properties/relationships in advance.
|
10
|
-
* Storage of ruby object to a file system.
|
11
|
-
* Fast traversal of relationships between nodes in a huge node space.
|
12
|
-
* Transaction with rollbacks support.
|
13
|
-
* Indexing and querying of ruby objects.
|
14
|
-
* Migration and BatchInserter support
|
15
|
-
* Can be used instead of ActiveRecord in Ruby on Rails or Merb
|
16
|
-
* Can be accessible as REST resources.
|
5
|
+
You can think of \Neo4j as a high-performance graph engine with all the features of a mature and robust database.
|
6
|
+
The programmer works with an object-oriented, flexible network structure rather than with strict and static tables — yet enjoys all the benefits of a fully transactional, enterprise-strength database.
|
17
7
|
|
18
8
|
It uses two powerful and mature Java libraries:
|
19
|
-
* Neo4J
|
20
|
-
* Lucene
|
9
|
+
* {Neo4J}[http://www.neo4j.org] - for persistence and traversal of the graph
|
10
|
+
* {Lucene}[http://lucene.apache.org/java/docs/index.html] for querying and indexing.
|
11
|
+
|
12
|
+
=== Why Neo4j.rb or a Graph Database ?
|
13
|
+
|
14
|
+
Here are some of the major benefits of Neo4j.rb
|
15
|
+
|
16
|
+
* Domain Modeling - use the language of a graph (nodes/relationship/properties) to express your domain !
|
17
|
+
* Schema Less and Efficient storage of Semi Structured Information
|
18
|
+
* No {O/R mismatch}[http://en.wikipedia.org/wiki/Object-relational_impedance_mismatch] - very natural to map a graph to an Object Oriented language like Ruby.
|
19
|
+
* {Performance}[http://www.oscon.com/oscon2009/public/schedule/detail/8364]
|
20
|
+
* Embedded Database - no database tier, easier to install, test, deploy and configure. It is run in the same process as your application.
|
21
|
+
* Express Queries as Traversals
|
22
|
+
* Fast deep traversal instead of slow SQL queries that span many table joins.
|
23
|
+
* Very natural to express graph related problem with traversals (recommendation engine, find shortest parth etc..)
|
24
|
+
* Seamless integration with Ruby on Rails.
|
25
|
+
* ACID Transaction with rollbacks support.
|
21
26
|
|
22
27
|
=== Documentation
|
23
|
-
* Wiki: http://github.com/andreasronge/neo4j/wiki
|
24
|
-
* API Documentation - http://neo4j.rubyforge.org/ (of the released version)
|
25
28
|
|
26
|
-
|
27
|
-
*
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
* {Documentation and Getting Started with Neo4j}[http://github.com/andreasronge/neo4j/wiki]
|
30
|
+
* {API Documentation (released version)}[http://neo4j.rubyforge.org]
|
31
|
+
|
32
|
+
=== Some Examples
|
33
|
+
|
34
|
+
==== Neo4j::Node
|
35
|
+
|
36
|
+
Example of creating a Neo4j::Node
|
37
|
+
|
38
|
+
require "rubygems"
|
39
|
+
require 'neo4j'
|
40
|
+
|
41
|
+
Neo4j::Transaction.run do
|
42
|
+
node = Neo4j::Node.new :name => 'andreas'
|
43
|
+
node.outgoing(:friends) << Neo4j::Node.new :name => 'peter'
|
44
|
+
node.outgoing(:friends).each {|node| puts "name #{node[:name]}"}
|
45
|
+
end
|
46
|
+
|
47
|
+
==== Neo4j::Mapping::NodeMixin
|
48
|
+
|
49
|
+
Example of mapping a ruby class to a node and delaring properties and relationships and lucene index.
|
50
|
+
|
51
|
+
class Person
|
52
|
+
include Neo4j::NodeMixin
|
53
|
+
property :name
|
54
|
+
property :city
|
55
|
+
|
56
|
+
has_n :friends
|
57
|
+
has_one :address
|
58
|
+
index :name
|
59
|
+
end
|
60
|
+
|
61
|
+
# assume we have an transaction already running
|
62
|
+
andreas = Person.new :name => 'andreas'
|
63
|
+
andreas.friends << Person.new :name => 'peter'
|
64
|
+
andreas.friends.each {|person| puts "name #{person.name}" }
|
65
|
+
Person.find("name: andreas").first.name # => 'andreas'
|
66
|
+
|
67
|
+
==== Neo4j::Rails::Model
|
68
|
+
|
69
|
+
Example of using Neo4j with Rails 3 (ActiveModel)
|
70
|
+
|
71
|
+
class User < Neo4j::Model
|
72
|
+
attr_accessor :password
|
73
|
+
attr_accessible :email, :password, :password_confirmation, :pending_account
|
74
|
+
|
75
|
+
after_save :encrypt_password
|
76
|
+
|
77
|
+
email_regex = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i
|
78
|
+
|
79
|
+
validates :email, :presence => true,:format => { :with => email_regex }
|
80
|
+
validates :email, :uniqueness => true, :unless => :pending_account?
|
81
|
+
accepts_nested_attributes_for :avatar, :allow_destroy => true
|
82
|
+
|
83
|
+
property :email
|
84
|
+
index :email
|
85
|
+
|
86
|
+
has_one(:avatar).to(Avator)
|
87
|
+
|
88
|
+
...
|
89
|
+
end
|
33
90
|
|
34
91
|
=== Presentation Materials and other URLs
|
35
|
-
*
|
36
|
-
* Nordic Ruby 2010
|
37
|
-
* Neo4j wiki
|
92
|
+
* {Intoduction to Neo4j.rb}[http://www.slideshare.net/andreasronge/neo4jrb]
|
93
|
+
* {Nordic Ruby 2010 May 21-23}[http://nordicruby.org/speakers#user_29]
|
94
|
+
* {Neo4j wiki, check the guidelines and domain modeling gallery pages}[http://wiki.neo4j.org/content/Main_Page]
|
95
|
+
|
96
|
+
=== Project information
|
97
|
+
* {GitHub}[http://github.com/andreasronge/neo4j/tree/master]
|
98
|
+
* {Lighthouse Issue Tracking}[http://neo4j.lighthouseapp.com]
|
99
|
+
* {Twitter}[http://twitter.com/ronge]
|
100
|
+
* IRC - #neo4j @ irc.freenode.net
|
101
|
+
* {Mailing list, neo4jrb@googlegroups.com}[http://groups.google.com/group/neo4jrb]
|
38
102
|
|
39
103
|
=== Contributing
|
40
104
|
|
@@ -46,4 +110,4 @@ Please also check/add issues at lighthouse, http://neo4j.lighthouseapp.com
|
|
46
110
|
=== License
|
47
111
|
* Neo4j.rb - MIT, see the LICENSE file http://github.com/andreasronge/neo4j/tree/master/LICENSE.
|
48
112
|
* Lucene - Apache, see http://lucene.apache.org/java/docs/features.html
|
49
|
-
* Neo4j - Dual free software/commercial license, see http://neo4j.org/
|
113
|
+
* \Neo4j - Dual free software/commercial license, see http://neo4j.org/
|
data/lib/neo4j.rb
CHANGED
@@ -12,10 +12,17 @@ require 'neo4j/jars/neo4j-lucene-index-0.1-20101002.153213-102.jar'
|
|
12
12
|
require 'neo4j/to_java'
|
13
13
|
require 'neo4j/version'
|
14
14
|
require 'neo4j/equal'
|
15
|
-
|
16
|
-
require 'neo4j/relationship_traverser'
|
15
|
+
|
17
16
|
require 'neo4j/event_handler'
|
18
17
|
require 'neo4j/database'
|
18
|
+
require 'neo4j/neo4j'
|
19
|
+
|
20
|
+
require 'neo4j/index/index'
|
21
|
+
require 'neo4j/index/class_methods'
|
22
|
+
require 'neo4j/index/index_registry'
|
23
|
+
require 'neo4j/index/indexer'
|
24
|
+
require 'neo4j/index/wrapped_query'
|
25
|
+
require 'neo4j/relationship_traverser'
|
19
26
|
require 'neo4j/node_traverser'
|
20
27
|
require 'neo4j/property'
|
21
28
|
require 'neo4j/transaction'
|
@@ -24,7 +31,6 @@ require 'neo4j/load'
|
|
24
31
|
require 'neo4j/relationship'
|
25
32
|
require 'neo4j/node'
|
26
33
|
require 'neo4j/config'
|
27
|
-
require 'neo4j/neo4j'
|
28
34
|
require 'neo4j/mapping/class_methods/init_node'
|
29
35
|
require 'neo4j/mapping/class_methods/init_rel'
|
30
36
|
require 'neo4j/mapping/class_methods/root'
|
data/lib/neo4j/database.rb
CHANGED
@@ -20,8 +20,11 @@ module Neo4j
|
|
20
20
|
if @running
|
21
21
|
@graph.unregister_transaction_event_handler(@event_handler)
|
22
22
|
@event_handler.neo4j_shutdown(self)
|
23
|
+
@graph.shutdown
|
24
|
+
@graph = nil
|
25
|
+
@lucene = nil
|
23
26
|
end
|
24
|
-
|
27
|
+
|
25
28
|
@running = false
|
26
29
|
end
|
27
30
|
|
@@ -37,7 +40,14 @@ module Neo4j
|
|
37
40
|
def each_node
|
38
41
|
iter = @graph.all_nodes.iterator
|
39
42
|
while (iter.hasNext)
|
40
|
-
yield
|
43
|
+
yield iter.next.wrapper
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def _each_node
|
48
|
+
iter = @graph.all_nodes.iterator
|
49
|
+
while (iter.hasNext)
|
50
|
+
yield iter.next
|
41
51
|
end
|
42
52
|
end
|
43
53
|
|
data/lib/neo4j/equal.rb
CHANGED
data/lib/neo4j/event_handler.rb
CHANGED
@@ -19,9 +19,9 @@ module Neo4j
|
|
19
19
|
data.created_nodes.each{|node| node_created(node)}
|
20
20
|
data.assigned_node_properties.each { |tx_data| property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, tx_data.value) }
|
21
21
|
data.removed_node_properties.each { |tx_data| property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, nil) unless data.deleted_nodes.include?(tx_data.entity) }
|
22
|
-
data.deleted_nodes.each { |node| node_deleted(node, deleted_properties_for(node,data))}
|
23
|
-
data.created_relationships.each {|rel| relationship_created(rel)}
|
24
|
-
data.deleted_relationships.each {|rel| relationship_deleted(rel, deleted_rel_properties_for(rel, data))}
|
22
|
+
data.deleted_nodes.each { |node| node_deleted(node, deleted_properties_for(node,data), data)}
|
23
|
+
data.created_relationships.each {|rel| relationship_created(rel, data)}
|
24
|
+
data.deleted_relationships.each {|rel| relationship_deleted(rel, deleted_rel_properties_for(rel, data), data)}
|
25
25
|
data.assigned_relationship_properties.each { |tx_data| rel_property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, tx_data.value) }
|
26
26
|
data.removed_relationship_properties.each {|tx_data| rel_property_changed(tx_data.entity, tx_data.key, tx_data.previously_commited_value, nil) unless data.deleted_relationships.include?(tx_data.entity) }
|
27
27
|
end
|
@@ -69,16 +69,16 @@ module Neo4j
|
|
69
69
|
@listeners.each {|li| li.on_node_created(node) if li.respond_to?(:on_node_created)}
|
70
70
|
end
|
71
71
|
|
72
|
-
def node_deleted(node,old_properties)
|
73
|
-
@listeners.each {|li| li.on_node_deleted(node,old_properties) if li.respond_to?(:on_node_deleted)}
|
72
|
+
def node_deleted(node,old_properties, data)
|
73
|
+
@listeners.each {|li| li.on_node_deleted(node,old_properties, data) if li.respond_to?(:on_node_deleted)}
|
74
74
|
end
|
75
75
|
|
76
|
-
def relationship_created(relationship)
|
77
|
-
@listeners.each {|li| li.on_relationship_created(relationship) if li.respond_to?(:on_relationship_created)}
|
76
|
+
def relationship_created(relationship, tx_data)
|
77
|
+
@listeners.each {|li| li.on_relationship_created(relationship, tx_data) if li.respond_to?(:on_relationship_created)}
|
78
78
|
end
|
79
79
|
|
80
|
-
def relationship_deleted(relationship, old_props)
|
81
|
-
@listeners.each {|li| li.on_relationship_deleted(relationship, old_props) if li.respond_to?(:on_relationship_deleted)}
|
80
|
+
def relationship_deleted(relationship, old_props, data)
|
81
|
+
@listeners.each {|li| li.on_relationship_deleted(relationship, old_props, data) if li.respond_to?(:on_relationship_deleted)}
|
82
82
|
end
|
83
83
|
|
84
84
|
def property_changed(node, key, old_value, new_value)
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Index
|
3
|
+
module ClassMethods
|
4
|
+
attr_reader :_indexer
|
5
|
+
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
##
|
9
|
+
# See Neo4j::Index::Indexer#index
|
10
|
+
# Forwards to the indexer that should be used.
|
11
|
+
# It is possible to share the same index for several different classes, see #node_indexer.
|
12
|
+
# :singleton-method: index
|
13
|
+
|
14
|
+
##
|
15
|
+
# See Neo4j::Index::Indexer#find
|
16
|
+
# Forwards to the indexer that should be used.
|
17
|
+
# It is possible to share the same index for several different classes, see #node_indexer.
|
18
|
+
# :singleton-method: find
|
19
|
+
|
20
|
+
|
21
|
+
def_delegators :@_indexer, :index, :find, :index?, :index_type?, :clear_index_type, :rm_index_type, :add_index, :rm_index, :index_type_for, :index_name
|
22
|
+
|
23
|
+
|
24
|
+
# Sets which indexer should be used for the given node class.
|
25
|
+
# You can share an indexer between several different classes.
|
26
|
+
#
|
27
|
+
# ==== Example
|
28
|
+
# class Contact
|
29
|
+
# include Neo4j::NodeMixin
|
30
|
+
# index :name
|
31
|
+
# has_one :phone
|
32
|
+
# end
|
33
|
+
#
|
34
|
+
# class Phone
|
35
|
+
# include Neo4j::NodeMixin
|
36
|
+
# property :phone
|
37
|
+
# index :phone, :indexer => Person, :via => proc{|node| node.incoming(:phone).first}
|
38
|
+
# end
|
39
|
+
#
|
40
|
+
# # Find an contact with a phone number, this works since they share the same index
|
41
|
+
# Contact.find('phone: 12345 AND name: 'pelle'')
|
42
|
+
#
|
43
|
+
# ==== Returns
|
44
|
+
# The indexer that should be used to index the given class
|
45
|
+
def node_indexer(clazz)
|
46
|
+
indexer(clazz, :node)
|
47
|
+
end
|
48
|
+
|
49
|
+
# Sets which indexer should be used for the given relationship class
|
50
|
+
# Same as #node_indexer except that it indexes relationships instead of nodes.
|
51
|
+
#
|
52
|
+
def rel_indexer(clazz)
|
53
|
+
indexer(clazz, :rel)
|
54
|
+
end
|
55
|
+
|
56
|
+
def indexer(clazz, type) #:nodoc:
|
57
|
+
@_indexer = IndexerRegistry.create_for(self, clazz, type)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Neo4j
|
2
|
+
|
3
|
+
module Index
|
4
|
+
|
5
|
+
# Adds an index on the given property
|
6
|
+
# Notice that you normally don't have to do that since you simply can declare
|
7
|
+
# that the property and index should be updated automatically by using the class method #index.
|
8
|
+
#
|
9
|
+
# The index operation will take place immediately unlike when using the Neo4j::Index::ClassMethods::index
|
10
|
+
# method which instead will guarantee that the neo4j database and the lucene database will be consistent.
|
11
|
+
# It uses a two phase commit when the transaction is about to be committed.
|
12
|
+
#
|
13
|
+
# ==== See also
|
14
|
+
# Neo4j::Index::ClassMethods::index
|
15
|
+
#
|
16
|
+
def add_index(field, value=self[field])
|
17
|
+
self.class.add_index(wrapped_entity, field.to_s, value)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Removes an index on the given property.
|
21
|
+
# Just like #add_index this is normally not needed since you instead can declare it with the
|
22
|
+
# #index class method instead.
|
23
|
+
#
|
24
|
+
# ==== See also
|
25
|
+
# Neo4j::Index::ClassMethods::index
|
26
|
+
# Neo4j::Index#add_index
|
27
|
+
#
|
28
|
+
def rm_index(field, value=self[field])
|
29
|
+
self.class.rm_index(wrapped_entity, field.to_s, value)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Index
|
3
|
+
class IndexerRegistry #:nodoc:
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def clear_all_indexes
|
7
|
+
@@indexers.values.each {|i| i.clear_index_type}
|
8
|
+
end
|
9
|
+
|
10
|
+
def create_for(this_clazz, using_other_clazz, type)
|
11
|
+
@@indexers ||= {}
|
12
|
+
@@indexers[this_clazz.to_s] = @@indexers[using_other_clazz.to_s] || Indexer.new(this_clazz, type)
|
13
|
+
@@indexers[this_clazz.to_s]
|
14
|
+
end
|
15
|
+
|
16
|
+
def find_by_class(classname)
|
17
|
+
@@indexers[classname]
|
18
|
+
end
|
19
|
+
|
20
|
+
def on_node_deleted(node, old_props, tx_data)
|
21
|
+
indexer = find_by_class(old_props['_classname'] || node.class.to_s)
|
22
|
+
indexer && indexer.remove_index_on_fields(node, old_props, tx_data)
|
23
|
+
end
|
24
|
+
|
25
|
+
def on_property_changed(node, field, old_val, new_val)
|
26
|
+
classname = node['_classname'] || node.class.to_s
|
27
|
+
indexer = find_by_class(classname)
|
28
|
+
indexer && indexer.update_index_on(node, field, old_val, new_val)
|
29
|
+
end
|
30
|
+
|
31
|
+
def on_rel_property_changed(rel, field, old_val, new_val)
|
32
|
+
# works exactly like for nodes
|
33
|
+
on_property_changed(rel, field, old_val, new_val)
|
34
|
+
end
|
35
|
+
|
36
|
+
def on_relationship_created(rel, tx_data)
|
37
|
+
end_node = rel._end_node
|
38
|
+
# if end_node was created in this transaction then it will be handled in on_property_changed
|
39
|
+
created = tx_data.created_nodes.find{|n| n.neo_id == end_node.neo_id}
|
40
|
+
unless created
|
41
|
+
indexer = find_by_class(end_node['_classname'])
|
42
|
+
indexer && indexer.update_on_new_relationship(rel)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_relationship_deleted(rel, old_props, tx_data)
|
47
|
+
on_node_deleted(rel, old_props, tx_data)
|
48
|
+
# if only the relationship has been deleted then we have to remove the index
|
49
|
+
# if both the relationship and the node has been deleted then the index will be removed in the
|
50
|
+
# on_node_deleted callback
|
51
|
+
end_node = rel._end_node
|
52
|
+
deleted = tx_data.deleted_nodes.find{|n| n.neo_id == end_node.neo_id}
|
53
|
+
unless deleted
|
54
|
+
indexer = find_by_class(end_node['_classname'])
|
55
|
+
indexer && indexer.update_on_deleted_relationship(rel)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def on_neo4j_shutdown(*)
|
60
|
+
@@indexers.each_value {|indexer| indexer.on_neo4j_shutdown}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
Neo4j.unstarted_db.event_handler.add(IndexerRegistry)
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,193 @@
|
|
1
|
+
module Neo4j
|
2
|
+
module Index
|
3
|
+
class Indexer #:nodoc:
|
4
|
+
attr_reader :indexer_for
|
5
|
+
|
6
|
+
def initialize(clazz, type)
|
7
|
+
# part of the unique name of the index
|
8
|
+
@indexer_for = clazz
|
9
|
+
|
10
|
+
# do we want to index nodes or relationships ?
|
11
|
+
@type = type
|
12
|
+
|
13
|
+
@indexes = {} # key = type, value = java neo4j index
|
14
|
+
@field_types = {} # key = field, value = type (e.g. :exact or :fulltext)
|
15
|
+
@via_relationships = {} # key = field, value = relationship
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"Indexer @#{object_id} [index_for:#{@indexer_for}, field_types=#{@field_types.keys.join(', ')}, via=#{@via_relationships.inspect}]"
|
20
|
+
end
|
21
|
+
|
22
|
+
# add an index on a field so that it will be automatically updated by neo4j events.
|
23
|
+
def index(field, conf = {})
|
24
|
+
if conf[:via]
|
25
|
+
rel_dsl = @indexer_for._decl_rels[conf[:via]]
|
26
|
+
raise "No relationship defined for '#{conf[:via]}'. Check class '#{@indexer_for}': index :#{field}, via=>:#{conf[:via]} <-- error. Define it with a has_one or has_n" unless rel_dsl
|
27
|
+
raise "Only incoming relationship are possible to define index on. Check class '#{@indexer_for}': index :#{field}, via=>:#{conf[:via]}" unless rel_dsl.incoming?
|
28
|
+
via_indexer = rel_dsl.to_class._indexer
|
29
|
+
|
30
|
+
field = field.to_s
|
31
|
+
@via_relationships[field] = rel_dsl
|
32
|
+
conf.delete :via # avoid endless recursion
|
33
|
+
via_indexer.index(field, conf)
|
34
|
+
else
|
35
|
+
raise "Already defined an (via?) index on #{field}, Using the same index for from two classes ? Check index :#{field}, :via => :#{@indexer_for}" if @field_types[field.to_s]
|
36
|
+
@field_types[field.to_s] = conf[:type] || :exact
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def remove_index_on_fields(node, props, tx_data)
|
41
|
+
@field_types.keys.each { |field| rm_index(node, field, props[field]) if props[field] }
|
42
|
+
# remove all via indexed fields
|
43
|
+
@via_relationships.each_value do |dsl|
|
44
|
+
indexer = dsl.to_class._indexer
|
45
|
+
tx_data.deleted_relationships.each do |rel|
|
46
|
+
start_node = rel._start_node
|
47
|
+
next if node != rel._end_node
|
48
|
+
indexer.remove_index_on_fields(start_node, props, tx_data)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def update_on_deleted_relationship(relationship)
|
54
|
+
update_on_relationship(relationship, false)
|
55
|
+
end
|
56
|
+
|
57
|
+
def update_on_new_relationship(relationship)
|
58
|
+
update_on_relationship(relationship, true)
|
59
|
+
end
|
60
|
+
|
61
|
+
def update_on_relationship(relationship, is_created)
|
62
|
+
rel_type = relationship.rel_type
|
63
|
+
end_node = relationship._end_node
|
64
|
+
# find which via relationship match rel_type
|
65
|
+
@via_relationships.each_pair do |field, dsl|
|
66
|
+
# have we declared an index on this changed relationship ?
|
67
|
+
next unless dsl.incoming_dsl.namespace_type == rel_type
|
68
|
+
|
69
|
+
# yes, so find the node and value we should update the index on
|
70
|
+
val = end_node[field]
|
71
|
+
start_node = relationship._start_node
|
72
|
+
|
73
|
+
# find the indexer to use
|
74
|
+
indexer = dsl.to_class._indexer
|
75
|
+
|
76
|
+
# is the relationship created or deleted ?
|
77
|
+
if is_created
|
78
|
+
indexer.update_index_on(start_node, field, nil, val)
|
79
|
+
else
|
80
|
+
indexer.update_index_on(start_node, field, val, nil)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def update_index_on(node, field, old_val, new_val)
|
86
|
+
if @via_relationships.include?(field)
|
87
|
+
dsl = @via_relationships[field]
|
88
|
+
to_class = dsl.to_class
|
89
|
+
|
90
|
+
dsl.all_relationships(node).each do |rel|
|
91
|
+
other = rel._start_node
|
92
|
+
to_class._indexer.update_index_on(other, field, old_val, new_val)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
if @field_types.include?(field)
|
97
|
+
rm_index(node, field, old_val) if old_val
|
98
|
+
add_index(node, field, new_val) if new_val
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def index?(field)
|
103
|
+
@field_types.include?(field.to_s)
|
104
|
+
end
|
105
|
+
|
106
|
+
def index_type_for(field)
|
107
|
+
return nil unless index?(field)
|
108
|
+
@field_types[field.to_s]
|
109
|
+
end
|
110
|
+
|
111
|
+
def index_type?(type)
|
112
|
+
@field_types.values.include?(type)
|
113
|
+
end
|
114
|
+
|
115
|
+
def add_index(entity, field, value)
|
116
|
+
index_for_field(field.to_s).add(entity, field, value)
|
117
|
+
end
|
118
|
+
|
119
|
+
def rm_index(entity, field, value)
|
120
|
+
index_for_field(field).remove(entity, field, value)
|
121
|
+
end
|
122
|
+
|
123
|
+
def find(query, params = {})
|
124
|
+
type = params[:type] || :exact
|
125
|
+
index = index_for_type(type)
|
126
|
+
query = (params[:wrapped].nil? || params[:wrapped]) ? WrappedQuery.new(index, query) : index.query(query)
|
127
|
+
|
128
|
+
if block_given?
|
129
|
+
begin
|
130
|
+
ret = yield query
|
131
|
+
ensure
|
132
|
+
query.close
|
133
|
+
end
|
134
|
+
ret
|
135
|
+
else
|
136
|
+
query
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# clears the index, if no type is provided clear all types of indexes
|
141
|
+
def clear_index_type(type=nil)
|
142
|
+
if type
|
143
|
+
#raise "can't clear index of type '#{type}' since it does not exist ([#{@field_types.values.join(',')}] exists)" unless index_type?(type)
|
144
|
+
@indexes[type] && @indexes[type].clear
|
145
|
+
else
|
146
|
+
@indexes.each_value { |index| index.clear }
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def on_neo4j_shutdown
|
151
|
+
# Since we might start the database again we must make sure that we don't keep any references to
|
152
|
+
# an old lucene index service.
|
153
|
+
@indexes.clear
|
154
|
+
end
|
155
|
+
|
156
|
+
def rm_index_type(type=nil)
|
157
|
+
if type
|
158
|
+
@field_types.delete_if { |k, v| v == type }
|
159
|
+
else
|
160
|
+
@field_types.clear
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
def index_for_field(field)
|
165
|
+
type = @field_types[field]
|
166
|
+
@indexes[type] ||= create_index_with(type)
|
167
|
+
end
|
168
|
+
|
169
|
+
def index_for_type(type)
|
170
|
+
@indexes[type] ||= create_index_with(type)
|
171
|
+
end
|
172
|
+
|
173
|
+
def lucene_config(type)
|
174
|
+
conf = Neo4j::Config[:lucene][type.to_sym]
|
175
|
+
raise "unknown lucene type #{type}" unless conf
|
176
|
+
conf
|
177
|
+
end
|
178
|
+
|
179
|
+
def create_index_with(type)
|
180
|
+
db=Neo4j.started_db
|
181
|
+
index_config = lucene_config(type)
|
182
|
+
if @type == :node
|
183
|
+
db.lucene.node_index("#{@indexer_for}-#{type}", index_config)
|
184
|
+
else
|
185
|
+
db.lucene.relationship_index("#{@indexer_for}-#{type}", index_config)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
end
|