neo4j 2.0.0.alpha.5-java → 2.0.0.alpha.6-java
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +12 -0
- data/Gemfile +0 -4
- data/README.rdoc +106 -62
- data/lib/neo4j.rb +7 -33
- data/lib/neo4j/performance.rb +43 -0
- data/lib/neo4j/rails/accept_id.rb +19 -18
- data/lib/neo4j/rails/attributes.rb +366 -120
- data/lib/neo4j/rails/finders.rb +41 -15
- data/lib/neo4j/rails/has_n.rb +203 -0
- data/lib/neo4j/rails/identity.rb +25 -0
- data/lib/neo4j/rails/model.rb +65 -242
- data/lib/neo4j/rails/nested_attributes.rb +108 -0
- data/lib/neo4j/rails/node_persistance.rb +56 -0
- data/lib/neo4j/rails/observer.rb +0 -2
- data/lib/neo4j/rails/persistence.rb +32 -154
- data/lib/neo4j/rails/rack_middleware.rb +26 -2
- data/lib/neo4j/rails/rails.rb +9 -6
- data/lib/neo4j/rails/railtie.rb +1 -2
- data/lib/neo4j/rails/relationship.rb +18 -125
- data/lib/neo4j/rails/relationship_persistence.rb +107 -0
- data/lib/neo4j/rails/relationships/node_dsl.rb +72 -44
- data/lib/neo4j/rails/relationships/relationships.rb +187 -59
- data/lib/neo4j/rails/relationships/rels_dsl.rb +18 -17
- data/lib/neo4j/rails/relationships/storage.rb +19 -17
- data/lib/neo4j/rails/timestamps.rb +53 -51
- data/lib/neo4j/rails/transaction.rb +9 -1
- data/lib/neo4j/rails/validations/uniqueness.rb +1 -1
- data/lib/neo4j/rails/versioning/versioning.rb +2 -2
- data/lib/neo4j/version.rb +1 -1
- data/lib/orm_adapter/adapters/neo4j.rb +47 -46
- data/neo4j.gemspec +1 -1
- metadata +10 -69
- data/lib/neo4j/algo/algo.rb +0 -294
- data/lib/neo4j/batch/batch.rb +0 -4
- data/lib/neo4j/batch/indexer.rb +0 -109
- data/lib/neo4j/batch/inserter.rb +0 -179
- data/lib/neo4j/batch/rule_inserter.rb +0 -24
- data/lib/neo4j/batch/rule_node.rb +0 -72
- data/lib/neo4j/config.rb +0 -177
- data/lib/neo4j/core_ext/class/inheritable_attributes.rb +0 -12
- data/lib/neo4j/core_ext/class/rewrite_inheritable_attributes.rb +0 -170
- data/lib/neo4j/database.rb +0 -158
- data/lib/neo4j/equal.rb +0 -21
- data/lib/neo4j/event_handler.rb +0 -263
- data/lib/neo4j/has_list/class_methods.rb +0 -11
- data/lib/neo4j/has_list/has_list.rb +0 -3
- data/lib/neo4j/has_list/mapping.rb +0 -133
- data/lib/neo4j/has_n/class_methods.rb +0 -119
- data/lib/neo4j/has_n/decl_relationship_dsl.rb +0 -246
- data/lib/neo4j/has_n/has_n.rb +0 -3
- data/lib/neo4j/has_n/mapping.rb +0 -98
- data/lib/neo4j/identity_map.rb +0 -140
- data/lib/neo4j/index/class_methods.rb +0 -108
- data/lib/neo4j/index/index.rb +0 -39
- data/lib/neo4j/index/indexer.rb +0 -341
- data/lib/neo4j/index/indexer_registry.rb +0 -68
- data/lib/neo4j/index/lucene_query.rb +0 -256
- data/lib/neo4j/load.rb +0 -25
- data/lib/neo4j/migrations/class_methods.rb +0 -110
- data/lib/neo4j/migrations/extensions.rb +0 -58
- data/lib/neo4j/migrations/lazy_node_mixin.rb +0 -41
- data/lib/neo4j/migrations/migration.rb +0 -112
- data/lib/neo4j/migrations/migrations.rb +0 -6
- data/lib/neo4j/migrations/node_mixin.rb +0 -80
- data/lib/neo4j/migrations/ref_node_wrapper.rb +0 -32
- data/lib/neo4j/model.rb +0 -4
- data/lib/neo4j/neo4j.rb +0 -216
- data/lib/neo4j/node.rb +0 -270
- data/lib/neo4j/node_mixin/class_methods.rb +0 -51
- data/lib/neo4j/node_mixin/node_mixin.rb +0 -141
- data/lib/neo4j/paginated.rb +0 -23
- data/lib/neo4j/property/class_methods.rb +0 -79
- data/lib/neo4j/property/property.rb +0 -111
- data/lib/neo4j/rails/mapping/property.rb +0 -183
- data/lib/neo4j/rails/rel_persistence.rb +0 -237
- data/lib/neo4j/relationship.rb +0 -239
- data/lib/neo4j/relationship_mixin/class_methods.rb +0 -36
- data/lib/neo4j/relationship_mixin/relationship_mixin.rb +0 -142
- data/lib/neo4j/relationship_set.rb +0 -58
- data/lib/neo4j/rels/rels.rb +0 -110
- data/lib/neo4j/rels/traverser.rb +0 -102
- data/lib/neo4j/rule/class_methods.rb +0 -201
- data/lib/neo4j/rule/event_listener.rb +0 -66
- data/lib/neo4j/rule/functions/count.rb +0 -43
- data/lib/neo4j/rule/functions/function.rb +0 -74
- data/lib/neo4j/rule/functions/functions.rb +0 -3
- data/lib/neo4j/rule/functions/sum.rb +0 -29
- data/lib/neo4j/rule/rule.rb +0 -150
- data/lib/neo4j/rule/rule_node.rb +0 -217
- data/lib/neo4j/to_java.rb +0 -31
- data/lib/neo4j/transaction.rb +0 -73
- data/lib/neo4j/traversal/filter_predicate.rb +0 -25
- data/lib/neo4j/traversal/prune_evaluator.rb +0 -14
- data/lib/neo4j/traversal/rel_expander.rb +0 -31
- data/lib/neo4j/traversal/traversal.rb +0 -141
- data/lib/neo4j/traversal/traverser.rb +0 -284
- data/lib/neo4j/type_converters/type_converters.rb +0 -288
data/lib/neo4j/has_n/has_n.rb
DELETED
data/lib/neo4j/has_n/mapping.rb
DELETED
@@ -1,98 +0,0 @@
|
|
1
|
-
require 'neo4j/has_n/class_methods'
|
2
|
-
require 'neo4j/has_n/decl_relationship_dsl'
|
3
|
-
require 'neo4j/has_n/mapping'
|
4
|
-
|
5
|
-
module Neo4j
|
6
|
-
module HasN
|
7
|
-
|
8
|
-
# The object created by a has_n or has_one Neo4j::NodeMixin class method which enables creating and traversal of nodes.
|
9
|
-
#
|
10
|
-
# Includes the Enumerable mixin.
|
11
|
-
# The Neo4j::Mapping::ClassMethods::Relationship#has_n and Neo4j::Mapping::ClassMethods::Relationship#one
|
12
|
-
# methods returns an object of this type.
|
13
|
-
#
|
14
|
-
# ==== See Also
|
15
|
-
# Neo4j::HasN::ClassMethods
|
16
|
-
#
|
17
|
-
class Mapping
|
18
|
-
include Enumerable
|
19
|
-
include ToJava
|
20
|
-
|
21
|
-
def initialize(node, dsl) # :nodoc:
|
22
|
-
@node = node
|
23
|
-
@dsl = dsl
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_s
|
27
|
-
"HasN::Mapping [#{@dsl.dir}, id: #{@node.neo_id} type: #{@dsl && @dsl.rel_type} dsl:#{@dsl}]"
|
28
|
-
end
|
29
|
-
|
30
|
-
def size
|
31
|
-
self.to_a.size # TODO: Optimise this
|
32
|
-
end
|
33
|
-
|
34
|
-
alias_method :length, :size
|
35
|
-
|
36
|
-
|
37
|
-
def [](index)
|
38
|
-
i = 0
|
39
|
-
each{|x| return x if i == index; i += 1}
|
40
|
-
nil # out of index
|
41
|
-
end
|
42
|
-
|
43
|
-
# Pretend we are an array - this is necessarily for Rails actionpack/actionview/formhelper to work with this
|
44
|
-
def is_a?(type)
|
45
|
-
# ActionView requires this for nested attributes to work
|
46
|
-
return true if Array == type
|
47
|
-
super
|
48
|
-
end
|
49
|
-
|
50
|
-
# Required by the Enumerable mixin.
|
51
|
-
def each
|
52
|
-
@dsl.each_node(@node) {|n| yield n} # Should use yield here as passing &block through doesn't always work (why?)
|
53
|
-
end
|
54
|
-
|
55
|
-
# returns none wrapped nodes, you may get better performance using this method
|
56
|
-
def _each
|
57
|
-
@dsl._each_node(@node) {|n| yield n}
|
58
|
-
end
|
59
|
-
|
60
|
-
# Returns an real ruby array.
|
61
|
-
def to_ary
|
62
|
-
self.to_a
|
63
|
-
end
|
64
|
-
|
65
|
-
# Returns true if there are no node in this type of relationship
|
66
|
-
def empty?
|
67
|
-
first == nil
|
68
|
-
end
|
69
|
-
|
70
|
-
|
71
|
-
# Creates a relationship instance between this and the other node.
|
72
|
-
# Returns the relationship object
|
73
|
-
def new(other)
|
74
|
-
@dsl.create_relationship_to(@node, other)
|
75
|
-
end
|
76
|
-
|
77
|
-
|
78
|
-
# Creates a relationship between this and the other node.
|
79
|
-
#
|
80
|
-
# ==== Example
|
81
|
-
#
|
82
|
-
# n1 = Node.new # Node has declared having a friend type of relationship
|
83
|
-
# n2 = Node.new
|
84
|
-
# n3 = Node.new
|
85
|
-
#
|
86
|
-
# n1 << n2 << n3
|
87
|
-
#
|
88
|
-
# ==== Returns
|
89
|
-
# self
|
90
|
-
#
|
91
|
-
def <<(other)
|
92
|
-
@dsl.create_relationship_to(@node, other)
|
93
|
-
self
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
end
|
98
|
-
end
|
data/lib/neo4j/identity_map.rb
DELETED
@@ -1,140 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
|
3
|
-
# = Neo4j Identity Map
|
4
|
-
#
|
5
|
-
# Ensures that each object gets loaded only once by keeping every loaded
|
6
|
-
# object in a map. Looks up objects using the map when referring to them.
|
7
|
-
#
|
8
|
-
# More information on Identity Map pattern:
|
9
|
-
# http://www.martinfowler.com/eaaCatalog/identityMap.html
|
10
|
-
#
|
11
|
-
# == Configuration
|
12
|
-
#
|
13
|
-
# In order to enable IdentityMap, set <tt>config.neo4j.identity_map = true</tt>
|
14
|
-
# in your <tt>config/application.rb</tt> file. If used outside rails, set Neo4j::Config[:identity_map] = true.
|
15
|
-
#
|
16
|
-
# IdentityMap is disabled by default and still in development (i.e. use it with care).
|
17
|
-
#
|
18
|
-
module IdentityMap
|
19
|
-
|
20
|
-
class << self
|
21
|
-
def enabled=(flag)
|
22
|
-
Thread.current[:neo4j_identity_map] = flag
|
23
|
-
end
|
24
|
-
|
25
|
-
def enabled
|
26
|
-
Thread.current[:neo4j_identity_map]
|
27
|
-
end
|
28
|
-
|
29
|
-
alias enabled? enabled
|
30
|
-
|
31
|
-
def node_repository
|
32
|
-
Thread.current[:node_identity_map] ||= java.util.HashMap.new
|
33
|
-
end
|
34
|
-
|
35
|
-
def rel_repository
|
36
|
-
Thread.current[:rel_identity_map] ||= java.util.HashMap.new
|
37
|
-
end
|
38
|
-
|
39
|
-
def repository_for(neo_entity)
|
40
|
-
return nil unless enabled?
|
41
|
-
if neo_entity.class == Neo4j::Node
|
42
|
-
node_repository
|
43
|
-
elsif neo_entity.class == Neo4j::Relationship
|
44
|
-
rel_repository
|
45
|
-
else
|
46
|
-
nil
|
47
|
-
end
|
48
|
-
end
|
49
|
-
|
50
|
-
def use
|
51
|
-
old, self.enabled = enabled, true
|
52
|
-
yield if block_given?
|
53
|
-
ensure
|
54
|
-
self.enabled = old
|
55
|
-
clear
|
56
|
-
end
|
57
|
-
|
58
|
-
def without
|
59
|
-
old, self.enabled = enabled, false
|
60
|
-
yield if block_given?
|
61
|
-
ensure
|
62
|
-
self.enabled = old
|
63
|
-
end
|
64
|
-
|
65
|
-
def get(neo_entity)
|
66
|
-
r = repository_for(neo_entity)
|
67
|
-
r && r.get(neo_entity.neo_id)
|
68
|
-
end
|
69
|
-
|
70
|
-
def add(neo_entity, wrapped_entity)
|
71
|
-
r = repository_for(neo_entity)
|
72
|
-
r && r.put(neo_entity.neo_id, wrapped_entity)
|
73
|
-
end
|
74
|
-
|
75
|
-
def remove(neo_entity)
|
76
|
-
r = repository_for(neo_entity)
|
77
|
-
r && r.remove(neo_entity.neo_id)
|
78
|
-
end
|
79
|
-
|
80
|
-
def remove_node_by_id(node_id)
|
81
|
-
node_repository.remove(node_id)
|
82
|
-
end
|
83
|
-
|
84
|
-
def remove_rel_by_id(rel_id)
|
85
|
-
rel_repository.remove(rel_id)
|
86
|
-
end
|
87
|
-
|
88
|
-
def clear
|
89
|
-
node_repository.clear
|
90
|
-
rel_repository.clear
|
91
|
-
end
|
92
|
-
|
93
|
-
def on_after_commit(*)
|
94
|
-
clear
|
95
|
-
end
|
96
|
-
|
97
|
-
def on_neo4j_started(db)
|
98
|
-
if not Neo4j::Config[:identity_map]
|
99
|
-
db.event_handler.remove(self)
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
end
|
104
|
-
|
105
|
-
|
106
|
-
class Middleware
|
107
|
-
class Body #:nodoc:
|
108
|
-
def initialize(target, original)
|
109
|
-
@target = target
|
110
|
-
@original = original
|
111
|
-
end
|
112
|
-
|
113
|
-
def each(&block)
|
114
|
-
@target.each(&block)
|
115
|
-
end
|
116
|
-
|
117
|
-
def close
|
118
|
-
@target.close if @target.respond_to?(:close)
|
119
|
-
ensure
|
120
|
-
IdentityMap.enabled = @original
|
121
|
-
IdentityMap.clear
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
def initialize(app)
|
126
|
-
@app = app
|
127
|
-
end
|
128
|
-
|
129
|
-
def call(env)
|
130
|
-
enabled = IdentityMap.enabled
|
131
|
-
IdentityMap.enabled = Neo4j::Config[:identity_map]
|
132
|
-
status, headers, body = @app.call(env)
|
133
|
-
[status, headers, Body.new(body, enabled)]
|
134
|
-
end
|
135
|
-
end
|
136
|
-
end
|
137
|
-
end
|
138
|
-
|
139
|
-
Neo4j.unstarted_db.event_handler.add(Neo4j::IdentityMap)
|
140
|
-
|
@@ -1,108 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module Index
|
3
|
-
module ClassMethods
|
4
|
-
attr_reader :_indexer
|
5
|
-
|
6
|
-
extend Forwardable
|
7
|
-
|
8
|
-
def wp_query(options, pager, args, &block) #:nodoc:
|
9
|
-
params = {}
|
10
|
-
params[:page] = pager.current_page
|
11
|
-
params[:per_page] = pager.per_page
|
12
|
-
query = if args.empty?
|
13
|
-
find(options, params, &block)
|
14
|
-
else
|
15
|
-
args << params.merge(options)
|
16
|
-
find(*args, &block)
|
17
|
-
end
|
18
|
-
|
19
|
-
pager.replace [*query]
|
20
|
-
pager.total_entries = query.size
|
21
|
-
end
|
22
|
-
|
23
|
-
|
24
|
-
##
|
25
|
-
# See Neo4j::Index::Indexer#index
|
26
|
-
# Forwards to the indexer that should be used.
|
27
|
-
# It is possible to share the same index for several different classes, see #node_indexer.
|
28
|
-
# :singleton-method: index
|
29
|
-
|
30
|
-
##
|
31
|
-
# See Neo4j::Index::Indexer#find
|
32
|
-
# Forwards to the indexer that should be used.
|
33
|
-
# It is possible to share the same index for several different classes, see #node_indexer.
|
34
|
-
# :singleton-method: find
|
35
|
-
|
36
|
-
##
|
37
|
-
# Specifies the location on the filesystem of the lucene index for the given index type.
|
38
|
-
#
|
39
|
-
# If not specified it will have the default location:
|
40
|
-
#
|
41
|
-
# Neo4j.config[:storage_path]/index/lucene/node|relationship/ParentModuleName_SubModuleName_ClassName-indextype
|
42
|
-
#
|
43
|
-
# Forwards to the Indexer#index_names class
|
44
|
-
#
|
45
|
-
# ==== Example
|
46
|
-
# module Foo
|
47
|
-
# class Person
|
48
|
-
# include Neo4j::NodeMixin
|
49
|
-
# index :name
|
50
|
-
# index_names[:fulltext] = 'my_location'
|
51
|
-
# end
|
52
|
-
# end
|
53
|
-
#
|
54
|
-
# Person.index_names[:fulltext] => 'my_location'
|
55
|
-
# Person.index_names[:exact] => 'Foo_Person-exact' # default Location
|
56
|
-
#
|
57
|
-
# The index can be prefixed, see Neo4j#threadlocal_ref_node= and multi dendency.
|
58
|
-
#
|
59
|
-
# :singleton-method: index_names
|
60
|
-
|
61
|
-
|
62
|
-
##
|
63
|
-
# Returns a hash of which indexes has been defined and the type of index (:exact or :fulltext)
|
64
|
-
#
|
65
|
-
# :singleton-method: index_types
|
66
|
-
|
67
|
-
|
68
|
-
def_delegators :@_indexer, :index, :find, :index?, :index_type?, :delete_index_type, :rm_field_type, :add_index, :rm_index, :index_type_for, :index_names, :index_types
|
69
|
-
|
70
|
-
# Sets which indexer should be used for the given node class.
|
71
|
-
# You can share an indexer between several different classes.
|
72
|
-
#
|
73
|
-
# ==== Example
|
74
|
-
# class Contact
|
75
|
-
# include Neo4j::NodeMixin
|
76
|
-
# index :name
|
77
|
-
# has_one :phone
|
78
|
-
# end
|
79
|
-
#
|
80
|
-
# class Phone
|
81
|
-
# include Neo4j::NodeMixin
|
82
|
-
# property :phone
|
83
|
-
# node_indexer Contact # put index on the Contact class instead
|
84
|
-
# index :phone
|
85
|
-
# end
|
86
|
-
#
|
87
|
-
# # Find an contact with a phone number, this works since they share the same index
|
88
|
-
# Contact.find('phone: 12345').first #=> a phone object !
|
89
|
-
#
|
90
|
-
# ==== Returns
|
91
|
-
# The indexer that should be used to index the given class
|
92
|
-
def node_indexer(clazz)
|
93
|
-
indexer(clazz, :node)
|
94
|
-
end
|
95
|
-
|
96
|
-
# Sets which indexer should be used for the given relationship class
|
97
|
-
# Same as #node_indexer except that it indexes relationships instead of nodes.
|
98
|
-
#
|
99
|
-
def rel_indexer(clazz)
|
100
|
-
indexer(clazz, :rel)
|
101
|
-
end
|
102
|
-
|
103
|
-
def indexer(clazz, type) #:nodoc:
|
104
|
-
@_indexer ||= IndexerRegistry.create_for(self, clazz, type)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
data/lib/neo4j/index/index.rb
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
require 'neo4j/index/class_methods'
|
2
|
-
require 'neo4j/index/indexer_registry'
|
3
|
-
require 'neo4j/index/indexer'
|
4
|
-
require 'neo4j/index/lucene_query'
|
5
|
-
|
6
|
-
module Neo4j
|
7
|
-
|
8
|
-
module Index
|
9
|
-
|
10
|
-
# Adds an index on the given property
|
11
|
-
# Notice that you normally don't have to do that since you simply can declare
|
12
|
-
# that the property and index should be updated automatically by using the class method #index.
|
13
|
-
#
|
14
|
-
# The index operation will take place immediately unlike when using the Neo4j::Index::ClassMethods::index
|
15
|
-
# method which instead will guarantee that the neo4j database and the lucene database will be consistent.
|
16
|
-
# It uses a two phase commit when the transaction is about to be committed.
|
17
|
-
#
|
18
|
-
# ==== See also
|
19
|
-
# Neo4j::Index::ClassMethods::index
|
20
|
-
#
|
21
|
-
def add_index(field, value=self[field])
|
22
|
-
converted_value = Neo4j::TypeConverters.convert(value, field, self.class)
|
23
|
-
self.class.add_index(wrapped_entity, field.to_s, converted_value)
|
24
|
-
end
|
25
|
-
|
26
|
-
# Removes an index on the given property.
|
27
|
-
# Just like #add_index this is normally not needed since you instead can declare it with the
|
28
|
-
# #index class method instead.
|
29
|
-
#
|
30
|
-
# ==== See also
|
31
|
-
# Neo4j::Index::ClassMethods::index
|
32
|
-
# Neo4j::Index#add_index
|
33
|
-
#
|
34
|
-
def rm_index(field, value=self[field])
|
35
|
-
self.class.rm_index(wrapped_entity, field.to_s, value)
|
36
|
-
end
|
37
|
-
|
38
|
-
end
|
39
|
-
end
|
data/lib/neo4j/index/indexer.rb
DELETED
@@ -1,341 +0,0 @@
|
|
1
|
-
module Neo4j
|
2
|
-
module Index
|
3
|
-
class Indexer
|
4
|
-
attr_reader :indexer_for, :field_types, :via_relationships, :entity_type, :parent_indexers, :via_relationships
|
5
|
-
alias_method :index_types, :field_types # public method accessible from node.index_types
|
6
|
-
|
7
|
-
def initialize(clazz, type) #:nodoc:
|
8
|
-
# part of the unique name of the index
|
9
|
-
@indexer_for = clazz
|
10
|
-
|
11
|
-
# do we want to index nodes or relationships ?
|
12
|
-
@entity_type = type
|
13
|
-
|
14
|
-
@indexes = {} # key = type, value = java neo4j index
|
15
|
-
@field_types = {} # key = field, value = type (e.g. :exact or :fulltext)
|
16
|
-
@via_relationships = {} # key = field, value = relationship
|
17
|
-
|
18
|
-
# to enable subclass indexing to work properly, store a list of parent indexers and
|
19
|
-
# whenever an operation is performed on this one, perform it on all
|
20
|
-
@parent_indexers = []
|
21
|
-
end
|
22
|
-
|
23
|
-
def inherit_fields_from(parent_index) #:nodoc:
|
24
|
-
return unless parent_index
|
25
|
-
@field_types.reverse_merge!(parent_index.field_types) if parent_index.respond_to?(:field_types)
|
26
|
-
@via_relationships.reverse_merge!(parent_index.via_relationships) if parent_index.respond_to?(:via_relationships)
|
27
|
-
@parent_indexers << parent_index
|
28
|
-
end
|
29
|
-
|
30
|
-
def to_s
|
31
|
-
"Indexer @#{object_id} [index_for:#{@indexer_for}, field_types=#{@field_types.keys.join(', ')}, via=#{@via_relationships.inspect}]"
|
32
|
-
end
|
33
|
-
|
34
|
-
# Add an index on a field so that it will be automatically updated by neo4j transactional events.
|
35
|
-
#
|
36
|
-
# The index method takes an optional configuration hash which allows you to:
|
37
|
-
#
|
38
|
-
# === Add an index on an a property
|
39
|
-
#
|
40
|
-
# Example:
|
41
|
-
# class Person
|
42
|
-
# include Neo4j::NodeMixin
|
43
|
-
# index :name
|
44
|
-
# end
|
45
|
-
#
|
46
|
-
# When the property name is changed/deleted or the node created it will keep the lucene index in sync.
|
47
|
-
# You can then perform a lucene query like this: Person.find('name: andreas')
|
48
|
-
#'
|
49
|
-
# === Add index on other nodes.
|
50
|
-
#
|
51
|
-
# Example:
|
52
|
-
#
|
53
|
-
# class Person
|
54
|
-
# include Neo4j::NodeMixin
|
55
|
-
# has_n(:friends).to(Contact)
|
56
|
-
# has_n(:known_by).from(:friends)
|
57
|
-
# index :user_id, :via => :known_by
|
58
|
-
# end
|
59
|
-
#
|
60
|
-
# Notice that you *must* specify an incoming relationship with the via key, as shown above.
|
61
|
-
# In the example above an index <tt>user_id</tt> will be added to all Person nodes which has a <tt>friends</tt> relationship
|
62
|
-
# that person with that user_id. This allows you to do lucene queries on your friends properties.
|
63
|
-
#
|
64
|
-
# === Set the type value to index
|
65
|
-
# By default all values will be indexed as Strings.
|
66
|
-
# If you want for example to do a numerical range query you must tell Neo4j.rb to index it as a numeric value.
|
67
|
-
# You do that with the key <tt>type</tt> on the property.
|
68
|
-
#
|
69
|
-
# Example:
|
70
|
-
# class Person
|
71
|
-
# include Neo4j::NodeMixin
|
72
|
-
# property :height, :weight, :type => Float
|
73
|
-
# index :height, :weight
|
74
|
-
# end
|
75
|
-
#
|
76
|
-
# Supported values for <tt>:type</tt> is <tt>String</tt>, <tt>Float</tt>, <tt>Date</tt>, <tt>DateTime</tt> and <tt>Fixnum</tt>
|
77
|
-
#
|
78
|
-
# === For more information
|
79
|
-
# * See Neo4j::Index::LuceneQuery
|
80
|
-
# * See #find
|
81
|
-
#
|
82
|
-
def index(*args)
|
83
|
-
conf = args.last.kind_of?(Hash) ? args.pop : {}
|
84
|
-
conf_no_via = conf.reject { |k,v| k == :via } # avoid endless recursion
|
85
|
-
|
86
|
-
args.uniq.each do | field |
|
87
|
-
if conf[:via]
|
88
|
-
rel_dsl = @indexer_for._decl_rels[conf[:via]]
|
89
|
-
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
|
90
|
-
raise "Only incoming relationship are possible to define index on. Check class '#{@indexer_for}': index :#{field}, via=>:#{conf[:via]}" unless rel_dsl.incoming?
|
91
|
-
via_indexer = rel_dsl.target_class._indexer
|
92
|
-
|
93
|
-
field = field.to_s
|
94
|
-
@via_relationships[field] = rel_dsl
|
95
|
-
via_indexer.index(field, conf_no_via)
|
96
|
-
else
|
97
|
-
@field_types[field.to_s] = conf[:type] || :exact
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
def remove_index_on_fields(node, props, deleted_relationship_set) #:nodoc:
|
103
|
-
@field_types.keys.each { |field| rm_index(node, field, props[field]) if props[field] }
|
104
|
-
# remove all via indexed fields
|
105
|
-
@via_relationships.each_value do |dsl|
|
106
|
-
indexer = dsl.target_class._indexer
|
107
|
-
deleted_relationship_set.relationships(node.getId).each do |rel|
|
108
|
-
indexer.remove_index_on_fields(rel._start_node, props, deleted_relationship_set)
|
109
|
-
end
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
def update_on_deleted_relationship(relationship) #:nodoc:
|
114
|
-
update_on_relationship(relationship, false)
|
115
|
-
end
|
116
|
-
|
117
|
-
def update_on_new_relationship(relationship) #:nodoc:
|
118
|
-
update_on_relationship(relationship, true)
|
119
|
-
end
|
120
|
-
|
121
|
-
def update_on_relationship(relationship, is_created) #:nodoc:
|
122
|
-
rel_type = relationship.rel_type
|
123
|
-
end_node = relationship._end_node
|
124
|
-
# find which via relationship match rel_type
|
125
|
-
@via_relationships.each_pair do |field, dsl|
|
126
|
-
# have we declared an index on this changed relationship ?
|
127
|
-
next unless dsl.rel_type == rel_type
|
128
|
-
|
129
|
-
# yes, so find the node and value we should update the index on
|
130
|
-
val = end_node[field]
|
131
|
-
start_node = relationship._start_node
|
132
|
-
|
133
|
-
# find the indexer to use
|
134
|
-
indexer = dsl.target_class._indexer
|
135
|
-
|
136
|
-
# is the relationship created or deleted ?
|
137
|
-
if is_created
|
138
|
-
indexer.update_index_on(start_node, field, nil, val)
|
139
|
-
else
|
140
|
-
indexer.update_index_on(start_node, field, val, nil)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
def update_index_on(node, field, old_val, new_val) #:nodoc:
|
146
|
-
if @via_relationships.include?(field)
|
147
|
-
dsl = @via_relationships[field]
|
148
|
-
target_class = dsl.target_class
|
149
|
-
|
150
|
-
dsl._all_relationships(node).each do |rel|
|
151
|
-
other = rel._start_node
|
152
|
-
target_class._indexer.update_single_index_on(other, field, old_val, new_val)
|
153
|
-
end
|
154
|
-
end
|
155
|
-
update_single_index_on(node, field, old_val, new_val)
|
156
|
-
end
|
157
|
-
|
158
|
-
def update_single_index_on(node, field, old_val, new_val) #:nodoc:
|
159
|
-
if @field_types.has_key?(field)
|
160
|
-
rm_index(node, field, old_val) if old_val
|
161
|
-
add_index(node, field, new_val) if new_val
|
162
|
-
end
|
163
|
-
end
|
164
|
-
|
165
|
-
# Returns true if there is an index on the given field.
|
166
|
-
#
|
167
|
-
def index?(field)
|
168
|
-
@field_types.include?(field.to_s)
|
169
|
-
end
|
170
|
-
|
171
|
-
# Returns the type of index for the given field (e.g. :exact or :fulltext)
|
172
|
-
#
|
173
|
-
def index_type_for(field) #:nodoc:
|
174
|
-
return nil unless index?(field)
|
175
|
-
@field_types[field.to_s]
|
176
|
-
end
|
177
|
-
|
178
|
-
# Returns true if there is an index of the given type defined.
|
179
|
-
def index_type?(type)
|
180
|
-
@field_types.values.include?(type)
|
181
|
-
end
|
182
|
-
|
183
|
-
# Adds an index on the given entity
|
184
|
-
# This is normally not needed since you can instead declare an index which will automatically keep
|
185
|
-
# the lucene index in sync. See #index
|
186
|
-
#
|
187
|
-
def add_index(entity, field, value)
|
188
|
-
return false unless @field_types.has_key?(field)
|
189
|
-
conv_value = indexed_value_for(field, value)
|
190
|
-
index = index_for_field(field.to_s)
|
191
|
-
index.add(entity, field, conv_value)
|
192
|
-
@parent_indexers.each { |i| i.add_index(entity, field, value) }
|
193
|
-
end
|
194
|
-
|
195
|
-
def indexed_value_for(field, value)
|
196
|
-
# we might need to know what type the properties are when indexing and querying
|
197
|
-
@decl_props ||= @indexer_for.respond_to?(:_decl_props) && @indexer_for._decl_props
|
198
|
-
|
199
|
-
type = @decl_props && @decl_props[field.to_sym] && @decl_props[field.to_sym][:type]
|
200
|
-
return value unless type
|
201
|
-
|
202
|
-
if String != type
|
203
|
-
org.neo4j.index.lucene.ValueContext.new(value).indexNumeric
|
204
|
-
else
|
205
|
-
org.neo4j.index.lucene.ValueContext.new(value)
|
206
|
-
end
|
207
|
-
end
|
208
|
-
|
209
|
-
# Removes an index on the given entity
|
210
|
-
# This is normally not needed since you can instead declare an index which will automatically keep
|
211
|
-
# the lucene index in sync. See #index
|
212
|
-
#
|
213
|
-
def rm_index(entity, field, value)
|
214
|
-
return false unless @field_types.has_key?(field)
|
215
|
-
index_for_field(field).remove(entity, field, value)
|
216
|
-
@parent_indexers.each { |i| i.rm_index(entity, field, value) }
|
217
|
-
end
|
218
|
-
|
219
|
-
# Performs a Lucene Query.
|
220
|
-
#
|
221
|
-
# In order to use this you have to declare an index on the fields first, see #index.
|
222
|
-
# Notice that you should close the lucene query after the query has been executed.
|
223
|
-
# You can do that either by provide an block or calling the Neo4j::Index::LuceneQuery#close
|
224
|
-
# method. When performing queries from Ruby on Rails you do not need this since it will be automatically closed
|
225
|
-
# (by Rack).
|
226
|
-
#
|
227
|
-
# === Example, with a block
|
228
|
-
#
|
229
|
-
# Person.find('name: kalle') {|query| puts "#{[*query].join(', )"}
|
230
|
-
#
|
231
|
-
# ==== Example
|
232
|
-
#
|
233
|
-
# query = Person.find('name: kalle')
|
234
|
-
# puts "First item #{query.first}"
|
235
|
-
# query.close
|
236
|
-
#
|
237
|
-
# === Return Value
|
238
|
-
# It will return a Neo4j::Index::LuceneQuery object
|
239
|
-
#
|
240
|
-
#
|
241
|
-
def find(query, params = {})
|
242
|
-
# we might need to know what type the properties are when indexing and querying
|
243
|
-
@decl_props ||= @indexer_for.respond_to?(:_decl_props) && @indexer_for._decl_props
|
244
|
-
|
245
|
-
index = index_for_type(params[:type] || :exact)
|
246
|
-
if query.is_a?(Hash) && (query.include?(:conditions) || query.include?(:sort))
|
247
|
-
params.merge! query.except(:conditions)
|
248
|
-
query.delete(:sort)
|
249
|
-
query = query.delete(:conditions) if query.include?(:conditions)
|
250
|
-
end
|
251
|
-
query = (params[:wrapped].nil? || params[:wrapped]) ? LuceneQuery.new(index, @decl_props, query, params) : index.query(query)
|
252
|
-
|
253
|
-
if block_given?
|
254
|
-
begin
|
255
|
-
ret = yield query
|
256
|
-
ensure
|
257
|
-
query.close
|
258
|
-
end
|
259
|
-
ret
|
260
|
-
else
|
261
|
-
query
|
262
|
-
end
|
263
|
-
end
|
264
|
-
|
265
|
-
# delete the index, if no type is provided clear all types of indexes
|
266
|
-
def delete_index_type(type=nil)
|
267
|
-
if type
|
268
|
-
#raise "can't clear index of type '#{type}' since it does not exist ([#{@field_types.values.join(',')}] exists)" unless index_type?(type)
|
269
|
-
key = index_key(type)
|
270
|
-
@indexes[key] && @indexes[key].delete
|
271
|
-
@indexes[key] = nil
|
272
|
-
else
|
273
|
-
@indexes.each_value { |index| index.delete }
|
274
|
-
@indexes.clear
|
275
|
-
end
|
276
|
-
end
|
277
|
-
|
278
|
-
def on_neo4j_shutdown #:nodoc:
|
279
|
-
# Since we might start the database again we must make sure that we don't keep any references to
|
280
|
-
# an old lucene index in memory.
|
281
|
-
@indexes.clear
|
282
|
-
end
|
283
|
-
|
284
|
-
# Removes the cached lucene index, can be useful for some RSpecs which needs to restart the Neo4j.
|
285
|
-
#
|
286
|
-
def rm_field_type(type=nil)
|
287
|
-
if type
|
288
|
-
@field_types.delete_if { |k, v| v == type }
|
289
|
-
else
|
290
|
-
@field_types.clear
|
291
|
-
end
|
292
|
-
end
|
293
|
-
|
294
|
-
def index_for_field(field) #:nodoc:
|
295
|
-
type = @field_types[field]
|
296
|
-
@indexes[index_key(type)] ||= create_index_with(type)
|
297
|
-
end
|
298
|
-
|
299
|
-
def index_for_type(type) #:nodoc:
|
300
|
-
@indexes[index_key(type)] ||= create_index_with(type)
|
301
|
-
end
|
302
|
-
|
303
|
-
def index_key(type)
|
304
|
-
index_names[type] + type.to_s
|
305
|
-
end
|
306
|
-
|
307
|
-
def lucene_config(type) #:nodoc:
|
308
|
-
conf = Neo4j::Config[:lucene][type.to_sym]
|
309
|
-
raise "unknown lucene type #{type}" unless conf
|
310
|
-
conf
|
311
|
-
end
|
312
|
-
|
313
|
-
def create_index_with(type) #:nodoc:
|
314
|
-
db = Neo4j.started_db
|
315
|
-
index_config = lucene_config(type)
|
316
|
-
if @entity_type == :node
|
317
|
-
db.lucene.for_nodes(index_names[type], index_config)
|
318
|
-
else
|
319
|
-
db.lucene.for_relationships(index_names[type], index_config)
|
320
|
-
end
|
321
|
-
end
|
322
|
-
|
323
|
-
def index_names
|
324
|
-
@index_names ||= Hash.new do |hash, index_type|
|
325
|
-
default_filename = index_prefix + @indexer_for.to_s.gsub('::', '_')
|
326
|
-
hash.fetch(index_type) {"#{default_filename}_#{index_type}"}
|
327
|
-
end
|
328
|
-
end
|
329
|
-
|
330
|
-
protected
|
331
|
-
def index_prefix
|
332
|
-
return "" unless Neo4j.running?
|
333
|
-
return "" unless @indexer_for.respond_to?(:ref_node_for_class)
|
334
|
-
ref_node = @indexer_for.ref_node_for_class.wrapper
|
335
|
-
prefix = ref_node.send(:_index_prefix) if ref_node.respond_to?(:_index_prefix)
|
336
|
-
prefix ||= ref_node[:name] # To maintain backward compatiblity
|
337
|
-
prefix.blank? ? "" : prefix + "_"
|
338
|
-
end
|
339
|
-
end
|
340
|
-
end
|
341
|
-
end
|