blob: a720a6c1531ed4f7c4aba644167fac9a95eb2a43 [file] [log] [blame]
package io.prediction.commons.appdata.mongodb
import io.prediction.commons.MongoUtils.{ emptyObj, mongoDbListToListOfString, idWithAppid }
import io.prediction.commons.MongoUtils.{ attributesToMongoDBObject, getAttributesFromDBObject }
import io.prediction.commons.appdata.{ Item, Items }
import com.mongodb.casbah.Imports._
import com.mongodb.casbah.commons.conversions.scala._
import com.github.nscala_time.time.Imports._
/** MongoDB implementation of Items. */
class MongoItems(db: MongoDB) extends Items {
private val itemColl = db("items")
/** Indices and hints. */
val starttimeIndex = MongoDBObject("starttime" -> -1)
val lnglatIndex = MongoDBObject("lnglat" -> "2d")
itemColl.ensureIndex(starttimeIndex)
itemColl.ensureIndex(lnglatIndex)
RegisterJodaTimeConversionHelpers()
def insert(item: Item) = {
val id = MongoDBObject("_id" -> idWithAppid(item.appid, item.id))
val appid = MongoDBObject("appid" -> item.appid)
val ct = MongoDBObject("ct" -> item.ct)
val itypes = MongoDBObject("itypes" -> item.itypes)
val starttime = item.starttime map { s => MongoDBObject("starttime" -> s) } getOrElse emptyObj
val endtime = item.endtime map { e => MongoDBObject("endtime" -> e) } getOrElse emptyObj
val price = item.price map { p => MongoDBObject("price" -> p) } getOrElse emptyObj
val profit = item.profit map { p => MongoDBObject("profit" -> p) } getOrElse emptyObj
val lnglat = item.latlng map { l => MongoDBObject("lnglat" -> MongoDBList(l._2, l._1)) } getOrElse emptyObj
val inactive = item.inactive map { i => MongoDBObject("inactive" -> i) } getOrElse emptyObj
//val attributes = item.attributes map { a => MongoDBObject("attributes" -> a) } getOrElse emptyObj
// add "ca_" prefix for custom attributes
val attributes = item.attributes map { a => attributesToMongoDBObject(a) } getOrElse emptyObj
itemColl.save(id ++ appid ++ ct ++ itypes ++ starttime ++ endtime ++ price ++ profit ++ lnglat ++ inactive ++ attributes)
}
def get(appid: Int, id: String) = {
itemColl.findOne(MongoDBObject("_id" -> idWithAppid(appid, id))) map { dbObjToItem(_) }
}
def getByAppid(appid: Int) = new MongoItemsIterator(itemColl.find(MongoDBObject("appid" -> appid)))
def getByAppidAndLatlng(appid: Int, latlng: Tuple2[Double, Double], within: Option[Double], unit: Option[String]) = {
val earthRadiusInKm = 6371
val earthRadiusInMiles = 3959
val nearSphereObj = MongoDBObject("$nearSphere" -> MongoDBList(latlng._2, latlng._1))
val maxDistObj = within map { maxDist =>
unit match {
case Some("km") => MongoDBObject("$maxDistance" -> maxDist / earthRadiusInKm)
case Some("mi") => MongoDBObject("$maxDistance" -> maxDist / earthRadiusInMiles)
case _ => MongoDBObject("$maxDistance" -> maxDist / earthRadiusInKm)
}
} getOrElse emptyObj
new MongoItemsIterator(itemColl.find(MongoDBObject("appid" -> appid, "lnglat" -> (nearSphereObj ++ maxDistObj))))
}
def getByAppidAndItypes(appid: Int, itypes: Seq[String]): Iterator[Item] = {
getByAppidAndItypesAndTime(appid, optItypes = Some(itypes), optTime = None)
}
def getByAppidAndItypesAndTime(appid: Int,
optItypes: Option[Seq[String]] = None,
optTime: Option[DateTime] = None): Iterator[Item] = {
val queryObj = ("appid" $eq appid)
if (!optItypes.isEmpty)
queryObj ++= ("itypes" $in optItypes.get)
if (!optTime.isEmpty) {
val time = optTime.get
queryObj ++= ("starttime" $lt time)
// Need to create an ugly DBObject as casbah don't have scala-ble support
// for $or
queryObj ++= DBObject("$or" -> MongoDBList(
("endtime" $exists false),
("endtime" $gt time)))
}
new MongoItemsIterator(itemColl.find(queryObj))
}
def getByIds(appid: Int, ids: Seq[String]) = {
itemColl.find(MongoDBObject("_id" -> MongoDBObject("$in" -> ids.map(idWithAppid(appid, _))))).toList map { dbObjToItem(_) }
}
def getRecentByIds(appid: Int, ids: Seq[String]) = {
itemColl.find(MongoDBObject("_id" -> MongoDBObject("$in" -> ids.map(idWithAppid(appid, _))))).sort(starttimeIndex).toList map { dbObjToItem(_) }
}
def update(item: Item) = {
val id = MongoDBObject("_id" -> idWithAppid(item.appid, item.id))
val appid = MongoDBObject("appid" -> item.appid)
val ct = MongoDBObject("ct" -> item.ct)
val itypes = MongoDBObject("itypes" -> item.itypes)
val starttime = item.starttime map { s => MongoDBObject("starttime" -> s) } getOrElse emptyObj
val endtime = item.endtime map { e => MongoDBObject("endtime" -> e) } getOrElse emptyObj
val price = item.price map { p => MongoDBObject("price" -> p) } getOrElse emptyObj
val profit = item.profit map { p => MongoDBObject("profit" -> p) } getOrElse emptyObj
val lnglat = item.latlng map { l => MongoDBObject("lnglat" -> MongoDBList(l._2, l._1)) } getOrElse emptyObj
val inactive = item.inactive map { i => MongoDBObject("inactive" -> i) } getOrElse emptyObj
//val attributes = item.attributes map { a => MongoDBObject("attributes" -> a) } getOrElse emptyObj
// add "ca_" prefix for custom attributes
val attributes = item.attributes map { a => attributesToMongoDBObject(a) } getOrElse emptyObj
itemColl.update(id, id ++ appid ++ ct ++ itypes ++ starttime ++ endtime ++ price ++ profit ++ lnglat ++ inactive ++ attributes)
}
def delete(appid: Int, id: String) = itemColl.remove(MongoDBObject("_id" -> idWithAppid(appid, id)))
def delete(item: Item) = delete(item.appid, item.id)
def deleteByAppid(appid: Int): Unit = {
itemColl.remove(MongoDBObject("appid" -> appid))
}
def countByAppid(appid: Int): Long = itemColl.count(MongoDBObject("appid" -> appid))
private def dbObjToItem(dbObj: DBObject) = {
val appid = dbObj.as[Int]("appid")
Item(
id = dbObj.as[String]("_id").drop(appid.toString.length + 1),
appid = appid,
ct = dbObj.as[DateTime]("ct"),
itypes = mongoDbListToListOfString(dbObj.as[MongoDBList]("itypes")),
starttime = dbObj.getAs[DateTime]("starttime"),
endtime = dbObj.getAs[DateTime]("endtime"),
price = dbObj.getAs[Double]("price"),
profit = dbObj.getAs[Double]("profit"),
latlng = dbObj.getAs[MongoDBList]("lnglat") map { lnglat => (lnglat(1).asInstanceOf[Double], lnglat(0).asInstanceOf[Double]) },
inactive = dbObj.getAs[Boolean]("inactive"),
//attributes = dbObj.getAs[DBObject]("attributes") map { dbObjToMap(_) }
attributes = Option(getAttributesFromDBObject(dbObj)).filter(!_.isEmpty)
)
}
class MongoItemsIterator(it: MongoCursor) extends Iterator[Item] {
def next = dbObjToItem(it.next)
def hasNext = it.hasNext
}
}