6

I'm using Salat with MongoDB and I'm trying to convert to natural keys to avoid duplicates in the database. The case class I'm using looks somewhat like:

case class Foo(someRelatedId: String, email: String ...)

I would like to add a natural key that consists of someRelatedId+email and have MongoDB use that instead of the default ObjectId. From the documentation I get the feeling it is possible, but I'm still groping around for a working solution. This is in a large part due to my lack of proficiency with Scala itself, I'm sure.

Update: I have a working solution now, but I'm still wondering if it is the best way to go

case class Foo(someRelatedId: String, email: String, naturalKey: String)

object Foo {
  def apply((someRelatedId: String, email: String) {
    apply(someRelatedId, email, someRelatedId+email)
  }
}

And then in package.scala I map to a custom salat context:

implicit val ctx = new Context() {
  val name = Some("Custom Context")
}
ctx.registerGlobalKeyOverride(remapThis = "naturalKey", toThisInstead = "_id")

This way I avoid having a mandatory (meaningless) _id field in my domain classes, but I do have to overload apply() on the companion object, which seems a bit clunky.

iwein
  • 25,788
  • 10
  • 70
  • 111
  • if you create another val as someRelatedId+email and annotate it with @Key("_id") and put @Ignore on someRelatedId if you don't need it persisted? – milan Jan 16 '12 at 20:29

1 Answers1

5

main Salat developer here.

Like Milan suggested, create a case class for your composite key:

case class FooKey(someRelatedId: String, email: String)

case class Foo(@Key("_id") naturalKey: FooKey) {

  // use @Persist if you want these fields serialized verbatim to Mongo - see https://github.com/novus/salat/wiki/Annotations for details
  @Persist val email =  naturalKey.email
  @Persist val someRelatedId = naturalKey.someRelatedId

}

object FooDAO extends SalatDAO[Foo, FooKey](collection = /*  some Mongo coll */ )

If you object to "_id" as a field name, you can use a global override in the context to remap "_id" to "naturalKey", or supply ad hoc @Key overrides on each object.

I don't personally like giving the _id a different name in your models as then your Mongo queries must use the serialized key "_id" while all your business logic must use the case class field name ("naturalKey" or whatever), but YMMV.

P.S. Our mailing list is at http://groups.google.com/group/scala-salat - I'll see your question quicker there than Stack Overflow.

prasinous
  • 798
  • 3
  • 6
  • Definitely works, but I don't like the framework specific annotations in my domain. I do like keeping the structure in the composite key though, worth some more late night hacking I guess. – iwein Jan 18 '12 at 20:31
  • Sorry, just saw this. You can avoid the annotation by just adding a per-class or global override in the `Context`. The only downside of this approach is that, since the object is not explicitly annotated, anyone making a query needs to understand that there are overrides in the `Context`. I think a lot of groups globally override `_id` to `id`, for instance. – prasinous Sep 13 '13 at 15:52