简体   繁体   中英

How to update part of a document inside an array? (MongoDB SpringBoot Reactive)

I know this question has been answered a bunch. However, I haven't been able to find a Spring Boot way of doing this "correctly". I have found examples but I can't get it to work for my project.

{"_id":"5b45572047f84e3d10c59b8a"
,"userRoomDatas":[{"isInGame":false,"isReady":true,"isSpectator":false,"userId":"5b45572047f84e3d10c59b84"}]
,"selectedScreen":"MATCHMAKING"
,"privacySetting":"PUBLIC"
,"roomLeaderId":"5b45572047f84e3d10c59b84"
,"botHostId":"5b45572047f84e3d10c59b84"
,"matchmakingData":   {"selectedQueId":"5b45572047f84e3d10c59b85","matchmakingRating":"M3"}
,"currentRoomStatus":"IN_ROOM"
,"canStartSearch":false
,"password":"AA=="
,"quickJoinEnabled":true
,"roomRating":"MEMERS_ONLY"
,"_class":"room"}

Here is my document. I want to be able to change the value of one of the objects in userRoomDatas. For instance "isSpectator":false to "isSpectator":true

public Mono<RoomData> findAndModifyUserSpectate(String roomId, String requestUserId,
        boolean isSpecator){
    Query query = new Query();
    query.addCriteria(Criteria.where("id").is(roomId).and("userRoomDatas")
            .elemMatch(Criteria.where("userId").is(requestUserId)));
    Update update = new Update();
    update.set("userRoomDatas.$.isSpectator : " + isSpecator, new Query(Criteria
            .where("userRoomDatas").elemMatch(Criteria.where("userId").is(requestUserId))));
    FindAndModifyOptions options = FindAndModifyOptions.options();
    options.returnNew(true);
    return template.findAndModify(query, update, options, RoomData.class);
}

This is the best I got. I have tried different ways but I know they are all wrong.

The first query from my understanding filters out my RoomData objects. But the Update part I don't know how to reference a document inside an array and tell it to update one of it's variables with a new value. I know the key part doesn't make a whole lot of sense. I have tried different ways but I don't know where to put the query with the value.

I'm an idiot. When creating my document I was not sending in the right UserRoomData object so the user was never added to the Room, thus the reason why I was nulling out. I correctly added the right user to the Room (correctly passed in the right userId) and also changed to how I had it before I made the desperate changes.

public Mono<RoomData> findAndModifyUserSpectate(String roomId, String requestUserId,
        boolean isSpecator){
    Query query = new Query();
    query.addCriteria(Criteria.where("id").is(roomId).and("userRoomDatas")
            .elemMatch(Criteria.where("userId").is(requestUserId)));
    Update update = new Update();
    update.set("userRoomDatas.$.isSpectator", isSpecator);
    FindAndModifyOptions options = FindAndModifyOptions.options();
    options.returnNew(true);
    return template.findAndModify(query, update, options, RoomData.class);
}

I still don't understand how the placeholder knows the correct UserRoomData to replace. I know in the query I have a eleMatch but I don't understand how after the query it knows to replace if that makes any sense. I always thought query was to get the correct Room object rather than being able to get UserRoomData object.

EDIT: Thought I was wrong. What the problem came down to was I was adding the same user twice to the list. One of the Users was changing correctly, the other wasn't. So when I did my asssertEquals test it was returning the one that didn't change thus failing the test. This works though. I just had to delete adding same user.

This is an old post but I hope this can help others. The most generic way to update a document inside an array is to use MongoDB arrayFilters . Using springboot there is no direct connection using a MongoTemplate but it is possible using a MongoDatabase object.

A practical example is easy. Suppose to have a document like

{ _id: "132456",
  statistics:
    2018: [
      {
         offset: 5,
         results: {
            a: 1,
            b: 2
         }
      },
      {
         offset: 4,
         results: {
            a: 13,
            b: 24
         }
      }
    ]
}

The Java code for update looks like

// Defines the query which selects the document
Bson filter = Filters.and(Filters.eq("_id", "132456"), Filters.exists("statistics"));

// Defines the udpate command. Notice the filtered positional operator $[element]
Bson update = Updates.set("statistics.$[element].results.b", result);

// IMPORTANT! This select the document into the array. 
// Notice that the document does not really contain a key called "element": 
// it is only a parametric variable later referenced in the arrayFilters
UpdateOptions updateOptions = new UpdateOptions();
List<Bson> arrayFilters = new ArrayList();
Bson arrayElement = Filters.eq("element.daysOffset", -4);
arrayFilters.add(arrayElement);
updateOptions.arrayFilters(arrayFilters);

// Finally update the element matching both the document in the collection 
// and the document in the inner array
mongoTemplate.getDb().getCollection("MyCollection").updateOne(filter, update, updateOptions);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM