web-dev-qa-db-ja.com

mongodbのグループから返されたドキュメントを削除するにはどうすればよいですか?

私はmongodbの初心者で、宿題の問題に取り組んでいます。データセットは次のようになります。

{ "_id" : { "$oid" : "50906d7fa3c412bb040eb577" }, "student_id" : 0, "type" : "exam", "score" : 54.6535436362647 }
{ "_id" : { "$oid" : "50906d7fa3c412bb040eb578" }, "student_id" : 0, "type" : "quiz", "score" : 31.95004496742112 }
{ "_id" : { "$oid" : "50906d7fa3c412bb040eb579" }, "student_id" : 0, "type" : "homework", "score" : 14.8504576811645 }
{ "_id" : { "$oid" : "50906d7fa3c412bb040eb57a" }, "student_id" : 0, "type" : "homework", "score" : 63.98402553675503 }
{ "_id" : { "$oid" : "50906d7fa3c412bb040eb57b" }, "student_id" : 1, "type" : "exam", "score" : 74.20010837299897 }
{ "_id" : { "$oid" : "50906d7fa3c412bb040eb57c" }, "student_id" : 1, "type" : "quiz", "score" : 96.76851542258362 }
{ "_id" : { "$oid" : "50906d7fa3c412bb040eb57d" }, "student_id" : 1, "type" : "homework", "score" : 21.33260810416115 }
{ "_id" : { "$oid" : "50906d7fa3c412bb040eb57e" }, "student_id" : 1, "type" : "homework", "score" : 44.31667452616328 }

生徒ごとに削除しなければならない問題の一部として、スコアが最も低い「宿題」ドキュメント。これが私の戦略です

総パイプラインで
1:最初にtype:homeworksですべてのドキュメントをフィルタリングします
2:student_idで並べ替え、スコア
3:student_idでグループを作成し、最初の要素を見つけます

これにより、スコアが最も低いすべてのドキュメントが得られます。

ただし、元のデータセットからこれらの要素を削除するにはどうすればよいですか?ガイダンスやヒントはありますか?

12
Dude

集計からのカーソル結果を使用して、カーソルの forEach() メソッドでドキュメントをループし、_idを使用してコレクションから各ドキュメントを削除します。 remove() メソッドのクエリ。このようなもの:

var cursor = db.grades.aggregate(pipeline);
cursor.forEach(function (doc){
    db.grades.remove({"_id": doc._id});
});

別のアプローチは、 map() メソッドを使用してドキュメントの_idsの配列を作成し、次のようにドキュメントを削除することです。

var cursor = db.grades.aggregate(pipeline),
    ids = cursor.map(function (doc) { return doc._id; });
db.grades.remove({"_id": { "$in": ids }});

-[〜#〜] update [〜#〜]-

大規模な削除操作の場合は、保持するドキュメントを新しいコレクションにコピーしてから、 drop() を使用する方が効率的です。オリジナルコレクション。重要なドキュメントをコピーするには、集約パイプラインが最下位の宿題ドキュメントのないドキュメントを返し、パイプラインの最終段階として $out 演算子を使用して別のコレクションにコピーする必要があります。次の集約パイプラインについて考えてみます。

db.grades.aggregate([    
    {
        '$group':{
            '_id': {
                "student_id": "$student_id",
                "type": "$type"
            },
            'lowest_score': { "$min": '$score'},
            'data': {
                '$Push': '$$ROOT'
            }
         }
    },    
    {
        "$unwind": "$data"
    },
    {
        "$project": {
            "_id": "$data._id",
            "student_id" : "$data.student_id",
            "type" : "$data.type",
            "score" : "$data.score",
            'lowest_score': 1,            
            "isHomeworkLowest": {
                "$cond": [
                    { 
                        "$and": [
                            { "$eq": [ "$_id.type", "homework" ] },
                            { "$eq": [ "$data.score", "$lowest_score" ] }
                        ] 
                    },
                    true,
                    false
                ]
            }
        }
    },
    {
        "$match": {"isHomeworkLowest" : false}
    },
    {
        "$project": {           
            "student_id": 1,
            "type": 1,
            "score": 1
        }
    },
    {
        "$out": "new_grades"
    }
])

次に、db.grades.drop()で古いコレクションを削除し、db.new_grades.find()でクエリを実行できます。

24
chridam

これは、MongoDBの宿題のデータベース部分だと思いますJava MongoDB大学が提供する開発者。個々の学生から最低スコアを削除する必要があります。とにかく、私はこの方法で解決しました。それがあなたに役立つことを願っています。私のgithubリンクから私のコードを複製することもできます(以下に提供)

public class Homework2Week2 {

public static void main(String[] args) {
    // TODO Auto-generated method stub
    // Here the the documentation is used for mongo-jva-driver-3.2.2.jar
    /*If you want to use different versionof  mongo-jva-driver 
      then you have look for that version specificatios.*/
    MongoClient mongoClient = new MongoClient();
    // get handle to "students" database
    MongoDatabase database = mongoClient.getDatabase("students");
    // get a handle to the "grades" collection
    MongoCollection<Document> collection = database.getCollection("grades");
    /*
     * Write a program in the language of your choice that will remove the grade of type "homework" with the lowest score for each student from the dataset in the handout. 
     * Since each document is one grade, it should remove one document per student. 
     * This will use the same data set as the last problem, but if you don't have it, you can download and re-import.
     * The dataset contains 4 scores each for 200 students.
     * First, letâs confirm your data is intact; the number of documents should be 800.

     *Hint/spoiler: If you select homework grade-documents, sort by student
      and then by score, you can iterate through and find the lowest score
      for each student by noticing a change in student id. As you notice
      that change of student_id, remove the document.
     */
    MongoCursor<Document> cursor = collection.find(eq("type", "homework")).sort(new Document("student_id", 1).append("score", 1)).iterator();
    int curStudentId = -1;
    try
    {
    while (cursor.hasNext()) {
        Document doc = cursor.next();
        int studentId=(int) doc.get("student_id");
        if (studentId != curStudentId) {
            collection.deleteMany(doc);
            curStudentId = studentId;
        }
    }
    }finally {
        //Close cursor
        cursor.close();
    }   
    //Close mongoClient
    mongoClient.close();
}

}

私の Github アカウントには、完全なプロジェクトコードがあります。誰かが欲しいなら、これから試すことができます リンク

5
db.grades.aggregate( [ 
                            { 
                                $match:{type:'homework'}
                            }, 
                            { $group: 
                                 { _id: {student_id:"$student_id",type:'$type'},                                   
                                   score: { $max: "$score" } 
                                 } 
                            } 
                            ]).forEach(function(doc){
db.grades.remove({'student_id':doc._id.student_id,'score':doc.score})

})
4
Deepak Challa

Mongo 4.4から、$groupステージに新しい集計演算子が追加されました $accumulator グループ化されたドキュメントのカスタム累積が可能になります。

$out ステージと組み合わせて、この場合、元のコレクションを集計パイプラインの結果(学生ごとの最低スコアから削除されたもの)に置き換えるために使用されます。

// > db.collection.find()
//     { "student_id" : 0, "type" : "exam",     "score" : 54.6535436362647  }
//     { "student_id" : 0, "type" : "homework", "score" : 14.8504576811645  }
//     { "student_id" : 0, "type" : "homework", "score" : 63.98402553675503 }
//     { "student_id" : 1, "type" : "homework", "score" : 21.33260810416115 }
//     { "student_id" : 1, "type" : "homework", "score" : 44.31667452616328 }
db.collection.aggregate(
  { $group: {
      _id: "$student_id",
      docs: { $accumulator: {
        accumulateArgs: ["$$ROOT"],
        init: function() { return []; },
        accumulate: function(docs, doc) { return docs.concat(doc); },
        merge: function(docs1, docs2) { return docs1.concat(docs2); },
        finalize: function(docs) {
          var min = Math.min(...docs.map(x => x.score));
          var i = docs.findIndex((doc) => doc.score == min);
          docs.splice(i, 1);
          return docs;
        },
        lang: "js"
      }}
  }},
  { $unwind: "$docs" },
  { $replaceWith: "$docs" },
  { $out: "collection" }
)
// > db.collection.find()
//     { "student_id" : 0, "type" : "exam",     "score" : 54.6535436362647  }
//     { "student_id" : 0, "type" : "homework", "score" : 63.98402553675503 }
//     { "student_id" : 1, "type" : "homework", "score" : 44.31667452616328 }

この:

  • $groupsドキュメントをstudent_idで作成し、スコアが最も低いドキュメントから削除された配列としてそれらを累積します。

    • accumulateArgsは、累積関数で使用されるフィールド(またはこの場合はドキュメント全体 $$ROOT )の組み合わせです。

    • 元の累積配列はそれぞれ、空の配列としてinitializedされます。

    • ドキュメントは単にconcatanated(accumulateおよびmerge)です。

    • 最後に、すべてのドキュメントがグループ化されると、finalizeステップで、スコアが最も低いグループ化されたドキュメントを見つけて削除できます。

    • この段階の終わりに、パイプライン化されたドキュメントは次のようになります。

      {
        "_id" : 0,
        "docs" : [
          { "student_id" : 0, "type" : "exam", "score" : 54.6535436362647 },
          { "student_id" : 0, "type" : "quiz", "score" : 31.95004496742112 },
          { "student_id" : 0, "type" : "homework", "score" : 63.98402553675503 }
        ]
      }
      ...
      
  • $unwindsグループ化されたドキュメントの累積フィールド。グループ化されたドキュメントの配列をフラット化し、次のようなものに戻ります。

    { "_id" : 0, "docs" : { "student_id" : 0, "type" : "exam", "score" : 54.6535436362647 } }
    { "_id" : 0, "docs" : { "student_id" : 0, "type" : "quiz", "score" : 31.95004496742112 } }
    { "_id" : 0, "docs" : { "student_id" : 0, "type" : "homework", "score" : 63.98402553675503 } }
    ...
    
  • $replaceWith 元の形式を見つけるために、累積されたフィールドの内容を含む各ドキュメントのすべての既存のフィールド。この段階の終わりには、次のようなものがあります。

    { "student_id" : 0, "type" : "exam", "score" : 54.6535436362647 }
    { "student_id" : 0, "type" : "quiz", "score" : 31.95004496742112 }
    { "student_id" : 0, "type" : "homework", "score" : 63.98402553675503 }
    ...
    
  • $out 集約パイプラインの結果を同じコレクションに挿入します。 $outは、指定されたコレクションのコンテンツを便利に置き換えるため、このソリューションが可能になることに注意してください。

0
Xavier Guihot

int studentId =(int)doc.get( "student_id");

変換タイプエラーが発生します。もう一度確認できますか?

私が知っているように、私たちは以下のように作ることができます。

int studentId = Integer.valueOf(doc.get( "student_id")。toString());

0
Tolga ÇELİK

この質問は、MongoDB大学によるM101P:MongoDB forDevelopersコースの一部です。ここでの要件は次のとおりです。-

データセットから、各学生のスコアが最も低いタイプ「宿題」の成績を削除します。各ドキュメントは1つの学年であるため、生徒ごとに1つのドキュメントを削除する必要があります。

つまり、すべてのstudent_idから4つの「タイプ」が存在し、そのうち2つの「タイプ」は「宿題」です。 2つの「タイプ」:「宿題」ドキュメントから最低スコアを削除する必要があります。

Pymongoの稼働中のコードは以下のとおりです:-

import pymongo
import sys

//establish a connection to database

connection=pymongo.MongoClient('localhost',27017)
//Get a handle to students database

db=connection.students
mycol=db.grades

def remove_documents():
        pipe=[
        {'$match':{'type':'homework'}},
        {'$group':{'_id':'$student_id','minscore':  {'$min':'$score'}}}
        ,{'$sort':{'_id':1}}
             ]
        result_cursor=mycol.aggregate(pipeline=pipe)
        counter=0

        for i in result_cursor:
            query={'student_id':i['_id'],'score':i['minscore']}
            del_record=mycol.delete_one(query)
            if (int(del_record.deleted_count) > 0):
                    counter+=1
            else:
                    continue
        print(counter)
remove_documents()

ターミナル出力:-$ python remove_grade.py

200

0