PageRenderTime 105ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/net.modelbased.sensapp.service.database.raw/src/main/scala/net/modelbased/sensapp/service/database/raw/backend/impl/MongoDB.scala

https://github.com/SINTEF-9012/sensapp
Scala | 238 lines | 160 code | 24 blank | 54 comment | 7 complexity | a9fd371254f3a2ccb936dba4297a4702 MD5 | raw file
  1. /**
  2. * ====
  3. * This file is part of SensApp [ http://sensapp.modelbased.net ]
  4. *
  5. * Copyright (C) 2011- SINTEF ICT
  6. * Contact: SINTEF ICT <nicolas.ferry@sintef.no>
  7. *
  8. * Module: net.modelbased.sensapp
  9. *
  10. * SensApp is free software: you can redistribute it and/or modify
  11. * it under the terms of the GNU Lesser General Public License as
  12. * published by the Free Software Foundation, either version 3 of
  13. * the License, or (at your option) any later version.
  14. *
  15. * SensApp is distributed in the hope that it will be useful, but
  16. * WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Lesser General Public License for more details.
  19. *
  20. * You should have received a copy of the GNU Lesser General
  21. * Public License along with SensApp. If not, see
  22. * <http://www.gnu.org/licenses/>.
  23. * ====
  24. *
  25. * This file is part of SensApp [ http://sensapp.modelbased.net ]
  26. *
  27. * Copyright (C) 2012- SINTEF ICT
  28. * Contact: SINTEF ICT <nicolas.ferry@sintef.no>
  29. *
  30. * Module: net.modelbased.sensapp.service.database.raw
  31. *
  32. * SensApp is free software: you can redistribute it and/or modify
  33. * it under the terms of the GNU Lesser General Public License as
  34. * published by the Free Software Foundation, either version 3 of
  35. * the License, or (at your option) any later version.
  36. *
  37. * SensApp is distributed in the hope that it will be useful, but
  38. * WITHOUT ANY WARRANTY; without even the implied warranty of
  39. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  40. * GNU Lesser General Public License for more details.
  41. *
  42. * You should have received a copy of the GNU Lesser General
  43. * Public License along with SensApp. If not, see
  44. * <http://www.gnu.org/licenses/>.
  45. */
  46. package net.modelbased.sensapp.service.database.raw.backend.impl
  47. import com.mongodb.casbah.commons.MongoDBObject
  48. import com.mongodb.casbah.commons.MongoDBList
  49. import com.mongodb.casbah.Imports._
  50. import net.modelbased.sensapp.service.database.raw.data._
  51. import net.modelbased.sensapp.library.senml.{Root, MeasurementOrParameter}
  52. import net.modelbased.sensapp.service.database.raw.backend._
  53. /**
  54. * MongoDB Implementation of the Raw Backend trait
  55. */
  56. class MongoDB extends Backend {
  57. def content: List[String] = {
  58. val data = metadata.find(MongoDBObject.empty, MongoDBObject("s" -> 1))
  59. val it = data map {e => e.getAs[String]("s").get }
  60. it.toList
  61. }
  62. def exists(sensor: String): Boolean = {
  63. metadata.findOne(MongoDBObject("s" -> sensor), MongoDBObject("s" -> 1)) != None
  64. }
  65. def create(request: CreationRequest): Boolean = {
  66. val schema = RawSchemas.withName(request.schema)
  67. val obj = SensorMetaData(request.sensor, request.baseTime, schema)
  68. metadata += metadata2dbobj(obj)
  69. true
  70. }
  71. def describe(sensor: String, prefix: String): Option[SensorDatabaseDescriptor] = {
  72. metadata.findOne(MongoDBObject("s" -> sensor)) match {
  73. case None => None
  74. case Some(dbobj) => {
  75. val schema = getSchema(sensor)
  76. val size = data.find(MongoDBObject("s" -> sensor)).size
  77. val obj = SensorDatabaseDescriptor(sensor, schema, size, prefix+sensor)
  78. Some(obj)
  79. }
  80. }
  81. }
  82. def delete(sensor: String): Boolean = {
  83. metadata.findOne(MongoDBObject("s" -> sensor)) match {
  84. case None => false
  85. case Some(dbObj) => {
  86. metadata -= dbObj;
  87. data.find(MongoDBObject("s" -> sensor)) foreach { data -= _ }
  88. true
  89. }
  90. }
  91. }
  92. def push(sensor: String, root: Root): Seq[MeasurementOrParameter] = {
  93. val canon = root.canonized
  94. val ref = getReferenceTime(sensor)
  95. canon.measurementsOrParameters match {
  96. case None => List()
  97. case Some(lst) => {
  98. val (accepted, rejected) = lst.par partition { mop => mop.name == Some(sensor) }
  99. accepted.foreach { mop =>
  100. val localStamp = mop.time.get - ref
  101. val legacy = data.findOne(MongoDBObject("s" -> sensor, "t" -> localStamp))
  102. legacy match { case Some(old) => data -= old; case None => }
  103. data += data2dbobj(sensor, mop2data(ref, mop))
  104. }
  105. rejected.seq
  106. }
  107. }
  108. }
  109. def importer(root: Root) {
  110. val canon = root.canonized
  111. var refTimes: Map[String, Long] = Map()
  112. canon.measurementsOrParameters match {
  113. case None =>
  114. case Some(lst) => {
  115. lst.par foreach { d =>
  116. val ref = refTimes.get(d.name.get) match {
  117. case None => {
  118. val ref = getReferenceTime(d.name.get)
  119. refTimes += d.name.get -> ref
  120. ref
  121. }
  122. case Some(ref) => ref
  123. }
  124. data.findOne(MongoDBObject("s" -> d.name.get, "t" -> (d.time.get - ref))) match {case Some(old) => data -= old; case None => }
  125. data += data2dbobj(d.name.get, mop2data(ref, d))
  126. }
  127. }
  128. }
  129. }
  130. def get(sensor: String, from: Long, to: Long, sorted: String, limit: Int): Root = {
  131. val sensorMetaData = dbobj2metadata(metadata.findOne(MongoDBObject("s" -> sensor)).get)
  132. val shiftedFrom = from - sensorMetaData.timestamp
  133. val shiftedTo = to - sensorMetaData.timestamp
  134. val query: DBObject = ("t" $lte shiftedTo $gte shiftedFrom) ++ ("s" -> sensor)
  135. val raw = sorted match {
  136. case "none" => data.find(query)
  137. case "asc" => {data.find(query).sort(MongoDBObject("t" -> 1)) }
  138. case "desc" => {data.find(query).sort(MongoDBObject("t" -> -1))}
  139. }
  140. val limited = if (limit > 0) raw.limit(limit) else raw
  141. val sensorData = limited.map{ dbobj2data(sensorMetaData.schema,_) }
  142. buildSenML(sensorMetaData, sensorData.toList)
  143. }
  144. def get(sensors: Seq[String], from: Long, to: Long, sorted: String, limit: Int): Root = {
  145. val all = sensors.par.map { s => this get(s,from, to, sorted, limit) }
  146. val data = all.map{r => r.canonized.measurementsOrParameters}
  147. .filter{ mop => mop != None }
  148. .map{ _.get }.flatten
  149. val content = if (data.isEmpty) None else Some(data.toList)
  150. Root(None, None, None, None, content)
  151. }
  152. def getSchema(sensor: String): String = {
  153. val obj = metadata.findOne(MongoDBObject("s" -> sensor)).get
  154. obj.getAs[String]("k").get
  155. }
  156. /**********************
  157. ** Private Elements **
  158. **********************/
  159. def ???(): Nothing = throw new RuntimeException("Not yet implemented")
  160. private def getReferenceTime(sensor: String): Long = {
  161. val obj = metadata.findOne(MongoDBObject("s" -> sensor)).get
  162. obj.getAs[Long]("t").get
  163. }
  164. private def metadata2dbobj(md: SensorMetaData): MongoDBObject = {
  165. MongoDBObject("s" -> md.name,
  166. "t" -> md.timestamp,
  167. "k" -> md.schema.toString)
  168. }
  169. private def dbobj2metadata(dbobj: MongoDBObject): SensorMetaData = {
  170. SensorMetaData(dbobj.getAs[String]("s").get,
  171. dbobj.getAs[Long]("t").get,
  172. RawSchemas.withName(dbobj.getAs[String]("k").get))
  173. }
  174. private def data2dbobj(sensor: String, d: SensorData): MongoDBObject = d match {
  175. case ne: NumericalData => MongoDBObject("s" -> sensor,
  176. "d" -> ne.data,
  177. "t" -> ne.delta,
  178. "u" -> ne.unit)
  179. case se: StringData => MongoDBObject("s" -> sensor,
  180. "d" -> se.data,
  181. "t" -> se.delta,
  182. "u" -> se.unit)
  183. case be: BooleanData => MongoDBObject("s" -> sensor,
  184. "d" -> be.data,
  185. "t" -> be.delta)
  186. case sume: SummedData => MongoDBObject("s" -> sensor,
  187. "d" -> sume.data,
  188. "t" -> sume.delta,
  189. "u" -> sume.unit,
  190. "i" -> sume.instant)
  191. case strm: NumericalStreamChunkData => ???
  192. }
  193. private def dbobj2data(schema: RawSchemas.Value, dbobj: MongoDBObject): SensorData = {
  194. val delta: Long = dbobj.getAs[Long]("t").get
  195. schema match {
  196. case RawSchemas.Numerical => NumericalData(delta, dbobj.getAs[Double]("d").get, dbobj.getAs[String]("u").get)
  197. case RawSchemas.String => StringData(delta, dbobj.getAs[String]("d").get, dbobj.getAs[String]("u").get)
  198. case RawSchemas.Boolean => BooleanData(delta, dbobj.getAs[Boolean]("d").get)
  199. case RawSchemas.Summed => SummedData(delta, dbobj.getAs[Double]("d").get, dbobj.getAs[String]("u").get, dbobj.getAs[Option[Double]]("i").get)
  200. case RawSchemas.NumericalStreamChunk => ???
  201. }
  202. }
  203. /*************************
  204. ** MongoDB Collections **
  205. *************************/
  206. private[this] lazy val metadata = mongoConn("sensapp_db")("raw.metadata")
  207. private[this] lazy val data = mongoConn("sensapp_db")("raw.data")
  208. private[this] lazy val mongoConn = MongoConnection()
  209. private[this] def indexing {
  210. metadata.ensureIndex(MongoDBObject("s" -> 1), "raw.metadata.idx", true)
  211. data.ensureIndex(MongoDBObject("s" -> 1, "t" -> -1), "raw.data.idx", true)
  212. }
  213. indexing
  214. }