2010-07-09 11 views
30

Je sais comment trier les requêtes dans MongoDB par plusieurs champs, par exemple, db.coll.find().sort({a:1,b:-1}). Puis-je trier avec une fonction définie par l'utilisateur?Tri complexe de Mongo?

par exemple, en supposant a et b sont des entiers, par la différence entre a et b (a-b)?

Merci! Pourquoi ne pas créer le champ avec cette opération et trier dessus?

+1

Je suis en cours d'exécution dans le même problème. Avez-vous trouvé un moyen d'accomplir cela du côté du serveur? Je préfère vraiment ne pas avoir à faire cela sur le client, ou ajouter le champ supplémentaire. –

Répondre

29

MISE À JOUR: Cette réponse semble être périmée; il semble que le tri personnalisé peut être plus ou moins réalisé en utilisant le $project function of the aggregation pipeline pour transformer les documents d'entrée avant le tri. Voir aussi la réponse d'Ari.

Je ne pense pas que ce soit possible directement; le sort documentation ne mentionne certainement aucun moyen de fournir une fonction de comparaison personnalisée.

Vous feriez probablement mieux de faire le tri dans le client, mais si vous êtes vraiment déterminé à le faire sur le serveur, vous pouvez utiliser db.eval() pour organiser le tri sur le serveur (si votre client le soutient).

tri côté serveur:

db.eval(function() { 
    return db.scratch.find().toArray().sort(function(doc1, doc2) { 
    return doc1.a - doc2.a 
    }) 
}); 

Versus l'équivalent tri côté client:

db.scratch.find().toArray().sort(function(doc1, doc2) { 
    return doc1.a - doc2.b 
}); 

Notez qu'il est également possible de trier par un aggregation pipeline et par le $orderby operator (en plus de .sort()) Cependant, aucun de ces moyens ne vous permet de fournir une fonction de tri personnalisée.

+2

Yup. Après avoir creusé plus loin j'ai trouvé qu'il y a eu un RFE classé pour permettre aux fonctions de commodité de faire exactement cela. Je n'aime vraiment pas l'apparence de ce 'toArray()' pour mes documents ~ 10m, mais c'est apparemment l'état des choses. – gilesc

+0

Salut @gilesc. Pouvez-vous me dire comment avez-vous exactement utilisé «RFE déposé» en détail? Je suis dans la même situation. – LotusH

+1

@Wasabi Je pense qu'il faisait référence à ceci: https://jira.mongodb.org/browse/SERVER-153 Ce n'est pas possible, mais il y a un ticket ouvert où ils envisagent de l'implémenter. Mais il a été ouvert pendant des années, donc je ne sais pas si cela arrivera jamais. – rmarscher

11

+3

J'ai toujours besoin d'accéder à a et b individuellement. Donc je devrais créer un troisième champ. Cela fonctionnerait pour mon application, mais comme un principe général serait vraiment mauvaise politique. Supposons que j'avais plusieurs champs entiers, il y aurait une explosion combinatoire d'attributs dérivés (a-b), (a-c), (a-d) ... Même avec un seul champ comme celui-ci, c'est vraiment un gaspillage d'espace. Je suis upvoting et si personne d'autre ne répond acceptant, mais il doit y avoir un meilleur moyen. En SQL, c'est aussi simple que 'SELECT * FROM coll ORDER BY (a-b)'. – gilesc

+1

Notez que bien que vous puissiez le faire en SQL, ce n'est pas très efficace. La base de données doit extraire toutes les valeurs a et b, calculer a-b, trier les résultats et retourner les enregistrements associés. Avec Mongo cela signifierait charger tous les documents en mémoire si vous n'avez pas d'index sur a et b. Si vous créez un nouveau champ, vous pouvez créer un index sur ce champ, ce qui rendra cette requête très rapide. – konrad

+0

Excellente idée. Tant que la valeur de la position d'un enregistrement est localisée dans les données de l'enregistrement lui-même, cela amortit parfaitement le coût du calcul du tri sur insertion/mise à jour. – DoctorPangloss

15

rencontré ce ce qui est ce que je suis venu avec:

db.collection.aggregate([ 
    { 
    $project: { 
     difference: { $subtract: ["$a", "$b"] } 
     // Add other keys in here as necessary 
    } 
    }, 
    { 
    $sort: { difference: -1 } 
    } 
])