MongoDB - Array element as variable - database

Lets say here is the same document:
{
"_id" : 1.0,
"custname" : "metlife",
"address" : {
"city" : "Bangalore",
"country" : "INDIA"
}
}
And if I want to push an extra field to this document, something like below:
db.customers.updateMany(
{"address.country":"INDIA"},
{$push : {city: "$address.country"}}
)
It results in wrong update:
{
"_id" : 1.0,
"custname" : "metlife",
"address" : {
"city" : "Bangalore",
"country" : "INDIA"
},
"city" : "$address.city"
}
Instead of this:
{
"_id" : 1.0,
"custname" : "metlife",
"address" : {
"city" : "Bangalore",
"country" : "INDIA"
},
"city" : "Bangalore"
}
How do I achieve the above result?

You can't refer to other field values in update currently (more here). There is a workaround in aggregation framework (using $out) but it will replace entire collection.
I think that you can consider using $rename in your case. It will not add new field but it can move city to the top level of your document.
db.customers.updateMany({"address.country":"INDIA"}, {$rename: {"address.city": "city"}})
will give you following structure:
{ "_id" : 1, "custname" : "metlife", "address" : { "country" : "INDIA" }, "city" : "Bangalore" }

like #mickl said : You can't refer to other field values in update currently,
you have to iterate through your collection to update the documents, try this :
db.eval(function() {
db.collection.find({"address.country":"INDIA"}).forEach(function(e) {
e.city= e.address.city;
db.collection.save(e);
});
});
Keep in mind that this will block the DB until all updates are done.

try this
db.customers.updateMany(
{"address.country":"INDIA"},
{$push : {city: "address.country"}}
)
remove $ sign

Related

MongoDB Transform element fields into array of values only

I need help with following thing in MongoDB, what I'm trying to do is to get only marks in reviews into it's own array of values only.
Code i got so far:
db.lodging.aggregate([
{$project:{
reviews:"$host.reviews",host:"$host"
}
},
{$unwind: "$reviews"},
])
JSON Example:
"host" : {
"name" : "Grimes",
"surname" : "Terrell",
"gender" : "male",
"age" : NumberInt(55),
"picture" : "https://api.adorable.io/avatars/285/GrimesTerrell.png",
"reviews" : [
{
"reviewer" : "Mae Ryan",
"date" : "2015-06-01T02:41:46 -02:00",
"helpful" : NumberInt(8),
"kind" : NumberInt(1),
"responsive" : NumberInt(5)
},
{
"reviewer" : "Nixon Johnson",
"date" : "2016-02-08T10:35:12 -01:00",
"helpful" : NumberInt(1),
"kind" : NumberInt(1),
"responsive" : NumberInt(9)
},
]
}
This is what im trying to achieve:
{
"host" : {
"name" : "Grimes",
"surname" : "Terrell",
"gender" : "male",
"age" : NumberInt(55),
"picture" : "https://api.adorable.io/avatars/285/GrimesTerrell.png",
"reviews" : [
{
"marks" : [8,1,5],
},
{
"marks" : [1,1,9],
},
]
}
Assuming your marks' field names are always helpful, kind, responsive, you can use $map.
Here is a mongo playground for you reference

Remove objects within array in MongoDB

I need to remove all the objects within array who meet the conditions i will show down below. I'll let here the documents and an example of what i've done.
//document 1
{
"_id" : ObjectId("5ec73abebd7e4d618a057350"),
"code" : "X20",
"title" : "Full stack developer",
"location" : "Paris",
"date" : ISODate("2020-05-22T02:36:46.272Z"),
"candidates" : [
{
"name" : "David",
"last_name" : "Broncano",
"telephone" : "642025552",
"email" : "david#gmail.com"
},
{
"name" : "Pablo",
"last_name" : "Claros",
"telephone" : "628721784",
"email" : "pablo#gmail.com"
}
]
}
// document 2
{
"_id" : ObjectId("4ec73abebd7e4d618a057350"),
"code" : "X50",
"title" : "Full stack developer",
"location" : "Madrid",
"date" : ISODate("2020-05-22T02:36:46.272Z"),
"candidates" : [
{
"name" : "Maria",
"last_name" : "Mars",
"telephone" : "642024582",
"email" : "dasd#gmail.com"
},
{
"name" : "Pablo",
"last_name" : "Claros",
"telephone" : "628721784",
"email" : "pablo#gmail.com"
}
]
}
So i need to remove all the candidates where location is Madrid.I have done this but it removes the field. Is it possible to just remove the content of it using $pull or something?
db.offers.update(
{ location : "Madrid"},
{
$unset:{
"candidates":""
} } ,
{
multi : true
}
)
According to my understanding, you need to just clear the candidates array and maintain that as candidates: []. For this, you can use use $set operator to set candidates to [] based on your condition
db.offers.update({ location : "Madrid"}, { $set:{ "candidates": [] } } , { multi : true })

Get index of an element mongodb aggregation

Here is my collection
{
"_id" : ObjectId("5c225f9a66d39d55c036fa66"),
"name" : "Sherlock",
"mobile" : "999999",
"adress" : [
{
"street" : "221b baker street",
"city" : "london"
},
{
"street" : "ben street",
"city" : "london"
}
],
"tags" : [
"Detective",
"Magician",
"Avenger"
]
}
Now I want to get the first or second value inside address array.
for that I'm using this command.
> db.agents.findOne({"name" : "Sherlock"},{"adress" : 1})
but instead of giving a single result it is giving the entire array like
{
"_id" : ObjectId("5c225f9a66d39d55c036fa66"),
"adress" : [
{
"street" : "221b baker street",
"city" : "london"
},
{
"street" : "ben street",
"city" : "london"
}
]
}
It can be done by comparing array value like
db.agents.find({"adress.street": "ben street"}, {_id: 0, 'adress.$': 1});
But I don't want to compare just to print the array indexes. How can I get the single result?
Any help is appreciated..
You can use $arrayElemAt to get the specific element from the array
db.collection.aggregate([
{ $addFields: { "$arrayElemAt": ["$adress", 0] }} //index
])
and if you want to get the sliced element then you can use $slice projection
db.collection.find({}, { adress: { $slice: [2, 1] }}) // 2 index and 1 number of element
You can $unwind with includeArrayIndex to get the index of address array
db.t11.aggregate([
{$match : {"adress.street" : "ben street"}},
{$unwind : {path : "$adress", includeArrayIndex : "idx"}},
{$match : {"adress.street" : "ben street"}}
]).pretty()
you can add $project to filter the fields not required
result
> db.t11.aggregate([{$match : {"adress.street" : "ben street"}},{$unwind : {path : "$adress", includeArrayIndex : "idx"}},{$match : {"adress.street" : "ben street"}}]).pretty()
{
"_id" : ObjectId("5c225f9a66d39d55c036fa66"),
"name" : "Sherlock",
"mobile" : "999999",
"adress" : {
"street" : "ben street",
"city" : "london"
},
"tags" : [
"Detective",
"Magician",
"Avenger"
],
"idx" : NumberLong(1)
}
>

Mongodb Java add new array element in document

I'm using mongo 2.2.3 and the java driver. My dilemma, I have to $push a field and value into an array element, but I cant seem to figure out how to do this. A sample of my data:
"_id" : 1,
"scores" : [
{
"type" : "english",
"score" : 78.97979
},
{
"type" : "spanish",
"score" : 6.99
}
]
I want to push one array attribute ("grade" : "A") in document where type = english.
after push document look like this :
"_id" : 1,
"scores" : [
{
"type" : "english",
"score" : 78.97979,
"grade" : "A"
},
{
"type" : "spanish",
"score" : 6.99
}
]
I tried using shell :
db.Sample.update({"scores.type" : "english"},{"$push" : {"scores": {"grade":"A"}}})
But this is not adding attribute on specific position.
Try this update with Set and reference:
db.Sample.update({"scores.type" : "english"},{"$set" : {"scores.$.grade":"A"}})

Mongoose Query: Find an element inside an array

Mongoose/Mongo noob here:
My Data
Here is my simplified data, each user has his own document
{ "__v" : 1,
"_id" : ObjectId( "53440e94c02b3cae81eb0065" ),
"email" : "test#test.com",
"firstName" : "testFirstName",
"inventories" : [
{ "_id" : "active",
"tags" : [
"inventory",
"active",
"vehicles" ],
"title" : "activeInventory",
"vehicles" : [
{ "_id" : ObjectId( "53440e94c02b3cae81eb0069" ),
"tags" : [
"vehicle" ],
"details" : [
{ "_id" : ObjectId( "53440e94c02b3cae81eb0066" ),
"year" : 2007,
"transmission" : "Manual",
"price" : 1000,
"model" : "Firecar",
"mileageReading" : 50000,
"make" : "Bentley",
"interiorColor" : "blue",
"history" : "CarProof",
"exteriorColor" : "blue",
"driveTrain" : "SWD",
"description" : "test vehicle",
"cylinders" : 4,
"mileageType" : "kms" } ] } ] },
{ "title" : "soldInventory",
"_id" : "sold",
"vehicles" : [],
"tags" : [
"inventory",
"sold",
"vehicles" ] },
{ "title" : "deletedInventory",
"_id" : "deleted",
"vehicles" : [],
"tags" : [
"inventory",
"sold",
"vehicles" ] } ] }
As you can see, each user has an inventories property that is an array that contains 3 inventories (activeInventory, soldInventory and deletedInventory)
My Query
Given an user's email a a vehicle ID, i would like my query to go through find the user's activeInventory and return just the vehicle that matches the ID. Here is what I have so far:
user = api.mongodb.userModel;
ObjectId = require('mongoose').Types.ObjectId;
return user
.findOne({email : params.username})
.select('inventories')
.find({'title': 'activeInventory'})
//also tried
//.where('title')
//.equals('activeInventory')
.exec(function(err, result){
console.log(err);
console.log(result);
});
With this, result comes out as an empty array. I've also tried .find('inventories.title': 'activeInventory') which strangely returns the entire inventories array. If possible, I'd like to keep the chaining query format as I find it much more readable.
My Ideal Query
return user
.findOne({email : params.username})
.select('inventories')
.where('title')
.equals('activeInventory')
.select('vehicles')
.id(vehicleID)
.exec(cb)
Obviously it does not work but it can give you an idea what I'm trying to do.
Using the $ positional operator, you can get the results. However, if you have multiple elements in the vehicles array all of them will be returned in the result, as you can only use one positional operator in the projection and you are working with 2 arrays (one inside another).
I would suggest you take a look at the aggregation framework, as you'll get a lot more flexibility. Here's an example query for your question that runs in the shell. I'm not familiar with mongoose, but I guess this will still help you and you'd be able to translate it:
db.collection.aggregate([
// Get only the documents where "email" equals "test#test.com" -- REPLACE with params.username
{"$match" : {email : "test#test.com"}},
// Unwind the "inventories" array
{"$unwind" : "$inventories"},
// Get only elements where "inventories.title" equals "activeInventory"
{"$match" : {"inventories.title":"activeInventory"}},
// Unwind the "vehicles" array
{"$unwind" : "$inventories.vehicles"},
// Filter by vehicle ID -- REPLACE with vehicleID
{"$match" : {"inventories.vehicles._id":ObjectId("53440e94c02b3cae81eb0069")}},
// Tidy up the output
{"$project" : {_id:0, vehicle:"$inventories.vehicles"}}
])
This is the output you'll get:
{
"result" : [
{
"vehicle" : {
"_id" : ObjectId("53440e94c02b3cae81eb0069"),
"tags" : [
"vehicle"
],
"details" : [
{
"_id" : ObjectId("53440e94c02b3cae81eb0066"),
"year" : 2007,
"transmission" : "Manual",
"price" : 1000,
"model" : "Firecar",
"mileageReading" : 50000,
"make" : "Bentley",
"interiorColor" : "blue",
"history" : "CarProof",
"exteriorColor" : "blue",
"driveTrain" : "SWD",
"description" : "test vehicle",
"cylinders" : 4,
"mileageType" : "kms"
}
]
}
}
],
"ok" : 1
}
getting the chaining query format ... i dont know how to parse it but, what you are searching for is projection, you should take a look to http://docs.mongodb.org/manual/reference/operator/projection/
it would probably look like this :
user.findOne({email: params.username}, {'inventories.title': {$elemMatch: "activeInventory", 'invertories.vehicle.id': $elemMatch: params.vehicleId}, function(err, result) {
console.log(err);
console.log(result);
})

Resources