Différence entre les appels find et findone de MongoDB

34

Je travaille sur un projet et je ne sais pas s’il existe une différence entre le fonctionnement du findcurseur et celui du findOnecurseur. FindOne est-il juste un wrapper find().limit(1)? Je cherchais quelque chose et peut-être que quelqu'un sait si mongodb a une méthode spéciale pour le faire ou non. Je travaille avec l'API PHP pour mongodb si cela fait une différence.

WojonsTech
la source

Réponses:

33

Selon mes propres critères, les find().limit(1)ordres de grandeur sont-ils plus rapides que findOne().

Il existe soit une erreur dans la documentation de MongoDB, soit un bogue dans findOne(). findOne()effectue plus comme find().limit(N)N où N est le nombre de documents que la requête renverrait. J'ai compris cela en essayant de comprendre pourquoi mes requêtes simples étaient si lentes!

update: réponse d'un ingénieur 10gen (MongoDB):

Les deux requêtes que vous exécutez sont très différentes. Une requête de recherche renvoie un curseur, il s’agit essentiellement d’un scénario sans opération, dans la mesure où aucune donnée réelle n’est renvoyée (uniquement les informations sur le curseur). Si vous appelez findOne, vous retournez les données et fermez le curseur. La documentation devrait certainement être plus claire :-)

Mise à jour: En effet, si le find().limit(1)document est récupéré, la différence de vitesse des ordres de grandeur semble disparaître. De plus, je ne pouvais pas reproduire la différence de vitesse majeure avec le pilote JavaScript MongoDB. À l'origine, j'ai effectué une analyse comparative à l'aide du pilote Java MongoDB.

Leftium
la source
1
Super trouvaille. Question importante, cependant: vos points de repère prennent-ils find().limit(1)en compte les opérations supplémentaires que vous auriez à faire au cours de la programmation normale (comme récupérer réellement les données et fermer le curseur) qui le font findOne()automatiquement de toute façon?
Nick Chammas
@ Nick: Je pense que les opérations supplémentaires ont été couvertes. Je trouvais un document au hasard ( cookbook.mongodb.org/patterns/random-attribute ), le récupérant avec .next () et le retirant de la collection. Je n'ai pas fermé manuellement les curseurs ...
Leftium
@Leftium alors je dois demander s'il est plus rapide de faire un find.limit (1) et ensuite obtenir la valeur cursur ou est-il plus rapide de faire un findone ()
WojonsTech
2
@WojonsTech: un repère rapide dans JS montre que findOne () est en réalité plus rapide. Les résultats peuvent varier en fonction du pilote / de la plateforme. Par exemple, je ne pouvais pas reproduire la différence de vitesse en ordres de grandeur dans JS que j'avais initialement observée avec le pilote Java.
Leftium
2
Leftium, je voudrais modifier votre réponse pour souligner que, lorsque vous récupérez le document (comme vous le feriez normalement), les deux fonctions sont en réalité identiques, tout comme la documentation l'indique. À l'heure actuelle, quelqu'un lira probablement la ligne en gras au début de votre réponse et conclura que, s'ils souhaitent récupérer un document, findOne()c'est pire que find().limit(1)ce qui est incorrect.
Nick Chammas
5

findOne()est en effet un sucre syntaxique pour find().limit(1), étant donné que vous récupérez réellement le document (au lieu de simplement renvoyer le curseur avec find()).

Voir la réponse et les mises à jour de Leftium pour plus de détails.

Nick Chammas
la source
ok merci je n'aime pas utiliser les fonctions de synimus dans ma programmation préférerais mettre une limite moi-même juste pour que tout mon code soit facile à retrouver.
WojonsTech
1
En fait, dans les tests, findOne () est un peu plus rapide que find (). Limit (1).
Vladimir
@ DairT'arg - Si vous avez des sources ou des données pour sauvegarder cette affirmation, envoyez-nous une réponse avec les détails! D'après ce que j'ai compris jusqu'à présent, ils devraient être identiques tant que vous récupérez le document dans les deux cas.
Nick Chammas
3

Le code source peut aider beaucoup.

C'est java mais je suppose que ça peut aider aussi.

Le findOne(),

DBObject findOne(DBObject o, DBObject fields, DBObject orderBy, ReadPreference readPref,
                 long maxTime, TimeUnit maxTimeUnit) {

    QueryOpBuilder queryOpBuilder = new QueryOpBuilder().addQuery(o).addOrderBy(orderBy)
                                                        .addMaxTimeMS(MILLISECONDS.convert(maxTime, maxTimeUnit));

    if (getDB().getMongo().isMongosConnection()) {
        queryOpBuilder.addReadPreference(readPref);
    }

    Iterator<DBObject> i = find(queryOpBuilder.get(), fields, 0, -1, 0, getOptions(), readPref, getDecoder());

    DBObject obj = (i.hasNext() ? i.next() : null);
    if ( obj != null && ( fields != null && fields.keySet().size() > 0 ) ){
        obj.markAsPartialObject();
    }
    return obj;
}

Et voici find()

public DBCursor find( DBObject ref ){
    return new DBCursor( this, ref, null, getReadPreference());
}

Comme nous pouvons le voir, cela findOne()appelle find()en lui-même, obtient tout le DBOjectcontenu iet retourne le premier.

shellbye
la source