如何对mongoDB中的集合logging进行sorting

MongoDB的noob在这里…

好吧,我有一个学生的集合,每个人都有一个如下所示的logging….我想按照降序排列“types”:“作业”分数。

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 

但它不工作。

您将需要在应用程序代码中操作embedded式数组或使用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 } 

这就是我们可以通过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(); } } } 

这很容易猜到,但无论如何,不​​要试图用蒙戈大学的课程作弊,因为那时你不会理解基础知识。

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

由于这个问题可以以不同的方式进行pipe理,我想说的另一种解决scheme是“插入和sorting”,这样你将得到Ordered数组,在你将做一个Find()。

考虑这些数据:

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

这里我们将更新文档,进行sorting。

 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

@Stennie的答案是好的,也许一个$ group操作符会保留原始文档,而不会在许多文档中爆炸(一个分数)。

当您的应用程序使用JavaScript时,我只是添加另一个解决scheme

如果只查询一个文档,有时用JSsortingembedded的数组更容易,而不是做一个聚合。 当你的文档有很多字段时,甚至比使用$ 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 }); 

对我来说这个工作,这是一个粗略的代码,但每个学生的最低任务的结果是正确的。

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

我相信你正在做的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}}) 

这是我使用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. 学生collections中获取分数数组
  2. 从分数数组中获得所有得分值,其中==types的作业
  3. 对得分值进行sorting,使得最低成为第一个元素[score.get(0)]
  4. 然后,循环遍历主要分数,并创build分数数组的新副本,同时跳过元素types== 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))); } } 

当然已经很晚了,但我只想在Mongo Shell上提供我自己的解决scheme:

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

根据比分sorting可以是简单的:

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

但你需要find一个types:作业…

它应该是这样的:

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