Mongodb Java:根据元素的某些条件删除文档中嵌入数组的元素

时间:2015-01-21 15:37:47

标签: java arrays mongodb

我有一份学生文件清单,其结构如下:

{
    "_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();
    }


}

13 个答案:

答案 0 :(得分: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();
    }        


}

}

答案 1 :(得分:2)

试试这个;我认为最高分是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));     
    }

答案 2 :(得分:2)

这里的所有答案都很棒。我只是想补充一下,如果有人想使用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();
}

答案 3 :(得分:1)

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


            }


        }

    }
}

答案 4 :(得分:1)

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

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


}

答案 5 :(得分:0)

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

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

答案 6 :(得分:0)

我尝试使用原生的mongodb命令,这些命令很容易执行。我尝试了给定的问题陈述测试。使用以下2个命令使其工作。

1)cursor = db.students.aggregate([{“$ unwind”:“$ scores”},{“$ match”:{“scores.type”:“homework”}},{“$ group” :{'_ id':'$ _id','minitem':{'$ min':“$ scores.score”}}}]),null

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

答案 7 :(得分:0)

我尝试使用Aggregater类的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;
        }

答案 8 :(得分:0)

这是我的方法,也许有人会发现它更清晰,更容易理解:

$pull

{% assign sum = 0 %} {% for donation in signup.donation %} {% if donation.succeeded_at | date: %s > 1483228801 %} {% assign sum = sum | plus: donation %} {% endif %} {% endfor %} {{ sum }} 运算符从现有数组中删除与指定条件匹配的值的所有实例。

答案 9 :(得分:0)

试试这段代码:

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

        }

答案 10 :(得分:0)

我不知道它是否是最佳选择,但有效:

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

        }
    }
}

答案 11 :(得分:0)

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

PROPERTIES = list(set().union(*[n.properties for n in NETWORKS]))  # random order

答案 12 :(得分:0)

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

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