access object in array for POST method - javascript

I am creating a POST method via mongo/mongoose:
Department
.create({
name: req.body.name,
link: req.body.link,
state: req.body.state,
requirements: req.body.requirements,
salary: req.body.salary,
description: req.body.description
})
requirements is an object containing other items:
requirements: {
age: 21,
citizenship: "yes",
degree: "4-year"
}
Prior to creating I am checking that all fields were provided:
router.post('/create', (req, res) => {
const requiredFields = ["name", "link", "state", "age", "citizenship", "degree" "salary", "description"];
for(let i=0; i < requiredFields.length; i++){
const field = requiredFields[i];
if(!(field in req.body)){
const message = `Missing \`${field}\` field in request body`;
console.error(message);
return res.status(400).send(message);
};
};
Due to age, citizenship, and degree being object items, I cannot put their string inside the requiredFields. It errors out at Missing age field in request body. Any idea how to check that they were provided in req.body?

To access the nested object you may save your required field as "requirement.age" instead of "age" and then may be use the get function in lodash library. So your condition would become:
`
if (typeof(_.get(req.body, "requirement.age") === 'undefined')) {
//Missing field
}
`
You can try to flatten the req.body structure like:
Object.assign(req.body, req.body.requirement)
// This would make your req.body look like
{
name: 'x',
link: 'y',
requirement: {
age: 1
..
},
age: 1
..
}
Or you may assign it to another variable and then use.
let reqBodyReplica = Object.assign({}, req.body, req.body,requirement)

Related

How to remove data in object from MongoDB document

The object contains the username and category. Categories is another object that contains categories and costs. How can I remove all key-values ​​in a category object? Now in my code, using deleteOne() I find all the fields of the object named "Bob". I am deleting the entire document named "Bob". And I need to clear the categories in this document, for example, deleteOne({ name: "Bob", category }, and have { name: "Bob", category: {} } output to the console
let users = [
{
name: "Bob",
сategories: {
eat: "20$",
entertainment: "100$",
},
},
];
mongoClient.connect(function (err, client) {
if (err) return console.log(err);
const db = client.db("expensesdb");
db.collection("users").deleteOne({ name: "Bob" }, function (err, result) {
console.log(result);
client.close();
});
});
You can use updateOne method to set сategories as empty object using $set or you can use $unset to unset your сategories data.

How to iterate through an array and have it return only the contacts missing a certain object

I've been working on this for over an hour and have no clue what to include...
directions:
-Filtering Data.. The application's search feature allows users to filter contacts in various ways. The interviewer would like you to filter out those who do not have an Instagram account.
Using the given contacts array, save the contacts who do not have an Instagram account to a variable called 'noInstagram.' Don't just hard-code the answer into the variable but rather filter the contacts out of the array programmatically.
let contacts = [
{
name: "Jane Doe",
age: 21,
social_media: {
instagram: "jane.doe",
twitter: "jane_doe"
}
},
{
name: "John Doe",
age: 21,
social_media: {
instagram: "john.doe",
twitter: "john_doe"
}
},
{
name: "Mary Deer",
age: 21,
social_media: {
twitter: "mary_deer"
}
},
{
name: "Gary Deer",
age: 21,
social_media: {
twitter: "gary_deer"
}
}
]
How Im starting off.
let noInstagram = contacts.filter((contact) => {
if ( contact.social_media. ????){
console.log(contact)
}
})
Try this :
let noInstagram = contacts.filter(c => !c.social_media.instagram);
console.log(noInstagram);
The filter method take a callback function as parameter and return an array.
This array is filled with all the element of the inital array that return true after passing throw the callback function.
let noInstagram = contacts.filter(contact => contact.social_media.instagram === undefined);
or
let noInstagram = contacts.filter(contact => !contact.social_media.hasOwnProperty("instagram"));
You are going in a good direcction with a filter, please visit Array.filter docs
let noInstagram = contacts.filter((contact) => {
//Return true if instagram is not found
return !contact.social_media.instagram
})
As you see, you must return true or false inside the filter to be able to filter or not the current object.

How do I connect two collections and get data from an array of object?

I'm a beginner in the backend (and development in general). Stuck for two weeks with one problem. Therefore, I ask for an expert opinion.
I have collections in Mongo - Order and Items. I generated IDs for them through UUID (I don't know if this info is important or not).
The Order object stores an array of items (items[]) with the id and quantity of a specific item:
Order = {
_id: '123123123123123',
items: [
{
_id: '0001',
quantity: 1
},
{
_id: '0002',
quantity: 4
}
]
}
The Items look like this:
Items = [
{
_id: '0001',
title: 'Pizza',
price: 650,
description: 'Some text'
},
{
_id: '0002',
title: 'Pasta',
price: 500,
description: 'Some text'
}
]
Question is: How to get data of Item via id from the array of Items and put it into an Order's Items array with full information about Item and send to the front? Including title, price, and description.
I tried to use for-loops and forEach - but there was no result. A call to a specific item in Order is working (like this Order.items[0]._id), but in a loop (like this Order.items[i]._id) it gives an error that Order.items[i] is undefined.
Controller:
class testController {
async getTestOrder(req, res) {
try {
const id = req.params.id
const order = await Order.findOne({_id: id})
const items = await Item.find({})
for (let i =0; i < order.items.length; i++){
for (let j = 0; j < items.length; j++){
if(order.items[i]._id === items[j]._id){
order.items.push(items[j]) // don't know how to add new data, leaving quantity
}
}
}
res.send(order)
} catch (error) {
console.log(error);
res.status(404).json({message: 'Order was not found'})
}
}
}
As a result, I should get the following response to the front:
Order = {
_id: '123123123123123',
items: [
{
_id: '0001',
quantity: 1,
title: 'Pizza',
price: 650,
description: 'Some text'
},
{
_id: '0002',
quantity: 4,
title: 'Pasta',
price: 500,
description: 'Some text'
}
]
}
You can loop over the Order.items array and find out entry from Items array based on _id. The merge and assign back to Order.items object.
Order.items.forEach(oi => {
let obj = Items.find(i => i._id === oi._id); //find the object
oi = Object.assign(oi,obj); //merge
})
In order to make the search for the right item in items easier I created a little auxiliary object itms (a "hash"), that allows me to access the target item directly:
const order={_id: '123123123123123',items: [{_id: '0001',quantity: 1},{_id: '0002',quantity: 4}]},
items=[{ _id: '0001',title: 'Pizza', price: 650,description: 'Some text'},{_id: '0002',title: 'Pasta',price: 500,description: 'Some text'}],
itms={};items.forEach(o=>itms[o._id]=o);
console.log(order.items.map(o=>({quantity:o.quantity,...itms[o._id]})))
You're changing the items array while iterating over it. If you run your code (for loop) in browser console you'll get 'Uncaught out of memory' error.
Instead of changing items consider to create new items:
const result = { // creating new order object
...order, // getting all order object props, but overwriting items
items: order.items.map(orderItem => { // iterating over order.items
const additionalData = items.find(i => i._id === orderItem._id); // find desired item by _id
return { // returning a new enriched item
...orderItem, // old item props (_id and quantity)
...(additionalData || {}) // found item props (title, price and description); Array.find returns null if it finds nothing, so `{}` is a an insurance that we won't spread null value
};
})
}

Normalizr - is it a way to generate IDs for non-ids entity model?

I'm using normalizr util to process API response based on non-ids model. As I know, typically normalizr works with ids model, but maybe there is a some way to generate ids "on the go"?
My API response example:
```
// input data:
const inputData = {
doctors: [
{
name: Jon,
post: chief
},
{
name: Marta,
post: nurse
},
//....
}
// expected output data:
const outputData = {
entities: {
nameCards : {
uniqueID_0: { id: uniqueID_0, name: Jon, post: uniqueID_3 },
uniqueID_1: { id: uniqueID_1, name: Marta, post: uniqueID_4 }
},
positions: {
uniqueID_3: { id: uniqueID_3, post: chief },
uniqueID_4: { id: uniqueID_4, post: nurse }
}
},
result: uniqueID_0
}
```
P.S.
I heard from someone about generating IDs "by the hood" in normalizr for such cases as my, but I did found such solution.
As mentioned in this issue:
Normalizr is never going to be able to generate unique IDs for you. We
don't do any memoization or anything internally, as that would be
unnecessary for most people.
Your working solution is okay, but will fail if you receive one of
these entities again later from another API endpoint.
My recommendation would be to find something that's constant and
unique on your entities and use that as something to generate unique
IDs from.
And then, as mentioned in the docs, you need to set idAttribute to replace 'id' with another key:
const data = { id_str: '123', url: 'https://twitter.com', user: { id_str: '456', name: 'Jimmy' } };
const user = new schema.Entity('users', {}, { idAttribute: 'id_str' });
const tweet = new schema.Entity('tweets', { user: user }, {
idAttribute: 'id_str',
// Apply everything from entityB over entityA, except for "favorites"
mergeStrategy: (entityA, entityB) => ({
...entityA,
...entityB,
favorites: entityA.favorites
}),
// Remove the URL field from the entity
processStrategy: (entity) => omit(entity, 'url')
});
const normalizedData = normalize(data, tweet);
EDIT
You can always provide unique id's using external lib or by hand:
inputData.doctors = inputData.doctors.map((doc, idx) => ({
...doc,
id: `doctor_${idx}`
}))
Have a processStrategy which is basically a function and in that function assign your id's there, ie. value.id = uuid(). Visit the link below to see an example https://github.com/paularmstrong/normalizr/issues/256

GraphQL mutation that accepts an array of dynamic size and common scalar types in one request

I need to be able to create a user and add it's favourite movies (An array of objects with a reference to the Movies collection and his personal rating for each movie) in a single request.
Something that could look like this (pseudocode)
var exSchema = `
type Mutation {
addUser(
name: String!
favMovies: [{ movie: String! #ref to movies coll
personal_rating: Int! # this is different for every movie
}]
) : User
}
...
`
What is the graphql way of doing this in a single request? I know I can achieve the result with multiple mutations/requests but I would like to do it in a single one.
You can pass an array like this
var MovieSchema = `
type Movie {
name: String
}
input MovieInput {
name: String
}
mutation {
addMovies(movies: [MovieInput]): [Movie]
}
`
Then in your mutation, you can pass an array like
mutation {
addMovies(movies: [{name: 'name1'}, {name: 'name2'}]) {
name
}
}
Haven't tested the code but you get the idea
I came up with this simple solution - NO JSON used. Only one input is used. Hope it will help someone else.
I had to add to this type:
type Option {
id: ID!
status: String!
products: [Product!]!
}
We can add to mutation type and add input as follows:
type Mutation {
createOption(data: [createProductInput!]!): Option!
// other mutation definitions
}
input createProductInput {
id: ID!
name: String!
price: Float!
producer: ID!
status: String
}
Then following resolver could be used:
const resolvers = {
Mutation: {
createOption(parent, args, ctx, info) {
const status = args.data[0].status;
// Below code removes 'status' from all array items not to pollute DB.
// if you query for 'status' after adding option 'null' will be shown.
// But 'status': null should not be added to DB. See result of log below.
args.data.forEach((item) => {
delete item.status
});
console.log('args.data - ', args.data);
const option = {
id: uuidv4(),
status: status, // or if using babel status,
products: args.data
}
options.push(option)
return option
},
// other mutation resolvers
}
Now you can use this to add an option (STATUS is taken from first item in the array - it is nullable):
mutation{
createOption(data:
[{
id: "prodB",
name: "componentB",
price: 20,
producer: "e4",
status: "CANCELLED"
},
{
id: "prodD",
name: "componentD",
price: 15,
producer: "e5"
}
]
) {
id
status
products{
name
price
}
}
}
Produces:
{
"data": {
"createOption": {
"id": "d12ef60f-21a8-41f3-825d-5762630acdb4",
"status": "CANCELLED",
"products": [
{
"name": "componentB",
"price": 20,
},
{
"name": "componentD",
"price": 15,
}
]
}
}
}
No need to say that to get above result you need to add:
type Query {
products(query: String): [Product!]!
// others
}
type Product {
id: ID!
name: String!
price: Float!
producer: Company!
status: String
}
I know it is not the best way, but I did not find a way of doing it in documentation.
I ended up manually parsing the correct schema, since JavaScript Arrays and JSON.stringify strings were not accepted as graphQL schema format.
const id = 5;
const title = 'Title test';
let formattedAttachments = '';
attachments.map(attachment => {
formattedAttachments += `{ id: ${attachment.id}, short_id: "${attachment.shortid}" }`;
// { id: 1, short_id: "abcxyz" }{ id: 2, short_id: "bcdqrs" }
});
// Query
const query = `
mutation {
addChallengeReply(
challengeId: ${id},
title: "${title}",
attachments: [${formattedAttachments}]
) {
id
title
description
}
}
`;
What i understand by your requirement is that if you have the following code
const user = {
name:"Rohit",
age:27,
marks: [10,15],
subjects:[
{name:"maths"},
{name:"science"}
]
};
const query = `mutation {
createUser(user:${user}) {
name
}
}`
you must be getting something like
"mutation {
createUser(user:[object Object]) {
name
}
}"
instead of the expected
"mutation {
createUser(user:{
name: "Rohit" ,
age: 27 ,
marks: [10 ,15 ] ,
subjects: [
{name: "maths" } ,
{name: "science" }
]
}) {
name
}
}"
If this is what you wanted to achieve, then gqlast is a nice tag function which you can use to get the expected result
Simply grab the js file from here and use it as:
const user = {
name:"Rohit",
age:27,
marks: [10,15],
subjects:[
{name:"maths"},
{name:"science"}
]
};
const query = gqlast`mutation {
createUser(user:${user}) {
name
}
}`
The result stored in the variable query will be :
"mutation {
createUser(user:{
name: "Rohit" ,
age: 27 ,
marks: [10 ,15 ] ,
subjects: [
{name: "maths" } ,
{name: "science" }
]
}) {
name
}
}"
Pass them as JSON strings. That's what I do.
For those of you who don't need to pass in an array for one request, and are open to the idea of making a request for every mutation. (I am using Vue3, compisition Api, but React and Angular developers still can understand this).
You cannot for loop the mutation like this:
function createProject() {
for (let i = 0; i < state.arrOfItems.length; i++) {
const { mutate: addImplementation } = useMutation(
post_dataToServer,
() => ({
variables: {
implementation_type_id: state.arrOfItems[i],
sow_id: state.newSowId,
},
})
);
addImplementation();
}
}
this will give you an error, because the mutation must be in the setup().
(here is the error you will recieve: https://github.com/vuejs/vue-apollo/issues/888)
Instead create a child component, and map the array in the parent.
in Parent.vue
<div v-for="(card, id) in state.arrOfItems">
<ChildComponent
:id="id"
:card="card"
/>
</div>
in ChildComponent.vue
recieve props and:
const { mutate: addImplementation } = useMutation(
post_dataToServer,
() => ({
variables: {
implementation_id: props.arrOfItems,
id: props.id,
},
})
);

Categories

Resources