From 20f65039f307858c5d9248740c8f836fbc7cff5d Mon Sep 17 00:00:00 2001 From: Durran Jordan Date: Tue, 6 Oct 2009 19:20:21 -0400 Subject: [PATCH] Updating sorting to new syntax --- lib/mongo/errors.rb | 3 + lib/mongo/message/query_message.rb | 27 +++---- lib/mongo/util/conversions.rb | 110 ++++++++++++++++++++++++++ test/test_conversions.rb | 121 +++++++++++++++++++++++++++++ 4 files changed, 243 insertions(+), 18 deletions(-) create mode 100644 lib/mongo/util/conversions.rb create mode 100644 test/test_conversions.rb diff --git a/lib/mongo/errors.rb b/lib/mongo/errors.rb index 60bd370..365c2ef 100644 --- a/lib/mongo/errors.rb +++ b/lib/mongo/errors.rb @@ -23,4 +23,7 @@ module Mongo # Raised when an invalid name is used. class InvalidName < RuntimeError; end + + # Raised when the client supplies an invalid values for a sorting. + class InvalidSortValueError < RuntimeError; end end diff --git a/lib/mongo/message/query_message.rb b/lib/mongo/message/query_message.rb index b53307a..8778605 100644 --- a/lib/mongo/message/query_message.rb +++ b/lib/mongo/message/query_message.rb @@ -16,11 +16,12 @@ require 'mongo/message/message' require 'mongo/message/opcodes' +require 'mongo/util/conversions' require 'mongo/util/ordered_hash' module Mongo - class QueryMessage < Message + include Mongo::Conversions attr_reader :query @@ -37,24 +38,14 @@ module Mongo sel = OrderedHash.new sel['query'] = query.selector if query.order_by && query.order_by.length > 0 - sel['orderby'] = case query.order_by - when String - {query.order_by => 1} - when Array - h = OrderedHash.new - query.order_by.each { |ob| - case ob - when String - h[ob] = 1 - when Hash # should have one entry; will handle all - ob.each { |k,v| h[k] = v } - else - raise "illegal query order_by value #{query.order_by.inspect}" - end - } - h + order_by = query.order_by + sel['orderby'] = case order_by + when String then string_as_sort_parameters(order_by) + when Symbol then symbol_as_sort_parameters(order_by) + when Array then array_as_sort_parameters(order_by) when Hash # Should be an ordered hash, but this message doesn't care - query.order_by + warn_if_deprecated(order_by) + order_by else raise "illegal order_by: is a #{query.order_by.class.name}, must be String, Array, Hash, or OrderedHash" end diff --git a/lib/mongo/util/conversions.rb b/lib/mongo/util/conversions.rb new file mode 100644 index 0000000..c75fc2d --- /dev/null +++ b/lib/mongo/util/conversions.rb @@ -0,0 +1,110 @@ +# -- +# Copyright (C) 2008-2009 10gen Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ++ +module Mongo #:nodoc: + + # Utility module to include when needing to convert certain types of + # objects to mongo-friendly parameters. + module Conversions + + ASCENDING = ["ascending", "asc", "1"] + DESCENDING = ["descending", "desc", "-1"] + + # Converts the supplied +Array+ to a +Hash+ to pass to mongo as + # sorting parameters. The returned +Hash+ will vary depending + # on whether the passed +Array+ is one or two dimensional. + # + # Example: + # + # *DEPRECATED + # + # array_as_sort_parameters(["field1", "field2"]) => + # { "field1" => "1", "field2" => "1" } + # + # *New Syntax: + # + # array_as_sort_parameters([["field1", :asc], ["field2", :desc]]) => + # { "field1" => 1, "field2" => -1} + def array_as_sort_parameters(value) + warn_if_deprecated(value) + order_by = OrderedHash.new + value.each do |param| + if (param.class.name == "String") + order_by[param] = 1 + else + order_by[param[0]] = sort_value(param[1]) unless param[1].nil? + end + end + order_by + end + + # Converts the supplied +String+ to a +Hash+ to pass to mongo as + # a sorting parameter with ascending order. If the +String+ + # is empty then an empty +Hash+ will be returned. + # + # Example: + # + # *DEPRECATED + # + # string_as_sort_parameters("field") => { "field" => 1 } + # string_as_sort_parameters("") => {} + def string_as_sort_parameters(value) + warn_if_deprecated(value) + return {} if value.empty? + { value => 1 } + end + + # Converts the supplied +Symbol+ to a +Hash+ to pass to mongo as + # a sorting parameter with ascending order. + # + # Example: + # + # *DEPRECATED + # + # symbol_as_sort_parameters(:field) => { "field" => 1 } + def symbol_as_sort_parameters(value) + warn_if_deprecated(value) + { value.to_s => 1 } + end + + # Converts the +String+, +Symbol+, or +Integer+ to the + # corresponding sort value in MongoDB. + # + # Valid conversions (case-insensitive): + # + # ascending, asc, :ascending, :asc, 1 => 1 + # descending, desc, :descending, :desc, -1 => -1 + # + # If the value is invalid then an error will be raised. + def sort_value(value) + val = value.to_s.downcase + return 1 if ASCENDING.include?(val) + return -1 if DESCENDING.include?(val) + raise InvalidSortValueError.new( + "#{self} was supplied as a sort value when acceptable values are: " + + "ascending, asc, :ascending, :asc, 1, descending, desc, :descending, :desc, -1.") + end + + # This is the method to call when the client needs to be warned of + # deprecation in the way sorting parameters are supplied. + def warn_if_deprecated(value) + unless value.is_a?(Array) && value.first.is_a?(Array) + warn("\nSorting has been deprecated in favor of a new syntax: \n" + + " :sort => [['field1', '(ascending|descending)'], ['field2', '(ascending|descending)']]") + end + end + + end +end \ No newline at end of file diff --git a/test/test_conversions.rb b/test/test_conversions.rb new file mode 100644 index 0000000..8d5a91f --- /dev/null +++ b/test/test_conversions.rb @@ -0,0 +1,121 @@ +$LOAD_PATH[0,0] = File.join(File.dirname(__FILE__), '..', 'lib') +require 'mongo/errors' +require 'mongo/util/conversions' +require 'mongo/util/ordered_hash' +require 'test/unit' + +class ConversionsTest < Test::Unit::TestCase + include Mongo::Conversions + + def test_array_as_sort_parameters_with_array_of_strings + params = array_as_sort_parameters(["field1", "field2"]) + assert_equal({ "field1" => 1, "field2" => 1 }, params) + end + + def test_array_as_sort_parameters_with_array_of_string_and_values + params = array_as_sort_parameters([["field1", :asc], ["field2", :desc]]) + assert_equal({ "field1" => 1, "field2" => -1 }, params) + end + + def test_string_as_sort_parameters_with_string + params = string_as_sort_parameters("field") + assert_equal({ "field" => 1 }, params) + end + + def test_string_as_sort_parameters_with_empty_string + params = string_as_sort_parameters("") + assert_equal({}, params) + end + + def test_symbol_as_sort_parameters + params = symbol_as_sort_parameters(:field) + assert_equal({ "field" => 1 }, params) + end + + def test_sort_value_when_value_is_one + assert_equal 1, sort_value(1) + end + + def test_sort_value_when_value_is_one_as_a_string + assert_equal 1, sort_value("1") + end + + def test_sort_value_when_value_is_negative_one + assert_equal -1, sort_value(-1) + end + + def test_sort_value_when_value_is_negative_one_as_a_string + assert_equal -1, sort_value("-1") + end + + def test_sort_value_when_value_is_ascending + assert_equal 1, sort_value("ascending") + end + + def test_sort_value_when_value_is_asc + assert_equal 1, sort_value("asc") + end + + def test_sort_value_when_value_is_uppercase_ascending + assert_equal 1, sort_value("ASCENDING") + end + + def test_sort_value_when_value_is_uppercase_asc + assert_equal 1, sort_value("ASC") + end + + def test_sort_value_when_value_is_symbol_ascending + assert_equal 1, sort_value(:ascending) + end + + def test_sort_value_when_value_is_symbol_asc + assert_equal 1, sort_value(:asc) + end + + def test_sort_value_when_value_is_symbol_uppercase_ascending + assert_equal 1, sort_value(:ASCENDING) + end + + def test_sort_value_when_value_is_symbol_uppercase_asc + assert_equal 1, sort_value(:ASC) + end + + def test_sort_value_when_value_is_descending + assert_equal -1, sort_value("descending") + end + + def test_sort_value_when_value_is_desc + assert_equal -1, sort_value("desc") + end + + def test_sort_value_when_value_is_uppercase_descending + assert_equal -1, sort_value("DESCENDING") + end + + def test_sort_value_when_value_is_uppercase_desc + assert_equal -1, sort_value("DESC") + end + + def test_sort_value_when_value_is_symbol_descending + assert_equal -1, sort_value(:descending) + end + + def test_sort_value_when_value_is_symbol_desc + assert_equal -1, sort_value(:desc) + end + + def test_sort_value_when_value_is_uppercase_symbol_descending + assert_equal -1, sort_value(:DESCENDING) + end + + def test_sort_value_when_value_is_uppercase_symbol_desc + assert_equal -1, sort_value(:DESC) + end + + def test_sort_value_when_value_is_invalid + assert_raise Mongo::InvalidSortValueError do + sort_value(2) + end + end + +end \ No newline at end of file