Query to aggregate a field from one collection to another in MongoDB - database

I have these two collections.
collectionname : req
{
reqId: "A123",
status: "1",
location: "hyd"
}
collection name : reqUser
{
req: "A123"
userId: "U1787"
designation: "employee"
}
Need an aggregate filtering the req collection based on location as hyd & aggregating the user field based on reqId.
Need Like this:
{
reqId: "A123",
status: "1",
userId: "U1787"
}
I used query on req collection with $match to filter based on location & $project to display only required fields from req collection and $lookup to match the reqId's from both collection but the issue is I am getting the whole reqUser object.
I am getting Something like this:
{
reqId: "A123",
status: "1",
reqUser:[
{
req: "A123"
userId: "U1787"
designation: "employee"
}
]
}
I need only userId field. Can anyone help me with obtaining the data as per my requirement mentioned above.
I am using mongo version 3.4.

Assuming req and reqUser has one-to-one relations, you could use the following query:
db.req.aggregate([
{
"$lookup": {
"from": "reqUser",
"localField": "reqId",
"foreignField": "req",
"as": "user"
}
},
{
$set: {
userId: {
$arrayElemAt: [
"$user.userId",
0
]
}
}
},
{
"$project": {
user: 0
}
}
])
Mongo playground ref: https://mongoplayground.net/p/V7bUdOXjnFC

Related

Create a new array of objects in a aggregate query in Mongodb

I'm running a query on Mongodb to get the combine data from two different collections: User and Store.
User collection has a property named as store_ids, which is an array that contains a list of ObjectIds of each store that the User has access to.
I'm trying to add the name of each store in the query result.
Example:
User Document:
{
_id: '58ebf8f24d52e9ab59b5538b',
store_ids: [
ObjectId("58dd4bb10e2898b0057be648"),
ObjectId("58ecd57d1a2f48e408ea2a30"),
ObjectId("58e7a0766de7403f5118afea"),
]
}
Store Documents:
{
_id: "58dd4bb10e2898b0057be648",
name: "Store A",
},
{
_id: "58ecd57d1a2f48e408ea2a30",
name: "Store B",
},
{
_id: "58e7a0766de7403f5118afea",
name: "Store C"
}
I'm looking for a query that returns an output like this:
{
_id: '58ebf8f24d52e9ab59b5538b',
stores: [
{
_id: ObjectId("58dd4bb10e2898b0057be648"),
name: "Store A"
},
{
id: ObjectId("58ecd57d1a2f48e408ea2a30"),
name: "Store B"
},
{
_id: ObjectId("58e7a0766de7403f5118afea"),
name: "Store C"
}
]
}
I've already tried operations like $map and $set. I don't know if I'm applying them in the right way because they didn't work for my case.
You can use an aggregate query:
db.users.aggregate([
{
$lookup: {
from: "stores", //Your store collection
localField: "store_ids",
foreignField: "_id",
as: "stores"
}
},
{
$project: {
store_ids: 0
}
}
])
You can see a working example here: https://mongoplayground.net/p/ICsEEsmRcg0
We can achieve this with a simple $lookup and with $project.
db.user.aggregate({
"$lookup": {
"from": "store",
"localField": "store_ids",
"foreignField": "_id",
"as": "stores"
}
},
{
"$project": {
store_ids: 0
}
})
$lookup will join with store table on with the store_ids array where the _id matches
$project removes the store_ids array from the resulting objects
Playground

How to remove a field having a specified value from a MongoDB document?

Here is a given document in mongo db as below
{
"emailid": "xxx.gmail",
"user_devices": [],
"devices": [{
"created_time": 1607153351,
"token": 123
},
{
"created_time": 1807153371,
"token": 1345
}]
}
Here i want to remove the field inside devices ie
"created_time": '', "token": '1345'
here token value is known, so need to delete "created_time" along with "token" where output is like
{
"emailid": "xxx.gmail",
"user_devices": [],
"devices": [{
"created_time": 1607153351,
"token": 123
}]
}
I tried unset code like below
var myquery = { 'emailid': emailid , 'devices.token':1345};
var newvalues = { $unset:{
'devices.created_time':'',
'devices.token':1345
} };
and used
updateOne(myquery, newvalues, function(err, res))
but it does not work. Can anyone tell how to remove this specified field. Thanks in advance.
Since the object you want to remove is in an array, you have to use $pull in this way:
db.collection.update({
"emailid": "xxx.gmail"
},
{
"$pull": {
"devices": {
"token": 123
}
}
})
In this query you are telling mongo: "Remove -using $pull- one field from the array devices where token is 123".
Example here
Edit:
Also, if you want to remove only one field into objec within array and not the object itself, the you can use $[<identifier>] to filter and then $unset like this:
db.collection.update({
"emailid": "xxx.gmail"
},
{
"$unset": {
"devices.$[elem].created_time": ""
}
},
{
"arrayFilters": [
{
"elem.token": 123
}
]
})
In this query, using arrayFilters you can $unset only in the object where token is 123. You can use created_time to filter if you want too.
Example here

Mongo error - Can't canonicalize query: BadValue Unsupported projection option

I am new to MongoDB and trying to execute a query. I have a company collection and company IDs array. I would like to get the results where attributes.0.ccode exist and attributes.0.ccode is not empty and will be checked within the ids provided in an array( cdata)
var query = Company.find({ _id: { $in: cdata } },{ "attributes.0.ccode": { $exists: true }, $and: [ { "attributes.0.ccode": { $ne: "" } } ] }).select({"attributes": 1}).sort({});
The error I am getting is
"$err": "Can't canonicalize query: BadValue Unsupported projection option: attributes.0.ccode: { $exists: true }",
"code": 17287
I think it's a bracketing issue but can't figure it out where.
Any help is highly appreciated.
In your code { _id: { $in: cdata } } is interpreted as query, and everything else, starting from ,{ "attributes.0.ccode": { $e.. as a Projection (which field to display). Try to refactor your code so _id: {$in ...} and the rest of the query belong to the same higher - level object. Something like this:
var query = Company.find({
_id: {
$in: cdata
},
"attributes.0.ccode": {
$exists: true
},
$and: [
{
"attributes.0.ccode": {
$ne: ""
}
}
]
}).select({"attributes": 1}).sort({});

MongoDB query into an array of arrays

I have the following schema into my DB:
{ name: 'AFG',
documents: [
{ name: 'doc1',
templates: [
{ name: 'templ1',
lastModified: <Date>},
...]
},
...]
}
I am trying to make a query to look for the template with name 'templ1'. If I find it, I have to compare the last modification date with another and update the value. If I don't find it, I have to push it into the array.
I have tried to do it with the following query:
Country.findOne({name : countrySelected, documents : {$elemMatch: {name: documentSelected}}, 'documents.templates' : {$elemMatch: {name: templateSelected}}}, function(err, templateFound){...}
But I get the following error:
MongoError: cannot use the part (documents of documents.templates) to traverse the element ({documents: [ { name: "Passport", templates: [] }, { name: "Visa", templates: [] }, { name: "Driver's Licence", templates: [] }, { name: "Id Card", templates: [] }, { name: "Other", templates: [] } ]})
Someone knows how can I do it? Thanks!!
You have to use the $in operator to search in arrays.
Country.findOne({
name : countrySelected,
documents: {
$elemMatch: {
$and: [{
name: {
$in: documentSelected //<-- must be array like ['doc1']
}
}, {
'templates.name': {
$in: documentSelected //<-- must be array like ['tmpl1']
}
}]
}
}
}, function(err, templateFound){
//do something with err and templateFound
});
To update the LastModified date you need to update it in the callback function and save the document.
See this answer for nested array updates

Filtering an embedded array in MongoDB

I have a Mongodb document that contains an an array that is deeply imbedded inside the document. In one of my action, I would like to return the entire document but filter out the elements of that array that don't match that criteria.
Here is some simplified data:
{
id: 123 ,
vehicles : [
{name: 'Mercedes', listed: true},
{name: 'Nissan', listed: false},
...
]
}
So, in this example I want the entire document but I want the vehicles array to only have objects that have the listed property set to true.
Solutions
Ideally, I'm looking for a solution using mongo's queries (e.g. `$unwind, $elemMatch, etc...) but I'm also using mongoose so solution that uses Mongoose is OK.
You could use aggregation framework like this:
db.test312.aggregate(
{$unwind:"$vehicles"},
{$match:{"vehicles.name":"Nissan"}},
{$group:{_id:"$_id",vehicles:{$push:"$vehicles"}}}
)
You can use $addToSet on the group after unwinding and matching by listed equals true.
Sample shell query:
db.collection.aggregate([
{
$unwind: "$vehicles"
},
{
$match: {
"vehicles.listed": {
$eq: true
}
}
},
{
$group: {
_id: "$id",
vehicles: {
"$addToSet": {
name: "$vehicles.name",
listed: "$vehicles.listed"
}
}
}
},
{
$project: {
_id: 0,
id: "$_id",
vehicles: 1
}
}
]).pretty();

Resources