簡體   English   中英

如何在MongoDB中的集合記錄中對數組進行排序?

[英]How to sort array inside collection record in MongoDB?

我有個同學的集合,每個看起來像下面,我想那種創紀錄的scores降序的順序排列score

這個咒語在 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

但它不起作用。

您將需要在應用程序代碼中操作嵌入的數組或使用 MongoDB 2.2 中的新聚合框架

mongo shell 中的示例聚合:

db.students.aggregate(
    // Initial document match (uses index, if a suitable one is available)
    { $match: {
        _id : 1
    }},

    // Expand the scores array into a stream of documents
    { $unwind: '$scores' },

    // Filter to 'homework' scores 
    { $match: {
        'scores.type': 'homework'
    }},

    // Sort in descending order
    { $sort: {
        'scores.score': -1
    }}
)

示例輸出:

{
    "result" : [
        {
            "_id" : 1,
            "name" : "Aurelia Menendez",
            "scores" : {
                "type" : "homework",
                "score" : 71.76133439165544
            }
        },
        {
            "_id" : 1,
            "name" : "Aurelia Menendez",
            "scores" : {
                "type" : "homework",
                "score" : 34.85718117893772
            }
        }
    ],
    "ok" : 1
}

由於可以通過不同方式管理這個問題,我想說另一種解決方案是“插入和排序”,這樣您將在進行 Find() 時獲得 Ordered 數組。

考慮這個數據:

{
   "_id" : 5,
   "quizzes" : [
      { "wk": 1, "score" : 10 },
      { "wk": 2, "score" : 8 },
      { "wk": 3, "score" : 5 },
      { "wk": 4, "score" : 6 }
   ]
}

在這里,我們將更新文檔,進行排序。

db.students.update(
   { _id: 5 },
   {
     $push: {
       quizzes: {
          $each: [ { wk: 5, score: 8 }, { wk: 6, score: 7 }, { wk: 7, score: 6 } ],
          $sort: { score: -1 },
          $slice: 3 // keep the first 3 values
       }
     }
   }
)

結果是:

{
  "_id" : 5,
  "quizzes" : [
     { "wk" : 1, "score" : 10 },
     { "wk" : 2, "score" : 8 },
     { "wk" : 5, "score" : 8 }
  ]
}

文檔: https : //docs.mongodb.com/manual/reference/operator/update/sort/#up._S_sort

這就是我們如何用 JS 和 mongo 控制台解決這個問題:

db.students.find({"scores.type": "homework"}).forEach(
  function(s){
    var sortedScores = s.scores.sort(
      function(a, b){
        return a.score<b.score && a.type=="homework";
      }
    );
    var lowestHomeworkScore = sortedScores[sortedScores.length-1].score;
    db.students.update({_id: s._id},{$pull: {scores: {score: lowestHomeworkScore}}}, {multi: true});
  })

這是 Java 代碼,可用於找出數組中的最低分數並將其刪除。

public class sortArrayInsideDocument{
public static void main(String[] args) throws UnknownHostException {
    MongoClient client = new MongoClient();
    DB db = client.getDB("school");
    DBCollection lines = db.getCollection("students");
    DBCursor cursor = lines.find();
    try {
        while (cursor.hasNext()) {
            DBObject cur = cursor.next();
            BasicDBList dbObjectList = (BasicDBList) cur.get("scores");
            Double lowestScore = new Double(0);
            BasicDBObject dbObject = null;
            for (Object doc : dbObjectList) {
                BasicDBObject basicDBObject = (BasicDBObject) doc;
                if (basicDBObject.get("type").equals("homework")) {
                    Double latestScore = (Double) basicDBObject
                            .get("score");
                    if (lowestScore.compareTo(Double.valueOf(0)) == 0) {
                        lowestScore = latestScore;
                        dbObject = basicDBObject;

                    } else if (lowestScore.compareTo(latestScore) > 0) {
                        lowestScore = latestScore;
                        dbObject = basicDBObject;
                    }
                }
            }
            // remove the lowest score here.
            System.out.println("object to be removed : " + dbObject + ":"
                    + dbObjectList.remove(dbObject));
            // update the collection
            lines.update(new BasicDBObject("_id", cur.get("_id")), cur,
                    true, false);
        }
    } finally {
        cursor.close();
    }
}
}

為了對數組進行排序,請按照下列步驟操作:

1)使用 unwind 遍歷數組

2)排序數組

3)使用 group 將數組的對象合並為一個數組

4)然后投影其他領域

詢問

db.taskDetails.aggregate([
    {$unwind:"$counter_offer"},
    {$match:{_id:ObjectId('5bfbc0f9ac2a73278459efc1')}},
    {$sort:{"counter_offer.Counter_offer_Amount":1}},
   {$unwind:"$counter_offer"},
   {"$group" : {_id:"$_id",
    counter_offer:{ $push: "$counter_offer" },
    "task_name": { "$first": "$task_name"},
    "task_status": { "$first": "$task_status"},
    "task_location": { "$first": "$task_location"},
}}

]).pretty()

這很容易猜到,但無論如何,請不要在 mongo 大學課程中作弊,因為那時您將無法理解基礎知識。

db.students.find({}).forEach(function(student){ 

    var minHomeworkScore,  
        scoresObjects = student.scores,
        homeworkArray = scoresObjects.map(
            function(obj){
                return obj.score;
            }
        ); 

    minHomeworkScore = Math.min.apply(Math, homeworkArray);

    scoresObjects.forEach(function(scoreObject){ 
        if(scoreObject.score === minHomeworkScore){ 
            scoresObjects.splice(scoresObjects.indexOf(minHomeworkScore), 1); 
        } 
    });

    printjson(scoresObjects);

});

我相信你在做M101P: MongoDB for Developers其中作業 3.1 是從兩個作業分數中刪除較低的一個。 由於到目前為止還沒有教授聚合,因此您可以執行以下操作:

import pymongo

conn = pymongo.MongoClient('mongodb://localhost:27017')
db = conn.school
students = db.students

for student_data in students.find():
    smaller_homework_score_seq = None
    smaller_homework_score_val = None
    for score_seq, score_data in enumerate(student_data['scores']):
        if score_data['type'] == 'homework':
            if smaller_homework_score_seq is None or smaller_homework_score_val > score_data['score']:
                smaller_homework_score_seq = score_seq
                smaller_homework_score_val = score_data['score']
    students.update({'_id': student_data['_id']}, {'$pop': {'scores': smaller_homework_score_seq}})

也訂購標題和數組標題並返回整個集合數據集合名稱是菜單

[
            {
                "_id": "5f27c5132160a22f005fd50d",
                "title": "Gift By Category",
                "children": [
                    {
                        "title": "Ethnic Gift Items",
                        "s": "/gift?by=Category&name=Ethnic"
                    },
                    {
                        "title": "Novelty Gift Items",
                        "link": "/gift?by=Category&name=Novelty"
                    }
                ],
                "active": true
            },
            {
                "_id": "5f2752fc2160a22f005fd50b",
                "title": "Gift By Occasion",
                "children": [
                    {
                        "title": "Gifts for Diwali",
                        "link": "/gift-for-diwali" 
                    },
                    {
                        "title": "Gifts for Ganesh Chathurthi",
                        "link": "/gift-for-ganesh-chaturthi",
                    }
                ],
                
                "active": true
            }
    ]

查詢如下

let menuList  = await  Menu.aggregate([
                { 
                    $unwind: '$children'
                }, 
                {
                    $sort:{"children.title":1}
                },
                {   
                    $group : { _id : "$_id",
                        root: { $mergeObjects: '$$ROOT' },   
                        children: { $push: "$children" } 
                    } 
                },
                {
                    $replaceRoot: {
                        newRoot: {
                            $mergeObjects: ['$root', '$$ROOT']
                        }
                    }
                },
                {
                    $project: {
                        root: 0 
                    }
                },
                { 
                    $match: {
                                $and:[{'active':true}],
                            }
                },
                {
                    $sort:{"title":1}
                }                  
    ]);

@Stennie 的回答很好,也許$group操作符對於保留原始文檔很有用,而不會在許多文檔中將其分解(一個分數)。

我只是在為您的應用程序使用 javascript 時添加了另一個解決方案

如果只查詢一個文檔,有時通過 JS 對嵌入的數組進行排序會更容易,而不是進行聚合。 當你的文檔有很多字段時,它甚至比使用$push運算符更好,否則你必須一個一個地推送所有字段,或者使用$$ROOT運算符(我錯了嗎?)

我的示例代碼使用Mongoose.js :假設您已初始化您的 Students 模型。

// 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
});

這項工作對我來說是一個有點粗糙的代碼,但每個學生的最低任務的結果是正確的。

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

這是我使用 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()

這就是我在 Java 中實現的方式(保持簡單以便更容易理解)-

方法 :

  1. 學生集合中獲取分數數組
  2. 類型 == 作業的分數數組中獲取所有分數
  3. 對分值進行排序,使最低成為第一個元素 [score.get(0)]
  4. 然后,遍歷主要分數並創建分數數組的新副本,同時跳過類型 == 作業 && 分數 == 分數.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)));
    }       
}

當然已經晚了,但我只想在 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}});
}

Mongo 5.2 release schedule 開始,這是新的$sortArray聚合運算符的確切用例:

// {
//   name: "Aurelia Menendez",
//   scores: [
//     { type: "exam",     score: 60.06 }
//     { type: "quiz",     score: 52.79 }
//     { type: "homework", score: 71.76 }
//     { type: "homework", score: 34.85 }
//   ]
// }
db.collection.aggregate([
  { $set: {
    scores: {
      $sortArray: {
        input: "$scores",
        sortBy: { score: -1 }
      }
    }
  }}
])
// {
//   name: "Aurelia Menendez",
//   scores: [
//     { type: "homework", score: 71.76 },
//     { type: "exam",     score: 60.06 },
//     { type: "quiz",     score: 52.79 },
//     { type: "homework", score: 34.85 }
//   ]
// }

這:

  • 排序( $sortArrayscores數組( input: "$scores"
  • 通過對score s 應用排序( sortBy: { score: -1 }
  • 無需應用昂貴的$unwind$sort$group階段的組合

按分數排序可以很簡單,例如:

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

但你需要找到一個 type:homework ...

它應該是這樣的:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM