Morphia - Query based on a subdocument - mongodb

I have a mongo collection which has documents which look like below :
{
"_id" : ObjectId("9873214jkhdkfjdsf8324"),
"nm" : "test",
"sts" : 1,
"updby" : NumberLong(0),
"tags" : [
{
"name" : "women",
"rank" : 1,
"type" : 3
},
{
"name" : "men",
"rank" : 1
},
{
"name" : "clothing",
"rank" : 2,
"type" : 1
}
]
}
I want to query the collection such that I want all the documents which have "name": "women" and "type" : 3 inside the tags subdocument of each document which is returned.
I know that the mongo query should be something like this :
db.collection.find("tags":{
$all:[
{"$elemMatch":{"name":"women","type":3}},
]})
I tried using the 'hasthiselement' provided by morphia, but I am not able to form the exact query which I want.
getMongoDAORead().getDatastore().createQuery(test.class)
.field("tags").hasThisElement("name").equal("women");
This query doesn't seem to be correct. Can someone help me form the correct query?

I fixed this by doing the following:
I created a object of the Tags Class and initialized it:
Tags tag = new Tags("women", null, 3);
Query<MyClass> t = getMongoDAORead().getDatastore()
.createQuery(MyClass.class)
.field("ctags").hasThisElement(tag);

Related

How create a MongoDB query

I have a collection with some documents like below:
{
"_id" : ObjectId("1"),
"className" : "model.MyClass",
"createdOn" : ISODate("2018-10-23T11:00:00.000+01:00"),
"status" : "A"
}
{
"_id" : ObjectId("2"),
"className" : "model.MyClass",
"createdOn" : ISODate("2018-10-23T11:01:00.000+01:00"),
"status" : "B"
}
{
"_id" : ObjectId("3"),
"className" : "model.MyClass",
"createdOn" : ISODate("2018-10-23T11:02:00.000+01:00"),
"status" : "C"
}
{
"_id" : ObjectId("4"),
"className" : "model.MyClass",
"createdOn" : ISODate("2018-10-23T11:03:00.000+01:00"),
"status" : "D"
}
Given a specific ID, how can I get the previous document that whose status not equals a specific status.
For example, I give the ID 4 and like to get the last document that status not is B neither C. So, I get the Object with Id 1.
How to create this query?
you could try this:
db.yourcollection.find( {"status":{"$nin":["B","C"]}}
).sort({_id:-1}).limit(1);
so use not in operator i.e. $nin, then sort the data in descending order and limit the records to 1
see below documentations for details.
$nin operator
mongo sort

Saving the result of a MongoDB query

When doing a research in mongo shell I often write quite complex queries and want the result to be stored in other collection. I know the way to do it with .forEach():
db.documents.find(query).forEach(function(d){db.results.insert(d)})
But it's kind of tedious to write that stuff each time. Is there a cleaner way? I'd like the syntax to be something like db.documents.find(query).dumpTo('collectionName').
Here's a solution I'll use: db.results.insert(db.docs.find(...).toArray())
There is still too much noise, though.
UPD: There is also an option to rewrite find using aggregation pipeline. Then you can use $out operator.
It looks like you are doing your queries from the mongo shell, which allows you to write code in javascript. You can assign the result of queries to a variable:
result = db.mycollection.findOne(my_query)
And save the result to another collection:
db.result.save(result)
You might have to remove the _id of the result if you want to append it to the result collection, to prevent a duplicate key error
Edit:
db.mycollection.findOne({'_id':db.mycollection.findOne()['_id']})
db.foo.save(db.bar.findOne(...))
If you want to save an array, you can write a javascript function. Something like the following should work (I haven't tested it):
function save_array(arr) {
for(var i = 0; i < arr.length; i++) {
db.result.save(arr[i])
}
}
...
result = db.mycollection.find(...)
save_array(result)
If you want the function to be available every time you start mongo shell, you can include it in your .mongorc.js file
As far as I know, there isn't built-in functionality to do this in MongoDB.
Other options would be to use mongoexport/mongoimport or mongodump/mongorestore functionalities.
In both mongoexport and mongodump you can filter the results by adding query options using --query <JSON> or -q <JSON>.
If your query is using an aggregation operator then the solution is as sample as using the $out.
I created a sample Collection with the name "tester" which contain the following records.
{ "_id" : ObjectId("4fb36bfd3d1c88bfa15103b1"), "name" : "bob", "value" : 5, "state" : "b"}
{ "_id" : ObjectId("4fb36c033d1c88bfa15103b2"), "name" : "bob", "value" : 3, "state" : "a"}
{ "_id" : ObjectId("4fb36c063d1c88bfa15103b3"), "name" : "bob", "value" : 7, "state" : "a"}
{ "_id" : ObjectId("4fb36c0c3d1c88bfa1a03b4"), "name" : "john", "value" : 2, "state" : "a"}
{ "_id" : ObjectId("4fb36c103d1c88bfa5103b5"), "name" : "john", "value" : 4, "state" : "b"}
{ "_id" : ObjectId("4fb36c143d1c88bfa15103b"), "name" : "john", "value" : 8, "state" : "b"}
{ "_id" : ObjectId("4fb36c163d1c88bfa15103a"), "name" : "john", "value" : 6, "state" : "a"}
Now using the aggregate operator I perform a group by and then save the result into a new collection using this magical operator "$out".
db.tester.aggregate([{$group:{
_id:{name:"$name",state:"$state"},
min:{$min:"$value"},
max:{$max:"$value"},
} },
{$out:"tester_max_min"}
])
What basically the query is trying to do is, group by name & state and find the min and max values for each individual group, and then save the result into a new collection named "tester_max_min"
db.tester_max_min.find();
The new collection formed will have the following documents in it :
{ "_id" : { "name" : "john", "state" : "b" }, "min" : 4, "max" : 8 }
{ "_id" : { "name" : "john", "state" : "a" }, "min" : 2, "max" : 6 }
{ "_id" : { "name" : "bob", "state" : "a" }, "min" : 3, "max" : 7 }
{ "_id" : { "name" : "bob", "state" : "b" }, "min" : 5, "max" : 5 }
I still need to explore how helpful can $out is but it works like a charm for any aggregator operator.

MongoDB update elements within array

I have a database of users like in the example below and I need to update the city of the user from Bonn to Berlin.
{
"_id" : "Louis",
"registered" : true,
"likes" : [
"tennis",
"cooking"
],
"addr" : {
"city" : "Bonn",
"country" : "Germany"
}
}
I have tried the following but it does not seem to work. Any ideas?
db.users.update( {_id:"Louis"}, {$set:{city:"Berlin"}} )
You should have quotes around field names in sub-documents
db.test.update( {_id : "Louis"}, {$set : {"addr.city" : "Berlin"}})

MongoDB query insert field into document from a list of Id's

I'm kind of stuck with the following problem. I have a MongoDB filled with documents, of these documents (I have a list with Id's) I need to insert a field.
I have this document:
{
"id" : 3639,
"type" : "P",
"createdate" : "2011-10-19T11:45:14+0200",
"name_creator" : "",
"latitude" : "50.887",
"longitude" : "9.14999",
"themes" : [{
"name" : "Fun",
"id" : "4"
}, {
"name" : "Kids",
"id" : "5"
}]
}
I need a query the can insert the themes field into the document, the current themes field does not have to be updates, just 1 new one. I have over 300 Id's where this has to be done.
The document should then look like this:
(all the other fields in themes should be removed, just one new one 'Outside')
{
"id" : 3639,
"type" : "P",
"createdate" : "2011-10-19T11:45:14+0200",
"name_creator" : "",
"latitude" : "50.887",
"longitude" : "9.14999",
"themes" : [{
"name" : "Outside",
"id" : "6"
}]
}
I would normally write a bit of Java code that would loop over the documents and change them, but I believe (hope) this could be done in a query.
Anyone got an idea on how I could do this?
Thanks for any help!
All you need to do is
db.collection.update(
{id : {$in : [your list of ids]}},
{$set : {
theme : [{
"name" : "Outside",
"id" : "6"
}]
}},
{multi : true}
)

mongodb part of the array

I have a schema which looks like this:
{
"_id" : ObjectId("4f6af5c7065f92581a000013"),
....
"conversation" : [{
"_id" : ObjectId("4f6af5c7065f92581a000013"),
"msg" : "message1",
"userID" : 1
},{
"_id" : ObjectId("4f6af5c7065f92581a000016"),
"msg" : "message3",
"userID" : 1
},{
"_id" : ObjectId("4f6af5c7065f92581a000023"),
"msg" : "msg",
"userID" : 1
}]
}
What I need is to output a list of elements whose value for the key msg contains 'msg'
Is it possible to do so?
db.dialogs.find({ "_id" : new ObjectId('4f6af5c7065f92581a000013'), "conversation.msg" : /msg/i })
but it outputs all the information about the object. What I need is just
{
"_id" : ObjectId("4f6af5c7065f92581a000023"),
"msg" : "msg",
"userID" : 1
}
No, you can't select elements of sub-array on their own. Structure of parent document will be preserved (though, you may choose not to select from fields other tnan conversation (as you did in the example)).