lib/neo4j/index/lucene_query.rb in neo4j-1.0.0.beta.18 vs lib/neo4j/index/lucene_query.rb in neo4j-1.0.0.beta.19

- old
+ new

@@ -43,76 +43,111 @@ @index = index @query = query @decl_props = decl_props end + # Since we include the Ruby Enumerable mixin we need this method. def each hits.each { |n| yield n.wrapper } end + # Close hits + # + # 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. + # It's necessary to call it so that underlying indexes can dispose of allocated resources for this search result. + # You can however skip to call this method if you loop through the whole result, then close() will be called automatically. + # Even if you loop through the entire result and then call this method it will silently ignore any consequtive call (for convenience). + # + # This must be done according to the Neo4j Java Documentation: def close @hits.close if @hits end + # True if there is no search hits. def empty? hits.size == 0 end + # returns the n'th search item + # Does simply loop all search items till the n'th is found. + # def [](index) each_with_index {|node,i| break node if index == i} end + # Returns the number of search hits def size hits.size end - def hits + def hits #:nodoc: @hits ||= perform_query end - def between(lower, upper) + # Performs a range query + # Notice that if you don't specify a type when declaring a property a String range query will be performed. + # + def between(lower, upper, lower_incusive=false, upper_inclusive=false) raise "Expected a symbol. Syntax for range queries example: index(:weight).between(a,b)" unless Symbol === @query raise "Can't only do range queries on Neo4j::NodeMixin, Neo4j::Model, Neo4j::RelationshipMixin" unless @decl_props - type = @decl_props[@query] && @decl_props[@query][:type] - raise "Missing type declaration of property #{@query}. E.g. property :#{@query}, :type => Float; index :#{@query}" unless type - if type != String - raise "find(#{@query}).between(#{lower}, #{upper}) to allowed since #{lower} is not a Float or Fixnum" if lower === Float or lower === Fixnum - raise "find(#{@query}).between(#{lower}, #{upper}) to allowed since #{upper} is not a Float or Fixnum" if upper === Float or upper === Fixnum - @query = org.apache.lucene.search.NumericRangeQuery.new_double_range(@query.to_s, lower, upper, false, false) - else - raise "find(#{@query}).between(#{lower}, #{upper}) to allowed since #{lower} is not a String" if lower === String - raise "find(#{@query}).between(#{lower}, #{upper}) to allowed since #{upper} is not a String" if upper === String - @query = org.apache.lucene.search.TermRangeQuery.new(@query.to_s, lower, upper, false, false) - end + # check that we perform a range query on the same values as we have declared with the property :key, :type => ... + type = @decl_props[@query] && @decl_props[@query][:type] + raise "find(#{@query}).between(#{lower}, #{upper}): #{lower} not a #{type}" if type && !type === lower.class + raise "find(#{@query}).between(#{lower}, #{upper}): #{upper} not a #{type}" if type && !type === upper.class + + # Make it possible to convert those values + lower = TypeConverters.convert(lower) + upper = TypeConverters.convert(upper) + + @query = case lower + when Fixnum + org.apache.lucene.search.NumericRangeQuery.new_long_range(@query.to_s, lower, upper, lower_incusive, upper_inclusive) + when Float + org.apache.lucene.search.NumericRangeQuery.new_double_range(@query.to_s, lower, upper, lower_incusive, upper_inclusive) + else + org.apache.lucene.search.TermRangeQuery.new(@query.to_s, lower, upper, lower_incusive, upper_inclusive) + end self end + # Create a compound lucene query. + # + # ==== Parameters + # query2 :: the query that should be AND together + # + # ==== Example + # + # Person.find(:name=>'kalle').and(:age => 3) + # def and(query2) new_query = LuceneQuery.new(@index, @decl_props, query2) new_query.left_and_query = self new_query end + + # Sort descending the given fields. def desc(*fields) @order = fields.inject(@order || {}) { |memo, field| memo[field] = true; memo } self end + # Sort ascending the given fields. def asc(*fields) @order = fields.inject(@order || {}) { |memo, field| memo[field] = false; memo } self end - def build_and_query(query) + def build_and_query(query) #:nodoc: left_query = @left_and_query.build_query and_query = org.apache.lucene.search.BooleanQuery.new and_query.add(left_query, org.apache.lucene.search.BooleanClause::Occur::MUST) and_query.add(query, org.apache.lucene.search.BooleanClause::Occur::MUST) and_query end - def build_sort_query(query) + def build_sort_query(query) #:nodoc: java_sort_fields = @order.keys.inject([]) do |memo, field| decl_type = @decl_props && @decl_props[field] && @decl_props[field][:type] type = case when Float == decl_type org.apache.lucene.search.SortField::DOUBLE @@ -125,11 +160,11 @@ end sort = org.apache.lucene.search.Sort.new(*java_sort_fields) org.neo4j.index.impl.lucene.QueryContext.new(query).sort(sort) end - def build_hash_query(query) + def build_hash_query(query) #:nodoc: and_query = org.apache.lucene.search.BooleanQuery.new query.each_pair do |key, value| raise "Only String values valid in find(hash) got :#{key} => #{value} which is not a String" if !value.is_a?(String) && @decl_props[key] && @decl_props[key][:type] != String term = org.apache.lucene.index.Term.new(key.to_s, value.to_s) @@ -137,18 +172,18 @@ and_query.add(term_query, org.apache.lucene.search.BooleanClause::Occur::MUST) end and_query end - def build_query + def build_query #:nodoc: query = @query query = build_hash_query(query) if Hash === query query = build_and_query(query) if @left_and_query query = build_sort_query(query) if @order query end - def perform_query + def perform_query #:nodoc: @index.query(build_query) end end end end