| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582 |
- "use strict";
- if (!module.parent) return require.cache[__filename] = 0, (new(require("mocha"))()).addFile(__filename).ui("exports").run(process.exit);
- var assert = require("assert"),
- async = require("async"),
- DocumentSource = require("../../../../lib/pipeline/documentSources/DocumentSource"),
- SortDocumentSource = require("../../../../lib/pipeline/documentSources/SortDocumentSource"),
- LimitDocumentSource = require("../../../../lib/pipeline/documentSources/LimitDocumentSource"),
- CursorDocumentSource = require("../../../../lib/pipeline/documentSources/CursorDocumentSource"),
- ArrayRunner = require("../../../../lib/query/ArrayRunner"),
- FieldPathExpression = require("../../../../lib/pipeline/expressions/FieldPathExpression");
- function getCursorDocumentSource(values) {
- return new CursorDocumentSource(null, new ArrayRunner(values), null);
- }
- /// An assertion for `ObjectExpression` instances based on Mongo's `ExpectedResultBase` class
- function assertExpectedResult(args) {
- {// check for required args
- if (args === undefined) throw new TypeError("missing arg: `args` is required");
- if (args.spec && args.throw === undefined) args.throw = true; // Assume that spec only tests expect an error to be thrown
- //if (args.spec === undefined) throw new Error("missing arg: `args.spec` is required");
- if (args.expected !== undefined && args.docs === undefined) throw new Error("must provide docs with expected value");
- }// check for required args
- // run implementation
- if(args.expected && args.docs){
- var sds = SortDocumentSource.createFromJson(args.spec),
- next,
- results = [],
- cds = new CursorDocumentSource(null, new ArrayRunner(args.docs), null);
- sds.setSource(cds);
- async.whilst(
- function() {
- return next !== null;
- },
- function(done) {
- sds.getNext(function(err, doc) {
- if(err) return done(err);
- next = doc;
- if(next === null) {
- return done();
- } else {
- results.push(next);
- return done();
- }
- });
- },
- function(err) {
- assert.equal(JSON.stringify(results), JSON.stringify(args.expected));
- if(args.done) {
- return args.done();
- }
- }
- );
- }else{
- if(args.throw) {
- assert.throws(function(){
- SortDocumentSource.createFromJson(args.spec);
- });
- } else {
- assert.doesNotThrow(function(){
- SortDocumentSource.createFromJson(args.spec);
- });
- }
- }
- }
- module.exports = {
- "SortDocumentSource": {
- "constructor()": {
- // $sort spec is not an object
- "should throw Error when constructing without args": function testConstructor(){
- assertExpectedResult({"throw":true});
- },
- // $sort spec is not an object
- "should throw Error when $sort spec is not an object": function testConstructor(){
- assertExpectedResult({spec:"Foo"});
- },
- // $sort spec is an empty object
- "should throw Error when $sort spec is an empty object": function testConstructor(){
- assertExpectedResult({spec:{}});
- },
- // $sort _id is specified as an invalid object expression
- "should throw error when _id is an invalid object expression": function testConstructor(){
- assertExpectedResult({
- spec:{_id:{$add:1, $and:1}},
- });
- },
- },
- "#getSourceName()": {
- "should return the correct source name; $sort": function testSourceName(){
- var sds = new SortDocumentSource();
- assert.strictEqual(sds.getSourceName(), "$sort");
- }
- },
- "#getFactory()": {
- "should return the constructor for this class": function factoryIsConstructor(){
- assert.strictEqual(new SortDocumentSource().getFactory(), SortDocumentSource);
- }
- },
- "#getNext()": {
- /** Assert that iterator state accessors consistently report the source is exhausted. */
- "should return EOF if there are no more sources": function noSources(next){
- var cds = getCursorDocumentSource([{"a": 1}]);
- var sds = SortDocumentSource.createFromJson({"sort":1});
- sds.setSource(cds);
- sds.getNext(function(err, val) {
- assert.deepEqual(val, {a:1});
- sds.getNext(function(err, val) {
- if (err) throw err;
- assert.equal(val, null);
- return next();
- });
- });
- },
- "should not return EOF if there are documents": function hitSort(next){
- var cds = getCursorDocumentSource([{a: 1}]);
- var sds = SortDocumentSource.createFromJson({"sort":1});
- sds.setSource(cds);
- async.series([
- cds.getNext.bind(cds),
- ],
- function(err,res) {
- if (err) throw err;
- assert.notEqual(res, null);
- return next();
- }
- );
- },
- "should return the current document source": function currSource(next){
- var cds = getCursorDocumentSource([{a: 1}]);
- var sds = SortDocumentSource.createFromJson({"sort":1});
- sds.setSource(cds);
- async.series([
- cds.getNext.bind(cds),
- ],
- function(err,res) {
- if (err) throw err;
- assert.deepEqual(res, [ { a: 1 } ]);
- return next();
- }
- );
- },
- "should return next document when moving to the next source sorted descending": function nextSource(next){
- var cds = getCursorDocumentSource([{a: 1}, {b:2}]);
- var sds = SortDocumentSource.createFromJson({"sort":1});
- sds.setSource(cds);
- async.series([
- cds.getNext.bind(cds),
- ],
- function(err,res) {
- if (err) throw err;
- assert.deepEqual(res, [ { a: 1 } ]);
- return next();
- }
- );
- },
- "should return false for no sources remaining sorted descending": function noMoar(next){
- var cds = getCursorDocumentSource([{a: 1}, {b:2}]);
- var sds = SortDocumentSource.createFromJson({"sort":1});
- sds.setSource(cds);
- async.series([
- cds.getNext.bind(cds),
- cds.getNext.bind(cds),
- ],
- function(err,res) {
- if (err) throw err;
- assert.deepEqual(res, [ { a: 1 }, { b: 2 } ]);
- return next();
- }
- );
- }
- },
- "#serialize()": {
- "should throw an error when trying to serialize": function serialize() {
- var sds = new SortDocumentSource();
- assert.throws(sds.serialize.bind(sds));
- }
- },
- "#serializeToArray()": {
- /**
- * Check that the BSON representation generated by the souce matches the BSON it was
- * created with.
- */
- "should have equal json representation": function serializeToArrayCheck(next){
- var sds = SortDocumentSource.createFromJson({"sort":1}, {});
- var array = [];
- sds.serializeToArray(array, false);
- assert.deepEqual(array, [{"$sort":{"sort":1}}]);
- return next();
- },
- "should create an object representation of the SortDocumentSource": function serializeToArrayTest(next){
- var sds = new SortDocumentSource();
- var fieldPathVar;
- sds.vSortKey.push(new FieldPathExpression("b", fieldPathVar) );
- var array = [];
- sds.serializeToArray(array, false);
- assert.deepEqual(array, [{"$sort":{"":-1}}] );
- return next();
- }
- },
- "#createFromJson()": {
- "should return a new SortDocumentSource object from an input JSON object": function createTest(next){
- var sds = SortDocumentSource.createFromJson({a:1});
- assert.strictEqual(sds.constructor, SortDocumentSource);
- var t = [];
- sds.serializeToArray(t, false);
- assert.deepEqual(t, [{"$sort":{"a":1}}] );
- return next();
- },
- "should return a new SortDocumentSource object from an input JSON object with a descending field": function createTest(next){
- var sds = SortDocumentSource.createFromJson({a:-1});
- assert.strictEqual(sds.constructor, SortDocumentSource);
- var t = [];
- sds.serializeToArray(t, false);
- assert.deepEqual(t, [{"$sort":{"a":-1}}]);
- return next();
- },
- "should return a new SortDocumentSource object from an input JSON object with dotted paths": function createTest(next){
- var sds = SortDocumentSource.createFromJson({ "a.b":1 });
- assert.strictEqual(sds.constructor, SortDocumentSource);
- var t = [];
- sds.serializeToArray(t, false);
- assert.deepEqual(t, [{"$sort":{"a.b":1}}]);
- return next();
- },
- "should throw an exception when not passed an object": function createTest(next){
- assert.throws(function() {
- SortDocumentSource.createFromJson(7);
- });
- return next();
- },
- "should throw an exception when passed an empty object": function createTest(next){
- assert.throws(function() {
- SortDocumentSource.createFromJson({});
- });
- return next();
- },
- "should throw an exception when passed an object with a non number value": function createTest(next){
- assert.throws(function() {
- SortDocumentSource.createFromJson({a:"b"});
- });
- return next();
- },
- "should throw an exception when passed an object with a non valid number value": function createTest(next){
- assert.throws(function() {
- SortDocumentSource.createFromJson({a:14});
- });
- next();
- }
- },
- "#sort": {
- "should sort a single document": function singleValue(next) {
- var cds = getCursorDocumentSource([{_id:0, a: 1}]);
- var sds = new SortDocumentSource();
- sds.addKey("_id", false);
- sds.setSource(cds);
- sds.getNext(function(err, actual) {
- if (err) throw err;
- assert.deepEqual(actual, {_id:0, a:1});
- return next();
- });
- },
- "should sort two documents": function twoValue(next) {
- var cds = getCursorDocumentSource([{_id:0, a: 1}, {_id:1, a:0}]);
- var sds = new SortDocumentSource();
- sds.addKey("_id", false);
- sds.setSource(cds);
- async.series([
- sds.getNext.bind(sds),
- sds.getNext.bind(sds),
- ],
- function(err,res) {
- if (err) throw err;
- assert.deepEqual([ { _id: 1, a: 0 }, { _id: 0, a: 1 } ], res);
- return next();
- }
- );
- },
- "should sort two documents in ascending order": function ascendingValue(next) {
- var cds = getCursorDocumentSource([{_id:0, a: 1}, {_id:5, a:12}, {_id:1, a:0}]);
- var sds = new SortDocumentSource();
- sds.addKey("_id", true);
- sds.setSource(cds);
- var docs = [], i = 0;
- async.doWhilst(
- function(cb) {
- sds.getNext(function(err, val) {
- docs[i] = val;
- return cb(err);
- });
- },
- function() {
- return docs[i++] !== null;
- },
- function(err) {
- if (err) throw err;
- assert.deepEqual([{_id:0, a: 1}, {_id:1, a:0}, {_id:5, a:12}, null], docs);
- return next();
- }
- );
- },
- "should sort documents with a compound key": function compoundKeySort(next) {
- var cds = getCursorDocumentSource([{_id:0, a: 1, b:3}, {_id:5, a:12, b:7}, {_id:1, a:0, b:2}]);
- var sds = SortDocumentSource.createFromJson({"sort":1});
- sds.addKey("a", false);
- sds.addKey("b", false);
- sds.setSource(cds);
- var docs = [], i = 0;
- async.doWhilst(
- function(cb) {
- sds.getNext(function(err, val) {
- docs[i] = val;
- return cb(err);
- });
- },
- function() {
- return docs[i++] !== null;
- },
- function(err) {
- if (err) throw err;
- assert.deepEqual([{_id:5, a:12, b:7}, {_id:0, a:1, b:3}, {_id:1, a:0, b:2}, null], docs);
- return next();
- }
- );
- },
- "should sort documents with a compound key in ascending order": function compoundAscendingKeySort(next) {
- var cds = getCursorDocumentSource([{_id:0, a: 1, b:3}, {_id:5, a:12, b:7}, {_id:1, a:0, b:2}]);
- var sds = new SortDocumentSource();
- sds.addKey("a", true);
- sds.addKey("b", true);
- sds.setSource(cds);
- var docs = [], i = 0;
- async.doWhilst(
- function(cb) {
- sds.getNext(function(err, val) {
- docs[i] = val;
- return cb(err);
- });
- },
- function() {
- return docs[i++] !== null;
- },
- function(err) {
- if (err) throw err;
- assert.deepEqual([{_id:1, a:0, b:2}, {_id:0, a:1, b:3}, {_id:5, a:12, b:7}, null], docs);
- return next();
- }
- );
- },
- "should sort documents with a compound key in mixed order": function compoundMixedKeySort(next) {
- var cds = getCursorDocumentSource([{_id:0, a: 1, b:3}, {_id:5, a:12, b:7}, {_id:1, a:0, b:2}, {_id:8, a:7, b:42}]);
- var sds = new SortDocumentSource();
- sds.addKey("a", true);
- sds.addKey("b", false);
- sds.setSource(cds);
- var docs = [], i = 0;
- async.doWhilst(
- function(cb) {
- sds.getNext(function(err, val) {
- docs[i] = val;
- return cb(err);
- });
- },
- function() {
- return docs[i++] !== null;
- },
- function(err) {
- if (err) throw err;
- assert.deepEqual([{_id:1, a:0, b:2}, {_id:0, a:1, b:3}, {_id:8, a:7, b:42}, {_id:5, a:12, b:7}, null], docs);
- return next();
- }
- );
- },
- "should not sort different types": function diffTypesSort(next) {
- var cds = getCursorDocumentSource([{_id:0, a: 1}, {_id:1, a:"foo"}]);
- var sds = new SortDocumentSource();
- sds.addKey("a", false);
- assert.throws(sds.setSource(cds));
- return next();
- },
- "should sort docs with missing fields": function missingFields(next) {
- var cds = getCursorDocumentSource([{_id:0, a: 1}, {_id:1}]);
- var sds = new SortDocumentSource();
- sds.addKey("a", true);
- sds.setSource(cds);
- var docs = [], i = 0;
- async.doWhilst(
- function(cb) {
- sds.getNext(function(err, val) {
- docs[i] = val;
- return cb(err);
- });
- },
- function() {
- return docs[i++] !== null;
- },
- function(err) {
- if (err) throw err;
- assert.deepEqual([{_id:1}, {_id:0, a:1}, null], docs);
- return next();
- }
- );
- },
- "should sort docs with null fields": function nullFields(next) {
- var cds = getCursorDocumentSource([{_id:0, a: 1}, {_id:1, a: null}]);
- var sds = new SortDocumentSource();
- sds.addKey("a", true);
- sds.setSource(cds);
- var docs = [], i = 0;
- async.doWhilst(
- function(cb) {
- sds.getNext(function(err, val) {
- docs[i] = val;
- return cb(err);
- });
- },
- function() {
- return docs[i++] !== null;
- },
- function(err) {
- if (err) throw err;
- assert.deepEqual([{_id:1, a:null}, {_id:0, a:1}, null], docs);
- return next();
- }
- );
- },
- "should not support a missing object nested in an array": function missingObjectWithinArray(next) {
- var cds = getCursorDocumentSource([{_id:0, a: [1]}, {_id:1, a:[0]}]);
- var sds = new SortDocumentSource();
- assert.throws(function() {
- sds.addKey("a.b", false);
- sds.setSource(cds);
- var c = [];
- while (!sds.eof()) {
- c.push(sds.getCurrent());
- sds.advance();
- }
- });
- return next();
- },
- "should compare nested values from within an array": function extractArrayValues(next) {
- var cds = getCursorDocumentSource([{_id:0,a:[{b:1},{b:2}]}, {_id:1,a:[{b:1},{b:1}]}]);
- var sds = new SortDocumentSource();
- sds.addKey("a.b", true);
- sds.setSource(cds);
- var docs = [], i = 0;
- async.doWhilst(
- function(cb) {
- sds.getNext(function(err, val) {
- docs[i] = val;
- return cb(err);
- });
- },
- function() {
- return docs[i++] !== null;
- },
- function(err) {
- if (err) throw err;
- assert.deepEqual([{_id:1,a:[{b:1},{b:1}]},{_id:0,a:[{b:1},{b:2}]}, null], docs);
- return next();
- }
- );
- }
- },
- "#coalesce()": {
- "should return false when coalescing a non-limit source": function nonLimitSource(next) {
- var cds = getCursorDocumentSource([{_id:0,a:[{b:1},{b:2}]}, {_id:1,a:[{b:1},{b:1}]} ]);
- var sds = SortDocumentSource.createFromJson({a:1});
- var newSrc = sds.coalesce(cds);
- assert.equal(newSrc, false);
- return next();
- },
- "should return limit source when coalescing a limit source": function limitSource(next) {
- var sds = SortDocumentSource.createFromJson({a:1});
- // TODO: add missing test cases.
- // array json getLimit
- // getShardSource
- // getMergeSource
- var newSrc = sds.coalesce(LimitDocumentSource.createFromJson(10));
- assert.ok(newSrc instanceof LimitDocumentSource);
- assert.equal(sds.getLimit(), 10);
- assert.equal(newSrc.limit, 10);
- sds.coalesce(LimitDocumentSource.createFromJson(5));
- assert.equal(sds.getLimit(), 5);
- var arr = [];
- sds.serializeToArray(arr);
- assert.deepEqual(arr, [{$sort: {a:1}}, {$limit: 5}]);
- // TODO: add missing test cases
- // doc array get limit
- // getShardSource
- // get MergeSource
- return next();
- },
- },
- "#dependencies": {
- /** Dependant field paths. */
- "should have Dependant field paths": function dependencies(next) {
- var sds = SortDocumentSource.createFromJson({sort: 1});
- sds.addKey("a", true);
- sds.addKey("b.c", false);
- var deps = {fields: {}, needWholeDocument: false, needTextScore: false};
- assert.equal(DocumentSource.GetDepsReturn.SEE_NEXT, sds.getDependencies(deps));
- // Sort keys are now part of deps fields.
- assert.equal(3, Object.keys(deps.fields).length);
- assert.equal(1, deps.fields.a);
- assert.equal(1, deps.fields["b.c"]);
- assert.equal(false, deps.needWholeDocument);
- assert.equal(false, deps.needTextScore);
- return next();
- }
- }
- }
- };
|