enabled keyf support for Collection#group
This commit is contained in:
parent
eaa12e2461
commit
c15e8c2d7d
@ -339,9 +339,9 @@ module Mongo
|
|||||||
# Performs a group query, similar to the 'SQL GROUP BY' operation.
|
# Performs a group query, similar to the 'SQL GROUP BY' operation.
|
||||||
# Returns an array of grouped items.
|
# Returns an array of grouped items.
|
||||||
#
|
#
|
||||||
# :keys :: an array of fields to group by
|
# :key :: either 1) an array of fields to group by, 2) a javascript function to generate
|
||||||
# :condition :: specification of rows to be considered (as a 'find'
|
# the key object, or 3) nil.
|
||||||
# query specification)
|
# :condition :: an optional document specifying a query to limit the documents over which group is run.
|
||||||
# :initial :: initial value of the aggregation counter object
|
# :initial :: initial value of the aggregation counter object
|
||||||
# :reduce :: aggregation function as a JavaScript string
|
# :reduce :: aggregation function as a JavaScript string
|
||||||
# :finalize :: optional. a JavaScript function that receives and modifies
|
# :finalize :: optional. a JavaScript function that receives and modifies
|
||||||
@ -350,26 +350,34 @@ module Mongo
|
|||||||
# :command :: if true, run the group as a command instead of in an
|
# :command :: if true, run the group as a command instead of in an
|
||||||
# eval - it is likely that this option will eventually be
|
# eval - it is likely that this option will eventually be
|
||||||
# deprecated and all groups will be run as commands
|
# deprecated and all groups will be run as commands
|
||||||
def group(keys, condition, initial, reduce, command=false, finalize=nil)
|
def group(key, condition, initial, reduce, command=false, finalize=nil)
|
||||||
|
|
||||||
if command
|
if command
|
||||||
hash = {}
|
|
||||||
keys.each do |k|
|
|
||||||
hash[k] = 1
|
|
||||||
end
|
|
||||||
|
|
||||||
reduce = Code.new(reduce) unless reduce.is_a?(Code)
|
reduce = Code.new(reduce) unless reduce.is_a?(Code)
|
||||||
|
|
||||||
group_command = {
|
group_command = {
|
||||||
"group" => {
|
"group" => {
|
||||||
"ns" => @name,
|
"ns" => @name,
|
||||||
"$reduce" => reduce,
|
"$reduce" => reduce,
|
||||||
"key" => hash,
|
|
||||||
"cond" => condition,
|
"cond" => condition,
|
||||||
"initial" => initial
|
"initial" => initial
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unless key.nil?
|
||||||
|
if key.is_a? Array
|
||||||
|
key_type = "key"
|
||||||
|
key_value = {}
|
||||||
|
key.each { |k| key_value[k] = 1 }
|
||||||
|
else
|
||||||
|
key_type = "$keyf"
|
||||||
|
key_value = key.is_a?(Code) ? key : Code.new(key)
|
||||||
|
end
|
||||||
|
|
||||||
|
group_command["group"][key_type] = key_value
|
||||||
|
end
|
||||||
|
|
||||||
# only add finalize if specified
|
# only add finalize if specified
|
||||||
if finalize
|
if finalize
|
||||||
finalize = Code.new(finalize) unless finalize.is_a?(Code)
|
finalize = Code.new(finalize) unless finalize.is_a?(Code)
|
||||||
@ -383,22 +391,28 @@ module Mongo
|
|||||||
else
|
else
|
||||||
raise OperationFailure, "group command failed: #{result['errmsg']}"
|
raise OperationFailure, "group command failed: #{result['errmsg']}"
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
raise OperationFailure, ":finalize can be specified only when " +
|
|
||||||
"group is run as a command (set command param to true)" if finalize
|
|
||||||
|
|
||||||
case reduce
|
|
||||||
when Code
|
|
||||||
scope = reduce.scope
|
|
||||||
else
|
else
|
||||||
scope = {}
|
|
||||||
end
|
warn "Collection#group must now be run as a command; you can do this by passing 'true' as the command argument."
|
||||||
scope.merge!({
|
|
||||||
"ns" => @name,
|
raise OperationFailure, ":finalize can be specified only when " +
|
||||||
"keys" => keys,
|
"group is run as a command (set command param to true)" if finalize
|
||||||
"condition" => condition,
|
|
||||||
"initial" => initial })
|
raise OperationFailure, "key must be an array of fields to group by. If you want to pass a key function,
|
||||||
|
run group as a command by passing 'true' as the command argument." unless key.is_a? Array || key.nil?
|
||||||
|
|
||||||
|
case reduce
|
||||||
|
when Code
|
||||||
|
scope = reduce.scope
|
||||||
|
else
|
||||||
|
scope = {}
|
||||||
|
end
|
||||||
|
scope.merge!({
|
||||||
|
"ns" => @name,
|
||||||
|
"keys" => key,
|
||||||
|
"condition" => condition,
|
||||||
|
"initial" => initial })
|
||||||
|
|
||||||
group_function = <<EOS
|
group_function = <<EOS
|
||||||
function () {
|
function () {
|
||||||
@ -425,7 +439,8 @@ function () {
|
|||||||
return {"result": map.values()};
|
return {"result": map.values()};
|
||||||
}
|
}
|
||||||
EOS
|
EOS
|
||||||
@db.eval(Code.new(group_function, scope))["result"]
|
@db.eval(Code.new(group_function, scope))["result"]
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Returns a list of distinct values for +key+ across all
|
# Returns a list of distinct values for +key+ across all
|
||||||
|
@ -401,47 +401,59 @@ class TestCollection < Test::Unit::TestCase
|
|||||||
assert_equal 1, x
|
assert_equal 1, x
|
||||||
end
|
end
|
||||||
|
|
||||||
def test_group_with_scope
|
context "Grouping" do
|
||||||
@@test.save("a" => 1)
|
setup do
|
||||||
@@test.save("b" => 1)
|
@@test.remove
|
||||||
|
@@test.save("a" => 1)
|
||||||
reduce_function = "function (obj, prev) { prev.count += inc_value; }"
|
@@test.save("b" => 1)
|
||||||
|
@initial = {"count" => 0}
|
||||||
assert_equal 2, @@test.group([], {}, {"count" => 0},
|
@reduce_function = "function (obj, prev) { prev.count += inc_value; }"
|
||||||
Code.new(reduce_function,
|
|
||||||
{"inc_value" => 1}))[0]["count"]
|
|
||||||
|
|
||||||
# TODO enable these tests when SERVER-262 is fixed
|
|
||||||
|
|
||||||
# assert_equal 2, @@test.group([], {}, {"count" => 0},
|
|
||||||
# Code.new(reduce_function,
|
|
||||||
# {"inc_value" => 1}), true)[0]["count"]
|
|
||||||
|
|
||||||
assert_equal 4, @@test.group([], {}, {"count" => 0},
|
|
||||||
Code.new(reduce_function,
|
|
||||||
{"inc_value" => 2}))[0]["count"]
|
|
||||||
# assert_equal 4, @@test.group([], {}, {"count" => 0},
|
|
||||||
# Code.new(reduce_function,
|
|
||||||
# {"inc_value" => 2}), true)[0]["count"]
|
|
||||||
|
|
||||||
assert_equal 1, @@test.group([], {}, {"count" => 0},
|
|
||||||
Code.new(reduce_function,
|
|
||||||
{"inc_value" => 0.5}))[0]["count"]
|
|
||||||
# assert_equal 1, @@test.group([], {}, {"count" => 0},
|
|
||||||
# Code.new(reduce_function,
|
|
||||||
# {"inc_value" => 0.5}), true)[0]["count"]
|
|
||||||
|
|
||||||
# test finalize
|
|
||||||
#assert_equal( 3,
|
|
||||||
# @@test.group(
|
|
||||||
# [], {}, {"count" => 0},
|
|
||||||
# Code.new(reduce_function,{"inc_value" => 2}), true,
|
|
||||||
# Code.new("function (o) { o.final_count = o.count - 1; }")
|
|
||||||
# )[0]["final_count"]
|
|
||||||
#)
|
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
||||||
|
should "group results using eval form" do
|
||||||
|
assert_equal 1, @@test.group([], {}, @initial, Code.new(@reduce_function, {"inc_value" => 0.5}))[0]["count"]
|
||||||
|
assert_equal 2, @@test.group([], {}, @initial, Code.new(@reduce_function, {"inc_value" => 1}))[0]["count"]
|
||||||
|
assert_equal 4, @@test.group([], {}, @initial, Code.new(@reduce_function, {"inc_value" => 2}))[0]["count"]
|
||||||
|
end
|
||||||
|
|
||||||
|
should "group results using command form" do
|
||||||
|
assert_equal 1, @@test.group([], {}, @initial, Code.new(@reduce_function, {"inc_value" => 0.5}), true)[0]["count"]
|
||||||
|
assert_equal 2, @@test.group([], {}, @initial, Code.new(@reduce_function, {"inc_value" => 1}), true)[0]["count"]
|
||||||
|
assert_equal 4, @@test.group([], {}, @initial, Code.new(@reduce_function, {"inc_value" => 2}), true)[0]["count"]
|
||||||
|
end
|
||||||
|
|
||||||
|
should "finalize grouped results" do
|
||||||
|
@finalize = "function(doc) {doc.f = doc.count + 200; }"
|
||||||
|
assert_equal 202, @@test.group([], {}, @initial, Code.new(@reduce_function, {"inc_value" => 1}), true, @finalize)[0]["f"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context "Grouping with a key function" do
|
||||||
|
setup do
|
||||||
|
@@test.remove
|
||||||
|
@@test.save("a" => 1)
|
||||||
|
@@test.save("a" => 2)
|
||||||
|
@@test.save("a" => 3)
|
||||||
|
@@test.save("a" => 4)
|
||||||
|
@@test.save("a" => 5)
|
||||||
|
@initial = {"count" => 0}
|
||||||
|
@keyf = "function (doc) { if(doc.a % 2 == 0) { return {even: true}; } else {return {odd: true}} };"
|
||||||
|
@reduce = "function (obj, prev) { prev.count += 1; }"
|
||||||
|
end
|
||||||
|
|
||||||
|
should "group results" do
|
||||||
|
results = @@test.group(@keyf, {}, @initial, @reduce, true).sort {|a, b| a['count'] <=> b['count']}
|
||||||
|
assert results[0]['even'] && results[0]['count'] == 2.0
|
||||||
|
assert results[1]['odd'] && results[1]['count'] == 3.0
|
||||||
|
end
|
||||||
|
|
||||||
|
should "raise an error if trying to use keyf as a group eval" do
|
||||||
|
assert_raise OperationFailure do
|
||||||
|
@@test.group(@keyf, {}, @initial, @reduce)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
context "A collection with two records" do
|
context "A collection with two records" do
|
||||||
setup do
|
setup do
|
||||||
@collection = @@db.collection('test-collection')
|
@collection = @@db.collection('test-collection')
|
||||||
|
Loading…
Reference in New Issue
Block a user