4

Given an java.util.UUID generated like this...

import java.util.UUID

val uuid = UUID.randomUUID

... is it possible to convert it into a MongoDB ObjectID and preserve uniqueness? Or shall I just set _id to the UUID value?

Of course, the best solution would be to use BSONObjectID.generate... but in my case I get a JSON Web Token with an UUID as its ID, and I need to trace it in a MongoDB collection.

Tx.

j3d
  • 9,492
  • 22
  • 88
  • 172
  • Not clear what you are trying to do. Do you just want to store your UUID and don't know what type to use for the attribute? Or do you want to use the value of your UUID for your _id attribute? – vptheron Mar 25 '14 at 12:54
  • I want to use the value of the UUID for the _id attribute... and store it as a standard 12-byte BSON value. – j3d Mar 25 '14 at 13:02
  • "and preserve uniqueness" - UUID is a 128-bit value. ObjectId is 96-bit. So no, not without losing _some_ uniqueness. – Sergio Tulentsev Jun 14 '18 at 10:17

3 Answers3

1

Why not simply save _id = uuid ?

MongoDB will handle it simply :)

vzamanillo
  • 9,905
  • 1
  • 36
  • 56
twillouer
  • 1,149
  • 9
  • 14
  • Tried... but it doesn't work. ReactiveMongo just crashes because the object id is invalid. – j3d Mar 25 '14 at 21:48
  • 1
    It's seem thant ReactiveMongo NEED an ObjectId for _id field. You can just lets him handle, and use your own field, and make an ensureIndex({ownfield:1}, {unique:true}) – twillouer Mar 26 '14 at 07:45
  • @twillouer Could you elaborate a bit more? How to implement in models? – PovilasID Mar 26 '15 at 17:00
1

The following implicit objects allow reactivemongo to convert UUID into BSONBinary.

  implicit val uuidBSONWriter: BSONWriter[UUID, BSONBinary] =
    new BSONWriter[UUID, BSONBinary] {
      override def write(uuid: UUID): BSONBinary = {
        val ba: ByteArrayOutputStream = new ByteArrayOutputStream(16)
        val da: DataOutputStream = new DataOutputStream(ba)
        da.writeLong(uuid.getMostSignificantBits)
        da.writeLong(uuid.getLeastSignificantBits)
        BSONBinary(ba.toByteArray, Subtype.OldUuidSubtype)
      }
    }

  implicit val uuidBSONReader: BSONReader[BSONBinary, UUID] =
    new BSONReader[BSONBinary, UUID] {
      override def read(bson: BSONBinary): UUID = {
        val ba = bson.byteArray
        new UUID(getLong(ba, 0), getLong(ba, 8))
      }
    }

  def getLong(array:Array[Byte], offset:Int):Long = {
    (array(offset).toLong & 0xff) << 56 |
    (array(offset+1).toLong & 0xff) << 48 |
    (array(offset+2).toLong & 0xff) << 40 |
    (array(offset+3).toLong & 0xff) << 32 |
    (array(offset+4).toLong & 0xff) << 24 |
    (array(offset+5).toLong & 0xff) << 16 |
    (array(offset+6).toLong & 0xff) << 8 |
    (array(offset+7).toLong & 0xff)
  }

The following is an example to use the above writer and reader objects

abstract class EntityDao[E <: Entity](db: => DB, collectionName: String) {

  val ATTR_ENTITY_UUID = "entityUuid"

  val collection = db[BSONCollection](collectionName)

  def ensureIndices(): Future[Boolean] = {
    collection.indexesManager.ensure(
      Index(Seq(ATTR_ENTITY_UUID -> IndexType.Ascending),unique = true)
    )
  }

  def createEntity(entity: E) = {
    val entityBSON = BSONDocument(ATTR_ENTITY_UUID -> entity.entityUuid)
    collection.insert(entityBSON)
  }

  def findByUuid(uuid: UUID)(implicit reader: BSONDocumentReader[E]): Future[Option[E]] = {
    val selector = BSONDocument(ATTR_ENTITY_UUID -> uuid)
    collection.find(selector).one[E]
  }
}
k.c. sham
  • 381
  • 2
  • 6
0

Here's an alternative BSON handler implementation for UUID without manual bit twiddling:

import reactivemongo.bson._
import java.nio.ByteBuffer
import java.util.UUID

implicit val uuidBSONHandler: BSONHandler[BSONBinary, UUID] = BSONHandler(
  { bson =>
    val lb = ByteBuffer.wrap(bson.byteArray).asLongBuffer
    new UUID(lb.get(0), lb.get(1))
  },
  { uuid =>
    val arr = Array.ofDim[Byte](16)
    ByteBuffer.wrap(arr).asLongBuffer.put(uuid.getMostSignificantBits).put(uuid.getLeastSignificantBits)
    BSONBinary(arr, Subtype.UuidSubtype)
  })