mongodb return fields optionally - mongodb

I have a document like this:
{
"_id" : "abcdefg",
"status" : 1,
"myData": {
"a": "a",
"b": "b"
}
},
{
"_id" : "zxcvbnm",
"status" : 3,
"myData": {
"a": "a",
"b": "b"
}
}
if "status" === 3, field "myData" will return, like
myCollection.find({}, {"status": 1, "myData": 1})
if "status" !== 3, field "myData" will not return, like:
myCollection.find({}, {"status": 1, "myData": 0})
How could I do that in mongodb?

You could set them to null with aggregation.
db.collection.aggregate({
$project: {
status: 1,
myData: {
$cond: [{
$eq: ['$status', 1]
}, '$myData', null]
}
}
})
It does not remove the field but saves you some traffic.

Related

Count by elements in list and by field

I have a MongoDB collection that looks like this:
{ "_id" : 1, "owner" : "Alice", airline: "RSAirlines", "content" : ["shoes", "pants", "sockets"]}
{ "_id" : 2, "owner" : "Bob", airline: "RSAirlines", "content" : ["phone", "pants"]}
{ "_id" : 3, "owner" : "Charlie", airline: "RSAirlines", "content" : ["shoes", "pants", "bag"]}
{ "_id" : 4, "owner" : "Mary" ,airline: "AirES" "content" : ["sandals", "coins", "sockets"]}
{ "_id" : 5, "owner" : "Olivia", airline: "AirES", "content" : ["gloves", "pants", "sockets"]}
{ "_id" : 6, "owner" : "Dan", airline: "AirES", "content" : ["sockets", "wallet"]}
{ "_id" : 7, "owner" : "Erin", airline: "AirES", "content" : ["pants", "sockets", "dress"]}
I would like to aggregate them to get the following results:
{ "_id": "RSAirlines", "counts": {
"shoes": 2,
"pants": 3,
"sockets": 1,
"phone": 1,
"bag": 1
}}
{ "_id": "AirES", "counts": {
"sandals": 1,
"coins": 1,
"sockets": 4,
"wallet": 1,
"dress": 1,
"pants": 2
}}
Previous I saw this answer for counting the elements, but now I would like to count them by airline.
$unwind deconstruct content array
$group by airline and content and get the total count
$group by the only airline and construct counts array key-value format
$arrayToObject convert key-value array to object
db.collection.aggregate([
{ $unwind: "$content" },
{
$group: {
_id: {
airline: "$airline",
content: "$content"
},
count: { $sum: 1 }
}
},
{
$group: {
_id: "$_id.airline",
counts: {
$push: {
k: "$_id.content",
v: "$count"
}
}
}
},
{ $project: { counts: { $arrayToObject: "$counts" } } }
])
Playground

MongoDB $lookup: Multiple Join Conditions on field in array

I have an issue with joining documents in mongo based on conditions on a nested field in an array. Basically, I want to filter the documents of the foreign collection.
I was following the example on the official MongoDB documentation for Multiple Join Conditions with $lookup and extended it to fit my requirements.
Testdata (Note, the warehouse documents are extended by the field foo):
db.orders.insert([
{ "_id" : 1, "item" : "almonds", "price" : 12, "ordered" : 2 },
{ "_id" : 2, "item" : "pecans", "price" : 20, "ordered" : 1 },
{ "_id" : 3, "item" : "cookies", "price" : 10, "ordered" : 60 }
])
db.warehouses.insert([
{ "_id" : 1, "stock_item" : "almonds", warehouse: "A", "instock" : 120, 'foo': [{ 'bar': 1 }] },
{ "_id" : 2, "stock_item" : "pecans", warehouse: "A", "instock" : 80, 'foo': [{ 'bar': 1 }] },
{ "_id" : 3, "stock_item" : "almonds", warehouse: "B", "instock" : 60, 'foo': [{ 'bar': 1 }] },
{ "_id" : 6, "stock_item" : "almonds", warehouse: "A", "instock" : 61, 'foo': [{ 'bar': 2 }] },
{ "_id" : 4, "stock_item" : "cookies", warehouse: "B", "instock" : 40, 'foo': [{ 'bar': 1 }] },
{ "_id" : 5, "stock_item" : "cookies", warehouse: "A", "instock" : 80, 'foo': [{ 'bar': 1 }] }
])
On the lookup, I want to add the additional condition foo.bar: 1:
db.orders.aggregate([
{
$lookup: {
from: "warehouses",
let: { order_item: "$item", order_qty: "$ordered" },
pipeline: [{
$match: {
$expr: {
$and: [
{ $eq: [ "$stock_item", "$$order_item" ] },
{ $gte: [ "$instock", "$$order_qty" ] },
{ $eq: [ "$foo.bar", 1 ] }
]
}
}
}, {
$project: { stock_item: 0, _id: 0 }
}],
as: "stockdata"
}
}
])
Unfortunately this extra condition does not work. Furthermore not a sigle document is returned by the lookup.
Can someone point me in the right direction? I know the problem can also be done by a pipeline using unwind, filter and group.
Thanks!

Mongodb group query for embeded docs

Is it possible to query value counts for a particular key inside embeded documents.
Here is my document:
{ "_id" : 1, "drives" : [ {"fw": "A"}, {"fw": "B"} ] }
{ "_id" : 2, "drives" : [ {"fw": "B"}, {"fw": "C"} ] }
{ "_id" : 3, "drives" : [ {"fw": "A"}, {"fw": "C"} ] }
{ "_id" : 4, "drives" : [ {"fw": "A"}, {"fw": "D"} ] }
And i would like to get the count of "fw":
Output:
counts : {"A": 3, "B": 2, "C": 2, "D": 1 }
Aggregation will do it. First $unwind the array to get a row per fw value, then $group by the value of fw along with a $sum of 1 to get the count per fw value;
db.test.aggregate( { $unwind: "$drives" },
{ $group: { _id: "$drives.fw", cnt: {$sum:1} } } )
# { "_id" : "D", "cnt" : 1 }
# { "_id" : "C", "cnt" : 2 }
# { "_id" : "B", "cnt" : 2 }
# { "_id" : "A", "cnt" : 3 }

How to update an element within an array of a sub-document in Mongodb

Facing issue while trying to update an element within an array of sub document in Mongo. If I consider the following document in the collection "resource"
{
"_id": 1,
"resources": [
{
"resource_id": 1,
"resource_list": ["item1","item2"]
},
{
"resource_id": 2,
"resource_list": ["item4","item3"]
}
]
}
I want to update "item4" with some other value like "item5" for "resource_id" = 2
The following statement gave me an error : Cannot apply the positional operator without a corresponding query field containing an array.
db.resource.update({"resources.resource_id": 2, "resources.resource_list": "item4"}, {$set: {"resources.$.resource_list.$": "item5"}})
Any help on this will be highly appreciated.
The positional operator can be used only once in a query. This is a limitation, there is an open ticket for improvement: https://jira.mongodb.org/browse/SERVER-831
An other way to solve this problem could be to push "item5" to the array and pull "item4". Something like this:
db.resource.update({ "resources.resource_id": 2, "resources.resource_list": "item4" }, { $pull: { "resources.$.resource_list": "item4" }, $push: { "resources.$.resource_list": "item5" } })
However this isn't possible either, $pull and $push can't be used in the same query for the same field. Related ticket: https://jira.mongodb.org/browse/SERVER-1050
I see two solutions. One is to execute two queries. In the first query, you push "item5", in the second query you pull "item4":
db.resource.update({ "resources.resource_id": 2, "resources.resource_list": "item4" }, { $push: { "resources.$.resource_list": "item5" } });
db.resource.update({ "resources.resource_id": 2, "resources.resource_list": "item4" }, { $pull: { "resources.$.resource_list": "item4" } });
If you want to do it in one query, you can use mongo shell. Something like this:
db.resource.find({ 'resources.resource_id': 2, 'resources.resource_list': 'item4' }).forEach( function(document) {
for (var i in document.resources) {
if (document.resources[i].resource_id == 2) {
var index = document.resources[i].resource_list.indexOf('item4');
document.resources[i].resource_list[index] = 'item5';
db.resource.save(document);
}
}
})
symbol $ only modifies the last position of the last array in the search, and you can use $addToSet or $push for add items...
> db.resource.find()
{ "_id" : 1,
"resources" : [
{ "resource_id" : 1,
"resource_list" : [ "item1","item2" ] },
{ "resource_id" : 2,
"resource_list" : [ "item4", "item3" ] } ] }
> db.resource.update({"resources.resource_id": 2,
"resources.resource_list": "item4"},
{$addToSet:{"resources.$.resource_list": "item5"}})
> db.resource.find()
{ "_id" : 1, "resources" : [
{ "resource_id" : 1,
"resource_list" : [ "item1", "item2" ] },
{ "resource_id" : 2,
"resource_list" : [ "item4", "item3", "item5" ] } ] }
> db.resource.update({"resources.resource_id": 2,
"resources.resource_list": "item4"},
{$pull: {"resources.$.resource_list": "item4"}})
> db.resource.find()
{ "_id" : 1, "resources" : [
{ "resource_id" : 1,
"resource_list" : [ "item1", "item2" ] },
{ "resource_id" : 2,
"resource_list" : [ "item3","item5" ] } ] }
But is better run two querys in same time?
> db.algo.update({"resources.resource_id": 2,
"resources.resource_list": "item4"},
{$addToSet:{"resources.$.resource_list": "item5"},
$pull: {"resources.$.resource_list": "item4"}})
Field name duplication not allowed with modifiers

Mongodb creating alias in a query

What is the mongodb's equivalent to this query:
SELECT "foo" as bar, id as "spec" from tablename
It is possible to create new field with given name and value taken from another field with $project:
{
"_id" : 1,
title: "abc123",
isbn: "0001122223334",
author: { last: "zzz", first: "aaa" },
copies: 5
}
The following $project stage adds the new fields isbn, lastName, and copiesSold:
db.books.aggregate(
[
{
$project: {
title: 1,
isbn: {
prefix: { $substr: [ "$isbn", 0, 3 ] },
group: { $substr: [ "$isbn", 3, 2 ] },
publisher: { $substr: [ "$isbn", 5, 4 ] },
title: { $substr: [ "$isbn", 9, 3 ] },
checkDigit: { $substr: [ "$isbn", 12, 1] }
},
lastName: "$author.last",
copiesSold: "$copies"
}
}
]
)
http://docs.mongodb.org/manual/reference/operator/aggregation/project/#pipe._S_project
You can use any operator like toUpper or toLower or concat or any other operator you feel like which you think you can work on and create an alias.
Example:
In the following example created_time is a field in the collection.
(I am not good with syntax so you can correct it, but this is the approach)
{$project {
"ALIAS_one" : {"$concat" : "$created_time"},
"ALIAS_two" : {"$concat" : "$created_time"},
"ALIAS_three" : {"$concat" : "$created_time"}
}}
So using an operator in that fashion you can create as many as aliases as your like.
you can use this, maybe help
database data
{ "_id" : "5ab0f445edf197158835be63", "userid" : "5aaf15c28264ee17fe869ad8", "lastmodified" : ISODate("2018-03-21T07:04:41.735Z") }
{ "_id" : "5ab0f445edf197158835be64", "userid" : "5aaf15c28264ee17fe869ad8", "lastmodified" : ISODate("2018-02-20T12:31:08.896Z") }
{ "_id" : "5ab0f445edf197158835be65", "userid" : "5aaf15c28264ee17fe869ad7", "lastmodified" : ISODate("2018-02-20T02:31:08.896Z") }
mongo command
db.zhb_test.aggregate(
[{
$group: {
_id: {
$dateToString: {
format: "%Y-%m",
date: "$lastmodified"
}
},
count: {
$sum: 1
}
}
},
{
$project: {
"month": "$_id",
count: 1,
"_id": 0
}
}])
result
{ "count" : 2, "month" : "2018-02" }
{ "count" : 1, "month" : "2018-03" }