Good morning everyone!
I'm developing an application with Node.js and Angular, and I'm getting a little stuck lately.
I need to append a custom key -> value to an existing query result collection.
This is the function I have:
exports.findAll = (req, res) => {
Project.findAll({
include: [{
all: true,
include: [{
all: true
}]
}]
})
.then(data => {
data.forEach(
(project) => {
// <-------- HERE GOES THE APPEND
}
);
res.send(data);
})
.catch(err => {
res.status(500).send({
message: err.message || "Error retrieving projects"
});
});
};
Description:
After getting the result from the model query, I iterate over each result (aka Project).
Then, what I need to do is append a key -> value pair to that very Project.
By now, I'd like to do something like this:
exports.findAll = (req, res) => {
Project.findAll({
include: [{
all: true,
include: [{
all: true
}]
}]
})
.then(data => {
data.forEach(
(project) => {
project.cat = "miaw";
}
);
res.send(data);
})
.catch(err => {
res.status(500).send({
message: err.message || "Error retrieving projects"
});
});
};
This try hasn't made any changes in my JSON collection, and I don't know how to accomplish it neither.
Could someone give me some help?
I've searched everywhere but couldn't find anything useful.
Thank you so much!
You just need to get plain objects from model instances and then you can add whatever you need to:
const projects = data.map(x => x.get({ plain: true }))
projects.forEach(
(project) => {
project.cat = "miaw";
}
);
res.send(projects);
Related
My problem is how can I bring my data structure into the query? I want to be able to search everything via one query. How can I implement this?
I tried to enter the query so => ?basicData.tasks.title=Fleetmanagement. With it I can find all the data that fleet management has in it. But now I want to search with two titles.
Example:
?basicData.tasks.title=Fleetmanagement&basicData.tasks.title=OCPP Backend
But it doesn't work. How can i implement that right?
This is my data structure:
"basicData": {
"tasks":[
{"title": "Fleetmanagement"},
{"title": "OCPP Backend"}
]
}
My Code:
export const allCompanyData = (req: Request, res: Response) => {
if (req.query) {
Company.find(req.query).then(docs => {
logger.info(`Query works ${req.query}`);
res.json({
query: req.query
})
res.status(200).json(docs);
}).catch(err => {
logger.error(`Error ${err}`);
res.status(400).json(`Error ${err}`)
})
} else {
Company.find()
.then((items: any) => {
logger.info("Successful finding");
res.status(200).json(items);
})
.catch((err: any) => {
logger.error(`Error ${err}`);
res.status(400).json(`Error ${err}`);
});
}
};
You append [] to the param name on the client side to indicate that you are providing an array:
?basicData.tasks.title[]=Fleetmanagement&basicData.tasks.title[]=OCPP%20Backend
Then req.query will be:
{ 'basicData.tasks.title': ['Fleetmanagement', 'OCPP Backend'] }
i'm not really good at English and beginner of coding. i use javascript and my OS is Mac.
Anyway, i wrote row query in MysqlWorkbench and now, i want to write it in VScode to sequelize.
select reservation_datetime
from LectureReservation
Inner Join Lecture
On LectureReservation.lecture_id = Lecture.id
Where Lecture.mentor_id = 1
this is my query and
module.exports = {
get: (req, res) => {
if (req.params.id) {
LectureReservation
.findOne({
include: [
{
model: Lecture,
where: {
Lecture_id: Lecture.id,
},
},
],
attributes: ['reservation_datetime'],
where: {
mentor_id: req.params.id,
},
})
.then((result) => {
if (result) {
res.status(200).json({ message: 'OK!' });
} else {
res.status(409).json({ message: 'Wrong Access' });
}
})
.catch((err) => {
res.status(500).send(err);
});
}
},
};
this is my sequelize code. and
this is my err code in postman..
Result: How can I edit my sequelize code to fix my postman err..?
It seems this is not a sequelize related error. The error code is 404. Which means the route is not available. Please check your routes.
Also you don't need to specify the include condition if your sequelize models associated properly. You can just write
include: [
{
model: Lecture
},
],
I'm quite newb to mongodb and mongoose, so I ask you to help me.
I've an API, which works and now I want to extend it with filtering for given params.
I've a Order model, which points to two different collections of documents Material & User Schemas and have a quantity element.
let Order = new Schema({
materials:
{
type: Array,
material: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Material'
},
qty: {
type: Number
}
},
userId: {
type: Schema.Types.ObjectId,
ref: 'User'
}
}, {
collection: 'orders'
})
Also I've method to create an order:
exports.createOrder = (req, res) => {
if (!req.body.user) {
res.status(400).send({message: 'Content can not be empty!'});
}
const order = new Order({
materials: req.body.materials,
userId: req.body.user
});
order
.save(order)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while creating the Order."
});
});
}
If I create Order filling only Material ID, it creates and filtering by given material ID in filter request.
post request
filter request
But If I trying to point qty it isn't present in response.
post request with qty
filter request ending with previous document id
There is my question: How can I create Order exact way I need (Material ID and qty number must persist) and How can I perform a filtering operations on them?
Any help appriciated.
My mistake was in method how I create order as well as I make a filtering request.
Correct method to create order with data storing in array type is following
exports.createOrder = (req, res) => {
if (!req.body.user) {
res.status(400).send({message: 'Content can not be empty!'});
}
const order = new Order({
materials: {material: req.body.materials, qty: req.body.qty},
userId: req.body.user
});
order
.save(order)
.then(data => {
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while creating the Order."
});
});
}
as you can see, difference is how I form materials array.
next thing is in filter request
exports.filterOrder = (req, res) => {
Order.find({"materials.material": req.body.material})
.then(data => {
console.log(data);
res.send(data);
})
.catch(err => {
res.status(500).send({
message:
err.message || "Some error occurred while retrieving Orders."
});
});
}
If I need to filter orders contain necessary material I need to place subelement of array in quotes with dot notation. This will work also with "material.qty" parameter if needed.
I have two collections, posts and tags. posts contains a postId and other metadata, including tags. This is what a post would look like:
{
"tags": [
"tag1",
"tag2",
...
],
"message": "test message"
...
}
This returns the post above, with a postId.
Using Axios, I receive this data in my function. I want to take each tag, put them in the tags collection along with the postId associated with it.
An example of the tags collection:
tags: {
tag1: {
"postId1",
"postId2"
},
tag2: {
"postId1",
"postId3"
}
}
I'm stuck adding the tags to its own collection in Firebase, and I've tried using forEach tag and updating the tags collection one by one, but that hasn't been working for me.
The code I currently have:
db.collection("posts")
.add(oneNewPost)
.then((doc) => {
// add the post body to the "posts" collection
const resPost = oneNewPost;
resPost.postId = doc.id;
res.json(resPost);
})
.then(() => {
// DOESNT WORK HERE --> overwrites changes in firebase "tags" collection
let batch = db.batch();
resPost.tags.forEach((doc) => {
const docRef = db.collection("tags").doc(doc);
batch.update(docRef, { postId: resPost.postId }, { merge: true });
});
batch.commit().then(() => {
return res.json({...resPost});
});
})
.catch((err) => {
res.status(500).json({ error: "something went wrong" });
console.error(err);
});
Please let me know if anything is unclear!
I got it to work using the following code:
.then((resPost) => {
// add postId's to tag collection here
let batch = db.batch();
resPost.tags.forEach((doc) => {
const docRef = db.doc(`/tags/${doc}`);
batch.update(
docRef,
{ postId: admin.firestore.FieldValue.arrayUnion(resPost.postId) },
{ merge: true }
);
});
batch
.commit()
.then(() => {
return res.json({ ...resPost });
})
.catch((err) => {
console.error(err);
return res.status(500).json({ error: err.code });
});
})
Basically, you need to do a batch update, but also for a specific FieldlValue for each postId.
I'm using Knex.js to query a MySQL database in a Hapi.js route. The following code works but requires a nested query:
{
path: '/recipes',
method: 'GET',
handler: (req, res) => {
const getOperation = Knex.from('recipes')
// .innerJoin('ingredients', 'recipes.guid', 'ingredients.recipe')
.select()
.orderBy('rating', 'desc')
.limit(10)
.then((recipes) => {
if (!recipes || recipes.length === 0) {
res({
error: true,
errMessage: 'no recipes found'
});
}
const recipeGuids = recipes.map(recipe => recipe.guid);
recipes.forEach(r => r.ingredients = []);
const getOperation2 = Knex.from('ingredients')
.whereIn('recipe', recipeGuids)
.select()
.then((ingredients) => {
recipes.forEach(r => {
ingredients.forEach(i => {
if (i.recipe === r.guid) {
r.ingredients.push(i);
}
});
});
res({
count: recipes.length,
data: recipes
});
});
});
}
}
Is there a way to create a return model with Knex.js that has nested objects that match the parent's id/guid so that I don't have nested promises?
Short answer: No.
With Knex, you can retrieve data the same as with SQL, which is record based, not object based, so the closest that you could come would be to use a join to allow doing just a single select to retrieve a single array having elements: recipes, guids, ingredients. This would repeat the recipe & guid for each ingredient, which you avoid by using nested objects. (See the answer below by #Fazal for an example of this.)
As another alternative, you could store the ingredients as a 'blob' field in the recipe table, but I don't believe that MySQL would allow you to create an Array field, so when retrieving the data, you would have to do a transform of the field into the array. And transform it from the Array before updating it into the table. Like: storableData = JSON.stringify(arrayData) and arrayData = JSON.parse(storableData)
There are a few other things that I would suggest to help you improve the code though. (Yeah, I know, not really the question here):
Separate the routing functionality from data handling.
Also, separate data manipulation functionality from retrieval.
Use throw & .catch for creating and handling unsuccessful responses.
The separation of routing, data retrieval, data manipulation makes testing, debugging, and future comprehension easier as each function has a more atomic purpose.
Throwing/.catching unsuccessful process conditions makes it much simpler to have more comprehensive error processing by allowing you to put (most of the time) a single .catch in your router response handling (Hapi.js may even do this .catch for you???).
Also, see the other .catch and .on('query-error' that I added for logging errors. You may have a different logging mechanism you want to use rather than the console. I use Winston. And note that .on('query-error' is NOT a .catch. There will still be an Error() that is thrown, and must be handled somewhere, this will just give you good info about the failure close to the source.
(Sorry, the below code is untested)
path: '/recipes',
method: 'GET',
handler: (req, res) => {
return getRecipeNIngredients()
.then((recipes) => {
res({
count: recipes.length,
data: recipes
});
})
.catch((ex) => {
res({
error: true,
errMessage: ex.message
});
});
};
function getRecipeNIngredients() {
let recipes = null;
return getRecipes()
.then((recipeList) => {
recipes = recipeList;
const recipeGuids = recipes.map(recipe => recipe.guid);
recipes.forEach(r => r.ingredients = []);
return getIngredients(recipeGuids);
})
.then((ingredients) => {
recipes.forEach(r => {
ingredients.forEach(i => {
if (i.recipe === r.guid) {
r.ingredients.push(i);
}
});
});
return recipes;
})
.catch((ex) => {
console.log(".getRecipeNIngredients ERROR ex:",ex); // log and rethrow error.
throw ex;
});
};
function getRecipes() {
return Knex.from('recipes')
// .innerJoin('ingredients', 'recipes.guid', 'ingredients.recipe')
.select()
.orderBy('rating', 'desc')
.limit(10)
.on('query-error', function(ex, obj) {
console.log("KNEX getRecipes query-error ex:", ex, "obj:", obj);
})
.then((recipes) => {
if (!recipes || recipes.length === 0) {
throw new Error('no recipes found')
}
})
};
function getIngredients(recipeGuids) {
return Knex.from('ingredients')
.whereIn('recipe', recipeGuids)
.select()
.on('query-error', function(ex, obj) {
console.log("KNEX getIngredients query-error ex:", ex, "obj:", obj);
})
};
I hope this is Useful!
Gary.
I created a library that return nested object even it has types for typescript
Nested Knex
import * as n from 'nested-knex';
n.array(
n.type({
id: n.number("recipe.id", { id: true }),
title: n.string("recipe.title"),
ingredients: n.array(
n.type({
id: n.number("ingredients.id", { id: true }),
title: n.string("ingredients.title")
})
)
})
)
.withQuery(
knex
.from("recipes")
.innerJoin("ingredients", "recipes.guid", "ingredients.recipe")
.select()
.orderBy("rating", "desc")
.limit(10)
)
.then(recipes => {});
so recipes even have types
You can easily avoid nest query. Just use subquery as-
knex.select('*')
.from(function () {
this.select('*').from('recipes').limit(10).as('recipes'); // limit here
})
.leftJoin('ingredients', 'ingredients.recipe_id', 'recipes.guid')
.then((rec) => {
console.log(rec);
})
see.. just few lines of code.