3 query for simple operation

Hello. I have structure document with nested array

{
    userId: 1,
    books: [
         { bookId: 55, otherfields },
         { bookId: 66, otherfields },
    ]
}

You all say that mongo is convenient and simple!

Right i understand that need do 3 query for simple action ‘savebook’?
Take into that the method can call many times. For clear collection and for exists document.

function savebook(mybookWithId55){
     // 1. create document if not exists 
     db.update( { userId: 1  }, { $setOnInsert: { books: [] } },  upsert: true )

     // 2. if not exists book in nested array -> push
     let dummy= { bookId: 55 };
     db.update( { userId: 1, books.bookId: { $ne: 55 } } , { $push: { books: dummy } } )

     // 3. update book with full fields 
     db.update( { userId: 1} , { $set: { books.$[i]: mybookWithId55} }, {  arrayFilters: [ { i.bookId: 55}  })
}

Hi @alexov_inbox ,

So in my opnion this is not a simplistic update and it involves some business logic in it. You can achieve it via 2 commands that will eventually update will run only when required:

function savebook(mybookWithId55){

     var result = db.update( { userId: 1 }, { $setOnInsert: { books: [mybookWithId55] } },  upsert: true )
    
   if (result.upsertedCount == 0) { 

     db.update( { userId: 1} , { $set: { books.$[i]: mybookWithId55} }, {  arrayFilters: [ { i.bookId: 55} ] })
}
}

That makes sense?

Thanks
Pavel

Hi. Your code is not same logic.
example

  1. clear collection
  2. call savebook(mybookWithId66)
  3. call savebook(mybookWithId55)

on step 3: bookwith 55 will not save. will be skip

@alexov_inbox ,

Ok I done some more work as I understood my previous command does not cover all scenarios you need.

I came with the following example using pipeline updates:

 var newBook={"bookId" : 3, "data" : "MyData"};
 db.coll.updateOne({userId :3},[{$addFields: {
    booksFind: {
     $filter: {
      input: '$books',
      cond: {
       $eq: [
        '$$this.bookId',
        newBook.bookId
       ]
      }
     }
    }
   }}, {$set: {
    books: {
     $cond: [
      {
       $gte: [
        {
         $size: {
          $ifNull: [
            '$booksFind',
             []
          ]
         }
        },
        1
       ]
      },
      {
       $map: {
        input: '$books',
        'in': {
         $cond: [
          {
           $eq: [
            '$$this.bookId',
            newBook.bookId
           ]
          },
          newBook,
          '$$this'
         ]
        }
       }
      },
      {
       $concatArrays: [
        {$ifNull : ['$books',[]]},
        [
            newBook
        ]
       ]
      }
     ]
    }
   }}, {$project: {
    booksFind: 0
   }}],{upsert : true})

So this pipeline gets a newBook object and tries to either push it as initial value if no document exist or adds/replaces a book it is there…

I know it is complex but I did not find a more simple way in one command.

Ty
Pavel

1 Like

oh its work but its CRAZY :smiley:

1 Like

Hi @alexov_inbox ,

Well yes mongodb is powerful :wink:

I fixed the update it has some bugs with $ifNull expression. Check out the new version it should cover it.

2 Likes

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.