updating a JSON array in AWS dynamoDB - arrays

My document looks like this:
{
"data": {
"eventId": "20161029125458-df-d",
"name": "first",
"purpose": "test",
"location": "yokohama",
"dateArray": [],
"attendees": [
{
"attendeeId": "2016102973634-df",
"attendeeName": "lakshman",
"personalizedDateSelection": {}
},
{
"attendeeId": "2016102973634-tyyu",
"attendeeName": "diwaakar",
"personalizedDateSelection": {}
}
]
}
}
Say, I need to update the attendee JSON array with attendeeId: 2016102973634-df. I tried many ways ways using update and condition expression, but no success.
Here is my try:
const params = {
TableName: "event",
Key: {
"eventId": eventId
},
UpdateExpression: "SET attendees[???] = ",
ConditionExpression: attendees.attendeeId = "2016102973634-df",
ExpressionAttributeValues: {
":attendee" : attendeeList
},
ReturnValues: "ALL_NEW"
};
dynamo.update(params, (err, data) => {
if (err) {
return reject(err);
}
console.log(data.Attributes);
});
Could not find any resources for updating an Json in a array.
After #notionquest's comment:
- Have not used any JsonMarshaller. Initially I added the empty array to attendees field like this:
{
"eventId": "20161029125458-df-d",
"name": "first",
"purpose": "test",
"location": "yokohama",
"dateArray": [],
"attendees": []
}
and then When a new attendee comes I add it to the attendees property like this:
const attendee = {
"attendeeName": "user1",
"personalizedDateSelection": {"today": "free"}
}
const attendeeList = [attendee];
const eventId = "20161029125458-df-d";
const params = {
TableName: "event",
Key: {
"eventId": eventId
},
UpdateExpression: "SET attendees = list_append(attendees, :attendee)",
ExpressionAttributeValues: {
":attendee" : attendeeList
},
ReturnValues: "ALL_NEW"
};
dynamo.update(params, (err, data) => {
if (err) {
return reject(err);
}
console.log("in update dynamo");
console.log(data.Attributes);
});
As you have seen in the above snippets, initially I add empty [] array and add a new attendee using the above code. Now, How do I update a specific JSON in an array. If you say that is not possible, what else can I try?
Should I try this :
Get the Full JSON.
Manipulate the JSOn and change the things I want in my nodeJS.
And then update the new JSON to dynamoDB.
But this consumes two calls to dynamoDB which seems to be inefficient.
Would like to know If there is any round way ?

you can store the index of list. while updating the list we can use them. For example ,
{
"data": {
"eventId": "20161029125458-df-d",
"name": "first",
"purpose": "test",
"location": "yokohama",
"dateArray": [],
"attendees": [
{
"index":0,
"attendeeId": "2016102973634-df",
"attendeeName": "lakshman",
"personalizedDateSelection": {}
},
{
"index":1,
"attendeeId": "2016102973634-tyyu",
"attendeeName": "diwaakar",
"personalizedDateSelection": {}
}
]
}
}
const params = {
TableName: "event",
Key: {
"eventId": eventId
},
UpdateExpression: "SET attendees[attendee.index].attendeeName = :value",
ExpressionAttributeValues: {
":value" : {"S":"karthik"}
},
ReturnValues: "ALL_NEW"
};
dynamo.update(params, (err, data) => {
if (err) {
return reject(err);
}
console.log(data.Attributes);
});

An example of an update query:
Data structure (saved in DynamoDB)
{
tenant_id: 'tenant_1',
users: {
user1: {
_id: 'user1',
email_address: 'test_email_1#gmail.com'
},
user2: {
_id: 'user2',
email_address: 'test_email_2#gmail.com'
}
}
}
Data for update (used in the params)
var user = {
email_address: 'updated#gmail.com'
}
Params
var params = {
TableName: 'tenant-Master',
Key: {
"tenant_id": 'tenant_1'
},
UpdateExpression: "set #users.user1 = :value",
ExpressionAttributeNames: {
"#users": "users"
},
ExpressionAttributeValues: {
":value": user,
},
};
Explanation
By switching to a map of maps from an array of maps we can now use UpdateExpression: "set #users.user1 = :value" to update our nested object at the map of users with the id of user1.
NOTE: This method as is will REPLACE the entire map object at users.user1. Some changes will need to be made if you want to keep pre-existing data.

I could not find any answer to query and update the JSON-array. I think this may be AWS profitable motive to not allow those features. If you need to query on a particular ID other than primary key, you need to make a secondary index which is cost effective. This secondary index cost is additional to the dyn
amoDB table cost.
Since, I did not want to pay extra bucks on secondary index, I changed my dynamoDB schema to the following:
{
"data": {
"eventId": "20161029125458-df-d",
"name": "first",
"purpose": "test",
"location": "yokohama",
"dateArray": [],
"attendees": {
"2016102973634-df": {
"attendeeId": "2016102973634-df",
"attendeeName": "lakshman",
"personalizedDateSelection": {}
},
"2016102973777-df": {
"attendeeId": "2016102973777-df",
"attendeeName": "ffff",
"personalizedDateSelection": {}
}
}
}
}
Changing attendees from [] to {}. This allows me the flexibility to query particular attendeeId and change the entire JSON associated with that. Even though, this is a redundant step, I do not want to spend extra bucks on my hobby project.

Related

How to return id with Mongoose Aggregate Lookup with Apollo GraphQL

I have an aggregate function that returns people in a collection:
const getById = ({ id }) => {
return Project.aggregate([
{ $match: { _id: Types.ObjectId(id) } },
{
$lookup: {
from: "members",
localField: "_id",
foreignField: "project_id",
as: "members"
}
},
])
.then(data => {
const [project] = data;
console.log(project) // see below
return {
id: project._id,
...project
};
})
.catch(err => console.log(err));
}
If I return the data from this I get the following:
// Server response
{ _id: 5e2f57b577a8ce59c79e74af,
title: 'ok',
user_id: 5e2dc7961e6b840c315b5a03,
__v: 0,
members:
[ { _id: 5e447683b4f732cc9c4a9531,
name: 'Karl Taylor',
email: 'karl#queuey.dev',
project_id: 5e2f57b577a8ce59c79e74af,
position: 1,
__v: 0 },
{ _id: 5e45be128ed96a5eaef5d13e,
name: 'John Smith',
email: 'john#queuey.dev',
project_id: 5e2f57b577a8ce59c79e74af,
position: 2,
__v: 0 } ] }
However, when I query from the frontend using Apollo GraphQL, the id is null. (But it works on other items, as id is a getter for id but this does not happen on aggregate functions).
What is the best practice to map the id to the correct value? I would normally just use array.map but I feel like that might be overkill if I have too many members (at which point I would probably break this out to do pagination, but that's a different story.)
This is the response from frontend
// Client response
"project": {
"id": "5e2f57b577a8ce59c79e74af",
"title": "ok",
"members": [
{
"id": null, // <-- Notice here is null
"name": "Karl Taylor",
"email": "karl#queuey.dev",
"__typename": "Member"
},
{
"id": null, // <-- Notice here is null
"name": "John Smith",
"email": "john#queuey.dev",
"__typename": "Member"
}
],
"__typename": "Project"
}
This question here is similar, however, I do not believe it is a duplicate because we are querying different data. (the answer does not solve my question.)
I need to be able to return id otherwise cached redirects will not work.
I ran into this problem as well when I try to use aggregations.
simply you can you both ID's(id and _id) but it's not good thing.
what I use is to iterate the data
if the returning data of the query is not array use this
return {
...res._doc,
id: res._id,
}
but the returning data is an array you can add id to the response variable like below using forEach
res.forEach(element => {
element.id = element._id
});
return res;

NodeJS - how to create a JSON file with an array inside in a specified format?

I would like to achieve this JSON structure:
{
"people": [
{
"name": "Peter",
"surname": "Green"
},
{
"name": "Jessica",
"surname": "Morgan"
},
...
}
I am trying to do it like this:
People({}).sort('-createdAt').exec(function(err, people) {
if(err) throw err;
console.log(people);
let data = {}
data['people'] = [];
data.push(people);
res.json(data);
});
When I look at the generated JSON structure, it is in this format:
{
"people": [
[
{
"name": "Peter",
"surname": "Green"
},
...
In the JSON, there are two arrays in the people section. How do I get rid one of the array from there?
Thank you
I assume you're reading from a database, and it looks like it's returning an array of people.
Try not pushing it into a new array?
People({}).sort('-createdAt').exec(function(err, people) {
if(err) throw err;
console.log(people);
let data = {
people: people
};
res.json(data);
});

Push object into array if exists otherwise set object in MongoDB

This is the document I currently have:
{
"_id": "",
"title": "My Watchlist",
"series": [{
"seriesId": 1,
"following": true,
"seasons": []
}, {
"seriesId": 1,
"following": false,
"seasons": []
}]
}
As you can see there are currently 2 objects with the seriesId 1, but with a different following boolean.
If the query matches with _id it should push the new object into series, if within the "series"-array an object with the same "seriesId" already exists it should change the fields within that object, instead of adding a new object.
I currently have the following query:
users.update(
{"_id": req.body.userId},
{
"$push": {
"series": {"seriesId": req.body.seriesId, "following": req.body.following}
}
}, (err, data) => {
if (err)
next(err);
});
If I use $set it does not add the object if it didn't originaly exist yet, and as far as I know you cannot both use $push and $set?
Can this be fixed in any way or do I have to rethink my Schema?
You can use two update query :
if _id is found and seriesId is not in the array, add the new item to the array :
db.series.update({
"_id": req.body.userId,
"series": {
"$not": {
"$elemMatch": {
"seriesId": req.body.seriesId
}
}
}
}, {
$addToSet: {
series: {
"seriesId": req.body.seriesId,
"following": req.body.following,
"seasons": []
}
}
}, { multi: true });
if _id is found and seriesId is found in the array, update the array item :
db.series.update({
"_id": req.body.userId,
"series.seriesId": req.body.seriesId
}, {
$set: {
"series.$.following": req.body.following
}
}, { multi: true });

Pushing to nested array inside specific element of parent array (MongoDB)

I'm rethinking how I want to structure some data which is currently being stored in the Users Collection. Previously, my server would receive messages, find the user document with the right profile.website field, and push an item into the profile.siteMessages array:
{
"_id": "hr9ck5Fis5YuvqCqP",
"profile": {
"website": "localhost",
"siteMessages": [
{
"text": "sddsd",
"createdAt": 1482001227204
},
]
}
}
Id like to change the structure to look something like the following. Instead of storing all messages, of which multiple messages could come from the same user, in a top level array in profile, I would have a profile.siteVisitors field which contains a visitorId and then the array of messages:
{
"_id": "dgfsdfdfsdf",
"emails": [
{
"address": "user2#test.com",
"verified": false
}
],
"profile": {
"website": "localhost",
"siteVisitors:" [
{
"visitorId": "74585242",
"messages": [
{
"text": "A string",
"createdAt": 1482001260853
},
{
"text": "Another string",
"createdAt": 1482001260854
}
]
},
{
"visitorId": "76672242",
"messages": [
{
"text": "A string",
"createdAt": 1482001260855
}
]
}
]
}
}
Keeping with the structure shown above, how would I query for and update the profile.siteVisitiors.messages array? Currently I query and update the Collection using something like the following:
Meteor.users.update(
{'profile.website': url},
{$push: {'profile.siteMessages': msgItem}},
function(err) {
if (err) {
console.log('whoops!' + err)
} else {
//success
}
});
How would I update the newly structured messages array? I would need to match a User documents profile.website field, match a visitorId in the profile.siteVisitors array, and then push a new element into the messages array, but I'm not sure how this would look as a MongoDB query.
EDIT I've hacked together the following which seems to work, but is very ugly. How can I improve this?
Meteor.users.update(
{"profile.website" : "localhost" , "profile.siteVisitors" : {$elemMatch: {"visitorId" : data.chirpVisitorId} } },
{$push: { "profile.siteVisitors.$.messages": {"text" : data.msg, "createdAt" : data.msg.createdAt} } },
function(err, res) {
if (err) {
console.log('failed to push ' + err)
} else {
console.log('success on new push ' + res)
if (res < 1) {
let item = {
"visitorId": data.chirpVisitorId,
"messages": [data.msg]
}
Meteor.users.update(
{"profile.website": "localhost"},
{$push: {'profile.siteVisitors': item}},
function(err, res) {
if (err) {
console.log(err)
} else {
console.log(res + " updated")
}
}
)
}
}
})
You can use the $(update) operator.
Try the following query:
db.collection.update(
{"profile.website" : "localhost" , "profile.siteVisitors" : {$elemMatch: {"visitorId" :'76672242'} } },
{ $push: { "profile.siteVisitors.$.messages": {"text" : <newText>, "createdAt" : <newCreatedDate>} } }
)
Hope this helps.

How to merge into array?

Here is my array from db,
[
{
"_id": "58144e6c0c8d7534f4307269",
"doctor_id": "5813221ace684e2b3f5f0a6d",
"prescription": [
{
"_id": "58144e6c0c8d7534f430726a",
"medicine_id": "10011241343"
}
]
I want to merge with only prescription like this
[
{
"_id": "58144e6c0c8d7534f4307269",
"doctor_id": "5813221ace684e2b3f5f0a6d",
"prescription": [
{
"_id": "58144e6c0c8d7534f430726a",
"medicine_id": "10011241343"
},
"prescription": [
{
"_id": "58144e6c0c8d7534f430726a", // it should be autogenerated
"medicine_id": "10011241344"
}
]
How can I do this?
I have tried like this
var arr = data.presription
arr=req.body// which contains only medicine id
and then by
dbModel.user.findById(data._id, function(err, data) {
data.prescription = arr;
data.save(function(err) {
if (err) {
res.status(202).json({
"success": "0",
});
} else {
res.status(200).json({
"success": "1"
});
}
})
});
But it is saving the same. How can I do this?
Note: Even when I do console.log(arr) only the old data is printing.
So data.presription is an array from the start.
data.presription.push( {
"_id": "58144e6c0c8d7534f430726a",
"medicine_id": "10011241344"
});
This is NOT a merge, what you want to do is to append (or push) items into an existing array.

Resources