Diferencia entre las llamadas find y findone de MongoDB

34

Estoy trabajando en un proyecto y no estoy seguro de si hay una diferencia entre la forma en que funciona el findcursor y la forma en que findOnefunciona. ¿FindOne es solo un contenedor find().limit(1)? Lo estaba buscando y tal vez alguien sabe si mongodb tiene un método especial para ello o no. Estoy trabajando con la API de PHP para mongodb si eso hace la diferencia.

WojonsTech
fuente

Respuestas:

33

Basado en mis propios puntos de referencia, find().limit(1)es un orden de magnitud más rápido que findOne().

Hay un error en la documentación de MongoDB o un error en findOne(). findOne()realiza más como find().limit(N)donde N es el número de documentos que devolvería la consulta. ¡Me di cuenta de esto mientras intentaba entender por qué mis consultas simples eran tan lentas!

actualización: respuesta de un ingeniero de 10gen (MongoDB):

Las dos consultas que está ejecutando son muy diferentes. Una consulta de búsqueda devuelve un cursor, esto es esencialmente un escenario sin operación, ya que no se devuelven datos reales (solo la información del cursor). Si llama a findOne, en realidad está devolviendo los datos y cerrando el cursor. Los documentos definitivamente deberían ser más claros :-)

Actualización: De hecho, si find().limit(1)se recupera el documento, la diferencia de velocidad de los órdenes de magnitud parece desaparecer. Además, no pude reproducir la gran diferencia de velocidad con el controlador MongoDB JavaScript. Originalmente comparé con el controlador MongoDB Java.

Leftium
fuente
1
Gran descubrimiento. Sin embargo, una pregunta importante: ¿Sus puntos de referencia explican las operaciones adicionales que tendría que hacer find().limit(1)en el curso de la programación normal (como recuperar los datos y cerrar el cursor) que findOne()de todos modos lo hace automáticamente para usted?
Nick Chammas el
@ Nick: Creo que se cubrieron operaciones adicionales. Estaba buscando un documento aleatorio ( cookbook.mongodb.org/patterns/random-attribute ), obtenía el documento con .next () y lo eliminaba de la colección. No
cerré
@Leftium, entonces debo preguntar si es más rápido hacer un find.limit (1) y luego obtener el valor cursur o es más rápido hacer un findone ()
WojonsTech
2
@WojonsTech: un punto de referencia rápido en JS muestra que findOne () es realmente más rápido. Sin embargo, los resultados pueden variar según el controlador / plataforma. Por ejemplo, no pude reproducir las órdenes de magnitud de diferencia de velocidad en JS que observé originalmente con el controlador Java.
Leftium
2
Leftium, editaría su respuesta para enfatizar que cuando realmente recupera el documento (lo que normalmente haría), las dos funciones son realmente idénticas, tal como lo indica la documentación. En este momento, es probable que alguien lea la línea en negrita al comienzo de su respuesta y concluya que si quieren recuperar un documento, findOne()es peor que find().limit(1), lo cual es incorrecto.
Nick Chammas
5

findOne()es de hecho el azúcar sintáctica para find().limit(1), considerando que se está recuperando en realidad el documento (en lugar de sólo devolver el cursor con find()).

Vea la respuesta y las actualizaciones de Leftium para más detalles.

Nick Chammas
fuente
bien, gracias, no me gusta usar las funciones synimus en mi programación, preferiría poner el límite uno solo para que todo mi código sea fácil de localizar.
WojonsTech
1
En realidad, en los puntos de referencia, findOne () es un poco más rápido que find (). Limit (1).
Vladimir
@ DairT'arg: si tiene fuentes o datos para respaldar este reclamo, ¡publique una respuesta con todos los detalles! Por lo que he reunido hasta ahora, deben ser idénticos siempre que recupere el documento en ambos casos.
Nick Chammas
3

El código fuente puede ayudar mucho.

Es Java, pero supongo que también puede ayudar.

El 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;
}

Y aqui esta find()

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

Como podemos ver que las findOne()llamadas find()en si mismo, se lleva todos los DBOjecten iy luego regresar a la primera.

adiós
fuente