web-dev-qa-db-fra.com

Mauvaises performances d'agrégation de recherche

J'ai deux collections

Des postes:

{
    "_Id": "1",
    "_PostTypeId": "1",
    "_AcceptedAnswerId": "192",
    "_CreationDate": "2012-02-08T20:02:48.790",
    "_Score": "10",
    ...
    "_OwnerUserId": "6",
    ...
},
...

et utilisateurs:

{
    "_Id": "1",
    "_Reputation": "101",
    "_CreationDate": "2012-02-08T19:45:13.447",
    "_DisplayName": "Geoff Dalgas",
    ...
    "_AccountId": "2"
},
...

et je veux trouver des utilisateurs qui écrivent entre 5 et 15 messages . Voici à quoi ressemble ma requête:

db.posts.aggregate([
    {
        $lookup: {
            from: "users", 
            localField: "_OwnerUserId",
            foreignField: "_AccountId", 
            as: "X"
        }
    },  
    {
        $group: {
            _id: "$X._AccountId", 
            posts: { $sum: 1 }
        }
    },   
    {
        $match : {posts: {$gte: 5, $lte: 15}}
    },  
    {
        $sort: {posts: -1 }
    },
    {
        $project : {posts: 1}
    }
])

et ça marche très lentement. Pour les utilisateurs de 6k et 10k postes, il a fallu plus de 40 secondes pour obtenir une réponse alors que dans une base de données relationnelle, je recevais une réponse en une fraction de seconde . Où est le problème? Je viens juste de commencer à utiliser mongodb et il est fort possible que je me suis plaint de cette requête.

8
user3616181

from https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/

foreignField Spécifie le champ à partir des documents dans le fichier from collection. $ lookup effectue une correspondance d'égalité sur foreignField à le localField à partir des documents d'entrée. Si un document est à partir de collection ne contient pas foreignField, la recherche $ traite le fichier valeur nulle pour les besoins de correspondance.

Cela sera effectué comme n'importe quelle autre requête.

Si vous n'avez pas d'index sur le champ _AccountId, une requête d'analyse complète de table sera effectuée pour chacun des 10 000 articles. La majeure partie du temps sera consacrée à cette analyse.

db.users.ensureIndex("_AccountId", 1) 

accélère le processus pour qu'il effectue 10 000 occurrences d'index au lieu de 10 000 analyses de table. 

11
bauman.space

En plus de la suggestion de bauman.space de mettre un index sur le champ _accountId (ce qui est essentiel), vous devez également effectuer l'étape $ match le plus tôt possible dans l'agrégation. pipeline (c’est-à-dire comme première étape). Même s'il n'utilisera aucun index (à moins d'indexer le champ posts), il filtrera le jeu de résultats avant de passer à l'étape $ lookup (join).

La raison pour laquelle votre requête est terriblement lente est que, pour every post, elle effectue une recherche non indexée (lecture séquentielle) pour chaque utilisateur. C'est environ 60m lit!

Consultez la section Optimisation du pipeline de la Documents d'agrégation MongoDB .

5
D G

Utilisez d'abord $ match puis $ lookup. $ match filter les lignes doivent être examinées en tant que $ lookup. C'est efficace. 

2
theshemul