DynamoDb: UpdateExpression for updating arrays - arrays

Env: NodeJS service using aws-sdk for interacting with DynamoDb.
Problem: When I set an attribute of an item to an array, it is saved as a string. I expect x: ['1'] but I get x: '1'. I believe this is because I'm incorrectly writing my UpdateExpression/ExpressionAttributeValues.
Situation: I have a table with a field called users. Users is an array of uuids that can be updated. An example of an item in the table:
{ x_name: 'Hello',
owner: '123456',
x_uuid: '1357911',
users: []
}
I want to update the users array with a user uuid. To my update function I pass through:
{ users: ['13245395'] }
The update function (data is { users: ['13245395'] }):
updateX(data, { x_uuid }) {
if (!x_uuid) {
throw new Error('No x_uuid supplied')
}
// new doc client
const docClient = new AWS.DynamoDB.DocumentClient();
var params = {
TableName: this.table,
Key: {
'x_uuid': x_uuid
},
UpdateExpression: "set users = :users",
ExpressionAttributeValues:{
":users": `${data.users}`
},
ReturnValues:"ALL_NEW"
};
return new Promise((resolve, reject) =>
docClient.update(params, (error, x) => {
return error ? reject(error) : resolve(x)
})
)
}
}
The result I get is
{ x_name: 'Hello',
owner: '123456',
x_uuid: '1357911',
users: '13245395'
}
but what I expected was:
{ x_name: 'Hello',
owner: '123456',
x_uuid: '1357911',
users: ['13245395']
}
Previously tried:
wrapping data.users in an array when creating params (works for the first id but the second id added gets appended to the same string as the first so it looks like ['123,456'] instead ['123', '456'].
UpdateExpression: "set users = :users",
ExpressionAttributeValues:{
":users": [${data.users}]
},
Using the "L" and "S" data types to determine that it's an array of strings, i.e.
UpdateExpression: "set users = :users",
ExpressionAttributeValues:{
":users": { "L": { "S":${data.users} } }
},

You are converting your users array to a string
":users": `${data.users}`
Try
":users": data.users
This will set users to the array in data.users

Related

Saving string array in MongoDB - throws me a CastError

I get a CastError when trying to save items to MongoDB using $each . I use FormData to send the array. If I display the array in the backend, everything is correct. I just can't store it in MongoDB
Frontend:
let array = ["ONE","TWO","THREE"]
let data = new FormData();
data.append("tags", JSON.stringify(array));
Backend:
const update = await newArt.updateOne(
{
$push: {
tags: {
$each: [JSON.parse(req.body.tags)],
},
},
},
{ new: true }
);
CastError: Cast to string failed for value "[
'ONE',
'TWO',
'THREE'
]" (type Array) at path "tags"
Schema:
tags: {
type: [String],
require: true
},
You are using 2 arrays; one inside the req.body.tags and one around the JSON.parse, Try removing the external one:
const update = await newArt.updateOne(
{
$push: {
tags: {
$each: JSON.parse(req.body.tags),
},
},
},
{ new: true }
);
See how it works on the playground example

Insert list data over the iteration(map)

Here I am trying to modify my data over the iteration and send some result to API call.
The API Call receives a request with a structured data format which is
{ list: [{ id: "1", name: "Hello" }, ... ] }
Somehow I managed to call the API with single data ( const params in my current code, it only accepts single data).
But now it has to be done with multiple data something like this:
{ list: [{ id: "1", name: "Hello" }, { id: "22", name: "Ed" }, { id: "36", name: "Jason" } ... ] }
Here is my current code
const [table, setTalbe] = useState(..); // assume, we have some table data here
const processNow = () => {
let id = 0;
let name = '';
// if table length is greater than 1, we go for the loop.
if (table.length >= 1) {
table.map(data => {
id = data.userId;
name = data.userName;
});
//insert table data to params, here I want to add whole table data into "list"
//the final result of this list should be something like this
//ex ) list: [{ id: '123', name: 'Josh' }, { id: '125', name: 'Sue' }, { id: '2222', name: 'Paker' } ...],
// but how??
const params: any = {
list: [
{
id: id,
name: name
},
],
};
//send PUT reqeust with params
axios
.put(
'/api/v1/tosent',
params,
)
.then(res => {
console.log('The response', res);
})
.catch(err => {
console.log('The error: ', err);
});
}
};
but I'm stuck with it, please help me to finish this code to work properly.
need your kind advice.
Array.prototype.map returns a new array with the function you pass applied to every element. You should study the MDN documentation on map to understand its use.
Your current code does nothing with the map return value:
table.map(data => {
id = data.userId;
name = data.userName;
});
You probably assumed .map would mutate the data, as in change it in place. Instead, the whole operation returns a new array.
It looks like you want to do:
const list = table.map(data => {
return {
id: data.userId,
name: data.userName
}
});
This is applying a function to every element in the array that will map each element to a new object, matching your question, with an id and name key. Then it looks like you want to pass the returned value of map (which we named list above) to your call:
const params: any = {
list: list
};

Duplicate keys in component v-for loop

v-for duplicate keys issue
This returns a list just fine, but when I modify a user inside the array, like user.role = 'something', it says that I have duplicate keys. The key is set with the user.id which is their firebase key, so duplication shouldn't be possible? The result is that the modified user takes over some other user in the list, so it shows up two times (I haven't tried with more than two users), but if I go back and then re-visit this list, it shows up as expected with the updated data.
V-for loop:
<ManageUsersListItem
v-for="user in sortedUsers"
:key="user.id"
:user="user"
#removeManager="removeManager(user)"
#makeManager="makeManager(user)"
#removeUser="removeUser(user)"
/>
Data:
data() {
return {
users: [],
}
},
firestore() {
return {
users: db.collection('brands').doc(this.brand.id).collection("users")
}
},
Sorting the array:
computed: {
sortedUsers() {
return this.users.sort(function(a,b) {
var c = new Date(a.userAddedOn)
var d = new Date(b.userAddedOn)
return c-d
})
}
},
The method I use to change a users role:
methods: {
makeManager(user) {
this.$firestore.users.doc(user.id).update({
role: 'admin'
})
},
},
to avoid duplicate key use index instead of user.id
<ManageUsersListItem
v-for="(user, index) in sortedUsers"
:key="index"
:user="user"
#removeManager="removeManager(user)"
#makeManager="makeManager(user)"
#removeUser="removeUser(user)"
/>

get array if array contains id mongoose

It is a tricky one. I thought I could use $in, but after querying, it wasn't where I was looking for.
This is my schema
var gameSchema = new mongoose.Schema({
state: {
type: String,
default: "invited"
},
finished: {
type: Boolean,
default: false
},
players: {
type: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'users'
}],
required: true,
},
scores: [scoreSchema],
chat : [chatSchema]
});
The request I'm trying to make is the following, I send a user Id, if the players array contains this Id, return the other id (the array will always have length 2) in the array.
The context is that you can lookup against whom you have played before.
This is what I had, but "players" should be an array and it's not games that I want to return, so
exports.getFriends = function(id, cb){
gameSchema.find({ id: { "$in": "players"} }, function(err, games){
if(err){
return cb(err, null);
}
else{
return cb(null, games);
}
});
};
Can you try this?
exports.getFriends = function(id, cb){
gameSchema.find({ players: id }, function(err, games) {
if (err) {
return cb(err);
}
const players = games.map(game => game.players);
const mergedPlayers = [].concat.apply([], players);
const mappedPlayers = mergedPlayers.map(String); // convert ObjectIds to strings for comparisons.
const idString = String(id);
const filteredPlayers = mappedPlayers.filter(player => player !== idString);
const uniquePlayers = filteredPlayers.filter((player, index, arr) => arr.indexOf(player) === index);
return cb(null, uniquePlayers);
});
};
I'm operating under the assumption that you want an array of the unique player ids that are not the player id you passed in. I kept the vars split apart instead of chaining all of the array methods, in an attempt to improve readability.

Meteor Autoform, collection hooks - How to insert into user profile array after a collection insert?

I'm trying to insert into a user profile array after an autoform inserts into another collection (Meteor.users).
My simple schema array is set up like this -
(within the profile schema)
listings: {
type: [String],
optional: true
},
"listings.$.id": {
type: String,
optional: true
}
And this is my collection-hook method that should be inserting after listing insert.
//Add listing to user collection on submit
Listings.after.insert(function(userId, doc) {
console.log("STUFF");
Meteor.users.update({_id : userId},
{
$push :
{
'profile.listings.$.id' : this._id
}
}
In my eyes, this should work. The form inserts properly without the collection-hook, but now when I submit the form I get this error in my JS console:
Error: After filtering out keys not in the schema, your modifier is now empty(…)
The console.log("stuff") triggers, I see that in the console before the error.
Anybody have any ideas on how to do this?
EDIT - fixed a few things by switching it to :
Listings.after.insert(function(userId, doc) {
console.log("STUFF" + userId + ' ' + this._id);
Meteor.users.update({_id: userId },
{
$set :
{
"profile.listings.$.id" : this._id
}
}
)
});
Now I can't insert into the array because of the $ operator.
Assuming listings is just an array of objects with the id field, you could do:
listings: {
type: [Object],
optional: true
},
"listings.$.id": {
type: String,
optional: true
}
Listings.after.insert(function(userId, doc) {
var id = this._id;
Meteor.users.update({_id: userId }, {
$push : {
"profile.listings" : { id: id }
}
});
});
This changes your listings from an array of Strings to an array of objects - you can't have a property of id on a String. This then allows you to do $push on the profile.listings array with the object in question. If you're literally just storing an ID on listings though, you could simplify this further:
listings: {
type: [String],
optional: true
}
Listings.after.insert(function(userId, doc) {
var id = this._id;
Meteor.users.update({_id: userId }, {
$push : {
"profile.listings" : id
}
});
});
Maybe you're leaving some code out, but with your current schema you don't need anything but an array of strings - no need for an id property.

Resources