如何在MongoDB的文档集合中对数组进行排序?

57

我有一组学生,每个学生都有一个类似以下样子的记录,我想按照分数score降序排序scores数组。

在Mongo shell上如何实现?

> db.students.find({'_id': 1}).pretty()
{
        "_id" : 1,
        "name" : "Aurelia Menendez",
        "scores" : [
                {
                        "type" : "exam",
                        "score" : 60.06045071030959
                },
                {
                        "type" : "quiz",
                        "score" : 52.79790691903873
                },
                {
                        "type" : "homework",
                        "score" : 71.76133439165544
                },
                {
                        "type" : "homework",
                        "score" : 34.85718117893772
                }
        ]
}

我正在尝试这个咒语....

 doc = db.students.find()

 for (_id,score) in doc.scores:
     print _id,score

但是它没有起作用。

17个回答

-1
这是我使用pyMongo的方法,pyMongo是与MongoDB配合使用的Python驱动程序:
import pymongo


conn = pymongo.MongoClient('mongodb://localhost')

def remove_lowest_hw():
    db = conn.school
    students = db.students

    # first sort scores in ascending order
    students.update_many({}, {'$push':{'scores':{'$each':[], '$sort':{'score': 1}}}})

    # then collect the lowest homework score for each student via projection
    cursor = students.find({}, {'scores':{'$elemMatch':{'type':'homework'}}})

    # iterate over each student, trimming each of the lowest homework score
    for stu in cursor:
        students.update({'_id':stu['_id']}, {'$pull':{'scores':{'score':stu['scores'][0]['score']}}})

remove_lowest_hw()

conn.close()

-2

这段代码对我来说可行,虽然有点粗糙,但每个学生的最低任务结果都是正确的。

var scores_homework = []
db.students.find({"scores.type": "homework"}).forEach(
  function(s){
    s.scores.forEach(
        function(ss){
            if(ss.type=="homework"){
                ss.student_id = s._id
                scores_homework.push(ss)
            }
        }
    )
})
for(i = 0; i < scores_homework.length; i++)
{
    var b = i+1;
    var ss1 = scores_homework[i];
    var ss2 = scores_homework[b];
    var lowest_score = {};
    if(ss1.score > ss2.score){
        lowest_score.type = ss2.type;
        lowest_score.score = ss2.score;
        db.students.update({_id: ss2.student_id},{$pull: {scores: {score: lowest_score.score}}});
    }else if(ss1.score < ss2.score){
        lowest_score.type = ss1.type;
        lowest_score.score = ss1.score;
        db.students.update({_id: ss1.student_id},{$pull: {scores: {score: lowest_score.score}}});
    }else{
        lowest_score.type = ss1.type;
        lowest_score.score = ss1.score;
        db.students.update({_id: ss1.student_id},{$pull: {scores: {score: lowest_score.score}}});
    }
    i++
}

-2

这是我在Java中的实现方式(保持简单易懂) -

方法:

  1. student集合中获取scores数组
  2. 从分数数组中获取所有type == homeworkscore
  3. 对分数值进行排序,使最低分成为第一个元素[score.get(0)]
  4. 然后,循环遍历主要的scores并创建新的分数数组副本,同时跳过type == homework && score == scores.get(0)的元素
  5. 最后,将新的分数数组更新到学生文档中。

以下是可行的Java代码:

    public void removeLowestScore(){
    //Create mongo client and database connection and get collection
    MongoClient client = new MongoClient("localhost");
    MongoDatabase database = client.getDatabase("school");
    MongoCollection<Document> collection = database.getCollection("students");


    FindIterable<Document> docs = collection.find();
    for (Document document : docs) {

        //Get scores array
        ArrayList<Document> scores = document.get("scores", ArrayList.class);           

        //Create a list of scores where type = homework
        List<Double> homeworkScores = new ArrayList<Double>();
        for (Document score : scores) {
            if(score.getString("type").equalsIgnoreCase("homework")){
                homeworkScores.add(score.getDouble("score"));   
            }
        }

        //sort homework scores
        Collections.sort(homeworkScores);

        //Create a new list to update into student collection
        List<Document> newScoresArray = new ArrayList<Document>();
        Document scoreDoc = null;

        //Below loop populates new score array with eliminating lowest score of "type" = "homework"
        for (Document score : scores) {
            if(score.getString("type").equalsIgnoreCase("homework") && homeworkScores.get(0) == score.getDouble("score")){                  
                    continue;                       
                }else{
                    scoreDoc = new Document("type",score.getString("type"));
                    scoreDoc.append("score",score.getDouble("score"));
                    newScoresArray.add(scoreDoc);
                }               
            }           

        //Update the scores array for every student using student _id
        collection.updateOne(Filters.eq("_id", document.getInteger("_id")), new Document("$set",new Document("scores",newScoresArray)));
    }       
}

-2

虽然有点晚了,但我想贡献一下我的Mongo Shell解决方案:

var students = db.getCollection('students').find({});
for(i = 0 ; i < students.length(); i++) {
    var scores = students[i].scores;
    var tmp = [];
    var min = -1 ;
    var valueTmp = {};
    for(j = 0 ; j < scores.length; j++) {        
        if(scores[j].type != 'homework') {
            tmp.push(scores[j]);
        } else {
            if (min == -1) {
                min = scores[j].score;
                valueTmp = scores[j];
            } else {
                if (min > scores[j].score) {
                    min = scores[j].score;
                    tmp.push(valueTmp);
                    valueTmp = scores[j];
                } else {
                    tmp.push(scores[j]);
                }
            }
        }
    }
    db.students.updateOne({_id:students[i]._id},
                            {$set:{scores:tmp}});
}

-2

@Stennie的答案很好,也许使用$group操作符可以保留原始文档,而不是将其分解为许多文档(每个得分一个)。

我只是添加了另一种使用JavaScript应用程序的解决方案

如果您只查询一个文档,则有时通过JS对嵌入式数组进行排序比执行聚合更容易。 当您的文档具有许多字段时,甚至比使用$push操作符更好,否则您必须逐个推送所有字段,或者使用$$ROOT操作符(我错了吗?)

我的示例代码使用Mongoose.js: 假设您已经初始化了学生模型。

// Sorting
function compare(a, b) {
  return a.score - b.score;
}

Students.findById('1', function(err, foundDocument){
  foundDocument.scores = foundDocument.scores.sort(compare);
  
  // do what you want here...
  // foundModel keeps all its fields
});

-3

按分数排序可以很简单,例如:

db.students.find({_id:137}).sort({score:-1}).pretty()

但你需要找到类型为“作业”的那个...


在Mongo shell中,$sort与$push和$each一起使用将执行以下操作:db.students.updateMany({}, {'$push':{'scores':{'$each':[], '$sort':{'score': 1}}}}),参见mongodb文档 - Treefish Zhang

-5

应该是这样的:

db.students.find().sort(scores: ({"score":-1}));

如果这是在mongoshell中编写的,那么它是无效的,也不能完成他所要求的工作。正确的写法应该是db.students.find().sort({"scores.score":-1}),但这并没有对任何东西进行排序(至少我看不到),特别是学生中的分数数组。据我所知,您需要手动迭代这些数组条目并进行排序,mongo不会自动执行此操作。 - philnate
同时,Philnate是正确的...这在Mongo shell中并不能带来期望的结果...感谢尝试。 - thefonso
我是一个Python和MongoDB的新手...这个链接提供了一个PHP的答案...我正在寻找Python或Mongo shell的解决方案。 - thefonso

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接