I would like to create an generic update method, that take as input the Object _id and a String (Json) that correspond to the update to do.
I need to transform the inputDocument variable to a Document type to be passed in the update method
I need to have this generic way of typing because i would like to use this method on any field of the collection,
How can i achieve this ?
def updateField(_id : String, inputDocument : String): Future[UpdateResult] = {
/* inputDocument = {"key" : value}*/
val mongoClient = MongoClient("mongodb://localhost:27017")
val database: MongoDatabase = mongoClient.getDatabase("databaseName")
val collection: MongoCollection[Document] = database.getCollection("collectionName")
val updateDocument = Document("$set" -> inputDocument)
collection
.updateOne(Filters.eq("_id", BsonObjectId(_id)), updateDocument)
.toFuture()
}
My thinking about handling the inputDocument variable was not correct.
It is indeed necessary to transform the input into Document.
org.mongodb.scala.bson.collection.mutable.Document has an apply() method to parse a json string to a Document.
Thank you all for your comments.
def updateField(_id : String, inputDocument : String): Future[UpdateResult] = {
/* inputDocument = {"key" : value}*/
val mongoClient = MongoClient("mongodb://localhost:27017")
val database: MongoDatabase = mongoClient.getDatabase("databaseName")
val collection: MongoCollection[Document] = database.getCollection("collectionName")
val updateDocument = Document("$set" -> Document(inputDocument))
collection
.updateOne(Filters.eq("_id", BsonObjectId(_id)), updateDocument)
.toFuture()
}
Related
I have a document collection like:
{ "_id":"ABC", "job": {...} }
How to read a batch of Jobs given a collection of IDs from Mongo DB in Kotlin?
We search like this:
MongoClients.create(clientSettings).use { client ->
val db = client.getDatabase(dbName)
val coll = db.getCollection(collName)
val filter = Filters.`in`("_id", jobs.ids)
val res = coll.find(filter).map{ it.get("job") /* ??? */ }.asIterable()
return JobBatch(res)
}
Please see /* ??? */ - How to convert Document to Job class?
I wrote following code to fetch data from MongoDB
import com.typesafe.config.ConfigFactory
import org.mongodb.scala.{ Document, MongoClient, MongoCollection, MongoDatabase }
import scala.concurrent.ExecutionContext
object MongoService extends Service {
val conf = ConfigFactory.load()
implicit val mongoService: MongoClient = MongoClient(conf.getString("mongo.url"))
implicit val mongoDB: MongoDatabase = mongoService.getDatabase(conf.getString("mongo.db"))
implicit val ec: ExecutionContext = ExecutionContext.global
def getAllDocumentsFromCollection(collection: String) = {
mongoDB.getCollection(collection).find()
}
}
But when I tried to get data from getAllDocumentsFromCollection I'm not getting each data for further manipulation. Instead I'm getting
FindObservable(com.mongodb.async.client.FindIterableImpl#23555cf5)
UPDATED:
object MongoService {
// My settings (see available connection options)
val mongoUri = "mongodb://localhost:27017/smsto?authMode=scram-sha1"
import ExecutionContext.Implicits.global // use any appropriate context
// Connect to the database: Must be done only once per application
val driver = MongoDriver()
val parsedUri = MongoConnection.parseURI(mongoUri)
val connection = parsedUri.map(driver.connection(_))
// Database and collections: Get references
val futureConnection = Future.fromTry(connection)
def db1: Future[DefaultDB] = futureConnection.flatMap(_.database("smsto"))
def personCollection = db1.map(_.collection("person"))
// Write Documents: insert or update
implicit def personWriter: BSONDocumentWriter[Person] = Macros.writer[Person]
// or provide a custom one
def createPerson(person: Person): Future[Unit] =
personCollection.flatMap(_.insert(person).map(_ => {})) // use personWriter
def getAll(collection: String) =
db1.map(_.collection(collection))
// Custom persistent types
case class Person(firstName: String, lastName: String, age: Int)
}
I tried to use reactivemongo as well with above code but I couldn't make it work for getAll and getting following error in createPerson
Please suggest how can I get all data from a collection.
This is likely too late for the OP, but hopefully the following methods of retrieving & iterating over collections using mongo-spark can prove useful to others.
The Asynchronous Way - Iterating over documents asynchronously means you won't have to store an entire collection in-memory, which can become unreasonable for large collections. However, you won't have access to all your documents outside the subscribe code block for reuse. I'd recommend doing things asynchronously if you can, since this is how the mongo-scala driver was intended to be used.
db.getCollection(collectionName).find().subscribe(
(doc: org.mongodb.scala.bson.Document) => {
// operate on an individual document here
},
(e: Throwable) => {
// do something with errors here, if desired
},
() => {
// this signifies that you've reached the end of your collection
}
)
The "Synchronous" Way - This is a pattern I use when my use-case calls for a synchronous solution, and I'm working with smaller collections or result-sets. It still uses the asynchronous mongo-scala driver, but it returns a list of documents and blocks downstream code execution until all documents are returned. Handling errors and timeouts may depend on your use case.
import org.mongodb.scala._
import org.mongodb.scala.bson.Document
import org.mongodb.scala.model.Filters
import scala.collection.mutable.ListBuffer
/* This function optionally takes filters if you do not wish to return the entire collection.
* You could extend it to take other optional query params, such as org.mongodb.scala.model.{Sorts, Projections, Aggregates}
*/
def getDocsSync(db: MongoDatabase, collectionName: String, filters: Option[conversions.Bson]): ListBuffer[Document] = {
val docs = scala.collection.mutable.ListBuffer[Document]()
var processing = true
val query = if (filters.isDefined) {
db.getCollection(collectionName).find(filters.get)
} else {
db.getCollection(collectionName).find()
}
query.subscribe(
(doc: Document) => docs.append(doc), // add doc to mutable list
(e: Throwable) => throw e,
() => processing = false
)
while (processing) {
Thread.sleep(100) // wait here until all docs have been returned
}
docs
}
// sample usage of 'synchronous' method
val client: MongoClient = MongoClient(uriString)
val db: MongoDatabase = client.getDatabase(dbName)
val allDocs = getDocsSync(db, "myCollection", Option.empty)
val someDocs = getDocsSync(db, "myCollection", Option(Filters.eq("fieldName", "foo")))
So I've tried seemingly countless things to get this to work. When I call queueWrite, the println statements give me this:
{ "uuid" : "49f2-0b64-4bf3-49f2a35b-bbe8-4954f742d88b" }
and this:
{ "uuid" : "49f2-0b64-4bf3-49f2a35b-bbe8-4954f742d88b", "name" : "personName", "key" : "3E6A" }
Which (I'm pretty sure) is just fine. However, after it prints, I get this:
java.lang.IllegalArgumentException: Invalid BSON field name uuid
Afaik, the field name uuid is fine, the only things about an improper name I could really find is to just make sure there are no '.' symbols in it (which there aren't)
def queueWrite(collection: String, filter: Map[String, () => String], data: Map[String, () => String]) {
val col = collections.get(collection).get
val filterDoc = new BsonDocument
filter.foreach(f => { filterDoc append (f._1, new BsonString(f._2.apply)) })
val filterBson = Document(filterDoc)
println("filter: \n" + filterBson.toJson)
val dataDoc = new BsonDocument
data.foreach(f => { dataDoc append (f._1, new BsonString(f._2.apply)) })
val dataBson = Document(dataDoc)
println("data: \n" + dataBson.toJson)
val options = new FindOneAndUpdateOptions
options.returnDocument(ReturnDocument.AFTER)
options.upsert(true)
val observer = new Observer[Document] {
override def onSubscribe(s: Subscription) = s.request(1)
override def onNext(doc: Document) = println(doc.toJson)
override def onError(e: Throwable) = e.printStackTrace
override def onComplete = println("onComplete")
}
val observable: Observable[Document] = col.findOneAndUpdate(filterBson, dataBson, options)
observable.subscribe(observer)
}
Any ideas / suggestions are greatly appreciated as always :)
How do I test a MongoDB connection? Here is my code:
class MongoDB(val SERVER:String, val PORT:Int, val DATABASE: String, val COLLECTION: String){
def establishConnection(): MongoCollection = {
val mongoClient = MongoClient(SERVER, PORT)
println("MongoDB client connection: "+ mongoClient)
val db_handle = mongoClient(DATABASE)
println("Connected to DB : "+ DATABASE)
println("Collections present are :")
println(db_handle.collectionNames)
assert (establishConnection.size > 0 )
db_handle(COLLECTION)
}
def insert(collection : MongoCollection, document : MongoDBObject) : Unit = {
println(collection.insert(document))
}
def find(collection : MongoCollection) = {
println("In find query() :")
val cursor = collection.find()
cursor.toList
}
def find(collection : MongoCollection, obj : MongoDBObject) = {
println("In find query() with condition:")
val cursor = collection.find(obj)
cursor.toList
}
def findOne(collection : MongoCollection) ={
println("In findOne query() :")
val cursor = collection.findOne()
cursor.toList
}
def findOne(collection: MongoCollection, obj : MongoDBObject) ={
println("In findOne query() with condition:")
val cursor = collection.findOne(obj)
cursor.toList
}
def update(collection : MongoCollection, query : MongoDBObject, update : MongoDBObject) = {
val result = collection.update(query, update) //update or findAndModify can be used
result
}
def delete(collection : MongoCollection, query : MongoDBObject) = {
val result = collection.findAndRemove(query);
result
}
}
You can use embedded mongo it's written in java, it first downloads mongo and stores it in home directory ( it downloads only for very first time) and then runs it, you can choose mongoDB version, here is a sample project how to use it from scala test https://github.com/SimplyScala/scalatest-embedmongo.
I'm trying to implement Count on a query but I'm having issues since I do not
work with BSONDocument but with JsObject objects.
https://github.com/ReactiveMongo/ReactiveMongo/blob/master/driver/samples/SimpleUseCasesSample.scala
Should I translate my JsObject to a BSONDocument or is there a better way?
How would you do this in ReactiveMongo 0.9?
Translate example:
Service:
def countSentMailForVenue(currentId: Long, from: DateTime, to: DateTime) : Future[Int] = {
val query = Json.obj("venueInfo.currentId" -> venueId, "origin" -> Origin.EMAIL_INVITE, "updated" -> Json.obj("$gte" -> from.getMillis, "$lt" -> to.getMillis))
count(BSONFormats.toBSON(query).get.asInstanceOf[BSONDocument])
}
And in Dao:
/**
* Currently Count() only supports BSONDocument.
*/
def count(query: BSONDocument) : Future[Int] = {
Logger.debug(s"Counting documents: "+BSONFormats.toJSON(query))
val futureCount = collection.db.command(
Count(
collection.name,
Some(query)
)
)
futureCount
}