Skip to content

Commit 74aeb43

Browse files
author
Mike Dirolf
committed
Merge branch 'master' of git://github.com/RedBeard0531/mongo-python-driver into redbeard/master
2 parents 2979ccb + d400dfd commit 74aeb43

File tree

2 files changed

+86
-47
lines changed

2 files changed

+86
-47
lines changed

pymongo/collection.py

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -553,7 +553,8 @@ def options(self):
553553
#
554554
# Waiting on this because group command support for CodeWScope
555555
# wasn't added until 1.1
556-
def group(self, keys, condition, initial, reduce, command=False):
556+
def group(self, keys, condition, initial, reduce, finalize=None,
557+
command=False):
557558
"""Perform a query similar to an SQL group by operation.
558559
559560
Returns an array of grouped items.
@@ -564,19 +565,33 @@ def group(self, keys, condition, initial, reduce, command=False):
564565
query specification)
565566
- `initial`: initial value of the aggregation counter object
566567
- `reduce`: aggregation function as a JavaScript string
568+
- `finalize`: function to be called on each object in output list.
567569
- `command` (optional): if True, run the group as a command instead
568570
of in an eval - it is likely that this option will eventually be
569-
deprecated and all groups will be run as commands
571+
deprecated and all groups will be run as commands. Please only use
572+
as a keyword argument, not as a positional argument.
570573
"""
574+
575+
#for now support people passing command in its old position
576+
if finalize in (True, False):
577+
command = finalize
578+
finalize = None
579+
warnings.warn("Please only pass 'command' as a keyword argument."
580+
,DeprecationWarning)
581+
571582
if command:
572583
if not isinstance(reduce, Code):
573584
reduce = Code(reduce)
574-
return self.__database._command({"group":
575-
{"ns": self.__collection_name,
576-
"$reduce": reduce,
577-
"key": self._fields_list_to_dict(keys),
578-
"cond": condition,
579-
"initial": initial}})["retval"]
585+
group = {"ns": self.__collection_name,
586+
"$reduce": reduce,
587+
"key": self._fields_list_to_dict(keys),
588+
"cond": condition,
589+
"initial": initial}
590+
if finalize is not None:
591+
if not isinstance(finalize, Code):
592+
finalize = Code(finalize)
593+
group["finalize"] = finalize
594+
return self.__database._command({"group":group})["retval"]
580595

581596
scope = {}
582597
if isinstance(reduce, Code):
@@ -590,6 +605,7 @@ def group(self, keys, condition, initial, reduce, command=False):
590605
var c = db[ns].find(condition);
591606
var map = new Map();
592607
var reduce_function = %s;
608+
var finalize_function = %s; //function or null
593609
while (c.hasNext()) {
594610
var obj = c.next();
595611
@@ -607,8 +623,18 @@ def group(self, keys, condition, initial, reduce, command=False):
607623
}
608624
reduce_function(obj, aggObj);
609625
}
610-
return {"result": map.values()};
611-
}""" % reduce
626+
627+
out = map.values();
628+
if (finalize_function !== null){
629+
for (var i=0; i < out.length; i++){
630+
var ret = finalize_function(out[i]);
631+
if (ret !== undefined)
632+
out[i] = ret;
633+
}
634+
}
635+
636+
return {"result": out};
637+
}""" % (reduce, (finalize or 'null'));
612638
return self.__database.eval(Code(group_function, scope))["result"]
613639

614640
def rename(self, new_name):

test/test_collection.py

Lines changed: 50 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -519,55 +519,68 @@ def test_group(self):
519519
db = self.db
520520
db.drop_collection("test")
521521

522-
self.assertEqual([], db.test.group([], {},
523-
{"count": 0},
524-
"function (obj, prev) { "
525-
"prev.count++; }"))
526-
self.assertEqual([], db.test.group([], {},
527-
{"count": 0},
528-
"function (obj, prev) { "
529-
"prev.count++; }", command=True))
522+
def group_checker(args, expected):
523+
eval = db.test.group(*args)
524+
cmd = db.test.group(*args, command=True)
525+
self.assertEqual(eval, expected)
526+
self.assertEqual(cmd, expected)
527+
self.assertEqual(eval, cmd)
528+
#last one is not strictly necessary but there for completeness
529+
530+
531+
args = [[], {},
532+
{"count": 0},
533+
"function (obj, prev) { prev.count++; }"]
534+
expected = []
535+
group_checker(args, expected)
530536

531537
db.test.save({"a": 2})
532538
db.test.save({"b": 5})
533539
db.test.save({"a": 1})
534540

535-
self.assertEqual(3, db.test.group([], {},
536-
{"count": 0},
537-
"function (obj, prev) { "
538-
"prev.count++; }")[0]["count"])
539-
self.assertEqual(3, db.test.group([], {},
540-
{"count": 0},
541-
"function (obj, prev) { "
542-
"prev.count++; }",
543-
command=True)[0]["count"])
544-
self.assertEqual(1, db.test.group([],
545-
{"a": {"$gt": 1}},
546-
{"count": 0},
547-
"function (obj, prev) { "
548-
"prev.count++; }")[0]["count"])
549-
self.assertEqual(1, db.test.group([],
550-
{"a": {"$gt": 1}},
551-
{"count": 0},
552-
"function (obj, prev) { "
553-
"prev.count++; }",
554-
command=True)[0]["count"])
541+
args = [[], {},
542+
{"count": 0},
543+
"function (obj, prev) { prev.count++; }"]
544+
expected = [{'count': 3}]
545+
group_checker(args, expected)
546+
547+
args = [[],
548+
{"a": {"$gt": 1}},
549+
{"count": 0},
550+
"function (obj, prev) { prev.count++; }"]
551+
expected = [{'count': 1}]
552+
group_checker(args, expected)
555553

556554
db.test.save({"a": 2, "b": 3})
557555

556+
args = [["a"], {},
557+
{"count": 0},
558+
"function (obj, prev) { prev.count++; }"]
558559
# NOTE maybe we can't count on this ordering being right
559560
expected = [{"a": 2, "count": 2},
560561
{"a": None, "count": 1},
561562
{"a": 1, "count": 1}]
562-
self.assertEqual(expected, db.test.group(["a"], {},
563-
{"count": 0},
564-
"function (obj, prev) { "
565-
"prev.count++; }",
566-
command=True))
567-
self.assertEqual(expected, db.test.group(["a"], {},
568-
{"count": 0},
569-
"function (obj, prev) { "
570-
"prev.count++; }"))
563+
group_checker(args, expected)
564+
565+
# modifying finalize
566+
args = [["a"], {},
567+
{"count": 0},
568+
"function (obj, prev) { prev.count++; }",
569+
"function(obj){obj.count++;}"]
570+
expected = [{"a": 2, "count": 3},
571+
{"a": None, "count": 2},
572+
{"a": 1, "count": 2}]
573+
group_checker(args, expected)
574+
575+
# returning finalize
576+
args = [["a"], {},
577+
{"count": 0},
578+
"function (obj, prev) { prev.count++; }",
579+
"function(obj){ return obj.count;}"]
580+
expected = [2, # a:2
581+
1, # a:None
582+
1] # a:1
583+
group_checker(args, expected)
571584

572585
self.assertRaises(OperationFailure, db.test.group, [], {}, {}, "5 ++ 5")
573586
self.assertRaises(OperationFailure, db.test.group, [], {}, {}, "5 ++ 5", command=True)

0 commit comments

Comments
 (0)