SpringデータMongoDB:ドキュメントの更新

Springデータ - MongoDBでは、以下のメソッドを使用してドキュメントを更新することができます。

  1. save - オブジェクト全体を更新します. "__id"が存在する場合は、

それ以外の場合は挿入してください。

  1. updateFirst - クエリに一致する最初のドキュメントを更新します.

  2. updateMulti - クエリに一致するすべてのドキュメントを更新します.

  3. アップアップ - クエリに一致するドキュメントがない場合、新しいドキュメントが

クエリオブジェクトと更新オブジェクトを組み合わせて作成されます。

  1. findAndModify - updateMultiと同じですが、追加のオプションがあります

古い文書または新しく更新された文書を返します。

P.S全ての例は mongo-java-driver-2.11.0.jar`と spring-data-mongodb-1.2.0.RELEASE.jar` でテストされています

1. saveOrUpdate - パート1の例

下記のjsonデータがMongoDBに挿入されていると仮定します。

{
    "__id" : ObjectId("id"),
    "ic" : "1001",
    "name" : "appleA",
    "age" : 20,
    "createdDate" : ISODate("2013-04-06T23:17:35.530Z")
}

ドキュメントを見つけ、 `save()`メソッドでドキュメントを修正して更新します。

    Query query = new Query();
    query.addCriteria(Criteria.where("name").is("appleA"));

    User userTest1 = mongoOperation.findOne(query, User.class);

    System.out.println("userTest1 - " + userTest1);

   //modify and update with save()
    userTest1.setAge(99);
    mongoOperation.save(userTest1);

   //get the updated object again
    User userTest1__1 = mongoOperation.findOne(query, User.class);

    System.out.println("userTest1__1 - " + userTest1__1);

出力

userTest1 - User[id=id, ic=1001, name=appleA, age=20, createdDate=Sat Apr 06 23:17:35 MYT 2013]userTest1__1 - User[id=id, ic=1001, name=appleA, age=99, createdDate=Sat Apr 06 23:17:35 MYT 2013]....

** 注** 例2を参照してください。ほとんどの開発者が間違いを犯しています。

===  2. saveOrUpdate  - パート2の例

これは失敗した例です。本当によくある間違いです。

下記のjsonデータがMongoDBに挿入されていると仮定します。

{ "__id" : ObjectId("id"), "ic" : "1002", "name" : "appleB", "age" : 20, "createdDate" : ISODate("2013-04-06T15:22:34.530Z") }

`Query`では、単一の" name "フィールド値だけで返されたドキュメントを取得します。オブジェクト返されたサイズを保存することがよくありました。返された "User"オブジェクトは、age、ic、およびcreatedDateの各フィールドにnull値を持ちます。 'age'フィールドを変更してそれを更新すると、変更されたフィールド -  'age'を更新するのではなく、すべてをオーバーライドします。
Query query = new Query();
query.addCriteria(Criteria.where("name").is("appleB"));
query.fields().include("name");
User userTest2 = mongoOperation.findOne(query, User.class);
System.out.println("userTest2 - " + userTest2);
userTest2.setAge(99);
mongoOperation.save(userTest2);
//ooppss, you just override everything, it caused ic=null and
//createdDate=null
Query query1 = new Query();
query1.addCriteria(Criteria.where("name").is("appleB"));
User userTest2__1 = mongoOperation.findOne(query1, User.class);
System.out.println("userTest2__1 - " + userTest2__1);
出力

userTest2 - User[id=51603dba3004d7fffc202391, ic=null, name=appleB, age=0, createdDate=null]userTest2__1 - User[id=51603dba3004d7fffc202391, ic=null, name=appleB, age=99, createdDate=null]…​.

save()の後、フィールド 'age’は正しく更新されますが、icとcreatedDateは両方ともnullに設定され、「user」オブジェクト全体が更新されます。

単一のフィールド/キー値を更新するには、save()を使用せず、代わりにupdateFirst()またはupdateMulti()を使用してください。

3. update最初の例

クエリに一致する最初のドキュメントを更新します。この場合、単一のフィールド「年齢」のみが更新されます。

{
    "__id" : ObjectId("id"),
    "ic" : "1003",
    "name" : "appleC",
    "age" : 20,
    "createdDate" : ISODate("2013-04-06T23:22:34.530Z")
}
       //returns only 'name' field
        Query query = new Query();
        query.addCriteria(Criteria.where("name").is("appleC"));
        query.fields().include("name");

        User userTest3 = mongoOperation.findOne(query, User.class);
        System.out.println("userTest3 - " + userTest3);

        Update update = new Update();
        update.set("age", 100);

        mongoOperation.updateFirst(query, update, User.class);

       //returns everything
        Query query1 = new Query();
        query1.addCriteria(Criteria.where("name").is("appleC"));

        User userTest3__1 = mongoOperation.findOne(query1, User.class);
        System.out.println("userTest3__1 - " + userTest3__1);

出力

userTest3 - User[id=id, ic=null, name=appleC, age=0, createdDate=null]userTest3__1 - User[id=id, ic=1003, name=appleC, age=100, createdDate=Sat Apr 06 23:22:34 MYT 2013]....

===  4. updateMultiの例

クエリに一致するすべてのドキュメントを更新します。

{ " id" : ObjectId("id"), "ic" : "1004", "name" : "appleD", "age" : 20, "createdDate" : ISODate("2013-04-06T15:22:34.530Z") } { " id" : ObjectId("id"), "ic" : "1005", "name" : "appleE", "age" : 20, "createdDate" : ISODate("2013-04-06T15:22:34.530Z") }


//show the use of $or operator
 Query query = new Query();
 query.addCriteria(Criteria
         .where("name").exists(true)
         .orOperator(Criteria.where("name").is("appleD"),
                 Criteria.where("name").is("appleE")));
 Update update = new Update();
//update age to 11
 update.set("age", 11);
//remove the createdDate field
 update.unset("createdDate");
//if use updateFirst, it will update 1004 only.
//mongoOperation.updateFirst(query4, update4, User.class);
//update all matched, both 1004 and 1005
 mongoOperation.updateMulti(query, update, User.class);
System.out.println(query.toString());
List<User> usersTest4 = mongoOperation.find(query4, User.class);
for (User userTest4 : usersTest4) {
    System.out.println("userTest4 - " + userTest4);
}
出力

Query: { "name" : { "$exists" : true} , "$or" :[{ "name" : "appleD"} , { "name" : "appleE"}]}, Fields: null, Sort: null

userTest4 - User[id=id, ic=1004, name=appleD, age=11, createdDate=null]userTest4 - User[id=id, ic=1005, name=appleE, age=11, createdDate=null]…​.

5.アップサンプルの例

ドキュメントが一致する場合は更新し、そうでない場合はクエリと更新オブジェクトを組み合わせて新しいドキュメントを作成します。これは `findAndModifyElseCreate()`のように動作します:)

{
   //no data
}
       //search a document that doesn't exist
        Query query = new Query();
        query.addCriteria(Criteria.where("name").is("appleZ"));

        Update update = new Update();
        update.set("age", 21);

        mongoOperation.upsert(query, update, User.class);

        User userTest5 = mongoOperation.findOne(query, User.class);
        System.out.println("userTest5 - " + userTest5);

出力、クエリと更新オブジェクトの両方を組み合わせて新しいドキュメントが作成されます。

userTest5 - User[id=id, ic=null, name=appleZ, age=21, createdDate=null]....

===  6. findAndModifyの例

1回の操作から新しく更新されたオブジェクトを検索して変更して取得する。

{ "__id" : ObjectId("id"), "ic" : "1006", "name" : "appleF", "age" : 20, "createdDate" : ISODate("2013-04-07T13:11:34.530Z") }


Query query6 = new Query();
query6.addCriteria(Criteria.where("name").is("appleF"));
Update update6 = new Update();
update6.set("age", 101);
update6.set("ic", 1111);
//FindAndModifyOptions().returnNew(true) = newly updated document
//FindAndModifyOptions().returnNew(false) = old document (not update yet)
 User userTest6 = mongoOperation.findAndModify(
         query6, update6,
         new FindAndModifyOptions().returnNew(true), User.class);
 System.out.println("userTest6 - " + userTest6);
出力

userTest6 - User[id=id, ic=1111, name=appleF, age=101, createdDate=Sun Apr 07 13:11:34 MYT 2013]…​.

7.完全な例

例1〜6のすべてを組み合わせる完全なアプリケーション。

package com.mkyong.core;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.data.mongodb.core.FindAndModifyOptions;
import org.springframework.data.mongodb.core.MongoOperations;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import com.mkyong.config.SpringMongoConfig;
import com.mkyong.model.User;

public class UpdateApp {

    public static void main(String[]args) {
       //For Annotation
        ApplicationContext ctx =
            new AnnotationConfigApplicationContext(SpringMongoConfig.class);
        MongoOperations mongoOperation =
            (MongoOperations) ctx.getBean("mongoTemplate");

       //insert 6 users for testing
        List<User> users = new ArrayList<User>();

        User user1 = new User("1001", "appleA", 20, new Date());
        User user2 = new User("1002", "appleB", 20, new Date());
        User user3 = new User("1003", "appleC", 20, new Date());
        User user4 = new User("1004", "appleD", 20, new Date());
        User user5 = new User("1005", "appleE", 20, new Date());
        User user6 = new User("1006", "appleF", 20, new Date());
        users.add(user1);
        users.add(user2);
        users.add(user3);
        users.add(user4);
        users.add(user5);
        users.add(user6);
        mongoOperation.insert(users, User.class);

       //Case 1 ... find and update
        System.out.println("Case 1");

        Query query1 = new Query();
        query1.addCriteria(Criteria.where("name").is("appleA"));

        User userTest1 = mongoOperation.findOne(query1, User.class);

        System.out.println("userTest1 - " + userTest1);

        userTest1.setAge(99);
        mongoOperation.save(userTest1);

        User userTest1__1 = mongoOperation.findOne(query1, User.class);

        System.out.println("userTest1__1 - " + userTest1__1);

       //Case 2 ... select single field only
        System.out.println("\nCase 2");

        Query query2 = new Query();
        query2.addCriteria(Criteria.where("name").is("appleB"));
        query2.fields().include("name");

        User userTest2 = mongoOperation.findOne(query2, User.class);
        System.out.println("userTest2 - " + userTest2);

        userTest2.setAge(99);

        mongoOperation.save(userTest2);

       //ooppss, you just override everything, it caused ic=null and
       //createdDate=null

        Query query2__1 = new Query();
        query2__1.addCriteria(Criteria.where("name").is("appleB"));

        User userTest2__1 = mongoOperation.findOne(query2__1, User.class);
        System.out.println("userTest2__1 - " + userTest2__1);

        System.out.println("\nCase 3");
        Query query3 = new Query();
        query3.addCriteria(Criteria.where("name").is("appleC"));
        query3.fields().include("name");

        User userTest3 = mongoOperation.findOne(query3, User.class);
        System.out.println("userTest3 - " + userTest3);

        Update update3 = new Update();
        update3.set("age", 100);

        mongoOperation.updateFirst(query3, update3, User.class);

        Query query3__1 = new Query();
        query3__1.addCriteria(Criteria.where("name").is("appleC"));

        User userTest3__1 = mongoOperation.findOne(query3__1, User.class);
        System.out.println("userTest3__1 - " + userTest3__1);

        System.out.println("\nCase 4");
        Query query4 = new Query();
        query4.addCriteria(Criteria
                .where("name")
                .exists(true)
                .orOperator(Criteria.where("name").is("appleD"),
                        Criteria.where("name").is("appleE")));
        Update update4 = new Update();
        update4.set("age", 11);
        update4.unset("createdDate");

       //update 1004 only.
       //mongoOperation.updateFirst(query4, update4, User.class);

       //update all matched
        mongoOperation.updateMulti(query4, update4, User.class);

        System.out.println(query4.toString());

        List<User> usersTest4 = mongoOperation.find(query4, User.class);

        for (User userTest4 : usersTest4) {
            System.out.println("userTest4 - " + userTest4);
        }

        System.out.println("\nCase 5");
        Query query5 = new Query();
        query5.addCriteria(Criteria.where("name").is("appleZ"));

        Update update5 = new Update();
        update5.set("age", 21);

        mongoOperation.upsert(query5, update5, User.class);

        User userTest5 = mongoOperation.findOne(query5, User.class);
        System.out.println("userTest5 - " + userTest5);

        System.out.println("\nCase 6");
        Query query6 = new Query();
        query6.addCriteria(Criteria.where("name").is("appleF"));

        Update update6 = new Update();
        update6.set("age", 101);
        update6.set("ic", 1111);

        User userTest6 = mongoOperation.findAndModify(query6, update6,
                new FindAndModifyOptions().returnNew(true), User.class);
        System.out.println("userTest6 - " + userTest6);

        mongoOperation.dropCollection(User.class);

    }

}

出力

Case 1
userTest1 - User[id=id, ic=1001, name=appleA, age=20, createdDate=Sun Apr 07 13:22:48 MYT 2013]userTest1__1 - User[id=id, ic=1001, name=appleA, age=99, createdDate=Sun Apr 07 13:22:48 MYT 2013]
Case 2
userTest2 - User[id=id, ic=null, name=appleB, age=0, createdDate=null]userTest2__1 - User[id=id, ic=null, name=appleB, age=99, createdDate=null]
Case 3
userTest3 - User[id=id, ic=null, name=appleC, age=0, createdDate=null]userTest3__1 - User[id=id, ic=1003, name=appleC, age=100, createdDate=Sun Apr 07 13:22:48 MYT 2013]
Case 4
Query: { "name" : { "$exists" : true} , "$or" :[{ "name" : "appleD"} , { "name" : "appleE"}]}, Fields: null, Sort: null
userTest4 - User[id=id, ic=1004, name=appleD, age=11, createdDate=null]userTest4 - User[id=id, ic=1005, name=appleE, age=11, createdDate=null]
Case 5
userTest5 - User[id=id, ic=null, name=appleZ, age=21, createdDate=null]
Case 6
userTest6 - User[id=id, ic=1006, name=appleF, age=20, createdDate=Sun Apr 07 13:22:48 MYT 2013]....

=== ソースコードをダウンロードする

ダウンロードする -  link://wp-content/uploads/2011/05/SpringMongoDB-Update-Example.zip[SpringMongoDB-Update-Example.zip](29 KB)

=== 参考文献

.  http://static.springsource.org/spring-data/data-document/docs/current/reference/html/#mongodb-template-update[MongoDB

テンプレート更新のドキュメント]。 http://static.springsource.org/spring-data/mongodb/docs/current/reference/html/mongo.core.html#mongo-template.save-update-remove[Spring

データMongoDB  - 文書の保存、更新、削除]。リンク://mongodb/java-mongodb-update-document/[Java MongoDB update

例/]。 http://docs.mongodb.org/manual/core/update/[MongoDB更新モディファイア

オペレーション]。リンク://mongodb/spring-data-mongodb-hello-world-example/[Springデータ

MongoDB Hello Worldの例]

link://tag/mongodb/[mongodb]link://タグ/spring-data/[spring-data]link://tag/update/[update]