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

net.liftweb.mongodb.MongoDocument.scala Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2010-2020 WorldWide Conferencing, LLC
 *
 * Licensed 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 net.liftweb
package mongodb

import net.liftweb.common.Box
import net.liftweb.json._
import net.liftweb.util.{ConnectionIdentifier, DefaultConnectionIdentifier}
import net.liftweb.util.Helpers.tryo

import scala.collection.JavaConverters._

import java.util.UUID

import org.bson.{BsonDocument, Document, UuidRepresentation}
import org.bson.codecs.{PatternCodec, UuidCodecProvider}
import org.bson.codecs.configuration.{CodecRegistries, CodecRegistry}
import org.bson.conversions.Bson
import org.bson.types.ObjectId

import com.mongodb._
import com.mongodb.client.{MongoCollection, MongoDatabase}
import com.mongodb.client.model.{DeleteOptions, IndexOptions, InsertOneOptions, ReplaceOptions, UpdateOptions}
import com.mongodb.client.model.Filters.{eq => eqs}
import com.mongodb.client.result.{DeleteResult, UpdateResult}

/**
 * extend case class with this trait
 */
trait MongoDocument[BaseDocument] extends JsonObject[BaseDocument] {
  self: BaseDocument =>

  def _id: Any

  def meta: MongoDocumentMeta[BaseDocument]

  def delete: Box[DeleteResult] = {
    meta.deleteOne("_id", _id)
  }

  def save: UpdateResult = meta.save(this)

  def getRef: Option[MongoRef] = _id match {
    case oid: ObjectId => Some(MongoRef(meta.collectionName, oid))
    case _ => None
  }
}

/**
 * extend case class companion objects with this trait
 */
trait MongoDocumentMeta[BaseDocument] extends JsonObjectMeta[BaseDocument] with MongoMeta[BaseDocument, BsonDocument] {

  private val bsonDocumentClass = classOf[BsonDocument]

  def codecRegistry: CodecRegistry = CodecRegistries.fromRegistries(
    MongoClientSettings.getDefaultCodecRegistry(),
    CodecRegistries.fromProviders(new UuidCodecProvider(UuidRepresentation.JAVA_LEGACY)),
    CodecRegistries.fromCodecs(new PatternCodec())
  )

  /**
   * Override this to specify a ConnectionIdentifier.
   */
  def connectionIdentifier: ConnectionIdentifier = DefaultConnectionIdentifier

  /**
   * Use the collection associated with this Meta.
   */
  def useCollection[T](f: MongoCollection[BsonDocument] => T): T =
    MongoDB.useMongoCollection(connectionIdentifier, collectionName, bsonDocumentClass) { mc =>
      f(mc.withCodecRegistry(codecRegistry).withWriteConcern(writeConcern))
    }

  def useCollection[T](db: MongoDatabase)(f: MongoCollection[BsonDocument] => T): T = {
    val mc = db.getCollection(collectionName, bsonDocumentClass)

    f(mc.withCodecRegistry(codecRegistry).withWriteConcern(writeConcern))
  }

  @deprecated("Use useCollection instead", "3.4.3")
  def useColl[T](f: DBCollection => T): T =
    MongoDB.useCollection(connectionIdentifier, collectionName)(f)

  /**
   * Use the db associated with this Meta.
   */
  def useDatabase[T](f: MongoDatabase => T): T =
    MongoDB.useDatabase(connectionIdentifier) { md =>
      f(md.withCodecRegistry(codecRegistry).withWriteConcern(writeConcern))
    }

  @deprecated("Use useDatabase instead", "3.4.3")
  def useDb[T](f: DB => T): T = MongoDB.use(connectionIdentifier)(f)

  def create(dbo: Bson): BaseDocument = {
    val jv = BsonParser.serialize(dbo)
    create(jv.asInstanceOf[JObject])
  }

  /**
   * Find a single row by a qry, using a Bson.
   */
  def find(qry: Bson): Option[BaseDocument] = {
    useCollection { coll =>
      coll.find(qry).limit(1).first match {
        case null => None
        case dbo => {
          Some(create(dbo))
        }
      }
    }
  }

  /**
   * Find a single document by _id using a String.
   */
  def find(s: String): Option[BaseDocument] =
    if (ObjectId.isValid(s))
      find(eqs("_id", new ObjectId(s)))
    else
      find(eqs("_id", s))

  /**
   * Find a single document by _id using an ObjectId.
   */
  def find(oid: ObjectId): Option[BaseDocument] = find(eqs("_id", oid))

  /**
   * Find a single document by _id using a UUID.
   */
  def find(uuid: UUID): Option[BaseDocument] = find(eqs("_id", uuid))

  /**
   * Find a single document by a qry using String, Any inputs
   */
  def find(k: String, v: Any): Option[BaseDocument] = find(eqs(k, v))

  /**
   * Find a single document by a qry using a json query
   */
  def find(json: JObject): Option[BaseDocument] = find(BsonParser.parse(json))

  /**
   * Find all documents in this collection
   */
  def findAll: List[BaseDocument] = {
    useCollection { coll =>
      /** Mongo Cursors are both Iterable and Iterator,
       * so we need to reduce ambiguity for implicits
       */
      coll.find.iterator.asScala.map(create).toList
    }
  }

  /**
   * Find all documents using a Bson query.
   */
  def findAll(qry: Bson, sort: Option[Bson], opts: FindOption*): List[BaseDocument] = {
    val findOpts = opts.toList

    useCollection { coll =>
      val cur = coll.find(qry).limit(
        findOpts.find(_.isInstanceOf[Limit]).map(x => x.value).getOrElse(0)
      ).skip(
        findOpts.find(_.isInstanceOf[Skip]).map(x => x.value).getOrElse(0)
      )
      sort.foreach(s => cur.sort(s))
      /** Mongo Cursors are both Iterable and Iterator,
       * so we need to reduce ambiguity for implicits
       */
      cur.iterator.asScala.map(create).toList
    }
  }

  /**
   * Find all documents using a Bson query.
   */
  def findAll(qry: Bson, opts: FindOption*): List[BaseDocument] =
    findAll(qry, None, opts :_*)

  /**
   * Find all documents using a Bson query with sort
   */
  def findAll(qry: Bson, sort: Bson, opts: FindOption*): List[BaseDocument] =
    findAll(qry, Some(sort), opts :_*)

  /**
   * Find all documents using a JObject query
   */
  def findAll(qry: JObject, opts: FindOption*): List[BaseDocument] =
    findAll(BsonParser.parse(qry), None, opts :_*)

  /**
   * Find all documents using a JObject query with sort
   */
  def findAll(qry: JObject, sort: JObject, opts: FindOption*): List[BaseDocument] =
    findAll(BsonParser.parse(qry), Some(BsonParser.parse(sort)), opts :_*)

  /**
   * Find all documents using a k, v query
   */
  def findAll(k: String, o: Any, opts: FindOption*): List[BaseDocument] =
    findAll(eqs(k, o), None, opts :_*)

  /**
   * Find all documents using a k, v query with JObject sort
   */
  def findAll(k: String, o: Any, sort: JObject, opts: FindOption*): List[BaseDocument] =
    findAll(eqs(k, o), Some(BsonParser.parse(sort)), opts :_*)

  def insertOne(inst: BaseDocument, opts: InsertOneOptions = new InsertOneOptions): Box[BaseDocument] = tryo {
    useCollection { coll =>
      val bson = BsonParser.parse(toJObject(inst))
      coll.insertOne(bson, opts)
      inst
    }
  }

  def replaceOne(inst: BaseDocument, opts: ReplaceOptions = new ReplaceOptions): Box[UpdateResult] = tryo {
    useCollection { coll =>
      val bson = BsonParser.parse(toJObject(inst))
      val id = bson.get("_id")
      coll.replaceOne(eqs("_id", id), bson, opts)
    }
  }

  def replaceOne(qry: Bson, inst: BaseDocument, opts: ReplaceOptions): Box[UpdateResult] = tryo {
    useCollection { coll =>
      val bson = BsonParser.parse(toJObject(inst))
      coll.replaceOne(qry, bson, opts)
    }
  }

  def replaceOne(qry: Bson, inst: BaseDocument): Box[UpdateResult] =
    replaceOne(qry, inst, new ReplaceOptions)

  def replaceOne(qry: JObject, inst: BaseDocument, opts: ReplaceOptions): Box[UpdateResult] = tryo {
    useCollection { coll =>
      val bson = BsonParser.parse(toJObject(inst))
      coll.replaceOne(BsonParser.parse(qry), bson, opts)
    }
  }

  def replaceOne(qry: JObject, inst: BaseDocument): Box[UpdateResult] =
    replaceOne(qry, inst, new ReplaceOptions)

  /**
   * Save a document to the db
   */
  def save(inst: BaseDocument): UpdateResult = {
    val opts = new ReplaceOptions().upsert(true)
    useCollection { coll =>
      val bson = BsonParser.parse(toJObject(inst))
      val id = bson.get("_id")
      coll.replaceOne(eqs("_id", id), bson, opts)
    }
  }

  @deprecated("Use save instead", "3.4.3")
  def save(in: BaseDocument, db: DB) {
    db.getCollection(collectionName).save(JObjectParser.parse(toJObject(in)))
  }

  @deprecated("Use updateOne, updateMany, or replaceOne instead", "3.4.3")
  def update(qry: JObject, newbd: BaseDocument, db: DB, opts: UpdateOption*) {
    update(qry, toJObject(newbd), db, opts :_*)
  }

  @deprecated("Use updateOne, updateMany, or replaceOne instead", "3.4.3")
  def update(qry: JObject, newbd: BaseDocument, opts: UpdateOption*) {
    MongoDB.use(connectionIdentifier) ( db => {
      update(qry, newbd, db, opts :_*)
    })
  }

}





© 2015 - 2025 Weber Informatics LLC | Privacy Policy