nested pull with scala mongodb casbah - scala

lets say i have a simple object
{
"id":"xyz"
"answers" : [{
"name" : "Yes",
}, {
"name" : "No",
}]
}
I want to remove answer Yes from the array
I'm trying something like this without much luck:
import com.mongodb.casbah.MongoCollection
val searchObject = MongoDBObject("id"->"xyz");
getCollection().update(searchObject,$pull( "answers" -> ( "name" -> "Yes")));

You need to declare ("name" -> "Yes") as a MongoDBObject because look at:
scala> $pull( "answers" -> ( "name" -> "Yes"))
res10: com.mongodb.casbah.query.Imports.DBObject = { "$pull" : { "answers" : [ "name" , "Yes"]}}
Which is not what you want, you want to pull a subdocument:
scala> $pull ( "answers" -> MongoDBObject("name" -> "Yes") )
res11: com.mongodb.casbah.query.Imports.DBObject = { "$pull" : { "answers" : { "name" : "Yes"}}}

Related

Query given list of values equivalent to sql IN

I am stuck trying to figure out how to use the equivalent of "IN" in sql with MongoDB as I am not getting any results.
Here is sample output of the record/document:
[
{
"_id" : ObjectId("12348749e4b04b2d017ff78e"),
"account" : "foo",
"archivedFilteredEvents" : [
{
"id" : "all_events",
"name" : "All Events",
"enabled" : false
},
{
"id" : "f123dsad2",
"name" : "test1",
"enabled" : true
}
]
Here is the query I need help with:
printjson(db.mytestdb_profiles.find({
"account" : "foo",
"archivedFilteredEvents.id" : [ "all_events","f123dsad2"]}
));
I am basically looking for the equivalent of:
select id, name, enabled
from mytestdb_profiles
where account = 'foo'
and id.archivedFilteredDEvents IN ('all_events', 'f123dsad2');
You're just missing the $in operator in your query:
db.mytestdb_profiles.find({
"account" : "foo",
"archivedFilteredEvents.id" : { $in: [ "all_events","f123dsad2"] } }
)

Querying for a field inside another

I have got this document in myDb.myCollection:
"_id" : ObjectId("55fc0ec8666292b85178c180"),
"firstname" : "george",
"surname" : "abitbol",
"data" : {
"a" : "secret value",
"b" : "4"
},
"tags" : "[t]"
How do I query for the b field in data to equal "4"?
I tried this:
val r = myCollection.find({"data.b" -> "4"})
for (d <- r)
println (d)
But here is the error I get:
No implicit view available from (String, String) => com.mongodb.casbah.commons.TypeImports.DBObject.
You should import following statements-
import com.mongodb.casbah.Imports._
import com.mongodb.casbah.commons.MongoDBObject
And you should use query something like:
collection.find(MongoDBObject("data.a" -> "4"))

How to get last array element while Projection mongodb

I have following document structure (This is dummy document for understanding purpose)
{
"id" : "p1245",
"Info" : [
{
"cloth_name" : "ABC",
"cloth_type" : "C"
},
{
"cloth_name" : "PQR",
"cloth_type" : "J"
},
{
"cloth_name" : "SAM",
"cloth_type" : "T"
}
]
},
{
"id" : "p124576",
"Info" : [
{
"cloth_name" : "HTC",
"cloth_type" : "C"
}
]
}
From these document I want to project the "cloth_type", so I tried following java code
DBObject fields = new BasicDBObject("id", 1);
fields.put("ClothType","$Info.cloth_type");
DBObject project = new BasicDBObject("$project", fields);
List<DBObject> pipeline = Arrays.asList(project);
AggregationOptions aggregationOptions = AggregationOptions.builder().batchSize(100).outputMode(AggregationOptions.OutputMode.CURSOR).allowDiskUse(true).build();
Cursor cursor = collection.aggregate(pipeline, aggregationOptions);
while (cursor.hasNext())
{
System.out.println(cursor.next());
}
(I don't want to use "$unwind" here)
and get following output:
{ "id" : "p1245" , "ClothType" : [ "C" , "J" , "T"]}
{ "id" : "p124576" , "ClothType" : [ "C"]}
If there are multiple "cloth_type" for single id, then I want only the last cloth_type from this array.
I want something like, e.g. if there is array of "ClothType" [ "C", "J", "T"] then I want to project only [ "T"] i.e last element of array.
Is there any ways to achive this without using "$unwind".

Casbah cas from BasicDBObject to my type

I have a collection in the database that looks like below:
Question
{
"_id" : ObjectId("52b3248a43fa7cd2bc4a2d6f"),
"id" : 1001,
"text" : "Which is a valid java access modifier?",
"questype" : "RADIO_BUTTON",
"issourcecode" : true,
"sourcecodename" : "sampleques",
"examId" : 1000,
"answers" : [
{
"id" : 1,
"text" : "private",
"isCorrectAnswer" : true
},
{
"id" : 2,
"text" : "personal",
"isCorrectAnswer" : false
},
{
"id" : 3,
"text" : "protect",
"isCorrectAnswer" : false
},
{
"id" : 4,
"text" : "publicize",
"isCorrectAnswer" : false
}
]
}
I have a case class that represents both the Question and Answer. The Question case class has a List of Answer objects. I tried converting the result of the find operation to convert the DBObject to my Answer type:
def toList[T](dbObj: DBObject, key: String): List[T] =
(List[T]() ++ dbObject(key).asInstanceOf[BasicDBList]) map { _.asInstanceOf[T]}
The result of the above operation when I call it like
toList[Answer](dbObj, "answers") map {y => Answer(y.id,y.text, y.isCorrectAnswer)}
fails with the following exception:
com.mongodb.BasicDBObject cannot be cast to domain.content.Answer
Why should it fail? Is there a way to convert the DBObject to Answer type that I want?
You have to retrieve values from BasicDBObject, cast them and then populate the Answer class:
Answer class:
case class Answer(id:Int,text:String,isCorrectAnswer:Boolean)
toList, I changed it to return List[Answer]
def toList(dbObj: DBObject, key: String): List[Answer] = dbObj.get(key).asInstanceOf[BasicDBList].map { o=>
Answer(
o.asInstanceOf[BasicDBObject].as[Int]("id"),
o.asInstanceOf[BasicDBObject].as[String]("text"),
o.asInstanceOf[BasicDBObject].as[Boolean]("isCorrectAnswer")
)
}.toList

Mongo $elemMatch in Casbah

I'm using Casbah 2.9.2
My mongodb schema looks like:
[ _id : "Triangle", Info : [ Color : "Red", Line : "Thin", UseID : "1", SourceId : "2" ] ]
I want to be able to write an update line that first checks if _id, Color and Line are together unique, if so update the UseID and SourceID otherwise create a new "Info" entry. This gets me exactly what I want from the command line:
db.shapes.update( { _id : 'Triangle', Info : { $not : { $elemMatch : { Color : 'Red', Line : 'Thick' } } } }, { $push : { Info : { Color : 'Red', Line : 'Thick', UseID : '2', SourceId : '3' } } }, true)
Giving me this entry:
[ _id : "Triangle", Info : [ { Color : "Red", Line : "Thin", UseID : "1", SourceId : "2" }, { Color : "Red", Line : "Thick", UseID : "2", SourceId : "3" } ] ]
However when I translate to Casbah for Scala it does not:
shapesCollection.update( { "_id" -> shape, "Info" $not { _ $elemMatch { "Color" -> color, "Line" -> line } } }, { $push -> { "Info" -> { "Color" -> color, "Line" -> line, "UseId" -> useId, "SourceId" -> srcId } } }, true )
The first complaint (from IntelliJ) is it wants all of the ','s to be ';'s which I feel is not correct, but in order to debug I oblige, which takes me to the second error saying:
"error: No Implicit view available from (java.lang.String, java.lang.String) => com.mongodb.casbah.query.Imports.DBObject. Error occurred in an application involving default arguments. "Info" $not { _ $elemMatch {"
So I guess the questions are, Can I even do the update I'm doing from the command line in Casbah? If so, how?
Thanks!
So for anyone that cares, first my update string was a little off, it would throw errors if the "Color" and "Line" didn't exist but the "_id" did. Basically it looks like $push with upsetter as true doesn't add to an existing "_id", so I was getting duplicate key errors as it tried to add the entry. But, that's moot, the real solution to what I wanted to do was to not use Casbah and instead use the MongoDB Java Driver. I was then able to get $elemMatch to do what I wanted.
Also this was probably part of my problem, since I went a different route I didn't get a chance to test it:
"If you really feel the need to use ++ with a mix of DSL and bare matches, we provide additive support for -> Tuple pairs. You should make the query operator calls first:"
val qMix = ("baz" -> 5) ++ ("foo" $gte 5) ++ ("x" -> "y")
/* error: value ++ is not a member of (java.lang.String, Int)
val qMix = ("baz" -> 5) ++ ("foo" $gte 5) ++ ("x" -> "y") */
The operator is chained against the result of DSL operators (which incidentally properly return a DBObject):
val qMix = ("foo" $gte 5) ++ ("baz" -> 5) ++ ("x" -> "y")
/* qMix: com.mongodb.casbah.commons.Imports.DBObject =
{ "foo" : { "$gte" : 5} , "baz" : 5 , "x" : "y"} */
From: http://api.mongodb.org/scala/casbah/current/tutorial.html