-
Notifications
You must be signed in to change notification settings - Fork 12
/
Copy pathmongoose-simple-random.js
123 lines (109 loc) · 3.46 KB
/
mongoose-simple-random.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
// Utility methods - some were pulled partially from other MIT-licensed projects
var utils = (function () {
var random = function(max) { return Math.floor(Math.random() * (max+1)); };
var shuffle = function(a) {
var length = a.length,
shuffled = Array(length);
for (var index = 0, rand; index < length; ++index) {
rand = random(index);
if (rand !== index) shuffled[index] = shuffled[rand];
shuffled[rand] = a[index];
}
return shuffled;
};
var range = function(length) {return Array(length).fill(null).map(function(cv, i) {return i}); };
var sample = function(a, n) { return shuffle(a).slice(0, Math.max(0, n)); };
var randomMap = function(count, limit, next, callback) { return asyncMap(sample(range(count), limit), next, callback); };
var asyncMap = function(items, next, callback) {
var transformed = new Array(items.length),
count = 0,
halt = false;
if (items.length === 0) {
return callback()
}
items.forEach(function(item, index) {
next(item, function(error, transformedItem) {
if (halt) return;
if (error) {
halt = true;
return callback(error);
}
transformed[index] = transformedItem;
if (++count === items.length) return callback(undefined, transformed);
});
});
};
var checkParams = function (conditions, fields, options, callback) {
if (typeof conditions === 'function') {
callback = conditions;
conditions = {};
fields = {};
options = {};
} else if (typeof fields === 'function') {
callback = fields;
fields = {};
options = {};
} else if (typeof options === 'function') {
callback = options;
options = {};
}
if (options.skip) {
delete options.skip;
}
return {
conditions: conditions,
fields: fields,
options: options,
callback: callback
}
};
return {
randomMap: randomMap,
checkParams: checkParams
};
}());
module.exports = exports = function (schema) {
schema.statics.findRandom = function (conditions, fields, options, callback) {
var args = utils.checkParams(conditions, fields, options, callback),
_this = this;
return _this.estimatedDocumentCount(args.conditions, function(err, count) {
var limit = 1,
populate = null;
if (err) {
return args.callback(err, undefined);
}
if (args.options.limit) {
limit = args.options.limit;
delete args.options.limit;
}
if (limit > count) {
limit = count;
}
if (args.options.populate) {
populate = args.options.populate;
delete args.options.populate;
}
return utils.randomMap(count, limit, (item, next) => {
args.options.skip = item;
var find = _this.findOne(args.conditions, args.fields, args.options);
if (populate) {
find.populate(populate);
}
find.exec(next);
}, args.callback);
});
};
schema.statics.findOneRandom = function (conditions, fields, options, callback) {
var args = utils.checkParams(conditions, fields, options, callback);
args.options.limit = 1;
this.findRandom(args.conditions, args.fields, args.options, function(err, docs) {
if (docs && docs.length === 1) {
return args.callback(err, docs[0]);
}
if (!err) {
err = "findOneRandom yielded no results.";
}
return args.callback(err);
});
};
};