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

swaydb.Set.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2019 Simer Plaha (@simerplaha)
 *
 * This file is a part of SwayDB.
 *
 * SwayDB is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * SwayDB is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with SwayDB. If not, see .
 */

package swaydb

import scala.collection.generic.CanBuildFrom
import scala.concurrent.duration.{Deadline, FiniteDuration}
import swaydb.PrepareImplicits._
import swaydb.data.IO
import swaydb.data.accelerate.Level0Meter
import swaydb.data.compaction.LevelMeter
import swaydb.data.slice.Slice
import swaydb.serializers.{Serializer, _}

object Set {
  def apply[T](api: SwayDB)(implicit serializer: Serializer[T]): Set[T] =
    new Set(api, None)
}

/**
  * Set database API.
  *
  * For documentation check - http://swaydb.io/api/
  */
case class Set[T](private val db: SwayDB,
                  private val from: Option[From[T]],
                  private[swaydb] val reverse: Boolean = false,
                  private val till: T => Boolean = (_: T) => true)(implicit serializer: Serializer[T]) extends Iterable[T] {

  def get(elem: T): IO[Option[T]] =
    db.getKey(elem).map(_.map(_.read[T]))

  def contains(elem: T): IO[Boolean] =
    db contains elem

  def mightContain(elem: T): IO[Boolean] =
    db mightContain elem

  def add(elem: T): IO[Level0Meter] =
    db.put(key = elem)

  def add(elem: T, expireAt: Deadline): IO[Level0Meter] =
    db.put(elem, None, expireAt)

  def add(elem: T, expireAfter: FiniteDuration): IO[Level0Meter] =
    db.put(elem, None, expireAfter.fromNow)

  def add(elems: T*): IO[Level0Meter] =
    add(elems)

  def add(elems: Iterable[T]): IO[Level0Meter] =
    db.commit(elems.map(elem => Prepare.Put(key = serializer.write(elem), value = None, deadline = None)))

  def remove(elem: T): IO[Level0Meter] =
    db.remove(elem)

  def remove(from: T, to: T): IO[Level0Meter] =
    db.remove(from, to)

  def remove(elems: T*): IO[Level0Meter] =
    remove(elems)

  def remove(elems: Iterable[T]): IO[Level0Meter] =
    db.commit(elems.map(elem => Prepare.Remove(serializer.write(elem))))

  def expire(elem: T, after: FiniteDuration): IO[Level0Meter] =
    db.expire(elem, after.fromNow)

  def expire(elem: T, at: Deadline): IO[Level0Meter] =
    db.expire(elem, at)

  def expire(from: T, to: T, after: FiniteDuration): IO[Level0Meter] =
    db.expire(from, to, after.fromNow)

  def expire(from: T, to: T, at: Deadline): IO[Level0Meter] =
    db.expire(from, to, at)

  def expire(elems: (T, Deadline)*): IO[Level0Meter] =
    expire(elems)

  def expire(elems: Iterable[(T, Deadline)]): IO[Level0Meter] =
    db.commit {
      elems map {
        elemWithExpire =>
          Prepare.Remove(
            from = serializer.write(elemWithExpire._1),
            to = None,
            deadline = Some(elemWithExpire._2)
          )
      }
    }

  def registerFunction(functionID: T, function: (T, Option[Deadline]) => Apply.Set[T]): T = {
    db.registerFunction(functionID, SwayDB.toCoreFunction(function))
    functionID
  }

  def applyFunction(from: T, to: T, functionID: T): IO[Level0Meter] =
    db.function(from, to, functionID)

  def applyFunction(elem: T, function: T): IO[Level0Meter] =
    db.function(elem, function)

  def commit(prepare: Prepare[T, Nothing]*): IO[Level0Meter] =
    db.commit(prepare)

  def commit(prepare: Iterable[Prepare[T, Nothing]]): IO[Level0Meter] =
    db.commit(prepare)

  def level0Meter: Level0Meter =
    db.level0Meter

  def levelMeter(levelNumber: Int): Option[LevelMeter] =
    db.levelMeter(levelNumber)

  def sizeOfSegments: Long =
    db.sizeOfSegments

  def elemSize(elem: T): Int =
    (elem: Slice[Byte]).size

  def expiration(elem: T): IO[Option[Deadline]] =
    db deadline elem

  def timeLeft(elem: T): IO[Option[FiniteDuration]] =
    expiration(elem).map(_.map(_.timeLeft))

  def from(key: T): Set[T] =
    copy(from = Some(From(key = key, orBefore = false, orAfter = false, before = false, after = false)))

  def before(key: T) =
    copy(from = Some(From(key = key, orBefore = false, orAfter = false, before = true, after = false)))

  def fromOrBefore(key: T) =
    copy(from = Some(From(key = key, orBefore = true, orAfter = false, before = false, after = false)))

  def after(key: T) =
    copy(from = Some(From(key = key, orBefore = false, orAfter = false, before = false, after = true)))

  def fromOrAfter(key: T) =
    copy(from = Some(From(key = key, orBefore = false, orAfter = true, before = false, after = false)))

  def till(condition: T => Boolean) =
    copy(till = condition)

  override def iterator = new Iterator[T] {

    private var started: Boolean = false
    private var nextKeyBytes: Slice[Byte] = _
    private var nextKeyTyped: T = _

    private def start: IO[Option[Slice[Byte]]] =
      from match {
        case Some(from) =>
          val fromKeyBytes: Slice[Byte] = from.key
          if (from.before)
            db.beforeKey(fromKeyBytes)
          else if (from.after)
            db.afterKey(fromKeyBytes)
          else
            db.getKey(fromKeyBytes)
              .flatMap {
                case Some(key) =>
                  IO.Success(Some(key))

                case _ =>
                  if (from.orAfter)
                    db.afterKey(fromKeyBytes)
                  else if (from.orBefore)
                    db.beforeKey(fromKeyBytes)
                  else
                    IO.Success(None)
              }

        case None =>
          if (reverse)
            db.lastKey
          else
            db.headKey
      }

    override def hasNext: Boolean =
      if (started) {
        if (nextKeyBytes == null)
          false
        else {
          val next =
            if (reverse)
              db.beforeKey(nextKeyBytes)
            else
              db.afterKey(nextKeyBytes)

          next match {
            case IO.Success(key) =>
              key match {
                case Some(key) =>
                  val keyT = key.read[T]
                  if (till(keyT)) {
                    nextKeyBytes = key
                    nextKeyTyped = keyT
                    true
                  } else
                    false

                case _ =>
                  false
              }
            case IO.Failure(error) =>
              System.err.println("Failed to iterate", error)
              throw error.exception
          }
        }
      } else
        start match {
          case IO.Success(value) =>
            started = true
            value match {
              case Some(key) =>
                val keyT = key.read[T]
                if (till(keyT)) {
                  nextKeyBytes = key
                  nextKeyTyped = keyT
                  true
                } else
                  false

              case _ =>
                false
            }
          case IO.Failure(error) =>
            System.err.println("Failed to start Key iterator", error)
            throw error.exception
        }

    override def next(): T =
      nextKeyTyped

    override def toString(): String =
      classOf[Set[_]].getClass.getSimpleName
  }

  override def head: T =
    headOption.get

  override def last: T =
    lastOption.get

  override def size: Int =
    db.keyValueCount.get

  override def isEmpty: Boolean =
    db.headKey.get.isEmpty

  override def nonEmpty: Boolean =
    !isEmpty

  override def headOption: Option[T] =
    if (from.isDefined)
      this.take(1).headOption
    else
      db.headKey.map(_.map(_.read[T])).get

  override def lastOption: Option[T] =
    db.lastKey.map(_.map(_.read[T])).get

  def foreachRight[U](f: T => U): Unit =
    copy(reverse = true) foreach f

  def mapRight[B, C](f: T => B)(implicit bf: CanBuildFrom[Iterable[T], B, C]): C = {
    copy(reverse = true) map f
  }

  override def foldRight[B](z: B)(op: (T, B) => B): B =
    copy(reverse = true).foldLeft(z) {
      case (b, k) =>
        op(k, b)
    }

  override def takeRight(n: Int): Iterable[T] =
    copy(reverse = true).take(n)

  override def dropRight(n: Int): Iterable[T] =
    copy(reverse = true).drop(n)

  override def reduceRight[B >: T](op: (T, B) => B): B =
    copy(reverse = true).reduceLeft[B] {
      case (b, k) =>
        op(k, b)
    }

  override def reduceRightOption[B >: T](op: (T, B) => B): Option[B] =
    copy(reverse = true).reduceLeftOption[B] {
      case (b, k) =>
        op(k, b)
    }

  override def scanRight[B, That](z: B)(op: (T, B) => B)(implicit bf: CanBuildFrom[Iterable[T], B, That]): That =
    copy(reverse = true).scanLeft(z) {
      case (z, k) =>
        op(k, z)
    }

  def closeDatabase(): IO[Unit] =
    db.close

  override def toString(): String =
    classOf[Map[_, _]].getClass.getSimpleName
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy