web-dev-qa-db-fra.com

limit () et sort () ordonnent pymongo et mongodb

Malgré la lecture des réponses des gens indiquant que le tri est effectué en premier, les preuves montrent que la limite est effectuée avant le tri. Existe-t-il un moyen de forcer le tri toujours en premier?

views = mongo.db.view_logging.find().sort([('count', 1)]).limit(10)

Que j'utilise .sort().limit() ou .limit().sort(), la limite est prioritaire. Je me demande si cela a quelque chose à voir avec pymongo...

20
disruptive

Selon la documentation , quelle que soit la première de votre chaîne de commandes, sort() sera toujours appliqué avant la limit().

Vous pouvez également étudier les résultats .explain() de votre requête et regarder les étapes d'exécution - vous constaterez que l'étape d'entrée de tri examine tous les éléments filtrés (dans votre cas, tous les documents dans la collection), puis la limite est appliquée.


Voyons un exemple.

Imaginez qu'il existe une base de données foo avec une collection test contenant 6 documents:

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

Maintenant, exécutons les requêtes avec un ordre différent de sort() et limit() et vérifions les résultats et le plan d'explication.

Triez puis limitez:

>>> 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'}}

Limiter puis trier:

>>> 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'}}

Comme vous pouvez le voir, dans les deux cas, le tri est appliqué en premier et affecte tous les 6 documents, puis la limite limite les résultats à 3.

Et, les plans d'exécution sont exactement les mêmes :

>>> 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

Regarde aussi:

23
alecxe

La documentation de mongodb indique que la méthode skip() contrôle le point de départ de l'ensemble de résultats, suivi de sort() et se termine par la méthode limit().

C'est indépendamment de l'ordre de votre code. La raison en est que mongo obtient toutes les méthodes de la requête, puis il ordonne les méthodes skip-sort-limit dans cet ordre exact, puis exécute la requête.

3
jquijano

Logiquement, cela devrait être ce qui vient en premier dans le pipeline, mais MongoDB trie toujours en premier avant la limite.

Dans mon test, l'opération de tri a la priorité, qu'elle se produise avant ou après. Cependant, cela semble être un comportement très étrange pour moi.

Mon exemple de jeu de données est:

[
    {
        "_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
    }
]

Code de test 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)

Résultat:

----------------[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}
3
Saleem

Je soupçonne que vous passez une mauvaise clé dans le paramètre de tri. quelque chose comme "$ key_name" au lieu de simplement "key_name"

refer Comment dites-vous à Mongo de trier une collection avant de limiter les résultats?solution pour le même problème que le vôtre

3
Hari Krishnan

La réponse acceptée n'a pas fonctionné pour moi, mais cela fonctionne:

last5 = db.collection.find(   {'key': "YOURKEY"},   sort=[( '_id', pymongo.DESCENDING )] ).limit(5)

avec la limite à l'extérieur et le tri à l'intérieur de l'argument find.

0
Evan