Saving string array in MongoDB - throws me a CastError - reactjs

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

Related

Error: Response is not a function, when trying to find if the name exists

So I'm using mongodb to fetch some data from the database.
The issue is when I try to check for something in an array
Here is what the structure looks like:
Example array structure
{ // ...
likedPeople: [
{
name: "foo"
image: "test",
},
{
name: "bar",
image: "baz",
}
]
}
This is the array i get Back.
So when i try to find if it includes a certain value,
eg:
const displayName = "foo";
console.log(
likedPeople.map((likedPerson) => {
return likedPerson.name === displayName; // Output: [true, false]
})
);
But then If i again try to do some other method on it like map() or includes(), It breaks the setup:
const response = likedPerson.name === displayName; // Output: [true, false]
response.map((res) => console.log(res)); // Output: ERROR: response.map() is not a function
But the fact is that I am getting an array with the values, so what am I even doing wrong here?
I tried adding an optional chaining response?.map() but still it gave me the same error.
Also the includes() method also returns me the same response.includes is not a function error.
Can anyone help?
Use the some method to check the name exists in likedPeople :
const likedPeople = [
{
name: "foo",
image: "test",
},
{
name: "bar",
image: "baz",
}
];
const displayName = "foo";
const isExist = likedPeople.some(people => people.name === displayName);
console.log(isExist)

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
};

DynamoDb: UpdateExpression for updating 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

Add array values into MongoDB where element is not in array

In MongoDB, this is the simplified structure of my account document:
{
"_id" : ObjectId("5a70a60ca7fbc476caea5e59"),
"templates" : [
{
"name" : "Password Reset",
"content" : "AAAAAAAA"
},
{
"name" : "Welcome Message",
"content" : "BBBBBB"
}
]
}
There's a similar default_templates collection
let accnt = await Account.findOne({ _id: req.account._id }, { templates: 1 });
let defaults = await DefaultTemplate.find({}).lean();
My goal is to find the missing templates under account and grab them from defaults. (a) I need to upsert templates if it doesn't exist in an account and (b) I don't want to update a template if it already exists in an account.
I've tried the following:
if (!accnt.templates || accnt.templates.length < defaults.length) {
const accountTemplates = _.filter(accnt.templates, 'name');
const templateNames = _.map(accountTemplates, 'name');
Account.update({ _id: req.account._id, 'templates.name' : { $nin: templateNames } },
{ '$push': { 'templates': { '$each' : defaults } } }, { 'upsert' : true },
function(err, result) {
Logger.error('error %o', err);
Logger.debug('result %o', result);
}
);
}
This succeeds at the upsert but it will enter all default templates even if there's a matching name in templateNames. I've verified that templateNames array is correct and I've also tried using $addToSet instead of $push, so I must not understand Mongo subdoc queries.
Any ideas on what I'm doing wrong?
Edit: I've gotten this to work by simply removing elements from the defaults array before updating, but I'd still like to know how this could be accomplished with Mongoose.
You can try with bulkWrite operation in mongodb
Account.bulkWrite(
req.body.accountTemplates.map((data) =>
({
updateOne: {
filter: { _id: req.account._id, 'templates.name' : { $ne: data.name } },
update: { $push: { templates: { $each : data } } },
upsert : true
}
})
)
})

Push to array within subdocument in mongoose

I'd like to push to an array that's within a subdocument in Mongoose/MongoDB.
Here is the schema:
var UsersSchema = new mongoose.Schema({
user: String,
stream: String,
author: String,
tags: Array,
thumb: Number,
added: Number
})
var ContentSchema = new mongoose.Schema( {
title: String,
url: String,
description: String,
text: String,
users: [ UsersSchema ]
})
I'd like to push an array into the UserSchema.tags array for a specific users sub-document.
I have tried this and several variations: https://stackoverflow.com/a/19901207/2183008
By default, my front-end Angular app is sending the tags as an array of objects. So it's
[ { 'text': TAG_STRING_HERE } ]
or
[ { 'text': TAG_STRING_HERE }, { 'text': TAG2_STRING_HERE } ]
But I've also tried just using and array of strings, which I'm fine doing if objects are a problem for some reason.
I have tried this:
var tags = req.body.tags,
contentId = mongoose.Types.ObjectId( req.body.id )
Content.update(
{ 'users._id': contentId },
{ $push: { 'users.tags': { $each: tags } } }
).exec()
.then( function ( result ) {
console.log( result )
resolve( result )
}, function ( error ) {
if ( error ) return reject( error )
})
Which gives me this error:
{ [MongoError: cannot use the part (users of users.tags) to traverse the element ({users: [ { user: "54f6688c55407c0300b883f2", added: 1428080209443.0, stream: "watch", _id: ObjectId('551ec65125927f36cf4c04e9'), tags: [] }, { user: "54f6688c55407c0300b883f2", added: 1428080222696.0, stream: "listen", _id: ObjectId('551ec65e25927f36cf4c04ea'), tags: [] } ]})]
name: 'MongoError',
code: 16837,
err: 'cannot use the part (users of users.tags) to traverse the element ({users: [ { user: "54f6688c55407c0300b883f2", added: 1428080209443.0, stream: "watch", _id: ObjectId(\'551ec65125927f36cf4c04e9\'), tags: [] }, { user: "54f6688c55407c0300b883f2", added: 1428080222696.0, stream: "listen", _id: ObjectId(\'551ec65e25927f36cf4c04ea\'), tags: [] } ]})' }
The solution is below. Note this is using Q and Express. The part of note is the 'users.$.tags. I thought I had tried this but I guess not! I also used $pushAll instead, but $each might also work. My tags is always an array.
var tags = req.body.tags,
contentId = mongoose.Types.ObjectId( req.body.id )
console.log( tags )
Content.update(
{ 'users._id': contentId },
{ $pushAll: { 'users.$.tags': tags } }
).exec()
.then( function ( result ) {
console.log( result )
resolve( result )
}, function ( error ) {
if ( error ) return reject( error )
})

Resources