/*
  Run all the aggregation tests
*/

/* load the test documents */
load('jstests/aggregation/data/articles.js');

// make sure we're using the right db; this is the same as "use mydb;" in shell
db = db.getSiblingDB("aggdb");


// just passing through fields
var p1 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        tags : 1,
        pageViews : 1
    }}
]});

var p1result = [
    {
        "_id" : 1,
        "pageViews" : 5,
        "tags" : [
            "fun",
            "good",
            "fun"
        ]
    },
    {
        "_id" : 2,
        "pageViews" : 7,
        "tags" : [
            "fun",
            "nasty"
        ]
    },
    {
        "_id" : 3,
        "pageViews" : 6,
        "tags" : [
            "nasty",
            "filthy"
        ]
    }
];

assert.docEq(p1.result, p1result, 'p1 failed');


// a simple array unwinding
var u1 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $unwind : "$tags" }
]});

var u1result = [
    {
        "_id" : 1,
        "title" : "this is my title",
        "author" : "bob",
        "posted" : ISODate("2004-03-21T18:59:54Z"),
        "pageViews" : 5,
        "tags" : "fun",
        "comments" : [
            {
                "author" : "joe",
                "text" : "this is cool"
            },
            {
                "author" : "sam",
                "text" : "this is bad"
            }
        ],
        "other" : {
            "foo" : 5
        }
    },
    {
        "_id" : 1,
        "title" : "this is my title",
        "author" : "bob",
        "posted" : ISODate("2004-03-21T18:59:54Z"),
        "pageViews" : 5,
        "tags" : "good",
        "comments" : [
            {
                "author" : "joe",
                "text" : "this is cool"
            },
            {
                "author" : "sam",
                "text" : "this is bad"
            }
        ],
        "other" : {
            "foo" : 5
        }
    },
    {
        "_id" : 1,
        "title" : "this is my title",
        "author" : "bob",
        "posted" : ISODate("2004-03-21T18:59:54Z"),
        "pageViews" : 5,
        "tags" : "fun",
        "comments" : [
            {
                "author" : "joe",
                "text" : "this is cool"
            },
            {
                "author" : "sam",
                "text" : "this is bad"
            }
        ],
        "other" : {
            "foo" : 5
        }
    },
    {
        "_id" : 2,
        "title" : "this is your title",
        "author" : "dave",
        "posted" : ISODate("2030-08-08T04:11:10Z"),
        "pageViews" : 7,
        "tags" : "fun",
        "comments" : [
            {
                "author" : "barbara",
                "text" : "this is interesting"
            },
            {
                "author" : "jenny",
                "text" : "i like to play pinball",
                "votes" : 10
            }
        ],
        "other" : {
            "bar" : 14
        }
    },
    {
        "_id" : 2,
        "title" : "this is your title",
        "author" : "dave",
        "posted" : ISODate("2030-08-08T04:11:10Z"),
        "pageViews" : 7,
        "tags" : "nasty",
        "comments" : [
            {
                "author" : "barbara",
                "text" : "this is interesting"
            },
            {
                "author" : "jenny",
                "text" : "i like to play pinball",
                "votes" : 10
            }
        ],
        "other" : {
            "bar" : 14
        }
    },
    {
        "_id" : 3,
        "title" : "this is some other title",
        "author" : "jane",
        "posted" : ISODate("2000-12-31T05:17:14Z"),
        "pageViews" : 6,
        "tags" : "nasty",
        "comments" : [
            {
                "author" : "will",
                "text" : "i don't like the color"
            },
            {
                "author" : "jenny",
                "text" : "can i get that in green?"
            }
        ],
        "other" : {
            "bar" : 14
        }
    },
    {
        "_id" : 3,
        "title" : "this is some other title",
        "author" : "jane",
        "posted" : ISODate("2000-12-31T05:17:14Z"),
        "pageViews" : 6,
        "tags" : "filthy",
        "comments" : [
            {
                "author" : "will",
                "text" : "i don't like the color"
            },
            {
                "author" : "jenny",
                "text" : "can i get that in green?"
            }
        ],
        "other" : {
            "bar" : 14
        }
    }
];

assert.docEq(u1.result, u1result, 'u1 failed');

// unwind an array at the end of a dotted path
db.ut.drop();
db.ut.save({_id: 4, a:1, b:{e:7, f:[4, 3, 2, 1]}, c:12, d:17});
var u2 = db.runCommand(
{ aggregate : "ut", pipeline : [
    { $unwind : "$b.f" }
]});

var u2result = [
    {
        "_id" : 4,
        "a" : 1,
        "b" : {
            "e" : 7,
            "f" : 4
        },
        "c" : 12,
        "d" : 17
    },
    {
        "_id" : 4,
        "a" : 1,
        "b" : {
            "e" : 7,
            "f" : 3
        },
        "c" : 12,
        "d" : 17
    },
    {
        "_id" : 4,
        "a" : 1,
        "b" : {
            "e" : 7,
            "f" : 2
        },
        "c" : 12,
        "d" : 17
    },
    {
        "_id" : 4,
        "a" : 1,
        "b" : {
            "e" : 7,
            "f" : 1
        },
        "c" : 12,
        "d" : 17
    }
];

assert.docEq(u2.result, u2result, 'u2 failed');


// combining a projection with unwinding an array
var p2 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        author : 1,
        tags : 1,
        pageViews : 1
    }},
    { $unwind : "$tags" }
]});

var p2result = [
    {
        "_id" : 1,
        "author" : "bob",
        "pageViews" : 5,
        "tags" : "fun"
    },
    {
        "_id" : 1,
        "author" : "bob",
        "pageViews" : 5,
        "tags" : "good"
    },
    {
        "_id" : 1,
        "author" : "bob",
        "pageViews" : 5,
        "tags" : "fun"
    },
    {
        "_id" : 2,
        "author" : "dave",
        "pageViews" : 7,
        "tags" : "fun"
    },
    {
        "_id" : 2,
        "author" : "dave",
        "pageViews" : 7,
        "tags" : "nasty"
    },
    {
        "_id" : 3,
        "author" : "jane",
        "pageViews" : 6,
        "tags" : "nasty"
    },
    {
        "_id" : 3,
        "author" : "jane",
        "pageViews" : 6,
        "tags" : "filthy"
    }
];

assert.docEq(p2.result, p2result, 'p2 failed');


// pulling values out of subdocuments
var p3 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        otherfoo : "$other.foo",
        otherbar : "$other.bar"
    }}
]});

var p3result = [
    {
        "_id" : 1,
        "otherfoo" : 5
    },
    {
        "_id" : 2,
        "otherbar" : 14
    },
    {
        "_id" : 3,
        "otherbar" : 14
    }
];

assert.docEq(p3.result, p3result, 'p3 failed');


// projection includes a computed value
var p4 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        author : 1,
        daveWroteIt : { $eq:["$author", "dave"] }
    }}
]});

var p4result = [
    {
        "_id" : 1,
        "author" : "bob",
        "daveWroteIt" : false
    },
    {
        "_id" : 2,
        "author" : "dave",
        "daveWroteIt" : true
    },
    {
        "_id" : 3,
        "author" : "jane",
        "daveWroteIt" : false
    }
];

assert.docEq(p4.result, p4result, 'p4 failed');


// projection includes a virtual (fabricated) document
var p5 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        author : 1,
        pageViews : 1,
        tags : 1
    }},
    { $unwind : "$tags" },
    { $project : {
        author : 1,
        subDocument : { foo : "$pageViews", bar : "$tags"  }
    }}
]});

var p5result = [
    {
        "_id" : 1,
        "author" : "bob",
        "subDocument" : {
            "foo" : 5,
            "bar" : "fun"
        }
    },
    {
        "_id" : 1,
        "author" : "bob",
        "subDocument" : {
            "foo" : 5,
            "bar" : "good"
        }
    },
    {
        "_id" : 1,
        "author" : "bob",
        "subDocument" : {
            "foo" : 5,
            "bar" : "fun"
        }
    },
    {
        "_id" : 2,
        "author" : "dave",
        "subDocument" : {
            "foo" : 7,
            "bar" : "fun"
        }
    },
    {
        "_id" : 2,
        "author" : "dave",
        "subDocument" : {
            "foo" : 7,
            "bar" : "nasty"
        }
    },
    {
        "_id" : 3,
        "author" : "jane",
        "subDocument" : {
            "foo" : 6,
            "bar" : "nasty"
        }
    },
    {
        "_id" : 3,
        "author" : "jane",
        "subDocument" : {
            "foo" : 6,
            "bar" : "filthy"
        }
    }
];

assert.docEq(p5.result, p5result, 'p5 failed');


// multi-step aggregate
// nested expressions in computed fields
var p6 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        author : 1,
        tags : 1,
        pageViews : 1
    }},
    { $unwind : "$tags" },
    { $project : {
        author : 1,
        tag : "$tags",
        pageViews : 1,
        daveWroteIt : { $eq:["$author", "dave"] },
        weLikeIt : { $or:[ { $eq:["$author", "dave"] },
                           { $eq:["$tags", "good"] } ] }
    }}
]});

var p6result = [
    {
        "_id" : 1,
        "author" : "bob",
        "pageViews" : 5,
        "tag" : "fun",
        "daveWroteIt" : false,
        "weLikeIt" : false
    },
    {
        "_id" : 1,
        "author" : "bob",
        "pageViews" : 5,
        "tag" : "good",
        "daveWroteIt" : false,
        "weLikeIt" : true
    },
    {
        "_id" : 1,
        "author" : "bob",
        "pageViews" : 5,
        "tag" : "fun",
        "daveWroteIt" : false,
        "weLikeIt" : false
    },
    {
        "_id" : 2,
        "author" : "dave",
        "pageViews" : 7,
        "tag" : "fun",
        "daveWroteIt" : true,
        "weLikeIt" : true
    },
    {
        "_id" : 2,
        "author" : "dave",
        "pageViews" : 7,
        "tag" : "nasty",
        "daveWroteIt" : true,
        "weLikeIt" : true
    },
    {
        "_id" : 3,
        "author" : "jane",
        "pageViews" : 6,
        "tag" : "nasty",
        "daveWroteIt" : false,
        "weLikeIt" : false
    },
    {
        "_id" : 3,
        "author" : "jane",
        "pageViews" : 6,
        "tag" : "filthy",
        "daveWroteIt" : false,
        "weLikeIt" : false
    }
];

assert.docEq(p6.result, p6result, 'p6 failed');


// slightly more complex computed expression; $ifNull
var p7 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        theSum : { $add:["$pageViews",
                         { $ifNull:["$other.foo",
                                    "$other.bar"] } ] }
    }}
]});

var p7result = [
    {
        "_id" : 1,
        "theSum" : 10
    },
    {
        "_id" : 2,
        "theSum" : 21
    },
    {
        "_id" : 3,
        "theSum" : 20
    }
];

assert.docEq(p7.result, p7result, 'p7 failed');


// dotted path inclusion; _id exclusion
var p8 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        _id : 0,
        author : 1,
        tags : 1,
        "comments.author" : 1
    }},
    { $unwind : "$tags" }
]});

var p8result = [
    {
        "author" : "bob",
        "tags" : "fun",
        "comments" : [
            {
                "author" : "joe"
            },
            {
                "author" : "sam"
            }
        ]
    },
    {
        "author" : "bob",
        "tags" : "good",
        "comments" : [
            {
                "author" : "joe"
            },
            {
                "author" : "sam"
            }
        ]
    },
    {
        "author" : "bob",
        "tags" : "fun",
        "comments" : [
            {
                "author" : "joe"
            },
            {
                "author" : "sam"
            }
        ]
    },
    {
        "author" : "dave",
        "tags" : "fun",
        "comments" : [
            {
                "author" : "barbara"
            },
            {
                "author" : "jenny"
            }
        ]
    },
    {
        "author" : "dave",
        "tags" : "nasty",
        "comments" : [
            {
                "author" : "barbara"
            },
            {
                "author" : "jenny"
            }
        ]
    },
    {
        "author" : "jane",
        "tags" : "nasty",
        "comments" : [
            {
                "author" : "will"
            },
            {
                "author" : "jenny"
            }
        ]
    },
    {
        "author" : "jane",
        "tags" : "filthy",
        "comments" : [
            {
                "author" : "will"
            },
            {
                "author" : "jenny"
            }
        ]
    }
];

assert.docEq(p8.result, p8result, 'p8 failed');


// collapse a dotted path with an intervening array
var p9 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        _id : 0,
        author : 1,
        commentsAuthor : "$comments.author"
    }}
]});

var p9result = [
    {
        "author" : "bob",
        "commentsAuthor" : [
            "joe",
            "sam"
        ]
    },
    {
        "author" : "dave",
        "commentsAuthor" : [
            "barbara",
            "jenny"
        ]
    },
    {
        "author" : "jane",
        "commentsAuthor" : [
            "will",
            "jenny"
        ]
    }
];

assert.docEq(p9.result, p9result, 'p9 failed');


// simple sort
var p10 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $sort : { title : 1 }
    }
]});

var p10result = [
    {
        "_id" : 1,
        "title" : "this is my title",
        "author" : "bob",
        "posted" : ISODate("2004-03-21T18:59:54Z"),
        "pageViews" : 5,
        "tags" : [
            "fun",
            "good",
            "fun"
        ],
        "comments" : [
            {
                "author" : "joe",
                "text" : "this is cool"
            },
            {
                "author" : "sam",
                "text" : "this is bad"
            }
        ],
        "other" : {
            "foo" : 5
        }
    },
    {
        "_id" : 3,
        "title" : "this is some other title",
        "author" : "jane",
        "posted" : ISODate("2000-12-31T05:17:14Z"),
        "pageViews" : 6,
        "tags" : [
            "nasty",
            "filthy"
        ],
        "comments" : [
            {
                "author" : "will",
                "text" : "i don't like the color"
            },
            {
                "author" : "jenny",
                "text" : "can i get that in green?"
            }
        ],
        "other" : {
            "bar" : 14
        }
    },
    {
        "_id" : 2,
        "title" : "this is your title",
        "author" : "dave",
        "posted" : ISODate("2030-08-08T04:11:10Z"),
        "pageViews" : 7,
        "tags" : [
            "fun",
            "nasty"
        ],
        "comments" : [
            {
                "author" : "barbara",
                "text" : "this is interesting"
            },
            {
                "author" : "jenny",
                "text" : "i like to play pinball",
                "votes" : 10
            }
        ],
        "other" : {
            "bar" : 14
        }
    }
];

assert.docEq(p10.result, p10result, 'p10 failed');


// unwind on nested array
db.p11.drop();
db.p11.save( {
    _id : 5,
    name : 'MongoDB',
    items : {
        authors : ['jay', 'vivek', 'bjornar'],
        dbg : [17, 42]
    },
    favorites : ['pickles', 'ice cream', 'kettle chips']
});

var p11 = db.runCommand(
{ aggregate : "p11", pipeline : [
    { $unwind : "$items.authors" },
    { $project : {
        name : 1,
        author : "$items.authors"
    }},
]});

p11result = [
    {
        "_id" : 5,
        "name" : "MongoDB",
        "author" : "jay"
    },
    {
        "_id" : 5,
        "name" : "MongoDB",
        "author" : "vivek"
    },
    {
        "_id" : 5,
        "name" : "MongoDB",
        "author" : "bjornar"
    }
];

assert.docEq(p11.result, p11result, 'p11 failed');


// multiply test
var p12 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        theProduct : { $multiply:["$pageViews",
                         { $ifNull:["$other.foo",
                                    "$other.bar"] } ] }
    }}
]});

var p12result = [
    {
        "_id" : 1,
        "theProduct" : 25
    },
    {
        "_id" : 2,
        "theProduct" : 98
    },
    {
        "_id" : 3,
        "theProduct" : 84
    }
];

assert.docEq(p12.result, p12result, 'p12 failed');


// subtraction test
var p13 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        theDifference : { $subtract:["$pageViews",
                         { $ifNull:["$other.foo",
                                    "$other.bar"] } ] }
    }}
]});

var p13result = [
    {
        "_id" : 1,
        "theDifference" : 0
    },
    {
        "_id" : 2,
        "theDifference" : -7
    },
    {
        "_id" : 3,
        "theDifference" : -8
    }
];

assert.docEq(p13.result, p13result, 'p13 failed');


// mod test
var p14 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        theRemainder : { $mod:[
            { $ifNull:["$other.foo",
                    "$other.bar"] },
                "$pageViews", ] }
    }}
]});

var p14result = [
    {
        "_id" : 1,
        "theRemainder" : 0
    },
    {
        "_id" : 2,
        "theRemainder" : 0
    },
    {
        "_id" : 3,
        "theRemainder" : 2
    }
];

assert.docEq(p14.result, p14result, 'p14 failed');


// toUpper test
var p15 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        author : {$toUpper: "$author"},
        pageViews : 1
    }}
]});

var p15result = [
    {
        "_id" : 1,
        "author" : "BOB",
        "pageViews" : 5
    },
    {
        "_id" : 2,
        "author" : "DAVE",
        "pageViews" : 7
    },
    {
        "_id" : 3,
        "author" : "JANE",
        "pageViews" : 6
    }
];

assert.docEq(p15.result, p15result, 'p15 failed');


// toLower test
var p16 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        author : {$toUpper: "$author"},
        pageViews : 1
    }},
    { $project : {
    author : {$toLower: "$author"},
    pageViews : 1
    }}
]});

var p16result = [
    {
        "_id" : 1,
        "author" : "bob",
        "pageViews" : 5,
    },
    {
        "_id" : 2,
        "author" : "dave",
        "pageViews" : 7,
    },
    {
        "_id" : 3,
        "author" : "jane",
        "pageViews" : 6,
    }
];

assert.docEq(p16.result, p16result, 'p16 failed');


// substr test
var p17 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        author : {$substr: ["$author", 1, 2]},
    }}
]});

var p17result = [
    {
        "_id" : 1,
        "author" : "ob"
    },
    {
        "_id" : 2,
        "author" : "av"
    },
    {
        "_id" : 3,
        "author" : "an"
    }
];

assert.docEq(p17.result, p17result, 'p17 failed');


// strcasecmp test
var p18 = db.runCommand(
{aggregate : "article", pipeline : [
    { $project : {
        tags : 1,
        thisisalametest : {$strcasecmp: ["foo","bar"]},
        thisisalamepass : {$strcasecmp: ["foo","foo"]}
    }}
]});

var p18result = [
    {
        "_id" : 1,
        "tags" : [
            "fun",
            "good",
            "fun"
        ],
        "thisisalametest" : 1,
        "thisisalamepass" : 0
    },
    {
        "_id" : 2,
        "tags" : [
            "fun",
            "nasty"
        ],
        "thisisalametest" : 1,
        "thisisalamepass" : 0
    },
    {
        "_id" : 3,
        "tags" : [
            "nasty",
            "filthy"
        ],
        "thisisalametest" : 1,
        "thisisalamepass" : 0
    }
];

assert.docEq(p18.result, p18result, 'p18 failed');


// date tests
var p19 = db.runCommand({aggregate : "article", pipeline : [
    { $project : {
        authors: 1,
        posted: 1,
        seconds: {$second: "$posted"},
        minutes: {$minute: "$posted"},
        hour: {$hour: "$posted"},
        dayOfYear: {$dayOfYear: "$posted"},
        dayOfMonth: {$dayOfMonth: "$posted"},
        dayOfWeek: {$dayOfWeek: "$posted"},
        month: {$month: "$posted"},
        week: {$week: "$posted"},
        year: {$year: "$posted"}
    }}
]});

var p19result = [
    {
        "_id" : 1,
        "posted" : ISODate("2004-03-21T18:59:54Z"),
        "seconds" : 54,
        "minutes" : 59,
        "hour" : 18,
        "dayOfYear" : 81,
        "dayOfMonth" : 21,
        "dayOfWeek" : 1,
        "month" : 3,
        "week" : 12,
        "year" : 2004,
    },
    {
        "_id" : 2,
        "posted" : ISODate("2030-08-08T04:11:10Z"),
        "seconds" : 10,
        "minutes" : 11,
        "hour" : 4,
        "dayOfYear" : 220,
        "dayOfMonth" : 8,
        "dayOfWeek" : 5,
        "month" : 8,
        "week" : 31,
        "year" : 2030,
    },
    {
        "_id" : 3,
        "posted" : ISODate("2000-12-31T05:17:14Z"),
        "seconds" : 14,
        "minutes" : 17,
        "hour" : 5,
        "dayOfYear" : 366,
        "dayOfMonth" : 31,
        "dayOfWeek" : 1,
        "month" : 12,
        "week" : 53,
        "year" : 2000,
    }
];

assert.docEq(p19.result, p19result, 'p19 failed');


db.vartype.drop();
db.vartype.save({ x : 17, y : "foo"});

// ternary conditional operator
var p21 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        _id : 0,
        author : 1,
        pageViews : { $cond : [ {$eq:["$author", "dave"]},
                                {$add:["$pageViews", 1000]}, "$pageViews" ]
        }
    }}
]});

var p21result = [
    {
        "author" : "bob",
        "pageViews" : 5
    },
    {
        "author" : "dave",
        "pageViews" : 1007
    },
    {
        "author" : "jane",
        "pageViews" : 6
    }
];

assert.docEq(p21.result, p21result, 'p21 failed');


// simple matching
var m1 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $match : { author : "dave" } }
]});

var m1result = [
    {
        "_id" : 2,
        "title" : "this is your title",
        "author" : "dave",
        "posted" : ISODate("2030-08-08T04:11:10Z"),
        "pageViews" : 7,
        "tags" : [
            "fun",
            "nasty"
        ],
        "comments" : [
            {
                "author" : "barbara",
                "text" : "this is interesting"
            },
            {
                "author" : "jenny",
                "text" : "i like to play pinball",
                "votes" : 10
            }
        ],
        "other" : {
            "bar" : 14
        }
    }
];

assert.docEq(m1.result, m1result, 'm1 failed');


// combining matching with a projection
var m2 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        title : 1,
        author : 1,
        pageViews : 1,
        tags : 1,
        comments : 1
    }},
    { $unwind : "$tags" },
    { $match : { tags : "nasty" } }
]});

var m2result = [
    {
        "_id" : 2,
        "title" : "this is your title",
        "author" : "dave",
        "pageViews" : 7,
        "tags" : "nasty",
        "comments" : [
            {
                "author" : "barbara",
                "text" : "this is interesting"
            },
            {
                "author" : "jenny",
                "text" : "i like to play pinball",
                "votes" : 10
            }
        ]
    },
    {
        "_id" : 3,
        "title" : "this is some other title",
        "author" : "jane",
        "pageViews" : 6,
        "tags" : "nasty",
        "comments" : [
            {
                "author" : "will",
                "text" : "i don't like the color"
            },
            {
                "author" : "jenny",
                "text" : "can i get that in green?"
            }
        ]
    }
];

assert.docEq(m2.result, m2result, 'm2 failed');


// group by tag, _id is a field reference
var g1 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        author : 1,
        tags : 1,
        pageViews : 1
    }},
    { $unwind : "$tags" },
    { $group : {
        _id : "$tags",
        docsByTag : { $sum : 1 },
        viewsByTag : { $sum : "$pageViews" }
    }},
    {$sort: {'_id': 1}}
]});

var g1result = [
    {
        "_id" : "filthy",
        "docsByTag" : 1,
        "viewsByTag" : 6
    },
    {
        "_id" : "fun",
        "docsByTag" : 3,
        "viewsByTag" : 17
    },
    {
        "_id" : "good",
        "docsByTag" : 1,
        "viewsByTag" : 5
    },
    {
        "_id" : "nasty",
        "docsByTag" : 2,
        "viewsByTag" : 13
    },
];

assert.docEq(g1.result, g1result, 'g1 failed');


// $max, and averaging in a final projection; _id is structured
var g2 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        author : 1,
        tags : 1,
        pageViews : 1
    }},
    { $unwind : "$tags" },
    { $group : {
        _id: { tags : "$tags" },
        docsByTag : { $sum : 1 },
        viewsByTag : { $sum : "$pageViews" },
        mostViewsByTag : { $max : "$pageViews" },
    }},
    { $project : {
        _id: false,
        tag : "$_id.tags",
        mostViewsByTag : 1,
        docsByTag : 1,
        viewsByTag : 1,
        avgByTag : { $divide:["$viewsByTag", "$docsByTag"] }
    }},
    {$sort: {'docsByTag': 1, 'viewsByTag': 1}}
]});

var g2result = [
    {
        "docsByTag" : 1,
        "viewsByTag" : 5,
        "mostViewsByTag" : 5,
        "tag" : "good",
        "avgByTag" : 5
    },
    {
        "docsByTag" : 1,
        "viewsByTag" : 6,
        "mostViewsByTag" : 6,
        "tag" : "filthy",
        "avgByTag" : 6
    },
    {
        "docsByTag" : 2,
        "viewsByTag" : 13,
        "mostViewsByTag" : 7,
        "tag" : "nasty",
        "avgByTag" : 6.5
    },
    {
        "docsByTag" : 3,
        "viewsByTag" : 17,
        "mostViewsByTag" : 7,
        "tag" : "fun",
        "avgByTag" : 5.666666666666667
    }
];

assert.docEq(g2.result, g2result, 'g2 failed');


// $push as an accumulator; can pivot data
var g3 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        author : 1,
        tags : 1,
    }},
    { $unwind : "$tags" },
    { $group : {
        _id : { tags : "$tags" },
        authors : { $push : "$author" }
    }},
    {$sort: {'_id': 1}}
]});

var g3result = [
    {
        "_id" : {
            "tags" : "filthy"
        },
        "authors" : [
            "jane"
        ]
    },
    {
        "_id" : {
            "tags" : "fun"
        },
        "authors" : [
            "bob",
            "bob",
            "dave"
        ]
    },
    {
        "_id" : {
            "tags" : "good"
        },
        "authors" : [
            "bob"
        ]
    },
    {
        "_id" : {
            "tags" : "nasty"
        },
        "authors" : [
            "dave",
            "jane"
        ]
    }
];

assert.docEq(g3.result, g3result, 'g3 failed');


// $avg, and averaging in a final projection
var g4 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        author : 1,
        tags : 1,
        pageViews : 1
    }},
    { $unwind : "$tags" },
    { $group : {
        _id: { tags : "$tags" },
        docsByTag : { $sum : 1 },
        viewsByTag : { $sum : "$pageViews" },
        avgByTag : { $avg : "$pageViews" },
    }},
    {$sort: {'_id': 1}}
]});

var g4result = [
    {
        "_id" : {
            "tags" : "filthy"
        },
        "docsByTag" : 1,
        "viewsByTag" : 6,
        "avgByTag" : 6
    },
    {
        "_id" : {
            "tags" : "fun"
        },
        "docsByTag" : 3,
        "viewsByTag" : 17,
        "avgByTag" : 5.666666666666667
    },
    {
        "_id" : {
            "tags" : "good"
        },
        "docsByTag" : 1,
        "viewsByTag" : 5,
        "avgByTag" : 5
    },
    {
        "_id" : {
            "tags" : "nasty"
        },
        "docsByTag" : 2,
        "viewsByTag" : 13,
        "avgByTag" : 6.5
    }
];

assert.docEq(g4.result, g4result, 'g4 failed');


// $addToSet as an accumulator; can pivot data
var g5 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $project : {
        author : 1,
        tags : 1,
    }},
    { $unwind : "$tags" },
    { $group : {
        _id : { tags : "$tags" },
        authors : { $addToSet : "$author" }
    }},
    {$sort: {'_id': 1}}
]});

// $addToSet doesn't guarantee order so we shouldn't test for it.
g5.result.forEach(function(obj) {
    obj.authors.sort();
});

var g5result = [
    {
        "_id" : {
            "tags" : "filthy"
        },
        "authors" : [
            "jane"
        ]
    },
    {
        "_id" : {
            "tags" : "fun"
        },
        "authors" : [
            "bob",
            "dave",
        ]
    },
    {
        "_id" : {
            "tags" : "good"
        },
        "authors" : [
            "bob"
        ]
    },
    {
        "_id" : {
            "tags" : "nasty"
        },
        "authors" : [
            "dave",
            "jane",
        ]
    }
];

assert.docEq(g5.result, g5result, 'g5 failed');


// $first and $last accumulators, constant _id
var g6 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $sort : { author : -1 } },
    { $group : {
        _id : "authors", /* constant string, *not* a field reference */
        firstAuthor : { $last : "$author" }, /* note reverse sort above */
        lastAuthor : { $first : "$author" }, /* note reverse sort above */
        count : { $sum : 1 }
    }}
]});

var g6result = [
    {
        "_id" : "authors",
        firstAuthor : "bob",
        lastAuthor : "jane",
        count : 3
    }
];

// Test unwind on an unused field
var g7 = db.runCommand(
{ aggregate : "article", pipeline : [
    { $unwind : '$tags' },
    { $group : {
        _id : "tag_count", /* constant string, *not* a field reference */
        count : { $sum : 1 }
    }}
]});
assert.eq(g7.result[0].count, 7);
