Search code examples
mongodbscalacasbah

Doing a MongoDB findAndModify query including $addToSet in Casbah


I am both new to scala and cashbah. I am trying to

  • update a document if exists (by _id) and create if doesn't exist.
  • while updating, update some key values
  • while updating, update some keys which values are sets, include some data to those sets.

To achive this, I've written this:

DBObject = MongoDBObject("_id" -> uri.toString) ++
      $addToSet("appearsOn" -> sourceToAppend) ++
      $addToSet("hasElements" -> elementsToAppend) ++
      $addToSet("hasTriples" -> triplesToAppend) ++
      MongoDBObject("uDate" -> new DateTime)

    /* Find and replace here! */
    OntologyDocument.dao.collection.findAndModify(
      query = MongoDBObject({"_id" -> uri.toString}),
      update = update,
      upsert = true,
      fields = null,
      sort = null,
      remove = false,
      returnNew = true
    )

Documents looked by _id, some new items added to appearsOn hasElements hasTriples and uDate is updated.

sourceToAppend elementsToAppend and triplesToAppend are List[String]

When I run this, I got this error:

java.lang.IllegalArgumentException: fields stored in the db can't start with '$' (Bad Key: '$addToSet')
    at com.mongodb.DBCollection.validateKey(DBCollection.java:1444) ~[mongo-java-driver-2.11.1.jar:na]

I didn't get it. What is wrong with this query? $addToSet isn't a field, why casbah thinks it is a field? What am I doing wrong here?


Solution

  • The reason its failing is because the update query is invalid (it wont work in the js shell).

    $set is implicit for values in the update document, but you can't mix it with other update operators eg $addToSet. If you want to mix $set with other set operators then you can if you are explicit:

    val update = $set("uDate" -> new DateTime) ++ 
                 $addToSet("appearsOn" -> sourceToAppend, 
                           "hasElements" -> elementsToAppend, 
                           "hasTriples" -> triplesToAppend)
    

    You can't $set "_id" but as thats in the query and its an upsert - it will merge so don't include it in the update statement - otherwise it will error.

    Finally, @AsyaKamsky is right if you dont need the returned document - use an update its also atomic.