Non hai menzionato quale versione di MongoDB stai usando, ma la soluzione sarà simile a quanto presentato qui in ogni caso. Dimostrerò sopra 2.2.4, che è ciò che viene fornito con Ubuntu 13.04.
Il problema è di iniettare l'opzione nel cursore. Ecco dove le addOption
vita:
> var cursor = db.test.find()
> cursor.addOption
function (option) {
this._options |= option;
return this;
}
diamo un'occhiata a come mapReduce
è definito:
> db.test.mapReduce
function (map, reduce, optionsOrOutString) {
var c = {mapreduce:this._shortName, map:map, reduce:reduce};
...
var raw = this._db.runCommand(c);
...
return new MapReduceResult(this._db, raw);
}
Così si costruisce un documento per eseguire il comando tramite runCommand
. Diamo un'occhiata più al suo interno:
> db.runCommand
function (obj) {
if (typeof obj == "string") {
var n = {};
n[obj] = 1;
obj = n;
}
return this.getCollection("$cmd").findOne(obj);
}
Così il comando viene eseguito tramite findOne
. Diamo un'occhiata:
> db.test.findOne
function (query, fields, options) {
var cursor = this._mongo.find(this._fullName, this._massageObject(query) || {}, fields, -1, 0, 0, options || this.getQueryOptions());
if (!cursor.hasNext()) {
return null;
}
var ret = cursor.next();
...
return ret;
}
Ah, c'è qualcosa di interessante qui. Il cursore viene inizializzato con flag provenienti dal parametro options
, che sfortunatamente non aiuta il tuo caso perché runCommand
lascia non impostato, ma lo esegue con getQueryOptions()
, che proviene dalla raccolta. Diamo un'occhiata:
> db.collection.getQueryOptions
function() {
var options = 0;
if (this.getSlaveOk()) {
options |= 4;
}
return options;
}
Oops .. non va bene. Quindi non abbiamo accesso al cursore, né alcun modo per iniettare opzioni di query nel comando eseguito tramite mezzi non hacker.
Bene, ma abbiamo imparato molto su come i comandi di riduzione della mappa vengono effettivamente inviati al server attraverso tale processo. È solo un documento che viene interrogato su una specifica raccolta nel database. Ciò significa che possiamo costruire la stessa query ed eseguirla autonomamente, ma fornendo tutti i flag necessari.
Non mi occuperò della creazione dell'intero comando MongoDB e dell'impostazione del risultato, ma ti mostrerò che funziona effettivamente eseguendo il comando isMaster
.
Questo è il comando che esegue senza bandiere:
> db.getCollection("$cmd").findOne({isMaster: 1}).ismaster
true
Per vedere la differenza in effetti, ci TCPDUMP la comunicazione con il database.Possiamo vedere in the wire protocol documentation che le bandiere rilevanti vivono a destra prima del nome della raccolta, in un 32 bit integer, quindi è facile da individuare il pezzo rilevante della discarica:
. vvvvvvvvv
0x0040: d407 0000 0000 0000 7465 7374 2e24 636d ........test.$cm
0x0050: 6400 0000 0000 ffff ffff 1700 0000 0169 d..............i
Good. Possiamo vedere quattro byte azzerati, proprio prima del nome della collezione.
Ora, facciamo lo stesso mentre forniamo alcune bandiere. Abbiamo imparato dalla sezione di debug sopra che le bandiere di query possono essere forniti come la terza opzione di findOne
, quindi cerchiamo di fare quello:
> db.getCollection("$cmd").findOne({isMaster: 1}, undefined, 0xBEEF).ismaster
true
e vediamo la discarica:
. vvvvvvvvv
0x0040: d407 0000 efbe 0000 7465 7374 2e24 636d ........test.$cm
0x0050: 6400 0000 0000 ffff ffff 1700 0000 0169 d..............i
Hey, il nostro le bandiere sono state consegnate dove dovrebbero, e possiamo anche vederle invertite, il che significa che i byte sono codificati come little-endian, corrispondenti allo the docs.
Quindi, questo significa che è possibile fornire la bandiera DBQuery.Option.noTimeout
come la terza opzione di findOne
, e mano-codice il comando map-reduce come descritto in the documentation in un modo simile a quello che abbiamo fatto con isMaster
, ed otterrete quello che tu vuoi.