neo4j-core 0.0.1-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. data/Gemfile +27 -0
  2. data/README.rdoc +27 -0
  3. data/config/neo4j/config.yml +102 -0
  4. data/lib/db/active_tx_log +1 -0
  5. data/lib/db/index/lucene-store.db +0 -0
  6. data/lib/db/index/lucene.log.1 +0 -0
  7. data/lib/db/index/lucene.log.active +0 -0
  8. data/lib/db/lock +0 -0
  9. data/lib/db/messages.log +530 -0
  10. data/lib/db/neostore +0 -0
  11. data/lib/db/neostore.id +0 -0
  12. data/lib/db/neostore.nodestore.db +0 -0
  13. data/lib/db/neostore.nodestore.db.id +0 -0
  14. data/lib/db/neostore.propertystore.db +0 -0
  15. data/lib/db/neostore.propertystore.db.arrays +0 -0
  16. data/lib/db/neostore.propertystore.db.arrays.id +0 -0
  17. data/lib/db/neostore.propertystore.db.id +0 -0
  18. data/lib/db/neostore.propertystore.db.index +0 -0
  19. data/lib/db/neostore.propertystore.db.index.id +0 -0
  20. data/lib/db/neostore.propertystore.db.index.keys +0 -0
  21. data/lib/db/neostore.propertystore.db.index.keys.id +0 -0
  22. data/lib/db/neostore.propertystore.db.strings +0 -0
  23. data/lib/db/neostore.propertystore.db.strings.id +0 -0
  24. data/lib/db/neostore.relationshipstore.db +0 -0
  25. data/lib/db/neostore.relationshipstore.db.id +0 -0
  26. data/lib/db/neostore.relationshiptypestore.db +0 -0
  27. data/lib/db/neostore.relationshiptypestore.db.id +0 -0
  28. data/lib/db/neostore.relationshiptypestore.db.names +0 -0
  29. data/lib/db/neostore.relationshiptypestore.db.names.id +0 -0
  30. data/lib/db/nioneo_logical.log.2 +0 -0
  31. data/lib/db/nioneo_logical.log.active +0 -0
  32. data/lib/db/tm_tx_log.1 +0 -0
  33. data/lib/neo4j/config.rb +139 -0
  34. data/lib/neo4j/cypher.rb +156 -0
  35. data/lib/neo4j/neo4j.rb +244 -0
  36. data/lib/neo4j/neo4j.rb~ +214 -0
  37. data/lib/neo4j/node.rb +39 -0
  38. data/lib/neo4j/relationship.rb +61 -0
  39. data/lib/neo4j/transaction.rb +86 -0
  40. data/lib/neo4j/type_converters/type_converters.rb +287 -0
  41. data/lib/neo4j-core/cypher/cypher.rb +867 -0
  42. data/lib/neo4j-core/cypher/result_wrapper.rb +39 -0
  43. data/lib/neo4j-core/database.rb +191 -0
  44. data/lib/neo4j-core/equal/equal.rb +23 -0
  45. data/lib/neo4j-core/event_handler.rb +265 -0
  46. data/lib/neo4j-core/index/class_methods.rb +117 -0
  47. data/lib/neo4j-core/index/index.rb +36 -0
  48. data/lib/neo4j-core/index/index_config.rb +112 -0
  49. data/lib/neo4j-core/index/indexer.rb +243 -0
  50. data/lib/neo4j-core/index/indexer_registry.rb +55 -0
  51. data/lib/neo4j-core/index/lucene_query.rb +264 -0
  52. data/lib/neo4j-core/lazy_map.rb +21 -0
  53. data/lib/neo4j-core/node/class_methods.rb +77 -0
  54. data/lib/neo4j-core/node/node.rb +47 -0
  55. data/lib/neo4j-core/property/property.rb +94 -0
  56. data/lib/neo4j-core/relationship/class_methods.rb +80 -0
  57. data/lib/neo4j-core/relationship/relationship.rb +97 -0
  58. data/lib/neo4j-core/relationship_set.rb +61 -0
  59. data/lib/neo4j-core/rels/rels.rb +147 -0
  60. data/lib/neo4j-core/rels/traverser.rb +99 -0
  61. data/lib/neo4j-core/to_java.rb +51 -0
  62. data/lib/neo4j-core/traversal/evaluator.rb +36 -0
  63. data/lib/neo4j-core/traversal/filter_predicate.rb +30 -0
  64. data/lib/neo4j-core/traversal/prune_evaluator.rb +20 -0
  65. data/lib/neo4j-core/traversal/rel_expander.rb +35 -0
  66. data/lib/neo4j-core/traversal/traversal.rb +130 -0
  67. data/lib/neo4j-core/traversal/traverser.rb +295 -0
  68. data/lib/neo4j-core/version.rb +5 -0
  69. data/lib/neo4j-core.rb +64 -0
  70. data/neo4j-core.gemspec +31 -0
  71. metadata +145 -0
@@ -0,0 +1,112 @@
1
+ module Neo4j
2
+ module Core
3
+ module Index
4
+
5
+ # Responsible for holding the configuration for one index
6
+ # Is used in a DSL to configure the index.
7
+ class IndexConfig
8
+ attr_reader :_trigger_on, :_index_names, :entity_type
9
+
10
+ def initialize(entity_type)
11
+ @entity_type = entity_type
12
+ @index_type = {}
13
+ @numeric_types = []
14
+ @_trigger_on = {}
15
+ @decl_type = {}
16
+ end
17
+
18
+ # Specifies which property and values the index should be triggered on.
19
+ # Used in the Index DSL.
20
+ # @see Neo4j::Core::Index::ClassMethods#node_indexer
21
+ def trigger_on(hash)
22
+ merge_and_to_string(@_trigger_on, hash)
23
+ end
24
+
25
+ # Specifies the types of the properties being indexed so that the proper sort order can be applied.
26
+ # Used in the Index DSL.
27
+ # @see Neo4j::Core::Index::ClassMethods#node_indexer
28
+ def decl_type(prop_and_type_hash)
29
+ merge_and_to_string(@decl_type, prop_and_type_hash)
30
+ end
31
+
32
+ # Specifies an index with configuration
33
+ # Used in the Index DSL.
34
+ # @param [Hash] args the field and its configuration TODO
35
+ # @see Neo4j::Core::Index::ClassMethods#index
36
+ def index(args)
37
+ conf = args.last.kind_of?(Hash) ? args.pop : {}
38
+
39
+ args.uniq.each do |field|
40
+ @index_type[field.to_s] = conf[:type] || :exact
41
+ @numeric_types << field.to_s if conf[:numeric] == true
42
+ end
43
+ end
44
+
45
+ # @return [Class] the specified type of the property or String
46
+ # @see #decl_type
47
+ def decl_type_on(prop)
48
+ @decl_type[prop] || String
49
+ end
50
+
51
+ # @return [true, false] if the props can/should trigger an index operation
52
+ def trigger_on?(props)
53
+ @_trigger_on.each_pair { |k, v| break true if (a = props[k]) && (v.include?(a)) } == true
54
+ end
55
+
56
+ # @return the index name for the lucene index given a type
57
+ def index_name_for_type(type)
58
+ _prefix_index_name + @_index_names[type]
59
+ end
60
+
61
+ # Defines the
62
+ def prefix_index_name(&block)
63
+ @prefix_index_name_block = block
64
+ end
65
+
66
+ def _prefix_index_name
67
+ @prefix_index_name_block.nil? ? "" : @prefix_index_name_block.call
68
+ end
69
+
70
+ def index_names(hash)
71
+ @_index_names = hash
72
+ end
73
+
74
+
75
+ def rm_index_config
76
+ @index_type = {}
77
+ @numeric_types = []
78
+ end
79
+
80
+ def index_type(field)
81
+ @index_type[field.to_s]
82
+ end
83
+
84
+ def has_index_type?(type)
85
+ @index_type.values.include?(type)
86
+ end
87
+
88
+ def fields
89
+ @index_type.keys
90
+ end
91
+
92
+ def index?(field)
93
+ @index_type.include?(field.to_s)
94
+ end
95
+
96
+ def numeric?(field)
97
+ return true if @numeric_types.include?(field)
98
+ # TODO callback to numeric dsl for Neo4j::NodeMixin decl_props check
99
+ end
100
+
101
+ private
102
+
103
+ def merge_and_to_string(existing_hash, new_hash)
104
+ new_hash.each_pair do |k, v|
105
+ existing_hash[k.to_s] ||= Set.new
106
+ existing_hash[k.to_s].merge(v.is_a?(Array)? v : [v])
107
+ end
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,243 @@
1
+ module Neo4j
2
+ module Core
3
+ module Index
4
+
5
+ # This class is delegated from the Neo4j::Core::Index::ClassMethod
6
+ # @see Neo4j::Core::Index::ClassMethods
7
+ class Indexer
8
+ # @return [Neo4j::Core::Index::IndexConfig]
9
+ attr_reader :config
10
+
11
+ def initialize(config)
12
+ @config = config
13
+ @indexes = {} # key = type, value = java neo4j index
14
+ # to enable subclass indexing to work properly, store a list of parent indexers and
15
+ # whenever an operation is performed on this one, perform it on all
16
+ end
17
+
18
+
19
+ def to_s
20
+ "Indexer @#{object_id} index on: [#{@config.fields.map{|f| @config.numeric?(f)? "#{f} (numeric)" : f}.join(', ')}]"
21
+ end
22
+
23
+ # Add an index on a field so that it will be automatically updated by neo4j transactional events.
24
+ #
25
+ # The index method takes an optional configuration hash which allows you to:
26
+ #
27
+ # @example Add an index on an a property
28
+ #
29
+ # class Person
30
+ # include Neo4j::NodeMixin
31
+ # index :name
32
+ # end
33
+ #
34
+ # When the property name is changed/deleted or the node created it will keep the lucene index in sync.
35
+ # You can then perform a lucene query like this: Person.find('name: andreas')
36
+ #
37
+ # @example Add index on other nodes.
38
+ #
39
+ # class Person
40
+ # include Neo4j::NodeMixin
41
+ # has_n(:friends).to(Contact)
42
+ # has_n(:known_by).from(:friends)
43
+ # index :user_id, :via => :known_by
44
+ # end
45
+ #
46
+ # Notice that you *must* specify an incoming relationship with the via key, as shown above.
47
+ # In the example above an index <tt>user_id</tt> will be added to all Person nodes which has a <tt>friends</tt> relationship
48
+ # that person with that user_id. This allows you to do lucene queries on your friends properties.
49
+ #
50
+ # @example Set the type value to index
51
+ #
52
+ # class Person
53
+ # include Neo4j::NodeMixin
54
+ # property :height, :weight, :type => Float
55
+ # index :height, :weight
56
+ # end
57
+ #
58
+ # By default all values will be indexed as Strings.
59
+ # If you want for example to do a numerical range query you must tell Neo4j.rb to index it as a numeric value.
60
+ # You do that with the key <tt>type</tt> on the property.
61
+ #
62
+ # Supported values for <tt>:type</tt> is <tt>String</tt>, <tt>Float</tt>, <tt>Date</tt>, <tt>DateTime</tt> and <tt>Fixnum</tt>
63
+ #
64
+ # === For more information
65
+ #
66
+ # @see Neo4j::Core::Index::LuceneQuery
67
+ # @see #find
68
+ #
69
+ def index(*args)
70
+ @config.index(args)
71
+ end
72
+
73
+ # @return [true,false] if there is an index on the given field.
74
+ def index?(field)
75
+ @config.index?(field)
76
+ end
77
+
78
+ # @return [true,false] if the
79
+ def trigger_on?(props)
80
+ @config.trigger_on?(props)
81
+ end
82
+
83
+ # @return [Symbol] the type of index for the given field (e.g. :exact or :fulltext)
84
+ def index_type(field)
85
+ @config.index_type(field)
86
+ end
87
+
88
+ # @return [true,false] if there is an index of the given type defined.
89
+ def has_index_type?(type)
90
+ @config.has_index_type?(type)
91
+ end
92
+
93
+ # Adds an index on the given entity
94
+ # This is normally not needed since you can instead declare an index which will automatically keep
95
+ # the lucene index in sync.
96
+ # @see #index
97
+ def add_index(entity, field, value)
98
+ return false unless index?(field)
99
+ conv_value = indexed_value_for(field, value)
100
+ index = index_for_field(field.to_s)
101
+ index.add(entity, field, conv_value)
102
+ end
103
+
104
+
105
+ # Removes an index on the given entity
106
+ # This is normally not needed since you can instead declare an index which will automatically keep
107
+ # the lucene index in sync.
108
+ # @see #index
109
+ def rm_index(entity, field, value)
110
+ return false unless index?(field)
111
+ index_for_field(field).remove(entity, field, value)
112
+ end
113
+
114
+ # Performs a Lucene Query.
115
+ #
116
+ # In order to use this you have to declare an index on the fields first, see #index.
117
+ # Notice that you should close the lucene query after the query has been executed.
118
+ # You can do that either by provide an block or calling the Neo4j::Core::Index::LuceneQuery#close
119
+ # method. When performing queries from Ruby on Rails you do not need this since it will be automatically closed
120
+ # (by Rack).
121
+ #
122
+ # @example with a block
123
+ #
124
+ # Person.find('name: kalle') {|query| puts "#{[*query].join(', )"}
125
+ #
126
+ # @example
127
+ #
128
+ # query = Person.find('name: kalle')
129
+ # puts "First item #{query.first}"
130
+ # query.close
131
+ #
132
+ # @return [Neo4j::Core::Index::LuceneQuery] a query object
133
+ def find(query, params = {})
134
+ index = index_for_type(params[:type] || :exact)
135
+ if query.is_a?(Hash) && (query.include?(:conditions) || query.include?(:sort))
136
+ params.merge! query.reject { |k, _| k == :conditions }
137
+ query.delete(:sort)
138
+ query = query.delete(:conditions) if query.include?(:conditions)
139
+ end
140
+ query = (params[:wrapped].nil? || params[:wrapped]) ? LuceneQuery.new(index, @config, query, params) : index.query(query)
141
+
142
+ if block_given?
143
+ begin
144
+ ret = yield query
145
+ ensure
146
+ query.close
147
+ end
148
+ ret
149
+ else
150
+ query
151
+ end
152
+ end
153
+
154
+ # Delete all index configuration. No more automatic indexing will be performed
155
+ def rm_index_config
156
+ @config.rm_index_config
157
+ end
158
+
159
+ # delete the index, if no type is provided clear all types of indexes
160
+ def rm_index_type(type=nil)
161
+ if type
162
+ key = @config.index_name_for_type(type)
163
+ @indexes[key] && @indexes[key].delete
164
+ @indexes[key] = nil
165
+ else
166
+ @indexes.each_value { |index| index.delete }
167
+ @indexes.clear
168
+ end
169
+ end
170
+
171
+ # Called when the neo4j shutdown in order to release references to indexes
172
+ def on_neo4j_shutdown
173
+ @indexes.clear
174
+ end
175
+
176
+ # Called from the event handler when a new node or relationships is about to be committed.
177
+ def update_index_on(node, field, old_val, new_val)
178
+ if index?(field)
179
+ rm_index(node, field, old_val) if old_val
180
+ add_index(node, field, new_val) if new_val
181
+ end
182
+ end
183
+
184
+ # Called from the event handler when deleting a property
185
+ def remove_index_on(node, old_props)
186
+ @config.fields.each { |field| rm_index(node, field, old_props[field]) if old_props[field] }
187
+ end
188
+
189
+ # Creates a wrapped ValueContext for the given value. Checks if it's numeric value in the configuration.
190
+ # @return [Java::OrgNeo4jIndexLucene::ValueContext] a wrapped neo4j lucene value context
191
+ def indexed_value_for(field, value)
192
+ if @config.numeric?(field)
193
+ Java::OrgNeo4jIndexLucene::ValueContext.new(value).indexNumeric
194
+ else
195
+ Java::OrgNeo4jIndexLucene::ValueContext.new(value)
196
+ end
197
+ end
198
+
199
+ # @return [Java::OrgNeo4jGraphdb::Index] for the given field
200
+ def index_for_field(field)
201
+ type = @config.index_type(field)
202
+ index_name = index_name_for_type(type)
203
+ @indexes[index_name] ||= create_index_with(type, index_name)
204
+ end
205
+
206
+ # @return [Java::OrgNeo4jGraphdb::Index] for the given index type
207
+ def index_for_type(type)
208
+ index_name = index_name_for_type(type)
209
+ @indexes[index_name] ||= create_index_with(type, index_name)
210
+ end
211
+
212
+ # @return [String] the name of the index which are stored on the filesystem
213
+ def index_name_for_type(type)
214
+ @config.index_name_for_type(type)
215
+ end
216
+
217
+ # @return [Hash] the lucene config for the given index type
218
+ def lucene_config(type)
219
+ conf = Neo4j::Config[:lucene][type.to_s]
220
+ raise "unknown lucene type #{type}" unless conf
221
+ conf
222
+ end
223
+
224
+ # Creates a new lucene index using the lucene configuration for the given index_name
225
+ #
226
+ # @param [:node, :relationship] type relationship or node index
227
+ # @param [String] index_name the (file) name of the index
228
+ # @return [Java::OrgNeo4jGraphdb::Index] for the given index type
229
+ def create_index_with(type, index_name)
230
+ db = Neo4j.started_db
231
+ index_config = lucene_config(type)
232
+ if config.entity_type == :node
233
+ db.lucene.for_nodes(index_name, index_config)
234
+ else
235
+ db.lucene.for_relationships(index_name, index_config)
236
+ end
237
+ end
238
+
239
+
240
+ end
241
+ end
242
+ end
243
+ end
@@ -0,0 +1,55 @@
1
+ module Neo4j
2
+ module Core
3
+ module Index
4
+ class IndexerRegistry
5
+ def initialize
6
+ @indexers = []
7
+ end
8
+
9
+ def delete_all_indexes
10
+ @indexers.each { |i| i.rm_index_type }
11
+ end
12
+
13
+ def register(indexer)
14
+ @indexers << indexer
15
+ indexer
16
+ end
17
+
18
+ def indexers_for(props)
19
+ Enumerator.new(self, :each_indexer, props)
20
+ end
21
+
22
+ def each_indexer(props)
23
+ @indexers.each { |i| yield i if i.trigger_on?(props) }
24
+ end
25
+
26
+ def on_node_deleted(node, old_props, *)
27
+ each_indexer(old_props) { |indexer| indexer.remove_index_on(node, old_props) }
28
+ end
29
+
30
+ def on_property_changed(node, field, old_val, new_val)
31
+ each_indexer(node) { |indexer| indexer.update_index_on(node, field, old_val, new_val) }
32
+ end
33
+
34
+ def on_relationship_deleted(relationship, old_props, *)
35
+ each_indexer(old_props) { |indexer| indexer.remove_index_on(relationship, old_props) }
36
+ end
37
+
38
+ def on_rel_property_changed(rel, field, old_val, new_val)
39
+ each_indexer(rel) { |indexer| indexer.update_index_on(rel, field, old_val, new_val) }
40
+ end
41
+
42
+ def on_neo4j_shutdown(*)
43
+ @indexers.each { |indexer| indexer.on_neo4j_shutdown }
44
+ end
45
+
46
+ class << self
47
+ def instance
48
+ @@instance ||= IndexerRegistry.new
49
+ end
50
+ end
51
+ end
52
+ Neo4j.unstarted_db.event_handler.add(IndexerRegistry.instance) unless Neo4j.read_only?
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,264 @@
1
+ module Neo4j
2
+ module Core
3
+
4
+ module Index
5
+ # This object is returned when you call the #find method on the Node, Relationship.
6
+ # The actual query is not executed until the first item is requested.
7
+ #
8
+ # You can perform a query in many different ways:
9
+ #
10
+ # @example By Hash
11
+ #
12
+ # Person.find(:name => 'foo', :age => 3)
13
+ #
14
+ # @example By Range
15
+ #
16
+ # Person.find(:age).between(15,35)
17
+ #
18
+ # @example By Lucene Query Syntax
19
+ #
20
+ # Car.find('wheels:"4" AND colour: "blue")
21
+ #
22
+ # For more information about the syntax see http://lucene.apache.org/java/3_0_2/queryparsersyntax.html
23
+ #
24
+ # @example: By Compound Queries
25
+ #
26
+ # Vehicle.find(:weight).between(5.0, 100000.0).and(:name).between('a', 'd')
27
+ #
28
+ # You can combine several queries by <tt>AND</tt>ing those together.
29
+ #
30
+ # @see Neo4j::Core::Index::Indexer#index
31
+ # @see Neo4j::Core::Index::Indexer#find - which returns an LuceneQuery
32
+ #
33
+ class LuceneQuery
34
+ include Enumerable
35
+ attr_accessor :left_and_query, :left_or_query, :right_not_query, :query, :order
36
+
37
+ def initialize(index, index_config, query, params={})
38
+ @index = index
39
+ @query = query
40
+ @index_config = index_config
41
+ @params = params
42
+
43
+ if params.include?(:sort)
44
+ @order = {}
45
+ params[:sort].each_pair { |k, v| @order[k] = (v == :desc) }
46
+ end
47
+ end
48
+
49
+ # Implements the Ruby +Enumerable+ interface
50
+ def each
51
+ hits.each { |x| yield x.wrapper }
52
+ end
53
+
54
+ # Close hits
55
+ #
56
+ # Closes the underlying search result. This method should be called whenever you've got what you wanted from the result and won't use it anymore.
57
+ # It's necessary to call it so that underlying indexes can dispose of allocated resources for this search result.
58
+ # You can however skip to call this method if you loop through the whole result, then close() will be called automatically.
59
+ # Even if you loop through the entire result and then call this method it will silently ignore any consequtive call (for convenience).
60
+ #
61
+ # This must be done according to the Neo4j Java Documentation:
62
+ def close
63
+ @hits.close if @hits
64
+ @hits = nil
65
+ end
66
+
67
+ # @return [true, false] True if there is no search hits.
68
+ def empty?
69
+ hits.size == 0
70
+ end
71
+
72
+ # Does simply loop all search items till the n'th is found.
73
+ # @return[Neo4j::Node, Neo4j::Relationship] the n'th search item
74
+ def [](index)
75
+ i = 0
76
+ each { |x| return x if i == index; i += 1 }
77
+ nil # out of index
78
+ end
79
+
80
+ # @return[Fixnum] the number of search hits
81
+ def size
82
+ hits.size
83
+ end
84
+
85
+ # Performs a range query
86
+ # Notice that if you don't specify a type when declaring a property a String range query will be performed.
87
+ #
88
+ def between(lower, upper, lower_incusive=false, upper_inclusive=false)
89
+ raise "Expected a symbol. Syntax for range queries example: index(:weight).between(a,b)" unless Symbol === @query
90
+ # raise "Can't only do range queries on Neo4j::NodeMixin, Neo4j::Model, Neo4j::RelationshipMixin" unless @decl_props
91
+ # check that we perform a range query on the same values as we have declared with the property :key, :type => ...
92
+ type = @index_config.decl_type_on(@query)
93
+ raise "find(#{@query}).between(#{lower}, #{upper}): #{lower} not a #{type}" if type && !type === lower.class
94
+ raise "find(#{@query}).between(#{lower}, #{upper}): #{upper} not a #{type}" if type && !type === upper.class
95
+
96
+ # Make it possible to convert those values
97
+ @query = range_query(@query, lower, upper, lower_incusive, upper_inclusive)
98
+ self
99
+ end
100
+
101
+ def range_query(field, lower, upper, lower_incusive, upper_inclusive)
102
+ lower = TypeConverters.convert(lower)
103
+ upper = TypeConverters.convert(upper)
104
+
105
+ case lower
106
+ when Fixnum
107
+ Java::OrgApacheLuceneSearch::NumericRangeQuery.new_long_range(field.to_s, lower, upper, lower_incusive, upper_inclusive)
108
+ when Float
109
+ Java::OrgApacheLuceneSearch::NumericRangeQuery.new_double_range(field.to_s, lower, upper, lower_incusive, upper_inclusive)
110
+ else
111
+ Java::OrgApacheLuceneSearch::TermRangeQuery.new(field.to_s, lower, upper, lower_incusive, upper_inclusive)
112
+ end
113
+ end
114
+
115
+
116
+ # Create a compound lucene query.
117
+ #
118
+ # @param [String] query2 the query that should be AND together
119
+ # @return [Neo4j::Core::Index::LuceneQuery] a new query object
120
+ #
121
+ # @example
122
+ #
123
+ # Person.find(:name=>'kalle').and(:age => 3)
124
+ #
125
+ def and(query2)
126
+ LuceneQuery.new(@index, @index_config, query2).tap { |new_query| new_query.left_and_query = self }
127
+ end
128
+
129
+ # Create an OR lucene query.
130
+ #
131
+ # @param [String] query2 the query that should be OR together
132
+ # @return [Neo4j::Core::Index::LuceneQuery] a new query object
133
+ #
134
+ # @example
135
+ #
136
+ # Person.find(:name=>'kalle').or(:age => 3)
137
+ #
138
+ def or(query2)
139
+ LuceneQuery.new(@index, @index_config, query2).tap { |new_query| new_query.left_or_query = self }
140
+ end
141
+
142
+ # Create a NOT lucene query.
143
+ #
144
+ # @param [String] query2 the query that should exclude matching results
145
+ # @return [Neo4j::Core::Index::LuceneQuery] a new query object
146
+ #
147
+ # @example
148
+ #
149
+ # Person.find(:age => 3).not(:name=>'kalle')
150
+ #
151
+ def not(query2)
152
+ LuceneQuery.new(@index, @index_config, query2).tap { |new_query| new_query.right_not_query = self }
153
+ end
154
+
155
+
156
+ # Sort descending the given fields.
157
+ # @param [Symbol] fields it should sort
158
+ def desc(*fields)
159
+ @order = fields.inject(@order || {}) { |memo, field| memo[field] = true; memo }
160
+ self
161
+ end
162
+
163
+ # Sort ascending the given fields.
164
+ # @param [Symbol] fields it should sort
165
+ def asc(*fields)
166
+ @order = fields.inject(@order || {}) { |memo, field| memo[field] = false; memo }
167
+ self
168
+ end
169
+
170
+ protected
171
+
172
+ def build_and_query(query)
173
+ build_composite_query(@left_and_query.build_query, query, Java::OrgApacheLuceneSearch::BooleanClause::Occur::MUST)
174
+ end
175
+
176
+ def build_or_query(query)
177
+ build_composite_query(@left_or_query.build_query, query, Java::OrgApacheLuceneSearch::BooleanClause::Occur::SHOULD)
178
+ end
179
+
180
+ def build_not_query(query)
181
+ right_query = @right_not_query.build_query
182
+ composite_query = Java::OrgApacheLuceneSearch::BooleanQuery.new
183
+ composite_query.add(query, Java::OrgApacheLuceneSearch::BooleanClause::Occur::MUST_NOT)
184
+ composite_query.add(right_query, Java::OrgApacheLuceneSearch::BooleanClause::Occur::MUST)
185
+ composite_query
186
+ end
187
+
188
+ def build_composite_query(left_query, right_query, operator) #:nodoc:
189
+ composite_query = Java::OrgApacheLuceneSearch::BooleanQuery.new
190
+ version = Java::OrgApacheLuceneUtil::Version::LUCENE_35
191
+ analyzer = org.apache.lucene.analysis.standard::StandardAnalyzer.new(version)
192
+ parser = Java::org.apache.lucene.queryParser.QueryParser.new(version,'name', analyzer )
193
+
194
+ left_query = parser.parse(left_query) if left_query.is_a?(String)
195
+ right_query = parser.parse(right_query) if right_query.is_a?(String)
196
+
197
+ composite_query.add(left_query, operator)
198
+ composite_query.add(right_query, operator)
199
+ composite_query
200
+ end
201
+
202
+ def build_sort_query(query) #:nodoc:
203
+ java_sort_fields = @order.keys.inject([]) do |memo, field|
204
+ decl_type = @index_config.decl_type_on(field)
205
+ type = case
206
+ when Float == decl_type
207
+ Java::OrgApacheLuceneSearch::SortField::DOUBLE
208
+ when Fixnum == decl_type || DateTime == decl_type || Date == decl_type || Time == decl_type
209
+ Java::OrgApacheLuceneSearch::SortField::LONG
210
+ else
211
+ Java::OrgApacheLuceneSearch::SortField::STRING
212
+ end
213
+ memo << Java::OrgApacheLuceneSearch::SortField.new(field.to_s, type, @order[field])
214
+ end
215
+ sort = Java::OrgApacheLuceneSearch::Sort.new(*java_sort_fields)
216
+ Java::OrgNeo4jIndexLucene::QueryContext.new(query).sort(sort)
217
+ end
218
+
219
+ def build_hash_query(query) #:nodoc:
220
+ and_query = Java::OrgApacheLuceneSearch::BooleanQuery.new
221
+
222
+ query.each_pair do |key, value|
223
+ type = @index_config && @index_config[key.to_sym] && @index_config[key.to_sym][:type]
224
+ if !type.nil? && type != String
225
+ if Range === value
226
+ and_query.add(range_query(key, value.first, value.last, true, !value.exclude_end?), Java::OrgApacheLuceneIndex::BooleanClause::Occur::MUST)
227
+ else
228
+ and_query.add(range_query(key, value, value, true, true), Java::OrgApacheLuceneIndex::BooleanClause::Occur::MUST)
229
+ end
230
+ else
231
+ conv_value = type ? TypeConverters.convert(value) : value.to_s
232
+ term = Java::OrgApacheLuceneIndex::Term.new(key.to_s, conv_value)
233
+ term_query = Java::OrgApacheLuceneIndex::TermQuery.new(term)
234
+ and_query.add(term_query, Java::OrgApacheLuceneIndex::BooleanClause::Occur::MUST)
235
+ end
236
+ end
237
+ and_query
238
+ end
239
+
240
+ def build_query #:nodoc:
241
+ query = @query
242
+ query = build_hash_query(query) if Hash === query
243
+ query = build_and_query(query) if @left_and_query
244
+ query = build_or_query(query) if @left_or_query
245
+ query = build_not_query(query) if @right_not_query
246
+ query = build_sort_query(query) if @order
247
+ query
248
+ end
249
+
250
+ def perform_query #:nodoc:
251
+ @index.query(build_query)
252
+ end
253
+
254
+
255
+ def hits #:nodoc:
256
+ close
257
+ @hits = perform_query
258
+ end
259
+
260
+
261
+ end
262
+ end
263
+ end
264
+ end
@@ -0,0 +1,21 @@
1
+ module Neo4j
2
+ module Core
3
+ # A simple implementation of lazy map
4
+ # @notice since we must support ruby 1.8.7 we can't use the fancy Enumerator block constructor for this.
5
+ # @private
6
+ class LazyMap
7
+ include Enumerable
8
+
9
+ def initialize(stuff, &map_block)
10
+ @stuff = stuff
11
+ @map_block = map_block
12
+ end
13
+
14
+ def each
15
+ @stuff.each { |x| yield @map_block.call(x) }
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end