I am stuck with this issue for a few days now. I am not able to format the values according to the given format.
I have 2 Array objects.
var name = ["sam","Anthony"];
var age = ["4","10"];
The name and age array may contain more values in it. So it means that the 2 arrays above are dynamic.
I need to extract values from these arrays and add them in the Item section as shown in the following code. However, I am not able to hardcode the array content as this array is dynamic in size. It may contain more student records in it. In that case How am I able to construct the following params variable after populating the values of the name and age arrays ?
var params = {
RequestItems: {
"Student": [
{
PutRequest: {
Item: {
"name": "sam",
"age": "4"
}
}
},
{
PutRequest: {
Item: {
"name": "Anthony",
"age": "10"
}
}
}
]
}
};
Use the map function:
var names = ["sam", "Anthony"];
var age = ["4", "10"];
var params = {
RequestItems: {
"Student": names.map(function(c, index) {
return {
PutRequest: {
Item: {
"name": c,
"age": age[index]
}
}
};
})
}
};
alert(JSON.stringify(params, null, 4));
Related
This question already has answers here:
How to update a value in a json file and save it through node.js
(8 answers)
Closed 2 years ago.
I am new to Nodejs and JSON manipulations. I have a jSON that looks like
"ent": [
{
"employee": [
{
"emp_name": "",
"column": "employee",
"emp_id": 123456,
"first name": "Joe",
"last name": "Bloggs",
"email": "",
"ldapid":
} ,
{
"emp_name": "",
"column": "employee",
"emp_id": 123456,
"first name": "Foo",
"last name": "Bars",
"email": "",
"ldapid":
}
]
}
]
I need to fill the email, ldapid and emp_name based on the firstname and last name
The desired output is
"ent": [
{
"employee": [
{
"emp_name": "Joe Bloggs",
"column": "employee",
"emp_id": 123456,
"first name": "Joe",
"last name": "Bloggs",
"email": "jbloggs#mycompemail.com",
"ldapid": "jbloggs"
} ,
{
"emp_name": "Foo Bars",
"column": "employee",
"emp_id": 567891,
"first name": "Foo",
"last name": "Bars",
"email": "fbars#mycompemail.com",
"ldapid": "fbars"
}
]
}
]
Since I am super new to the nodeJS world , I am making some initial steps to get to where I want..
The following is what I have done..
EDITED my POST
Hi All, Thanks for all your responses.
I was hoping to get an answer that did something similar to the below. this may not be a code with best practices, but does what I want, may be experts in this group can make it better.
const fs = require('fs');
/** Method to start
*
*
*/
const main = async () => {
const myJSONObject = require('./people.json');
try {
for (var i = 0; i < myJSONObject.entities.length; i++) {
var entity = myJSONObject.entities[i];
if (entity.People) {
for (var j = 0; j < entity.People.length; j++) {
var people = entity.People[j];
var fn = people["first name"];
var ln = people["last name"];
var email = `${fn.substring(0, 3)}${ln.substring(0, 5)}#mycompmail.com`;
var ldapid = `${fn.substring(0, 3)}${ln.substring(0, 5)}`;
myJSONObject.entities[i].People[j]["email"] = email.toLowerCase();
myJSONObject.entities[i].People[j]["ldap id"] = ldapid.toLowerCase();
myJSONObject.entities[i].People[j]["preferred first name"] = fn;
myJSONObject.entities[i].People[j]["preferred last name"] = ln;
// console.log(`${fn}.${ln}`)
}
}
}
fs.writeFileSync('./new_people.json', JSON.stringify(myJSONObject, 0, 4));
}
catch (error) {
console.log(error);
}
};
(async () => {
await main();
})();
Any help in this is highly appreciated.
Vakichak
From your code snipped I assume, that the JSON is a string in a file.
So the first step you need to do is to import the file contents into a variable. You can do that with fs.readFileSync(). Now you have the string in a variable.
Next you need to do is to convert the string into an object. You can do that with JSON.parse(). Now you have an object that you can manipulate.
To write it the object back into a file, you can use JSON.stringify() to make it a string again and then fs.writeFileSync() to write it to the file.
Full script:
const jsonString = fs.readFileSync('./people.json')
const jsonObject = JSON.parse(jsonString)
// do stuff with jsonObject that is written into newJsonObject
const newJsonString = JSON.stringify(newJsonObject)
fs.writeFileSync('./new_people.json', newJsonObject)
Note: there's also async functions for writing and reading files. The sync functions are okay if you load a config or something like this at the beginning of a script. If you read/write many files during runtime, you should use the async functions.
I have to deal with objects of the following type in a NodeJS app (using mongodb driver):
data_test = {
"id": "105-20090412",
"date": new Date('2020-09-04T14:00:00.000Z'),
"station": {
"name": "AQ105",
"loc": {
"type": "Point",
"coordinates": [14.324498, 40.821930]
},
"properties": {}
},
"samples": [{
"t": new Date('2020-09-04T14:14:00.000Z'),
"data": {
//"temp_celsius": 31.81,
//"humRelPercent": 39,
"press_mBar": 1021.12,
"PM10": 200
}
}]
}
I receive every 2 minutes data as above.
I want to:
If the data received has an id not yet present on MongoDB do an insert
If the data received has a sample object with a Date (t property) yet present then add properties to this one (for example readings of different sensors)
If the data received has a sample object with a Date (t property) not yet present in samples array, then add this new one
I would like to do what described above with the minor count possible of round-trips to the MongoDB server.
I hope to have been clear enough.
Any suggestion?
Thanks in advance.
Here's my suggestion, this is not the correct answer. You will need to fiddle with the query portion. The query below should work for 1 & 3, for 2 you will have to play around.
db.collection.updateOne(
{ "id" : "105-20090412", "samples.t": <Date> },
{ $push: { "samples" : <sample> } },
{ $setOnInsert: { station: <station> } },
{ upsert: true }
);
References:
https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/
https://docs.mongodb.com/manual/reference/operator/update/setOnInsert/#up._S_setOnInsert
https://docs.mongodb.com/manual/reference/operator/update/push/
I finally came to the following solution, perhaps not the most efficient one:
try {
const db = client.db(dbName);
const collection = db.collection(collectionName);
// retrive id, station, date and samplesToAdd as separate objects
let {
id,
...dataToInsert
} = data
//id = new ObjectID(id)
const queryBy_id = {
_id: id
}
// first check if doc exists
let res_query = await collection.findOne(queryBy_id)
// if doc does not exists then insert a new one
if (!res_query) {
res_insert = await collection.insertOne({
_id: id,
...dataToInsert
})
return res_insert;
} else {
// retrive samples from initial query
let current_samples = res_query.samples
// check if sample in dataToInsert yet exists
// use getTime to correctly compare dates
let idx = current_samples.findIndex(x => x.t.getTime() == dataToInsert.samples[0].t.getTime())
if (idx >= 0) {
// find index of sample to update
let current_t = current_samples[idx].t
// merge data yet stored with new one
current_samples.data = {
...current_samples[idx].data,
...dataToInsert.samples[0].data
}
let resUpdateSample = await collection.updateOne({
_id: id,
'samples.t': current_t
}, {
$set: {
'samples.$.data': current_samples.data
}
})
return resUpdateSample
} else {
// add data to samples array
let resAddToSamples = await collection.updateOne({
_id: id
}, {
$push: {
samples: dataToInsert.samples[0]
}
})
return resAddToSamples
}
}
} catch (err) {
logger.error(err);
}
How can I improve it?
Thanks.
How can I getthe data that has email as abc#gmail.com in mongoDB?I don't know the Key Name and I want to iterate through all the data.
I have data like this:
{
"_id":"5c0a1589a5a41b2ae707317b",
"test1":{
"email":"abc#gmail.com",
"phoneNo":"123456897",
"endpointId":"test1"
}
}
{
"_id":"5c0a1989a5a41b2ae807317b",
"test2":{
"email":"abc#gmail.com",
"phoneNo":"123456897",
"endpointId":"test2"
}
}
{
"_id":"5c0a1989a5a41b2ae807317b",
"test2":{
"email":"pqr#gmail.com",
"phoneNo":"123456897",
"endpointId":"test3"
}
}
But the object key is not known at the time of searching. I want to iterate through all the data and get matched data that has specific email.
If I know the key name like test1,test2 etc then I can use find({test1:{...}}) but Here I don't know the key value.
So, how can I do that?
You can use below aggregation using $objectToArray in mongodb 3.4 and above
db.collection.aggregate([
{ "$addFields": {
"field": { "$objectToArray": "$$ROOT" }
}},
{ "$match": { "field.v.email": "abc#gmail.com" }},
{ "$project": { "field": 0 }}
])
I am assuming you get the objects in array type.
I made a method named findObject. This method will take the object array and the desired email.
Finally, return the first object, that matched with the email.
const data = [{
"_id":"5c0a1589a5a41b2ae707317b",
"test1":{
"email": "abc#gmail.com",
"phoneNo": "123456897",
"endpointId":"test1"
}
},
{
"_id":"5c0a1989a5a41b2ae807317b",
"test2":{
"email": "abc#gmail.com",
"phoneNo": "123456897",
"endpointId":"test2"
}
},
{
"_id":"5c0a1989a5a41b2ae807317b",
"test2":{
"email": "pqr#gmail.com",
"phoneNo": "123456897",
"endpointId": "test3"
}
}];
const findObject = (data, email) => {
for (let index=0; index<data.length; index++) {
const currentData = data[index];
for (let property in currentData) {
if (property != '_id' && currentData[property].email == email) {
return currentData;
}
}
}
return null;
}
let desiredObject;
const desiredEmail = 'abc#gmail.com';
desiredObject = findObject(data, desiredEmail);
console.log(desiredObject);
And the output will be
{ _id: '5c0a1589a5a41b2ae707317b',
test1:
{ email: 'abc#gmail.com',
phoneNo: '123456897',
endpointId: 'test1' } }
I think you can't do query on totally unknown field! if you could change your schema see here for more info, also you could write script to migrate to a new DB with new schema:
// new doc instance
{
"_id":"5c0a1589a5a41b2ae707317b",
"obj": {
"name": "test1"
"email":"abc#gmail.com"
"phoneNo":"123456897",
"endpointId":"test1"
}
},
{
"_id":"5c0a1989a5a41b2ae807317b",
"obj": {
"name": "test2"
"email":"abc#gmail.com"
"phoneNo":"123456897",
"endpointId":"test2"
}
},
{
"_id":"5c0a1989a5a41b2ae807317b",
"obj": {
"name": "test3"
"email":"pqr#gmail.com"
"phoneNo":"123456897",
"endpointId":"test3"
}
}
otherwise, check this may works correctly. if all of them is not effective so make a query to get all of your data as an Array and use filter method on it:
Model.find({}, (err, docs) => {
const result = docs.filter((doc) => {
for (key in doc) {
if (doc[key].email === 'abc#gmail.com')
return doc;
}
});
console.log(result);
});
I have the following code and I'm trying to do two things. First I want to have my query have one condition where it finds the 'originator' value in a doc, but the second par of that is not to update if is also finds 'owner_id' is the same as originator.
The second part of what I'm trying to do is only set/update a field is it is being passed in. Can I use a ternary statement, something like below???
Contacts.update(
{
'originator': profile.owner_id,
'owner_id': !profile.owner_id
},
{
$set: {
(phoneNumber) ? ('shared.phones.$.phone_number': phoneNumber):null,
(emailAddress) ? ('shared.emails.$.email_address': emailAddress):null
}
},
{
'multi': true
},
function(err) {
err === null ? console.log('No errors phone updated for contacts.shared') : console.log('Error: ', err);
}
)
You mean something like this:
var updateBlock = {};
if (phoneNumber)
updateBlock['shared.phones.$.phone_number'] = phoneNumber;
if (emailAddress)
updateBlock['shared.email.$.email_address'] = emailAddress;
Contacts.updateMany(
{
"originator": profile.owner_id
"owner_id": { "$ne": profile.owner_id }
},
{ "$set": updateBlock },
function(err, numAffected) {
// work with callback
}
)
That addresses your two "main" misconceptions here in that the "inequality" in the query condition requires the $ne operator and not the ! JavaScript expression. MongoDB does not use JavaScript expressions here for the query conditions.
The second "main" misconception is the construction of the "update block" with conditional keys. This is by contrast a "JavaScript Object" which you construct separately in order to specify only the keys you wish to effect.
However there is STILL A PROBLEM in that you want to use the positional $ operator. Presuming you actually have "arrays" in the document like this:
{
"originator": "Bill",
"owner_id": "Ted",
"shared": {
"phones": [ "5555 5555", "4444 4444" ],
"email": [ "bill#stalyns.org", "bill#example.com" ]
}
}
Then your "two-fold" new issue is that:
You must specify a query condition that matches the array element "in the query block" in order to obtain the "matched position" at which to update.
You can only return ONE matched array index via use of the positional $ operator and NOT TWO as would be inherent to updating such a document.
For those reasons ( and others ) it is strongly discouraged to have "multiple arrays" within a single document. The far better approach is to use a "singular" array, and use properties to denote what "type" of entry the list item actually contains:
{
"originator": "Bill",
"owner_id": "Ted",
"shared": [
{ "type": "phone", "value": "5555 5555" },
{ "type": "phone", "value": "4444 4444" },
{ "type": "email", "value": "bill#stalyns.org" },
{ "type": "email", "value": "bill#example.com" }
]
}
In this way you can actually address the "matched" element in which to update:
// phoneNumberMatch = "4444 4444";
// phoneNumber = "7777 7777";
// emailAddress = null; // don't want this one
// emailAddressMatch = null; // or this one
// profile = { owner_id: "Bill" };
var query = {
"originator": profile.owner_id,
"owner_id": { "$ne": profile.owner_id },
"shared": {
"$elemMatch": {
"type": (phoneNumber) ? "phone" : "email",
"value": (phoneNumber) ? phoneNumberMatch : emailAddressMatch
}
}
};
var updateBlock = {
"$set": {
"shared.$.value": (phoneNumber) ? phoneNumber : emailAddress
}
};
Contacts.updateMany(query, updateBlock, function(err, numAffected) {
// work with callback
})
In such a case and with a "binary" choice then you "can" use ternary conditions in construction since you are not reliant on "naming keys" within the construction.
If you want "either, or indeed both" supplied values in combination then you need a bit more advanced statement:
// phoneNumberMatch = "5555 5555";
// phoneNumber = "7777 7777";
// emailAddress = "bill#nomail.com";
// emailAddressMatch = "bill#example.com";
// profile = { owner_id: "Bill" };
var query = {
"originator": profile.owner_id,
"owner_id": { "$ne": profile.owner_id },
"$or": []
};
var updateBlock = { "$set": {} };
var arrayFilters = [];
if (phoneNumber) {
// Add $or condition for document match
query.$or.push(
{
"shared.type": "phone",
"shared.value": phoneNumberMatch
}
);
// Add update statement with named identifier
updateBlock.$set['shared.$[phone].value'] = phoneNumber;
// Add filter condition for named identifier
arrayFilters.push({
"phone.type": "phone",
"phone.value": phoneNumberMatch
})
}
if (emailAddress) {
// Add $or condition for document match
query.$or.push(
{
"shared.type": "email",
"shared.value": emailAddressMatch
}
);
// Add update statement with named identifier
updateBlock.$set['shared.$[email].value'] = emailAddress;
// Add filter condition for named identifier
arrayFilters.push({
"email.type": "email",
"email.value": emailAddressMatch
})
}
Contacts.updateMany(query, updateBlock, arrayFilters, function(err, numAffected) {
// work with callback
})
Noting of course here that the positional filtered $[<identifier>] syntax from MongoDB 3.6 and upwards is required in order to effect multiple array elements within a single update statement.
Much the same applies to the "original" structure I first described using "multiple" arrays in the documents instead of named properties on a "singular" array as the above examples deal with:
var query = {
"originator": "Bill",
"owner_id": { "$ne": "Bill" },
"$or": []
};
var updateBlock = { "$set": {} };
var arrayFilters = [];
if (phoneNumber) {
query.$or.push({
"shared.phones": phoneNumberMatch
});
updateBlock.$set['shared.phones.$[phone]'] = phoneNumber;
arrayFilters.push({
"phone": phoneNumberMatch
});
}
if (emailAddress) {
query.$or.push({
"shared.email": emailAddressMatch
});
updateBlock.$set['shared.email.$[email]'] = emailAddress;
arrayFilters.push({
"email": emailAddressMatch
});
}
Contacts.updateMany(query, updateBlock, arrayFilters, function(err, numAffected) {
// work with callback
})
Of course if you don't even have arrays at all ( the question posted lacks any example document ) then positional matches are not even needed in any form, but you do however still "conditionally" construct JavaScript object "keys" via construction code blocks. You cannot "conditionally" specify a "key" in JSON-like notation.
Here is a simple example with switch condition in some variation like this:
const transfоrmFunc = function(val) {
if(val){
// do whatever you want with the value here
return val;
}
return null;
};
AnyModel.updateMany({ fieldId: { $in: ["MATCH1", "MATCH2"] } }, [
{
$set: {
field2: {
$switch: {
branches: [
{
case: { $eq: ["$fieldId", "MATCH1"] },
then: transfоrmFunc("$field3")
},
{
case: { $eq: ["$fieldId", "MATCH2"] },
then: transfоrmFunc("$field4.subfield")
}
]
}
}
}
}
]);
That way you work with both record data and outside data and update conditionally. You can modify query conditions as pleased. Plus it's really fast.
So I'm working with NodeJS and MongoDB, and I'm making an endpoint that lets clients update their user profiles with several optional data fields. So, one of the update queries can look like this:
{
name: { givenName: 'first' },
about: 'whatever',
auth: { password: 'hashedPW' }
}
The Mongoose API docs state the following info about findByIdAndUpdate: All top level update keys which are not atomic operation names are treated as set operations.
So the top level key, about, works fine to update. However, the nested keys, name and auth are overwritten by the update values, rather than just having the values set.
Now I could go through and manually change each of the fields to be a $set key, but there are a lot of different fields, so to do this would be pretty annoying. Is there an easy way to apply the $set rule to the subdocuments as well? i.e. transform the statement into this, with a Mongoose option or something:
{
$set : { name: { givenName: 'first' } },
$set : { about: 'whatever' },
$set : { auth: { password: 'hashedPW' } }
}
You basically need to tranform your input object into "dot notation" form in order to avoid overwring other possible sub-keys in your update. This is quite simple really:
var obj = {
name: { givenName: 'first' },
about: 'whatever',
auth: { password: 'hashedPW' }
};
var target = {};
function dotNotate(obj,prefix) {
prefix = (typeof(prefix) === 'undefined') ? "" : prefix;
Object.keys(obj).forEach(function(key) {
if ( typeof(obj[key]) === "object" ) {
dotNotate(obj[key],key + ".")
} else {
target[prefix + key] = obj[key];
}
});
}
dotNotate(obj);
Now the target object looks like this:
{
"name.givenName" : "first",
"about" : "whatever",
"auth.password" : "hashedPW"
}
So the update block of your statement is merely written as:
{ "$set": target }
For reference, the dotNotate() function can be a bit more refined and self contained. Also including shorter default assignments as valid input would generally be considered be "truthy". Also the "prefix" should have been pre-pended on each call to make this work at arbitrary depth:
function dotNotate(obj,target,prefix) {
target = target || {},
prefix = prefix || "";
Object.keys(obj).forEach(function(key) {
if ( typeof(obj[key]) === "object" ) {
dotNotate(obj[key],target,prefix + key + ".");
} else {
return target[prefix + key] = obj[key];
}
});
return target;
}
Then you can use either inline:
var update = { "$set": dotNotate(obj) };
Or pass in a defined object like this if you prefer:
var update = { "$set": {} };
dotNotate(obj,update["$set"]);
With the same results.
Also fine for arrays and nested depth:
{
"things" : [
{
"a" : 1,
"b" : 2
},
{
"a" : 3,
"b" : 4
}
],
"bool" : false
}
With output:
{
"things.0.a" : 1,
"things.0.b" : 2,
"things.1.a" : 3,
"things.1.b" : 4,
"bool" : false
}