python - mongoclient - Limitar() y ordenar() orden pymongo y mongodb
pymongo find by id (4)
Lógicamente, debería ser lo que venga primero en la tubería, pero MongoDB siempre ordena primero antes del límite.
En mi prueba, la operación de ordenación tiene prioridad independientemente de si viene antes de saltar o después. Sin embargo, me parece un comportamiento muy extraño.
Mi conjunto de datos de muestra es:
[
{
"_id" : ObjectId("56f845fea524b4d098e0ef81"),
"number" : 48.98052410874508
},
{
"_id" : ObjectId("56f845fea524b4d098e0ef82"),
"number" : 50.98747461471063
},
{
"_id" : ObjectId("56f845fea524b4d098e0ef83"),
"number" : 81.32911244349772
},
{
"_id" : ObjectId("56f845fea524b4d098e0ef84"),
"number" : 87.95549919039071
},
{
"_id" : ObjectId("56f845fea524b4d098e0ef85"),
"number" : 81.63582683594402
},
{
"_id" : ObjectId("56f845fea524b4d098e0ef86"),
"number" : 43.25696270026136
},
{
"_id" : ObjectId("56f845fea524b4d098e0ef87"),
"number" : 88.22046335409453
},
{
"_id" : ObjectId("56f845fea524b4d098e0ef88"),
"number" : 64.00556739160076
},
{
"_id" : ObjectId("56f845fea524b4d098e0ef89"),
"number" : 16.09353150244296
},
{
"_id" : ObjectId("56f845fea524b4d098e0ef8a"),
"number" : 17.46667776660574
}
]
Código de prueba de Python:
import pymongo
client = pymongo.MongoClient("mongodb://localhost:27017")
database = client.get_database("test")
collection = database.get_collection("collection")
print("----------------[limit -> sort]--------------------------")
result = collection.find().limit(5).sort([("number", pymongo.ASCENDING)])
for r in result:
print(r)
print("----------------[sort -> limit]--------------------------")
result = collection.find().sort([("number", pymongo.ASCENDING)]).limit(5)
for r in result:
print(r)
Resultado:
----------------[limit -> sort]--------------------------
{u''_id'': ObjectId(''56f845fea524b4d098e0ef89''), u''number'': 16.09353150244296}
{u''_id'': ObjectId(''56f845fea524b4d098e0ef8a''), u''number'': 17.46667776660574}
{u''_id'': ObjectId(''56f845fea524b4d098e0ef86''), u''number'': 43.25696270026136}
{u''_id'': ObjectId(''56f845fea524b4d098e0ef81''), u''number'': 48.98052410874508}
{u''_id'': ObjectId(''56f845fea524b4d098e0ef82''), u''number'': 50.98747461471063}
----------------[sort -> limit]--------------------------
{u''_id'': ObjectId(''56f845fea524b4d098e0ef89''), u''number'': 16.09353150244296}
{u''_id'': ObjectId(''56f845fea524b4d098e0ef8a''), u''number'': 17.46667776660574}
{u''_id'': ObjectId(''56f845fea524b4d098e0ef86''), u''number'': 43.25696270026136}
{u''_id'': ObjectId(''56f845fea524b4d098e0ef81''), u''number'': 48.98052410874508}
{u''_id'': ObjectId(''56f845fea524b4d098e0ef82''), u''number'': 50.98747461471063}
A pesar de leer las respuestas de los pueblos que indican que el ordenamiento se realiza primero, la evidencia muestra algo diferente de que el límite se realiza antes del ordenamiento. ¿Hay alguna forma de forzar el orden siempre primero?
views = mongo.db.view_logging.find().sort([(''count'', 1)]).limit(10)
Si utilizo .sort().limit()
o .limit().sort()
, el límite tiene prioridad. Me pregunto si esto es algo que ver con pymongo
...
La documentación de mongodb indica que el método skip()
controla el punto de inicio del conjunto de resultados, seguido de sort()
y finaliza con el método limit()
.
Esto es independientemente del orden de su código. La razón es que mongo obtiene todos los métodos para la consulta, luego ordena los métodos de salto de clasificación en ese orden exacto y luego ejecuta la consulta.
Según la documentation , independientemente de lo que ocurra primero en su cadena de comandos, sort()
siempre se aplicará antes del limit()
.
También puede estudiar los resultados .explain()
de su consulta y observar las etapas de ejecución. Encontrará que la etapa de entrada de clasificación examina todos los filtros (en su caso, todos los documentos de la colección) y luego se aplica el límite.
Veamos un ejemplo.
Imagina que hay una base de datos foo
con una colección de test
tiene 6 documentos:
>>> col = db.foo.test
>>> for doc in col.find():
... print(doc)
{''time'': ''2016-03-28 12:12:00'', ''_id'': ObjectId(''56f9716ce4b05e6b92be87f2''), ''value'': 90}
{''time'': ''2016-03-28 12:13:00'', ''_id'': ObjectId(''56f971a3e4b05e6b92be87fc''), ''value'': 82}
{''time'': ''2016-03-28 12:14:00'', ''_id'': ObjectId(''56f971afe4b05e6b92be87fd''), ''value'': 75}
{''time'': ''2016-03-28 12:15:00'', ''_id'': ObjectId(''56f971b7e4b05e6b92be87ff''), ''value'': 72}
{''time'': ''2016-03-28 12:16:00'', ''_id'': ObjectId(''56f971c0e4b05e6b92be8803''), ''value'': 81}
{''time'': ''2016-03-28 12:17:00'', ''_id'': ObjectId(''56f971c8e4b05e6b92be8806''), ''value'': 90}
Ahora, ejecutemos consultas con orden diferente de sort()
y limit()
y verifiquemos los resultados y el plan de explicación.
Ordenar y luego limitar:
>>> from pprint import pprint
>>> cursor = col.find().sort([(''time'', 1)]).limit(3)
>>> sort_limit_plan = cursor.explain()
>>> pprint(sort_limit_plan)
{u''executionStats'': {u''allPlansExecution'': [],
u''executionStages'': {u''advanced'': 3,
u''executionTimeMillisEstimate'': 0,
u''inputStage'': {u''advanced'': 6,
u''direction'': u''forward'',
u''docsExamined'': 6,
u''executionTimeMillisEstimate'': 0,
u''filter'': {u''$and'': []},
u''invalidates'': 0,
u''isEOF'': 1,
u''nReturned'': 6,
u''needFetch'': 0,
u''needTime'': 1,
u''restoreState'': 0,
u''saveState'': 0,
u''stage'': u''COLLSCAN'',
u''works'': 8},
u''invalidates'': 0,
u''isEOF'': 1,
u''limitAmount'': 3,
u''memLimit'': 33554432,
u''memUsage'': 213,
u''nReturned'': 3,
u''needFetch'': 0,
u''needTime'': 8,
u''restoreState'': 0,
u''saveState'': 0,
u''sortPattern'': {u''time'': 1},
u''stage'': u''SORT'',
u''works'': 13},
u''executionSuccess'': True,
u''executionTimeMillis'': 0,
u''nReturned'': 3,
u''totalDocsExamined'': 6,
u''totalKeysExamined'': 0},
u''queryPlanner'': {u''indexFilterSet'': False,
u''namespace'': u''foo.test'',
u''parsedQuery'': {u''$and'': []},
u''plannerVersion'': 1,
u''rejectedPlans'': [],
u''winningPlan'': {u''inputStage'': {u''direction'': u''forward'',
u''filter'': {u''$and'': []},
u''stage'': u''COLLSCAN''},
u''limitAmount'': 3,
u''sortPattern'': {u''time'': 1},
u''stage'': u''SORT''}},
u''serverInfo'': {u''gitVersion'': u''6ce7cbe8c6b899552dadd907604559806aa2e9bd'',
u''host'': u''h008742.mongolab.com'',
u''port'': 53439,
u''version'': u''3.0.7''}}
Limita y luego ordena:
>>> cursor = col.find().limit(3).sort([(''time'', 1)])
>>> limit_sort_plan = cursor.explain()
>>> pprint(limit_sort_plan)
{u''executionStats'': {u''allPlansExecution'': [],
u''executionStages'': {u''advanced'': 3,
u''executionTimeMillisEstimate'': 0,
u''inputStage'': {u''advanced'': 6,
u''direction'': u''forward'',
u''docsExamined'': 6,
u''executionTimeMillisEstimate'': 0,
u''filter'': {u''$and'': []},
u''invalidates'': 0,
u''isEOF'': 1,
u''nReturned'': 6,
u''needFetch'': 0,
u''needTime'': 1,
u''restoreState'': 0,
u''saveState'': 0,
u''stage'': u''COLLSCAN'',
u''works'': 8},
u''invalidates'': 0,
u''isEOF'': 1,
u''limitAmount'': 3,
u''memLimit'': 33554432,
u''memUsage'': 213,
u''nReturned'': 3,
u''needFetch'': 0,
u''needTime'': 8,
u''restoreState'': 0,
u''saveState'': 0,
u''sortPattern'': {u''time'': 1},
u''stage'': u''SORT'',
u''works'': 13},
u''executionSuccess'': True,
u''executionTimeMillis'': 0,
u''nReturned'': 3,
u''totalDocsExamined'': 6,
u''totalKeysExamined'': 0},
u''queryPlanner'': {u''indexFilterSet'': False,
u''namespace'': u''foo.test'',
u''parsedQuery'': {u''$and'': []},
u''plannerVersion'': 1,
u''rejectedPlans'': [],
u''winningPlan'': {u''inputStage'': {u''direction'': u''forward'',
u''filter'': {u''$and'': []},
u''stage'': u''COLLSCAN''},
u''limitAmount'': 3,
u''sortPattern'': {u''time'': 1},
u''stage'': u''SORT''}},
u''serverInfo'': {u''gitVersion'': u''6ce7cbe8c6b899552dadd907604559806aa2e9bd'',
u''host'': u''h008742.mongolab.com'',
u''port'': 53439,
u''version'': u''3.0.7''}}
Como puede ver, en ambos casos, la clasificación se aplica primero y afecta a todos los 6 documentos y luego el límite limita los resultados a 3.
Y, los planes de ejecución son exactamente los mismos :
>>> from copy import deepcopy # just in case
>>> cursor = col.find().sort([(''time'', 1)]).limit(3)
>>> sort_limit_plan = deepcopy(cursor.explain())
>>> cursor = col.find().limit(3).sort([(''time'', 1)])
>>> limit_sort_plan = deepcopy(cursor.explain())
>>> sort_limit_plan == limit_sort_plan
True
Ver también:
Sospecho que estás pasando una clave incorrecta en el parámetro de ordenación. algo como "$ key_name" en lugar de solo "key_name"
consulte ¿Cómo le dice a Mongo que ordene una colección antes de limitar los resultados? Solución para el mismo problema que el tuyo.