For example, if I want to simplify the result set for a nested doc', get the field score.date as score_date in order to get a flat result
Yes, this is possible through the aggregation framework, in particular you would want to use the $project operator. This reshapes each document in the stream, such as by adding new fields or removing existing fields. For each input document, outputs one document. Using your example, suppose your collection has documents with this schema:
{
"_id" : ObjectId("557330473a79b31f0c805db3"),
"player": "A",
"score": {
"date": ISODate("2015-05-30T15:14:48.000Z"),
"value": 2
}
}
Then you would apply the $project operator in the aggregation pipeline as:
db.collection.aggregate([
{
"$project": {
"player": 1,
"score_date": "$score.date",
"score_value": "$score.value"
}
}
]);
Result:
/* 0 */
{
"result" : [
{
"_id" : ObjectId("557330473a79b31f0c805db3"),
"player" : "A",
"score_date" : ISODate("2015-05-30T15:14:48.000Z"),
"score_value" : 2
}
],
"ok" : 1
}
Related
I know this has got to be simple, but for the life of me I can't seem to generate the correct final stage in my pipeline to get this working. Here are the documents output from a stage that I have in a mongo query:
{ "_id" : ObjectId("61435ceb233ce0118c1d93ec") }
{ "_id" : ObjectId("61435cf29598d31c17f0d839") }
{ "_id" : ObjectId("611e5cf953396d78985d222f") }
{ "_id" : ObjectId("61435cf773b8b06c848af83e") }
{ "_id" : ObjectId("61435cfd7ac204efa857e7ce") }
{ "_id" : ObjectId("611e5cf953396d78985d2237") }
I would like to get these documents into ONE single document with an array as such:
{
"_id" : [
ObjectId("61435ceb233ce0118c1d93ec"),
ObjectId("61435cf29598d31c17f0d839"),
ObjectId("611e5cf953396d78985d222f"),
ObjectId("61435cf773b8b06c848af83e"),
ObjectId("61435cfd7ac204efa857e7ce"),
ObjectId("611e5cf953396d78985d2237")
]
}
My last stage in the pipeline is simply:
{
$group:{_id:"$uniqueIds"}
}
I've tried everything from $push to $mergeObjects, but no matter what I do, it keeps returning the original 6 documents in some shape or form instead of ONE document. Any advice would be greatly appreciated! Thanks in advance.
Test code here
Query
group by null, sees all collection as 1 group
db.collection.aggregate([
{
"$group": {
"_id": null,
"ids": {
"$push": "$_id"
}
}
},
{
"$unset": "_id"
}
])
I have collection that contains a document like the following:
I just want to get id of quiz
But my result expected is
{"id":1}
How to do that?
This solution works with MongoDB version 4.4 or higher:
Input document:
{ "_id" : 1, "quiz" : { "id" : 1, "time_limit" : 10 } }
The query uses the new functionality in projection:
db.names.find( { }, { "id" : "$quiz.id", "_id": 0 } )
{ "id" : 1 } // desired output
For more information see Projection.
You'll have to use an aggregation $project stage as the query language does not allow restructuring of data.
db.collection.aggregate([
{
$project: {
_id: 0,
id: "$quiz.id"
}
}
])
Mongo Playground
I have a mongoDB orders collection, the documents of which look as follows:
[{
"_id" : ObjectId("59537df80ab10c0001ba8767"),
"shipments" : {
"products" : [
{
"orderDetails" : {
"id" : ObjectId("59537df80ab10c0001ba8767")
}
},
{
"orderDetails" : {
"id" : ObjectId("59537df80ab10c0001ba8767")
}
}
]
},
}
{
"_id" : ObjectId("5953831367ae0c0001bc87e1"),
"shipments" : {
"products" : [
{
"orderDetails" : {
"id" : ObjectId("5953831367ae0c0001bc87e1")
}
}
]
},
}]
Now, from this collection, I want to filter out the elements in which, any of the values at shipments.products.orderDetails.id path is same as value at _id path.
I tried:
db.orders.aggregate([{
"$addFields": {
"same": {
"$eq": ["$shipments.products.orderDetails.id", "$_id"]
}
}
}])
to add a field same as a flag to decide whether the values are equal, but the value of same comes as false for all documents.
EDIT
What I want to do is compare the _id field the the documents with all shipments.products.orderDetails.id values in the array.
If even 1 of the shipments.products.orderDetails.ids match the value of the _id field, I want that document to be present in the final result.
PS I am using MongoDB 3.4, and have to use the aggregation pipeline.
Your current attempt fails because the notation returns an "array" in comparison with a "single value".
So instead either use $in where available, which can compare to see if one value is "in" an array:
db.orders.aggregate([
{ "$addFields": {
"same": {
"$in": [ "$_id", "$shipments.products.orderDetails.id" ]
}
}}
])
Or notate both as arrays using $setIsSubset
db.orders.aggregate([
{ "$addFields": {
"same": {
"$setIsSubset": [ "$shipments.products.orderDetails.id", ["$_id"] ]
}
}}
])
Where in that case it's doing a comparison to see if the "sets" have an "intersection" that makes _id the "subset" of the array of values.
Either case will return true when "any" of the id properties within the array entries at the specified path are a match for the _id property of the document.
I have a collection that contains following information
{
"_id" : 1,
"info" : { "createdby" : "xyz" },
"states" : [ 11, 10, 9, 3, 2, 1 ]}
}
I project only states by using query
db.jobs.find({},{states:1})
Then I get only states (and whole array of state values) ! or I can select only one state in that array by
db.jobs.find({},{states : {$slice : 1} })
And then I get only one state value, but along with all other fields in the document as well.
Is there a way to select only "states" field, and at the same time slice only one element of the array. Of course, I can exclude fields but I would like to have a solution in which I can specify both conditions.
You can do this in two ways:
1> Using mongo projection like
<field>: <1 or true> Specify the inclusion of a field
and
<field>: <0 or false> Specify the suppression of the field
so your query as
db.jobs.find({},{states : {$slice : 1} ,"info":0,"_id":0})
2> Other way using mongo aggregation as
db.jobs.aggregate({
"$unwind": "$states"
}, {
"$match": {
"states": 11
}
}, // match states (optional)
{
"$group": {
"_id": "$_id",
"states": {
"$first": "$states"
}
}
}, {
"$project": {
"_id": 0,
"states": 1
}
})
If I have the following payload:
{
"objs": [
{ "_id": "1234566", "some":"data", "key": "one" },
{ "_id": "1234576", "some":"data", "key": "one" },
{ "_id": "2345666", "some":"otherdata", "key": "two" },
{ "_id": "4566666", "some":"yetotherdata", "key": "three" },
]
}
How can I return all objects (objs) with the following:
key: "one"
_id: [1234566, 1234576]
Thanks
The find() query returns all the objs that have the sub documents that match both these conditions.
var input = ["1234566","1234576"];
db.collection.find({$and:[{"objs._id":{$in:input}},{"objs.key":"one"}]})
If you want to get the redacted documents inside the objs array, You can achieve this using the aggregate pipeline operations.
Define a variable to hold the input values.
$unwind by objs elements, this gives you seperate documents for each
element in the objs array.
$match only those documents that match the selection criteria.
$group by "_id" of the document which is autogenerated by mongo.
$project the required fields.
The Code:
var input = ["1234566","1234576"];
db.collection.aggregate([
{$unwind:"$objs"},
{$match:{"objs._id":{$in:input},"objs.key":"one"}},
{$group:{"_id":"_id","objs":{$push:"$objs"}}},
{$project:{"_id":0,"objs":1}}
])
o/p:
{ "objs" :
[ { "_id" : "1234566", "some" : "data", "key" : "one" },
{ "_id" : "1234576", "some" : "data", "key" : "one" } ] }
You can't. MongoDB returns the document that matches the query conditions, not individual pieces that match the query conditions. You can suppress or include fields of the matching documents with projection, but you cannot (as of 2.6) return an array restricted just to contain elements that matched conditions on the array. You can return just the first such match with $
db.collection.find(
{ "objs" : { "$elemMatch" : { "_id" : { "$in" : [1234566, 1234576] }, "key" : "one" } } }, // query
{ "_id" : 0, "objs.$" : 1 } // projection
)
If you want to return all matching objs elements, most likely you should make each subdocument in the objs array into its own document in the collection.