blob: f25be5fbd9dc95b443053bf8a97290cb0228c76e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.s2graph.core.schema
/**
* Created by shon on 6/3/15.
*/
import org.apache.s2graph.core.GraphExceptions.MaxPropSizeReachedException
import org.apache.s2graph.core.{GraphExceptions, JSONParser}
import play.api.libs.json.Json
import scalikejdbc._
import scala.util.Try
object LabelMeta extends SQLSyntaxSupport[LabelMeta] {
import Schema._
val className = LabelMeta.getClass.getSimpleName
/** dummy sequences */
val fromSeq = (-4).toByte
val toSeq = (-5).toByte
val lastOpSeq = (-3).toByte
val lastDeletedAtSeq = (-2).toByte
val timestampSeq = (0).toByte
val labelSeq = (-6).toByte
val directionSeq = -7.toByte
val fromHashSeq = -8.toByte
val countSeq = (Byte.MaxValue - 2).toByte
val degreeSeq = (Byte.MaxValue - 1).toByte
val maxValue = Byte.MaxValue
val emptySeq = Byte.MaxValue
/** reserved sequences */
// val deleted = LabelMeta(id = Some(lastDeletedAt), labelId = lastDeletedAt, name = "lastDeletedAt",
// seq = lastDeletedAt, defaultValue = "", dataType = "long")
val fromHash = LabelMeta(id = None, labelId = fromHashSeq, name = "_from_hash",
seq = fromHashSeq, defaultValue = fromHashSeq.toString, dataType = "long")
val from = LabelMeta(id = Some(fromSeq), labelId = fromSeq, name = "_from",
seq = fromSeq, defaultValue = fromSeq.toString, dataType = "string")
val to = LabelMeta(id = Some(toSeq), labelId = toSeq, name = "_to",
seq = toSeq, defaultValue = toSeq.toString, dataType = "string")
val timestamp = LabelMeta(id = Some(-1), labelId = -1, name = "_timestamp",
seq = timestampSeq, defaultValue = "0", dataType = "long")
val degree = LabelMeta(id = Some(-1), labelId = -1, name = "_degree",
seq = degreeSeq, defaultValue = "0", dataType = "long")
val count = LabelMeta(id = Some(-1), labelId = -1, name = "_count",
seq = countSeq, defaultValue = "-1", dataType = "long")
val lastDeletedAt = LabelMeta(id = Some(-1), labelId = -1, name = "_lastDeletedAt",
seq = lastDeletedAtSeq, defaultValue = "-1", dataType = "long")
val label = LabelMeta(id = Some(-1), labelId = -1, name = "label",
seq = labelSeq, defaultValue = "", dataType = "string")
val direction = LabelMeta(id = Some(-1), labelId = -1, name = "direction",
seq = directionSeq, defaultValue = "out", dataType = "string")
val empty = LabelMeta(id = Some(-1), labelId = -1, name = "_empty",
seq = emptySeq, defaultValue = "-1", dataType = "long")
// Each reserved column(_timestamp, timestamp) has same seq number, starts with '_' has high priority
val reservedMetas = List(empty, label, direction, lastDeletedAt, from, fromHash, to, degree, timestamp, count).flatMap { lm => List(lm, lm.copy(name = lm.name.drop(1))) }.reverse
val reservedMetasInner = List(empty, label, direction, lastDeletedAt, from, fromHash, to, degree, timestamp, count)
val reservedMetaNamesSet = reservedMetasInner.map(_.name).toSet
val defaultRequiredMetaNames = Set("from", "_from", "to", "_to", "_from_hash", "label", "direction", "timestamp", "_timestamp")
def apply(rs: WrappedResultSet): LabelMeta = {
LabelMeta(Some(rs.int("id")), rs.int("label_id"), rs.string("name"), rs.byte("seq"),
rs.string("default_value"), rs.string("data_type").toLowerCase, rs.boolean("store_in_global_index"))
}
/** Note: DegreeSeq should not be included in serializer/deserializer.
* only 0 <= seq <= CountSeq(Int.MaxValue - 2), not DegreeSet(Int.MaxValue - 1) should be
* included in actual bytes in storage.
* */
def isValidSeq(seq: Byte): Boolean = seq >= 0 && seq <= countSeq // || seq == fromHashSeq
def isValidSeqForAdmin(seq: Byte): Boolean = seq > 0 && seq < countSeq // || seq == fromHashSeq
def findById(id: Int)(implicit session: DBSession = AutoSession): LabelMeta = {
val cacheKey = className + "id=" + id
withCache(cacheKey) {
sql"""select * from label_metas where id = ${id}""".map { rs => LabelMeta(rs) }.single.apply
}.get
}
def findAllByLabelId(labelId: Int, useCache: Boolean = true)(implicit session: DBSession = AutoSession): List[LabelMeta] = {
val cacheKey = className + "labelId=" + labelId
lazy val labelMetas = sql"""select *
from label_metas
where label_id = ${labelId} order by seq ASC""".map(LabelMeta(_)).list.apply()
if (useCache) withCaches(cacheKey)(labelMetas)
else labelMetas
}
def findByName(labelId: Int, name: String, useCache: Boolean = true)(implicit session: DBSession = AutoSession): Option[LabelMeta] = {
name match {
case timestamp.name => Some(timestamp)
case from.name => Some(from)
case to.name => Some(to)
case _ =>
val cacheKey = className + "labelId=" + labelId + ":name=" + name
lazy val labelMeta = sql"""
select *
from label_metas where label_id = ${labelId} and name = ${name}"""
.map { rs => LabelMeta(rs) }.single.apply()
if (useCache) withCache(cacheKey)(labelMeta)
else labelMeta
}
}
def insert(labelId: Int, name: String, defaultValue: String, dataType: String, storeInGlobalIndex: Boolean = false)(implicit session: DBSession = AutoSession) = {
val ls = findAllByLabelId(labelId, useCache = false)
val seq = ls.size + 1
if (seq < maxValue) {
sql"""insert into label_metas(label_id, name, seq, default_value, data_type)
select ${labelId}, ${name}, ${seq}, ${defaultValue}, ${dataType}""".updateAndReturnGeneratedKey.apply()
} else {
throw MaxPropSizeReachedException("max property size reached")
}
}
def findOrInsert(labelId: Int,
name: String,
defaultValue: String,
dataType: String,
storeInGlobalIndex: Boolean = false)(implicit session: DBSession = AutoSession): LabelMeta = {
findByName(labelId, name) match {
case Some(c) => c
case None =>
insert(labelId, name, defaultValue, dataType, storeInGlobalIndex)
val cacheKey = "labelId=" + labelId + ":name=" + name
val cacheKeys = "labelId=" + labelId
expireCache(className + cacheKey)
expireCaches(className + cacheKeys)
findByName(labelId, name, useCache = false).get
}
}
def delete(id: Int)(implicit session: DBSession = AutoSession) = {
val labelMeta = findById(id)
val (labelId, name) = (labelMeta.labelId, labelMeta.name)
sql"""delete from label_metas where id = ${id}""".execute.apply()
val cacheKeys = List(s"id=$id", s"labelId=$labelId", s"labelId=$labelId:name=$name")
cacheKeys.foreach { key =>
expireCache(className + key)
expireCaches(className + key)
}
}
def findAll()(implicit session: DBSession = AutoSession) = {
val ls = sql"""select * from label_metas""".map { rs => LabelMeta(rs) }.list.apply
putsToCacheOption(ls.flatMap { x =>
Seq(
s"id=${x.id.get}",
s"labelId=${x.labelId}:name=${x.name}",
s"labelId=${x.labelId}:seq=${x.seq}"
).map(cacheKey => (className + cacheKey, x))
})
putsToCaches(ls.groupBy(x => x.labelId).map { case (labelId, ls) =>
val cacheKey = s"labelId=${labelId}"
cacheKey -> ls
}.toList)
ls
}
def updateStoreInGlobalIndex(id: Int, storeInGlobalIndex: Boolean)(implicit session: DBSession = AutoSession): Try[Long] = Try {
sql"""
update label_metas set store_in_global_index = ${storeInGlobalIndex} where id = ${id}
""".updateAndReturnGeneratedKey.apply()
}
}
case class LabelMeta(id: Option[Int],
labelId: Int,
name: String,
seq: Byte,
defaultValue: String,
dataType: String,
storeInGlobalIndex: Boolean = false) {
lazy val toJson = Json.obj("name" -> name, "defaultValue" -> defaultValue, "dataType" -> dataType, "storeInGlobalIndex" -> storeInGlobalIndex)
override def equals(other: Any): Boolean = {
if (!other.isInstanceOf[LabelMeta]) false
else {
val o = other.asInstanceOf[LabelMeta]
// labelId == o.labelId &&
seq == o.seq
}
}
override def hashCode(): Int = seq.toInt
// (labelId, seq).hashCode()
}