Uploaded image for project: 'Core Server'
  1. Core Server
  2. SERVER-3326

Support $ positional operator with an upsert

    • Type: Icon: New Feature New Feature
    • Resolution: Won't Do
    • Priority: Icon: Major - P3 Major - P3
    • None
    • Affects Version/s: 1.8.2
    • Component/s: Write Ops
    • Environment:
      All
    • Query

      According to the documentation, this limitation currently applies to upserts:
      <quote>
      The positional operator cannot be combined with an upsert since it requires
      a matching array element. If your update results in an insert then the "$"
      will literally be used as the field name.
      </quote>

      When performing a findAndModify it would be incredibly powerful to support the insert condition for a document that does not exist. It would save on multiple calls to the Mongo Server and allow for a single atomic update/insert for a document with embedded arrays.

      Below is JavaScript that can be run in the Mongo Shell to demonstrate this powerful capability:

      The purpose of these documents is keep track of the last login per user per type of login. And to create the user document if it does not already exist in a single atomic transaction.

      db.user_identities.ensureIndex({"user" : 1}, {unique : true});
      db.user_identities.save({
          user: 'user1',
          first_seen: 'date1',
          last_seen: [
              { type: 'TA', date: 'date1' }
          ]
      })
      
      db.user_identities.save({
          user: 'user2',
          first_seen: 'date2',
          last_seen: [
              { type: 'TA', date: 'date2' },
              { type: 'PP', date: 'date3' }
          ]
      })
      
      db.user_identities.save({
          user: 'user3',
          first_seen: 'date3',
          last_seen: [
              { type: 'TA', date: 'date3' },
              { type: 'PP', date: 'date4' }
          ]
      })
      
      // Existing entry - Works as expected
      db.user_identities.findAndModify({
          query: {"user":"user3", "last_seen" : {$elemMatch : {"type": "PP", "date" : { $lte: "date4"} }}},
          update: {$set: {"last_seen.$":{ type: 'PP', date: 'date5' }}},
          upsert: true,
          "new": true}) 
      
      /* Returns the following:
      {
              "_id" : ObjectId("4e034dd979ee2fd2e506ce24"),
              "user" : "user3",
              "first_seen" : "date3",
              "last_seen" : [
                      {
                              "type" : "TA",
                              "date" : "date3"
                      },
                      {
                              "type" : "PP",
                              "date" : "date5"
                      }
              ]
      } 
      */
      
      // Non-existent - Inserts the $ as the element name in the new
      document
      db.user_identities.findAndModify({
          query: {"user":"user4", "last_seen" : {$elemMatch : {"type": "PP", "date" : { $lte: "date6"} }}},
          update: {$set: {"last_seen.$":{ type: 'PP', date: 'date6' }}},
          upsert: true,
          "new": true}) 
      
      /* Returns:
      {
              "_id" : ObjectId("4e034dd9f0e57cca9b6f045a"),
              "last_seen" : {
                      "$" : {
                              "type" : "PP",
                              "date" : "date6"
                      }
              },
              "user" : "user4"
      
      }
      */
      

            Assignee:
            backlog-server-query Backlog - Query Team (Inactive)
            Reporter:
            reidmo@gmail.com Reid Morrison
            Votes:
            45 Vote for this issue
            Watchers:
            37 Start watching this issue

              Created:
              Updated:
              Resolved: