【问题标题】:Mongodb Java: remove an element of an embedded array within a document based on certain condition of elementsMongodb Java:根据元素的某些条件删除文档中嵌入数组的元素
【发布时间】:2018-08-24 04:45:05
【问题描述】:

我有一个学生文档列表,其结构如下:

{
    "_id" : 0,
    "name" : "aimee Zank",
    "scores" : [
            {
                    "type" : "exam",
                    "score" : 1.463179736705023
            },
            {
                    "type" : "quiz",
                    "score" : 11.78273309957772
            },
            {
                    "type" : "homework",
                    "score" : 6.676176060654615
            },
            {
                    "type" : "homework",
                    "score" : 35.8740349954354
            }
    ]
}

如您所见,每个学生都有一个包含 4 个分数的列表。 我需要删除每个学生文档的最低“作业”分数。每个学生都有 2 个“homewok”类型分数的条目(4 个元素的数组中的最后 2 个条目)。分数类型的模式和顺序是一致的,并且对所有学生都有相同的模式 感谢您的帮助。

这是我迄今为止努力实现的目标:

    DBCursor cursor = collection.find();

    try {

        while(cursor.hasNext()) {
           BasicDBObject doc = (BasicDBObject) cursor.next();
           BasicDBList scoreList =  (BasicDBList) doc.get("scores");

           BasicDBObject hw1 = (BasicDBObject) scoreList.get("2");
           double hw1Score = hw1.getDouble("score");

           BasicDBObject hw2 = (BasicDBObject) scoreList.get("3");
           double hw2Score = hw2.getDouble("score");

           if (hw1Score > hw2Score) {
               BasicDBObject update = new BasicDBObject("scores.score", hw2Score);               
               collection.update(doc, new BasicDBObject("$pull",update));                                                  
           } else {
               BasicDBObject update = new BasicDBObject("scores.score", hw1Score);

               collection.update(doc, new BasicDBObject("$pull",update));                                  
           }
           System.out.println(doc);
        }

    } finally {
       cursor.close();
    }


}

【问题讨论】:

  • 您是否尝试过任何方法来实现这一目标?如果是,请分享
  • @PradeepSimha 可能不是因为这是从university.mongodb.com借来的一个众所周知的家庭作业问题
  • 你试过搜索最低分和$pull吗?
  • 对不起,我看不出你的问题是什么。请不要只问“我该怎么做?”
  • 我只是找不到根据数组元素的某些条件删除数组元素的java示例

标签: java arrays mongodb


【解决方案1】:

这里的所有答案都很棒。我只是想补充一点,如果有人想使用 Java 运算符(自驱动程序 v3.1 起),那么他可以执行以下操作,而不是使用“$pull”运算符:

...
Bson studentFilter = Filters.eq( "_id", doc.get("_id") );
Bson delete = Updates.pull("scores", new Document("score", lowestHomework).append("type", "homework"));
collection.updateOne(studentFilter, delete);

我认为它更优雅。所以我的完整答案是:

public static void main(String[] args) {
    MongoClient client = new MongoClient();
    MongoDatabase database = client.getDatabase("school");
    MongoCollection<Document> collection = database.getCollection("students");

    List<Document> homeworks = collection.find()
        .into(new ArrayList<Document>());

    for(Document doc : homeworks)
    {
        ArrayList<Document> scores = (ArrayList<Document>) doc.get("scores");
        //iterate over the scores of each student (there are 4 scores: quiz, exam and 2*homework)
        double lowestHomework = Double.MAX_VALUE;
        for(Document embeddedDoc : scores)
        {
            if(embeddedDoc.getString("type").equals("homework"))
            {
                Double score = embeddedDoc.getDouble("score");
                if(score < lowestHomework)
                {
                    lowestHomework = score;
                }
            }
        }
        Bson studentFilter = Filters.eq( "_id", doc.get("_id") );
        Bson delete = Updates.pull("scores", new Document("score", lowestHomework).append("type", "homework"));

        collection.updateOne(studentFilter, delete);
    }

    client.close();
}

【讨论】:

    【解决方案2】:

    我知道这不是最好的解决方案(更好的方法是对每个文档的作业分数进行排序,然后将数组大小限制为 3)。但这也有效:)

         try {
    
            while(cursor.hasNext()) {
               BasicDBObject doc = (BasicDBObject) cursor.next();
               BasicDBList scoreList =  (BasicDBList) doc.get("scores");              
               doc.remove("scores");
    
               BasicDBObject hw1 = (BasicDBObject) scoreList.get("2");
               double hw1Score = hw1.getDouble("score");
    
               BasicDBObject hw2 = (BasicDBObject) scoreList.get("3");
               double hw2Score = hw2.getDouble("score");
    
               if (hw1Score > hw2Score) {                                                  
                   scoreList.remove(3);                     
               } else {
                   scoreList.remove(2);                                          
               }
               doc.put("scores",scoreList);  
               collection.save(doc);
               System.out.println(doc);
            }
    
        } finally {
           cursor.close();
        }        
    
    
    }
    

    }

    【讨论】:

      【解决方案3】:

      试试这个;我假设最高分是 100 :

      for (Document document : cursor) {
          ArrayList<Document> list =  (ArrayList<Document>) document.get("scores");
          double score = 100;
          for (Document doc : list) {
              if(doc.getString("type").equals("homework")){
                  if(doc.getDouble("score") < score){
                      score = doc.getDouble("score");
                  }
              }
          }
          BasicDBObject update = new BasicDBObject("scores", new BasicDBObject("score", score).append("type", "homework"));
          collection.updateOne(document, new BasicDBObject("$pull", update));     
          }
      

      【讨论】:

        【解决方案4】:
        package com.mongodb;
        
        import java.util.ArrayList;
        import java.util.List;
        
        import org.bson.Document;
        import com.mongodb.client.MongoCollection;
        import com.mongodb.client.MongoDatabase;
        
        public class HWDeleteArray {
        
            public static void main(String[] args) {
                MongoClient client = new MongoClient();
                MongoDatabase database = client.getDatabase("school");
                MongoCollection<Document> collection = database.getCollection("students");
        
                List<Document> all = collection.find().into(new ArrayList<Document>());
                int i = 0;
                Double s1 =0.0;
                Double s2 =0.0;
                Document doc1 = null;
                Document doc2 = null;
                for(Document cur:all) {
                    List<Document> scores = (List<Document>) cur.get("scores");
                    for(Document score:scores) {
                        if(score.getString("type").equals("homework")) {
        
                            if(i==0) {
                                i++;
                                s1 = (Double) score.get("score");
                                doc1 = score;
        
                            }else {
                                i--;
                                s2 = (Double) score.get("score");
                                doc2 = score;
                                if(s1 < s2) {
                                    doc1.clear();
                                    collection.replaceOne(new Document("_id",cur.get("_id")),cur);
                                }else {
                                    doc2.clear();
                                    collection.replaceOne(new Document("_id",cur.get("_id")),cur);
                                }
                            }
                        }
        
        
                    }
        
        
                }
        
            }
        }
        

        【讨论】:

        • 添加为什么这是问题的答案而不只是发布代码是个好主意。
        【解决方案5】:

        最好使用$pull 和过滤器的方法,以便从数组中删除特定分数。下面的代码使用带有模型 API 的 MongoDB Java Driver v3.6。

        import com.mongodb.MongoClient;
        import com.mongodb.client.MongoCollection;
        import com.mongodb.client.MongoDatabase;
        import org.bson.Document;
        import java.util.ArrayList;
        import java.util.List;
        import static com.mongodb.client.model.Updates.pull;
        
        public class RemoveLowestScoreArray {
        
            public static void main(String[] args) {
                MongoDatabase database;
                try (MongoClient client = new MongoClient()) {
                    database = client.getDatabase("school");
                    MongoCollection<Document> collection = database.getCollection("students");
                    List<Document> students = collection.find().into(new ArrayList<>());
        
                    for (Document student : students) {
                        Document lowestScore = null;
                        for (Document score : (List<Document>) student.get("scores")) {
                            if (score.getString("type").equals("homework")) {
                                if (lowestScore == null || score.getDouble("score") < (lowestScore.getDouble("score"))) {
                                    lowestScore = score;
                                }
                            }
                        }
                        collection.updateOne(student, pull("scores", lowestScore));
                    }
                }
            }
        
        
        }
        

        【讨论】:

          【解决方案6】:

          您迭代数组并找到最低分数。伪代码:

          min <- infinity
          minIndex = -1
          for index <- 0; index < elements.getScores().size(); index <- index + 1 do
              if min > elements.getScores().get(index) then
                  min <- elements.getScores().get(index)
                  minIndex <- index
              end if
          end for
          

          【讨论】:

          • 谢谢,但我正在寻找一个通过基于数组元素的某些条件从其嵌入数组中拉出一个元素来更新文档的 java 示例。
          【解决方案7】:

          我尝试使用执行起来非常简单的本机 mongodb 命令。我尝试对给定的问题陈述进行测试。使用以下 2 个命令使其工作。

          1) cursor = db.students.aggregate([{ "$unwind": "$scores" }, { "$match": { "scores.type": "homework"}},{ "$group" : {'_id': '$_id', 'miniitem': {'$min':"$scores.score"}}}]), null

          2) cursor.forEach(function(coll) {db.students.update({'_id': coll._id}, {'$pull': {'scores': {'score': coll.miniitem} }})})

          【讨论】:

            【解决方案8】:

            我尝试使用 MongoDB java 驱动程序的聚合类来解决这个问题。请参阅下面的工作代码以供参考。

            AggregateIterable<Document> aiter = collection.aggregate(
                            Arrays.asList(Aggregates.unwind("$scores"),Aggregates.match(Filters.eq("scores.type", "homework")),
                                    Aggregates.sort(Sorts.orderBy(Sorts.ascending("_id"), Sorts.ascending("scores.score")))));
            
                    collection = database.getCollection("students");
                    MongoCursor<Document> cursor = aiter.iterator();
                    int pid = -1;
                    while (cursor.hasNext()) {
                        Document doc = cursor.next();
                        int cid = doc.getInteger("_id");
                        double scoresScore = doc.get("scores", Document.class).getDouble("score");
                        if (pid != cid) {
                            // delete
                            BasicDBObject update = new BasicDBObject("scores",
                                    new BasicDBObject("score", scoresScore).append("type", "homework"));
                            collection.updateOne(Filters.eq("_id", cid), new BasicDBObject("$pull", update));
                        }
                        pid = cid;
                    }
            

            【讨论】:

            • 欢迎来到 Stack Overflow!虽然您可能已经解决了这个用户的问题,但纯代码的答案对于将来遇到这个问题的用户来说并不是很有帮助。请编辑您的答案以解释为什么您的代码解决了原始问题。
            【解决方案9】:

            这是我的做法,也许有人会觉得它更简洁易懂:

            MongoClient client = new MongoClient();
            MongoDatabase database = client.getDatabase("school");
            final MongoCollection<BasicDBObject> collection = database.getCollection("students",BasicDBObject.class);
            
            MongoCursor<BasicDBObject> cursor = collection.find().iterator();
            
            while(cursor.hasNext())
            {
                double min_score = 999;
                BasicDBObject doc = cursor.next();
                BasicDBList scores = (BasicDBList) doc.get("scores");
            
                for (Object score : scores)
                {
                    BasicDBObject x = (BasicDBObject) score;
                    if (x.get("type").equals("homework"))
                    {
                        if (x.getDouble("score") < min_score)
                        {
                            min_score = x.getDouble("score");
                        }
                    }
                }
            
                if (min_score == 999){
                    continue;
                }
            
                BasicDBObject query = new BasicDBObject("_id", doc.get("_id")); // Find this Document
                BasicDBObject fields = new BasicDBObject("scores",
                        new BasicDBObject( "score", min_score)); // With those fields
                BasicDBObject update = new BasicDBObject("$pull",fields); // And remove any the matched results. 
                collection.updateOne(query, update);
            }
            

            $pull 运算符从现有数组中删除与指定条件匹配的一个或多个值的所有实例。

            【讨论】:

              【解决方案10】:

              试试这个代码:

              import java.util.ArrayList;
              import java.util.List;
              import org.bson.Document;
              import org.bson.conversions.Bson;
              import static com.mongodb.client.model.Filters.eq;
              
              import com.mongodb.MongoClient;
              import com.mongodb.client.MongoCollection;
              import com.mongodb.client.MongoCursor;
              import com.mongodb.client.MongoDatabase;
              import com.mongodb.client.model.Projections;
              
              
                      MongoClient client = new MongoClient();
              
              
                      String str, str2;
                      Double sub;
                      MongoDatabase db = client.getDatabase("school");
                      MongoCollection<Document> coll = db.getCollection("students");
                      //coll.drop();
                      MongoCursor<Document> cursor = coll.find().iterator();
              
                      List<Document> student = coll.find().into(new ArrayList<Document>());
              
                      for(Document doc :student){
                          List<Document> scores = (List<Document>)doc.get("scores");
                          doc.remove("scores");
              
              
                          List<Document> scores2 = scores.subList(2,3);
                          System.out.println(scores2.toString());
                          str = (scores2.toString()).substring(32, (scores2.toString()).length()-3);
              
                          System.out.println(str);
              
                          List<Document> scores3 = scores.subList(3,4);
                          System.out.println(scores3.toString());
                          str2 = (scores3.toString()).substring(32, (scores3.toString()).length()-3);
                          System.out.println(str2);
              
                          sub =  Double.parseDouble(str2) -  Double.parseDouble(str);
              
              
                          if(sub >0){
                              scores.remove(2);
                              doc.put("scores", scores);
              
              
                          }else if(sub == 0){
                              scores.remove(2);
                              doc.put("scores", scores);
                          }else{
                              scores.remove(3);
                              doc.put("scores", scores);
                          }
                          Document cur = cursor.next();
                          System.out.println(cur);
                          System.out.println(doc);
                          coll.findOneAndReplace(cur, doc);
              
                      }
              

              【讨论】:

                【解决方案11】:

                我不知道它是否是最好的选择,但可以:

                List<Document> all = (List<Document>) collection.find().into(new ArrayList<Document>());
                
                 for (Document current : all){
                        Object id = current.get("_id");
                        List<Document> i = (List<Document>) current.get("scores");
                
                        if(i.get(2).getDouble("score")>i.get(3).getDouble("score")){
                            collection.updateOne(new Document("_id", id),new Document("$pull",new Document("scores",new Document("score",i.get(3).getDouble("score")))));
                        } else{
                            collection.updateOne(new Document("_id", id),new Document("$pull",new Document("scores",new Document("score",i.get(2).getDouble("score")))));
                
                        }
                    }
                }
                

                【讨论】:

                  【解决方案12】:

                  这是我解决这个问题的方法。

                      List<Document> documents = collection.find().into(new ArrayList<Document>());
                  
                      for (Document document : documents) {
                          List<Document> scores = (List<Document>) document.get("scores");
                          Document minDoc = null;
                          for (Document score : scores) {
                              if ("homework".equals(score.getString("type")) && (minDoc == null || minDoc.getDouble("score") > score.getDouble("score"))) {
                                      minDoc = score;
                                  }
                          }
                          collection.updateOne(new Document("_id", document.get("_id")), new Document("$pull", new Document("scores", minDoc)));
                      }
                  

                  【讨论】:

                    【解决方案13】:

                    我对上述问题的解决方案是:

                        List<Document> results =
                                            collection.aggregate(asList(
                                                    new Document("$unwind","$scores"),
                                                    new Document("$match", new Document("scores.type", new Document("$eq", "homework"))),
                                                    new Document("$group", new Document("_id", "$_id")
                                                            .append("score", new Document("$min", "$scores.score")))))
                                            .into(new ArrayList<Document>());
                                    for(Document doc : results)
                                    {
                                        Integer id = doc.getInteger("_id");
                                        Double score = doc.getDouble("score");
                                        UpdateResult result = collection.updateOne(new Document("_id",new Document("$eq",id)), 
                                                new Document("$pull", new Document("scores",
                                                        new Document("score", score))));
                                    }
                    

                    【讨论】:

                      猜你喜欢
                      • 2020-12-13
                      • 2011-06-25
                      • 1970-01-01
                      • 2017-02-17
                      • 1970-01-01
                      • 1970-01-01
                      • 2016-10-14
                      • 2013-04-06
                      • 1970-01-01
                      相关资源
                      最近更新 更多