All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.foursquare.rogue.spindle.SpindleRogueSerializer.scala Maven / Gradle / Ivy

// Copyright 2013 Foursquare Labs Inc. All Rights Reserved.

package com.foursquare.rogue.spindle

import com.foursquare.common.thrift.bson.TBSONObjectProtocol
import com.foursquare.rogue.MongoHelpers.MongoSelect
import com.foursquare.rogue.{RogueReadSerializer, RogueWriteSerializer, SelectField}
import com.foursquare.spindle.{UntypedFieldDescriptor, UntypedMetaRecord, UntypedRecord}
import com.mongodb.DBObject

class SpindleRogueReadSerializer[M <: UntypedMetaRecord, R](meta: M, select: Option[MongoSelect[M, R]])
    extends RogueReadSerializer[R] {

  private def getValueFromRecord(metaRecord: UntypedMetaRecord, record: Any, fieldName: String): Option[Any] = {
    val fieldList: List[UntypedFieldDescriptor] = metaRecord.untypedFields.toList
    val fieldDescriptor = fieldList.find(fd => fd.name == fieldName).getOrElse(
      throw new Exception("The meta record does not have a definition for field %s".format(fieldName))
    )
    fieldDescriptor.unsafeGetterOption(record)
  }

  private def getValueFromAny(sourceObj: Option[Any], fieldName: String): Option[Any] = sourceObj.flatMap(_ match {
    case (map: Map[_, _]) => map.find({case(key, value) => key.toString == fieldName}).map(_._2)
    case (seq: Seq[_]) => Some(seq.map(v => getValueFromAny(Some(v), fieldName)))
    case (rec: UntypedRecord) => getValueFromRecord(rec.meta, rec, fieldName)
    case _ => throw new Exception("Rogue bug: unepected object type")
  })

  private def extractRecordFromDbo(dbo: DBObject): R = {
    dbo match {
      case sdbo: SpindleDBObject => sdbo.record.asInstanceOf[R]
      case _ =>
        val record = meta.createUntypedRawRecord
        val protocolFactory = new TBSONObjectProtocol.ReaderFactory
        val protocol = protocolFactory.getProtocol
        protocol.setSource(dbo)
        record.read(protocol)
        record.asInstanceOf[R]
    }
  }

  override def fromDBObject(dbo: DBObject): R = select match {
    case Some(MongoSelect(Nil, transformer)) => {
      // A MongoSelect clause exists, but has empty fields. Return null.
      // This is used for .exists(), where we just want to check the number
      // of returned results is > 0.
      transformer(null)
    }
    case Some(MongoSelect(fields, transformer)) => {
      val record: R = extractRecordFromDbo(dbo)

      val values = {
        fields.map(fld => {
          if (fld.field.isInstanceOf[UntypedFieldDescriptor]) {
            val valueOpt = fld.field.asInstanceOf[UntypedFieldDescriptor].unsafeGetterOption(record)
            fld.valueOrDefault(valueOpt)
          } else {
            // We need to handle a request for a subrecord, such as foo.x.y
            val (rootFieldName :: subPath) = fld.field.name.split('.').toList.filter(_ != "$")
            val rootValueOpt = getValueFromRecord(fld.field.owner.asInstanceOf[UntypedMetaRecord], record, rootFieldName)
            val valueOpt = subPath.foldLeft(rootValueOpt)(getValueFromAny)
            fld.valueOrDefault(valueOpt)
          }
        })
      }
      transformer(values)
    }
    case None => {
      extractRecordFromDbo(dbo)
    }
  }
}

class SpindleRogueWriteSerializer extends RogueWriteSerializer[UntypedRecord] {
  def toDBObject(record: UntypedRecord): DBObject = {
    val protocolFactory = new TBSONObjectProtocol.WriterFactoryForDBObject
    val protocol = protocolFactory.getProtocol
    record.write(protocol)
    protocol.getOutput.asInstanceOf[DBObject]
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy