0

I have a database like this:

[
   {
      "universe":"comics",
      "saga":[
         {
            "name":"x-men",
            "characters":[
               {
                  "character":"wolverine",
                  "picture":"618035022351.png"
               },
               {
                  "character":"cyclops",
                  "picture":"618035022352.png"
               }
            ]
         }
      ]
   },
   {
      "universe":"dc",
      "saga":[
         {
            "name":"spiderman",
            "characters":[
               {
                  "character":"venom",
                  "picture":"618035022353.png"
               }
            ]
         }
      ]
   }
]

and with this code I manage to update one of the objects in my array. specifically the object where character: wolverine

db.mydb.findOneAndUpdate({
        "universe": "comics",
        "saga.name": "x-men",
        "saga.characters.character": "wolverine"
    }, {
        $set: {
            "saga.$[].characters.$[].character": "lobezno",
            "saga.$[].characters.$[].picture": "618035022354.png",

        }
    }, {
        new: false
    }

)

it returns all my document, I need ONLY the document matched

I would like to return the object that I have updated without having to make more queries to the database.

Note

I have been told that my code does not work well as it should, apparently my query to update this bad, I would like to know how to fix it and get the object that matches these search criteria.

In other words how can I get this output:

           {
              "character":"wolverine",
              "picture":"618035022351.png"
           }

in a single query using filters

    {
     "universe": "comics",
     "saga.name": "x-men",
     "saga.characters.character": "wolverine"
    }

My MongoDB knowledge prevents me from correcting this.

halfer
  • 19,824
  • 17
  • 99
  • 186
yavg
  • 2,761
  • 7
  • 45
  • 115
  • try providing new:true instead of false. – Purushoth.Kesav Jul 20 '20 at 03:19
  • not works, it returns all my document, I need ONLY the document matched – yavg Jul 20 '20 at 03:21
  • You can try using the `projection` parameter - defines as _"A subset of fields to return."_ – prasad_ Jul 20 '20 at 03:33
  • @prasad_ I know, although I don't know how to do it, I understand that I should use agreattions, but in this case I am updating ... – yavg Jul 20 '20 at 03:35
  • (1) Your update statement doesn't look right - the syntax used needs `arrayFilters`. (2) The `$[]` array update all positional operator updates _all elements_ of the array. To update specific array element, you need to use `$[id]` positional filter operator _or_ `$` positional operator. (3) Yes, you can use [Updates with Aggregation Pipeline](https://docs.mongodb.com/manual/tutorial/update-documents-with-aggregation-pipeline/index.html), and I think it may not make a difference in case of projection. – prasad_ Jul 20 '20 at 03:45
  • @prasad_ God .. I think I am wrong about many things, but I am excited that I can learn a lot. Would it be too much trouble asking you to help me with this please? My knowledge of mongodb for now is not enough, and surely with a great help I will understand better. – yavg Jul 20 '20 at 03:48
  • There are examples for _each_ of the array update operations in the documentation: [Array Update Operators](https://docs.mongodb.com/manual/reference/operator/update-array/). Please try the examples. Also, a specific search on the net / SO can find appropriate posts suitable to your needs. Also, see the [Projection Operators](https://docs.mongodb.com/manual/reference/operator/query/#projection-operators) used with array elements. – prasad_ Jul 20 '20 at 03:51
  • @prasad_ I will try, I had also tried `$ elemMatch` but I don't know what I did wrong. – yavg Jul 20 '20 at 03:58
  • As @prasad_ said, 1. you have to give the position to array filter, otherwise it will update all documents. 2. is "character" or "picture" unique? If so, you can use aggregation operation to return the object, else you would have "_id" for each object – varman Jul 20 '20 at 04:44
  • @varman only picture is unique.... – yavg Jul 20 '20 at 05:16
  • I am practicing what the friend @prasad_ said, it is a lot of information at once, if you have time it would be great to guide me. – yavg Jul 20 '20 at 05:19
  • Try positional projection operator ($). – D. SM Jul 20 '20 at 05:37
  • @prasad_ If you have a moment I would really appreciate it if you could help me .. I have tried to follow what you recommended, but I have some syntax errors or it just doesn't work. I know that with this answer many users of this community will learn and know how to solve several problems at the same time – yavg Jul 20 '20 at 06:30
  • What is the error? – prasad_ Jul 20 '20 at 06:44
  • You can try working with an example, with one level of nested array _and_then try with two levels of nesting. It is, in general, not easy to work with nested arrays, especially if you are new to working with array data. – prasad_ Jul 20 '20 at 07:07
  • Here is a post about updating nested array elements: [Updating nested array inside array mongodb](https://stackoverflow.com/questions/29634150/updating-nested-array-inside-array-mongodb) – prasad_ Jul 20 '20 at 07:17
  • Just to understand the latest state of your question, your updating is working fine but you issue it is returning the whole document instead of those 2 fields inside the document? – Kavithakaran Kanapathippillai Jul 22 '20 at 06:54
  • @prasad_ Friend, I have tried many things, I prefer not to put the code I have tried because it simply does not work, my knowledge is not adequate. I put a bounty on this question, I am very interested to understand how I can return the matched object using an update and it would be great if you could please teach me how to update my data correctly (according to your suggestion). Please ask me to help me with that, I know it will be quite important to understand it. – yavg Jul 22 '20 at 15:13

2 Answers2

3

Use the shell method findAndModify to suit your needs.

But you cannot use the positional character $ more than once while projecting in MongoDb, so you may have to keep track of it yourself at client-side.

Use arrayFilters to update deeply nested sub-document, instead of positional all operator $[].

Below is a working query -

var query = {
    universe: 'comics'
};

var update = {
    $set: {
        'saga.$[outer].characters.$[inner].character': 'lobezno',
        'saga.$[outer].characters.$[inner].picture': '618035022354.png',
    }
};

var fields = {
    'saga.characters': 1
};

var updateFilter = {
    arrayFilters: [
        {
            'outer.name': 'x-men'
        },
        {
            'inner.character': 'wolverine'
        }
    ]
};

db.collection.findAndModify({
    query,
    update,
    fields,
    arrayFilters: updateFilter.arrayFilters
    new: true
});
Kunal Mukherjee
  • 5,775
  • 3
  • 25
  • 53
  • I really appreciate your help, but if you see the comments here, you can see that a user told me that it is possible directly from mongodb to return the matched object, precisely for that reason I asked this question. By the way, "outer", "inner" can be any word or do I always have to use them? – yavg Jul 22 '20 at 19:09
  • @yavg `outer` and `inner` can be any identifier which you please, I just found them to be meaningful in this context – Kunal Mukherjee Jul 22 '20 at 19:11
  • @yavg Its possible if there is only one array level. Using the `$elemMatch` operator – Kunal Mukherjee Jul 22 '20 at 19:12
  • Thank you, I am very pleased with your answers, so it is definitely impossible to return a single object in my case while doing an update. – yavg Jul 22 '20 at 19:14
  • @yavg Its possible when the array is nested at only 1 level, not multi-level. See [`this`](https://stackoverflow.com/a/33915197/7177029) answer – Kunal Mukherjee Jul 22 '20 at 19:22
  • One last question, why can't I use findOneAndUpdate? – yavg Jul 22 '20 at 20:13
0
  • If I understand your question correctly, your updating is working as expected and your issue is that it returns the whole document and you don't want to query the database to just to return these two fields.

  • Why don't you just extract the fields from the document returned from your update? You are not going to the database when doing that.

    var extractElementFromResult = null;
    if(result != null) {
       extractElementFromResult = result.saga
          .filter(item => item.name == "x-men")[0]
          .characters
         .filter(item => item.character == "wolverine")[0];
    }