Find from another find in mongodb - mongodb

In Mysql I can do a Select from another Select.
So I would ask if I can do the same in Mongodb.
For more explanation, I need to retreive the transaction of the a specific userOwner with just the last dateTransaction in the history object of this collection.So if this userOwner isn't in the last history we shoudn't retreive this transaction.
I used at first this query:
#Query(value = "{'history':{'$elemMatch':{'userOwner': ?0}}}")
but it returns all the elements even those where this userOwner isn't with the last "dateTransaction".
So I aim to de a query that it returns for each transaction just the last dateTransaction with userOwner of the "history" array :
.find({},{dateTransaction: {$slice: 1} }).limit(1)
and after do another query from this one.
Anyone has an idea or examples.
Tnx
This is my Collection called "piece":
{
"_id" : ObjectId("1"),
"history" : [{
"userOwner" : "3",
"dateTransaction" : ISODate("2016-05-30T00:00:00.000+0000"),
}, {
"userOwner" : "1",
"dateTransaction" : ISODate("2016-05-26T00:00:00.000+0000"),
}, {
"userOwner" : "2",
"dateTransaction" : ISODate("2016-05-23T00:00:00.000+0000"),
}
]
}{
"_id" : ObjectId("2"),
"transactions" : [{
"userOwner" : "2",
"dateTransaction" : ISODate("2016-05-26T00:00:00.000+0000"),
}, {
"userOwner" : "3",
"dateTransaction" : ISODate("2016-05-15T00:00:00.000+0000"),
}
]
}{
"_id" : ObjectId("3"),
"transactions" : [{
"userOwner" : "2",
"dateTransaction" : ISODate("2016-05-26T00:00:00.000+0000"),
}, {
"userOwner" : "1",
"dateTransaction" : ISODate("2016-05-15T00:00:00.000+0000"),
}
]
}
As example the result for the userOwner 2 should be :
{
"_id" : ObjectId("2"),
"transactions" : [{
"userOwner" : "2",
"dateTransaction" : ISODate("2016-05-26T00:00:00.000+0000"),
}, {
"userOwner" : "3",
"dateTransaction" : ISODate("2016-05-15T00:00:00.000+0000"),
}
]
}{
"_id" : ObjectId("3"),
"transactions" : [{
"userOwner" : "2",
"dateTransaction" : ISODate("2016-05-26T00:00:00.000+0000"),
}, {
"userOwner" : "1",
"dateTransaction" : ISODate("2016-05-15T00:00:00.000+0000"),
}
]
}

it looks like your data is stored in one collection - so this is a kind of bad design as it needs more overhead to work with....
below aggregation query which has a lookup to the same collection to match data for user:2
var unwind = {
$unwind : "$history"
}
var matchUser = {
$match : {
"history.userOwner" : "2"
}
}
var lookUp = {
$lookup : {
from : "shm",
localField : "userOwner",
foreignField : "userOwner",
as : "t"
}
}
var unwindTransactions = {
$unwind : "$t"
}
var unwindTransactions2 = {
$unwind : "$t.transactions"
}
var match2 = {
$match : {
"t.transactions.userOwner" : "2"
}
}
var project = {
$project : {
_id : 0,
recordId : "$_id",
transactionId : "$t._id",
dateOfTransaction : "$history.dateTransaction",
userOwner : "$history.userOwner",
}
}
db.shm.aggregate([unwind,
matchUser,
lookUp,
unwindTransactions,
unwindTransactions2,
match2,
project
])
and as a result we have two records of user transactions
{
"recordId" : ObjectId("575e7e8b852cb76369c9e446"),
"transactionId" : ObjectId("575e7e8b852cb76369c9e447"),
"dateOfTransaction" : ISODate("2016-05-23T00:00:00.000Z"),
"userOwner" : "2"
},{
"recordId" : ObjectId("575e7e8b852cb76369c9e446"),
"transactionId" : ObjectId("575e7e8b852cb76369c9e448"),
"dateOfTransaction" : ISODate("2016-05-23T00:00:00.000Z"),
"userOwner" : "2"
}
Please consider split of that collection as in current form will give you a lot of headache when processing more complex queries

Related

How to group mongo sub document

I am newbie to mongo, i am trying to take the group by values in a subdocument, and having the mongo collection structure as like :
{
"_id" : ObjectId("589d4e4b270f8b1635d400b1"),
"myShopId" : 439,
"products" : [
{
"productId" : "1234",
"productName" : "sambarpowder 500 gm",
"productCategory" : "masala",
"mrp" : "90",
"_id" : ObjectId("589d595f6da20b72fe006ea9")
},
{
"productId" : "5678",
"productName" : "moong dhal 200 gms",
"productCategory" : "dhal",
"mrp" : "38 ",
"_id" : ObjectId("589d595f6da20b72fe006eaa")
},
{
"productId" : "5678",
"productName" : "moong dhal 200 gms",
"productCategory" : "dhal",
"mrp" : "38 ",
"_id" : ObjectId("589d595f6da20b72fe006eaa")
}
],
"isAlive" : 1,
"__v" : 3
}
Here, I want to do group by in this.
for eg in mysql:
select productCategory from products where shopId = '439' groupby productCategory
How can i achieve the group by in mongo sub document
My Expected output is like :
category : [{
productCategory : masala
_id : ObjectId("589d595f6da20b72fe006ea9")
},
{
productCategory : dhal
_id : ObjectId("589d595f6da20b72fe006eaa")
}
]
Hope this will help,
db.test.aggregate([{
$match: {
myShopId: 439
}
}, {
$unwind: "$products"
}, {
$group: {
_id: {
"productCategory": "$products.productCategory"
},
"id": {
$first: "$products._id"
}
}
}])
Output:
{ "_id" : { "productCategory" : "dhal" }, "id" : ObjectId("589d595f6da20b72fe006eaa") }
{ "_id" : { "productCategory" : "masala" }, "id" : ObjectId("589d595f6da20b72fe006ea9") }

Mongodb : get whether a document is the latest with a field value and filter on the result

I am trying to port an existing SQL schema into Mongo.
We have document tables, with sometimes several times the same document, with a different revision but the same reference. I want to get only the latest revisions of the documents.
A sample input data:
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-A",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:23:18.807Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-A",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:30:35.757Z"),
"creator" : "X"
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X"
}
Given this data, I want this result set (sometimes I want only the last revision, sometimes I want all revisions with an attribute telling me whether it's the latest):
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X",
"lastrev" : true
},
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X",
"lastrev" : true
},
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X",
"lastrev" : true
}
I already have a bunch of filters, sorting, and skip/limit (for pagination of data), so the final result set should be mindful of these constraints.
The current "find" query (built with the .Net driver), which filters fine but gives me all revisions of each document:
coll.find(
{ "$and" : [
{ "$or" : [
{ "deletedid" : { "$exists" : false } },
{ "deletedid" : null }
] },
{ "$or" : [
{ "taskid" : { "$exists" : false } },
{ "taskid" : null }
] },
{ "objecttypeuid" : { "$in" : ["xxxxx"] } }
] },
{ "_id" : 0, "Uid" : 1, "lastrev" : 1, "title" : 1, "code" : 1, "creator" : 1, "owner" : 1, "modificator" : 1, "status" : 1, "reference": 1, "creationdate": 1 }
).sort({ "creationdate" : 1 }).skip(0).limit(10);
Using another question, I have been able to build this aggregation, which gives me the latest revision of each document, but with not enough attributes in the result:
coll.aggregate([
{ $sort: { "creationdate": 1 } },
{
$group: {
"_id": "$reference",
result: { $last: "$creationdate" },
creationdate: { $last: "$creationdate" }
}
}
]);
I would like to integrating the aggregate with the find query.
I have found the way to mix aggregation and filtering:
coll.aggregate(
[
{ $match: {
"$and" : [
{ "$or" : [
{ "deletedid" : { "$exists" : false } },
{ "deletedid" : null }
] },
{ "$or" : [
{ "taskid" : { "$exists" : false } },
{ "taskid" : null }
] },
{ "objecttypeuid" : { "$in" : ["xxx"] } }
]
}
},
{ $sort: { "creationdate": 1 } },
{ $group: {
"_id": "$reference",
"doc": { "$last": "$$ROOT" }
}
},
{ $sort: { "doc.creationdate": 1 } },
{ $skip: skip },
{ $limit: limit }
],
{ allowDiskUse: true }
);
For each result node, this gives me a "doc" node with the document data. It has too much data still (it's missing projections), but it's a start.
Translated in .Net:
FilterDefinitionBuilder<BsonDocument> filterBuilder = Builders<BsonDocument>.Filter;
FilterDefinition<BsonDocument> filters = filterBuilder.Empty;
filters = filters & (filterBuilder.Not(filterBuilder.Exists("deletedid")) | filterBuilder.Eq("deletedid", BsonNull.Value));
filters = filters & (filterBuilder.Not(filterBuilder.Exists("taskid")) | filterBuilder.Eq("taskid", BsonNull.Value));
foreach (var f in fieldFilters) {
filters = filters & filterBuilder.In(f.Key, f.Value);
}
var sort = Builders<BsonDocument>.Sort.Ascending(orderby);
var group = new BsonDocument {
{ "_id", "$reference" },
{ "doc", new BsonDocument("$last", "$$ROOT") }
};
var aggregate = coll.Aggregate(new AggregateOptions { AllowDiskUse = true })
.Match(filters)
.Sort(sort)
.Group(group)
.Sort(sort)
.Skip(skip)
.Limit(rows);
return aggregate.ToList();
I'm pretty sure there are better ways to do this, though.
You answer is pretty close. Instead of $last, $max is better.
About $last operator:
Returns the value that results from applying an expression to the last document in a group of documents that share the same group by a field. Only meaningful when documents are in a defined order.
Get the last revision in each group, see code below in mongo shell:
db.collection.aggregate([
{
$group: {
_id: '$reference',
doc: {
$max: {
"creationdate" : "$creationdate",
"code" : "$code",
"Uid" : "$Uid",
"status" : "$status",
"title" : "$title",
"creator" : "$creator"
}
}
}
},
{
$project: {
_id: 0,
Uid: "$doc.Uid",
status: "$doc.status",
reference: "$_id",
code: "$doc.code",
title: "$doc.title",
creationdate: "$doc.creationdate",
creator: "$doc.creator"
}
}
]).pretty()
The output as your expect:
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC501",
"code" : "501-B",
"title" : "Document 501",
"creationdate" : ISODate("2011-11-19T06:40:32.957Z"),
"creator" : "X"
}
{
"Uid" : "xxx",
"status" : "COMMENTED",
"reference" : "DOC306",
"code" : "306-B",
"title" : "Document 306",
"creationdate" : ISODate("2011-11-28T07:26:49.447Z"),
"creator" : "X"
}
{
"Uid" : "xxx",
"status" : "ACCEPTED",
"reference" : "DOC305",
"code" : "305-D",
"title" : "Document 305",
"creationdate" : ISODate("2011-11-24T15:13:28.887Z"),
"creator" : "X"
}

MongoDB - query for a nested item inside a collection

i have a mongodb collection "result" with data like
{ "_id" : {
"user" : "Howard",
"friend" : "Sita"
},
"value" : {
"mutualFriend" :[ "Hanks", "Bikash", "Shyam", "Bakshi" ]
}
}
{ "_id" : {
"user" : "Shiva",
"friend" : "Tom"
},
"value" : {
"friendList" :[ "Hanks", " Tom", " Karma", " Hari", " Dinesh" ]
}
}
{ "_id" : {
"user" : "Hari",
"friend" : "Shiva"
},
"value" : {
"mutualFriend" :[ "Tom", "Karma", "Dinesh" ]
}
}
Now, here i want to query whole Document having value.mutualFriend. how can i get the result?
Expected Output
{ "_id" : {
"user" : "Howard",
"friend" : "Sita"
},
"value" : {
"mutualFriend" :[ "Hanks", "Bikash", "Shyam", "Bakshi" ]
}
}
{ "_id" : {
"user" : "Hari",
"friend" : "Shiva"
},
"value" : {
"mutualFriend" :[ "Tom", "Karma", "Dinesh" ]
}
}
i have large number of document in MongoDB collection, containing value.friendList and value.mutualFriend and then i want to find only documents with value.mutualFriend
db.collection.find({"value.mutualFriend.0" : { $exists : true }})
Its just make sure that the 0th element exists. you can customize your query over various array length.

MongoDB query with multiple conditions

I have data with multiple documents :
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e486"),
"empId" : "1"
"type" : "WebUser",
"city" : "Pune"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e487"),
"empId" : "2"
"type" : "Admin",
"city" : "Mumbai"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e488"),
"empId" : "3"
"type" : "Admin",
"city" : "Pune"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e489"),
"empId" : "4"
"type" : "User",
"city" : "Mumbai"
}
I want to get data according to my multiple conditions :
condition 1:- {"type" : "WebUser", "city" : "Pune"}
condition 2:- {"type" : "WebUser", "city" : "Pune"} & {"type" : "User", "city" : "Mumbai"}
I want below result when run condition 1 :
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e486"),
"empId" : "1"
"type" : "WebUser",
"city" : "Pune"
}
When I run second condition :
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e486"),
"empId" : "1"
"type" : "WebUser",
"city" : "Pune"
}
{
"_id" : ObjectId("57b68dbbc19c0bd86d62e489"),
"empId" : "4"
"type" : "User",
"city" : "Mumbai"
}
I want above result by one query,
Currently I am using below aggregate query,
db.emp.aggregate([
{ $match: { '$and': [
{"type" : "WebUser", "city" : "Pune"},
{"type" : "User", "city" : "Mumbai"}
] } },
{ $group: { _id: 1, ids: { $push: "$empId" } } }
])
Above query work for first condition & fails for other. Please help me.
For the second condition, you can use the $in operator in your query as:
db.emp.find({
"type" : { "$in": ["WebUser", "User"] },
"city" : { "$in": ["Pune", "Mumbai"] }
})
If you want to use in aggregation:
db.emp.aggregate([
{
"$match": {
"type" : { "$in": ["WebUser", "User"] },
"city" : { "$in": ["Pune", "Mumbai"] }
}
},
{ "$group": { "_id": null, "ids": { "$push": "$empId" } } }
])
or simply use the distinct() method to return an array of distinct empIds that match the above query as:
var employeeIds = db.emp.distinct("empId", {
"type" : { "$in": ["WebUser", "User"] },
"city" : { "$in": ["Pune", "Mumbai"] }
});
If you are looking for the AND operator
This example checks if a field exists AND is null
db.getCollection('TheCollection').find({
$and: [
{'the_key': { $exists: true }},
{'the_key': null}
]
})
This example checks if a field has 'value1' OR 'value2'
db.getCollection('TheCollection').find({
$or: [
{'the_key': 'value1'},
{`the_key': 'value2'}
]
})
When just checking for null, the return contains non-existing fields plus fields with value null
db.getCollection('TheCollection').find({'the_key': null})
You can use mongo db $or operator.
db.emp.find({ $or: [
{ "type": "WebUser", "city": "Pune" },
{ "type": "user", "city": "Mumbai"}
]})
You can pass conditions in the array.
For more reference see mongo docs
Display the document where in the “StudName” has value “Ajay Rathod”.
db.Student.find({name:"ajay rathod"})
{ "_id" : ObjectId("5fdd895cd2d5a20ee8cea0de"), "
Retrieve only Student Name and Grade.
db.Student.find({},{name:1,grade:1,_id:0})
{ "name" : "dhruv", "grade" : "A" }
{ "name" : "jay", "grade" : "B" }
{ "name" : "abhi", "grade" : "C" }
{ "name" : "aayush", "grade" : "A" }
{ "name" : "sukhdev", "grade" : "B" }
{ "name" : "dhruval", "grade" : "B" }
{ "name" : "ajay rathod", "grade" : "D" }

In MongoDb, how to apply sort internal fields present in document?

My document looks like this
{
field1: somevalue,
name:xtz
nested_documents: [ // array of nested document
{ x:"1", y:"2" }, // first nested document
{ x:"2", y:"3" }, // second nested document
{ x:"-1", y:"3" }, // second nested document
// ...many more nested documents
]
}
How one can sort the data present in nested_documents?
Expected answer is shown below:
nested_documents: [ { x:"-1", y:"3" },{ x:"1", y:"2" },{ x:"2", y:"3" }]
To do this you would have to use the aggregation framework
db.test.aggregate([{$unwind:'$nested_documents'},{$sort:{'nested_documents.x':
1}}])
this returns
"result" : [
{
"_id" : ObjectId("5139ba3dcd4e11c83f4cea12"),
"field1" : "somevalue",
"name" : "xtz",
"nested_documents" : {
"x" : "-1",
"y" : "3"
}
},
{
"_id" : ObjectId("5139ba3dcd4e11c83f4cea12"),
"field1" : "somevalue",
"name" : "xtz",
"nested_documents" : {
"x" : "1",
"y" : "2"
}
},
{
"_id" : ObjectId("5139ba3dcd4e11c83f4cea12"),
"field1" : "somevalue",
"name" : "xtz",
"nested_documents" : {
"x" : "2",
"y" : "3"
}
}
],
"ok" : 1
Hope this helps