MongoDB: upsert when arrayFilters can't find a

2020-08-09 06:44发布

问题:

I have a collection like this:

readers

[{
    "id": 123,
    "name": "John Doe",
    "books": [
      {
        "type": "comic",
        "names": [
          "batman",
          "tintin"
        ]
      },
      {
        "type": "drama",
        "names": [
          "hamlet",
          "otelo"
        ]
      }
    ]
 },
 {...}]

Then I want to update my collection adding in array names matching by type.

When type exists works perfectly, in this example romeo and juliet is pushed correctly:

db.collection('readers').updateOne(
    { id: 123 },           
    {
      $push: {
        'books.$[element].names': 'romeo and juliet'
      }
    },
    {
      upsert: true,
      arrayFilters: [{ 'element.type': 'drama' }] // type exists
    }
  );

The problem come when type is not matched:

db.collection('readers').updateOne(
    { id: 123 },           
    {
      $push: {
        'books.$[element].names': 'hobbit'
      }
    },
    {
      upsert: true,
      arrayFilters: [{ 'element.type': 'fantasy' }] // new type
    }
  );

I expect this result but nothing is added:

{
   "id": 123,
   "name": "John Doe",
   "books": [
    {
      "type": "comic",
      "names": [
        "batman",
        "tintin"
      ]
    },
    {
      "type": "drama",
      "names": [
         "hamlet",
         "otelo"
      ]
    },
    {
      "type": "fantasy",
      "names": [
         "hobbit"             
      ]
    }
  ]
}

What am I doing wrong?

回答1:

You expect upsert to create the missing book but it's not how it works.

Upsert: If set to true, creates a new document when no document matches the query criteria. The default value is false, which does not insert a new document when no match is found.

I think you have create 2 queries, one to check if the book exists and another one to update/create it (either push or you create the book)