objectid is changing after inserting into database

30 views
Skip to first unread message

Marcin Szekalski

unread,
Jun 25, 2014, 8:39:30 AM6/25/14
to scala...@googlegroups.com

I've a problem with Salat library in scala - I've got a case class Item:

case class Item(_id: ObjectId = new ObjectId, var name: String, var active: Boolean) extends WithId {
  override def id: Option[ObjectId] = Some(_id)
}

The _id field is created upon Item instantiation.

I'm trying to test the functionality of inserting an Item as follows:

  var itemObj = Item(name = "testItem", active = true)

  "MainService" should {
    "put an item into database" in {
      Put("/items/", itemObj) ~> mainRoute ~> check {
        val item = responseAs[Item]
        item.name === "testItem"
        item.active === true
        item._id === itemObj._id
        Item.findAll().toList.size === 1
      }
    }
}

Where the PUT /items/ mapping corresponds to Spray HTTP Route:

put {
      entity(as[Item]) { item ⇒
        complete {
          Item.saveOrUpdate(item)
          logger.info("putting item {}", item)

          HttpResponse(
            StatusCodes.OK,
            HttpEntity(ContentTypes.`application/json`, grater[Item].toCompactJSON(item))
          )
        }
      }
    }

And the saveOrUpdate definition is as follows:

  def saveOrUpdate(t: T) = {
    t.id match {
      case Some(id) => dao.update(MongoDBObject("_id" -> id), t, false, false, new WriteConcern)
      case None => dao.insert(t)
    }

Now, the thing is the test fails on assertion

item._id === itemObj._id

I've no idea why would _id change if I'm setting it up before doing the save or update of entity.

Does anyone have any idea why does it acts that way and what can I do to fix this?

Best, Marcin

rose katherine toomey

unread,
Jun 25, 2014, 9:09:12 AM6/25/14
to scala-salat
Hi Marcin,

I think your saveOrUpdate logic is not correct.  Because you have a default argument for _id, def id will always return Some[ObjectId].

I'd recommend using the plain "save" method, which will upsert.  You could also use update with the upsert option set to true.

Best,
Rose


--
You received this message because you are subscribed to the Google Groups "scala-salat" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-salat...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Marcin Szekalski

unread,
Jun 25, 2014, 12:21:28 PM6/25/14
to scala...@googlegroups.com
Hey, I did change both, but the error stays the same.

I think I need auto generated ObjectId too, as I don't want to provide it in JSON that I PUT via REST

Best,
Marcin

rose katherine toomey

unread,
Jun 25, 2014, 12:31:58 PM6/25/14
to scala-salat
First off, you need to isolate where the ObjectId is being changed.  You are sending the object id in the JSON that you PUT via REST because the Item constructor has a default argument for "_id".  Mongo is not like a database where you need to rely on insert to return you some auto-generated id so you know what the id was.  If you try to save something without an id, mongo-java-driver will assign one for you before the insert.

Marcin Szekalski

unread,
Jun 25, 2014, 12:42:35 PM6/25/14
to scala...@googlegroups.com
Hey,

Thanks for your reply. I can see now that the ID changes as follows:
  • before the test execution (after instantiation) - 53aafb0a5f7b3ed2a03e3d55
  • in the controller's PUT mapping - 53aafb315f7b3ed2a03e3d56
  • inside the test (after making PUT) request - 53aafb5a5f7b3ed2a03e3d57
I can see the id changing every time and I've no idea why would it do that. I'll keep on searching.

Marcin Szekalski

unread,
Jun 29, 2014, 6:27:54 AM6/29/14
to scala...@googlegroups.com
Hey,

So i got back to this problem and I realized the id field is not being (Un)Marshalled -  the entity that goes with my PUT request is HttpEntity(application/json; charset=UTF-8,{"id":{},"itemId":1,"name":"testItem","active":false}) whilst for me it should be some of HttpEntity(application/json; charset=UTF-8,{"id":"123456712345671234567890","itemId":1,"name":"testItem","active":false}) (i did change the objectIdStrategy to StringObjectIdStrategy, that's why I'm expecting that and not nested objects).

I tried to debug the BaseJson4sSupport trait in spray, but there are too many anonymous functions and I'm getting lost (especially json4sUnmarshaller).

I thought it would just work, because both Spray and Salat support Json4s, so I expected it work out of the box, and now I'm lost.

Do you have any hints?

Best,
Marcin

rose katherine toomey

unread,
Jun 29, 2014, 10:01:23 AM6/29/14
to scala...@googlegroups.com
The issue is one side is using a string format to serialize the object id and the other is using a JSON object. Look at your salat JSON config and try different object id serialization/deserialization options until you find one that works. Strict bson object is (object with single "$oid" field) might work? 

Marcin Szekalski

unread,
Jun 29, 2014, 12:59:46 PM6/29/14
to scala...@googlegroups.com
Hey,

I'm not sure what you mean by saying "different object id serialization/deserialization options" - I tried to use both StrictJSONObjectIdStrategy  and StringObjectIdStrategy but none of them work. I've set breakpoints in JSONConfig.scala class from Salat both in StrictJSONObjectIdStrategy and StringObjectIdStrategy 's in and out definitions but I noticed it never enters "in", just goes to "out" three times. What bugs me:

1) Why doesn't it do anything in the "in" method?
2) Why it stops in StrictJSONObjectIdStrategy methods, even if I provided my custom context:

package pl.lovemyway.elka

import com.novus.salat.json.{JSONConfig, StringObjectIdStrategy}
import com.novus.salat.{Context, StringTypeHintStrategy, TypeHintFrequency}

package object salatcontext {
  implicit val ctx = new Context {
    val name = "salatContext"
    override val jsonConfig =  JSONConfig(objectIdStrategy = StringObjectIdStrategy)
    override val typeHintStrategy = StringTypeHintStrategy(when = TypeHintFrequency.WhenNecessary, typeHint = "_t")
  }
}

Best,
Marcin

To unsubscribe from this group and stop receiving emails from it, send an email to scala-salat+unsubscribe@googlegroups.com.

rose katherine toomey

unread,
Jun 29, 2014, 1:05:54 PM6/29/14
to scala...@googlegroups.com
Look at the JSON output - one side is expecting an object not a string. It's like you're playing a game of "telephone" where through repeated serialization and deserialization the object is being mutated unexpectedly. 

Two recommendations: turn off the default arg for ObjectId, and use salat instead of spray to deserialize the JSON received by the PUT.  I don't know what the problem is with the debugger, but if the "in" method weren't being hit, you couldn't copy paste JSON showing the object id as a string....
Reply all
Reply to author
Forward
0 new messages