Nested queries with express/mongoose - node.js

I have a Tag collection, they only have one value which is the label. They can be random tag or a tree tag (here a sample without the _id) :
{
"label": "/test1"
}
{
"label": "/test2"
}
{
"label": "/test1/test1-1"
}
{
"label": "/test2/test2-1"
}
{
"label": "/test1/test1-1/test1-1-1"
}
{
"label": "something"
}
What I want is to have a single object with the tree of my tags :
{
"/test1": {
"name": "test1"
, "children": {
"/test1/test1-1" : {
"name": "test1-1"
, "children": {
"/test1/test1-1/test1-1-1" : {
"name": "test1-1-1"
, "children": {}
}
}
}
}
}
, "/test2": {
"name": "test2"
, "children": {
"/test2/test1-2" : {
"name": "test1-2"
, "children": {}
}
}
}
}
Here is what I tried in my app :
app.get('/tree', function(req, res, next) {
var tree = {};
Tag
// If you have a better solution, I'm not really fan of this
.$where('this.label.split(new RegExp("/")).length === 2')
.exec(function(err, tags) {
tags.forEach(function(tag) {
tag.getChildren(function(children) {
tree[tag.label] = {
'title': tag.label
, 'children': children
}
});
});
});
// do some stuff with the `tree` var
// which does not work because of the asynchronousity of mongo
});
And in my model I have, it doesn't work, at first I wanted to return the path of the tree with with tag.getChildren() but then, I thought a callback will be a better option and I stop there.
Tag.methods.getChildren = function(callback) {
var tree = {};
Tag
.$where('this.label.split(new RegExp("' + this.label + '/")).length === 2')
.exec(function(err, tags) {
tags.forEach(function(tag) {
tag.getChildren(function(children) {
tree[tag.label] = {
'title': tag.label
, 'children': children
}
});
});
return tree
});
};
I have no idea how to to this, I'm fairly new to Node and asynchronous programming so any help will be appreciate.

You probably should look into the async.js module, which has some support for doing an iteration that calls asynchonous code on each step and executing a callback when all the async code is done.

Doing multiple Mongo request is stupid in this example, so I did only one, parse the result and create my tree, here is my code if somebody has the same problem :
app.get('/tree', function(req, res, next) {
var tree = {}
Tag
.find({ label: { $regex: /^\// } }, ['label'])
// Skip the "/"
.skip(1)
.exec(function(err, tags) {
tags.forEach(function(tag) {
var split = tag.label.split('/');
// Root
if (split.length === 2) {
tree[_.slugify(split[1])] = {
title: split[1]
, children: {}
}
} else {
var name = split.pop()
, path = tag.label
, pathSlug = _.slugify(path.replace(/\//g, '-'))
, parentPath = path.split('/')
, parentSlug = ''
, parent;
parentPath.shift();
parentPath.pop();
parentPath.forEach(function(step) {
step = parentSlug ? parentSlug + '-' + _.slugify(step) : _.slugify(step);
parentSlug = step;
parent = parent ? parent.children[step] : tree[step];
});
if (!parent) {
console.error('ERROR :')
console.log(tag.label)
console.log(path.split('/'))
console.log(name)
console.error('##################')
} else {
parent.children[pathSlug] = {
title: name
, children: {}
}
}
}
});
res.send(tree, 200);
});
});

Related

Submitting without files resets the all images in the array when making a PATCH request

I'm trying to make a dynamic field for adding team members using Formik.
In my backend, if I do not choose any file and edit only other field such as "memberName" I'm getting message saying;
"Cast to embedded failed for value "{
_id: '63c5687832a80d5d8f717715',
memberName: 'qqaa',
memberJobTitle: 'qq',
memberDescription: 'qq',
images: [ 'undefined' ]
}" (type Object) at path "team" because of "CastError""
I want to keep the existing images if there is no changes in the input field. I'm having this issue for a week and couldn't figure it out.
This is my controller for making a PATCH request;
const updateSection = async (req, res, next) => {
const files = req.files;
const {
pageId,
welcomeTitle,
welcomeDescription,
aboutUsTitle,
aboutUsDescription,
team,
teamTitle,
} = req.body;
let updates = {};
//update other fields if they are provided in the request body
if (welcomeTitle) {
updates.welcomeTitle = welcomeTitle;
}
if (welcomeDescription) {
updates.welcomeDescription = welcomeDescription;
}
if (aboutUsTitle) {
updates.aboutUsTitle = aboutUsTitle;
}
if (aboutUsDescription) {
updates.aboutUsDescription = aboutUsDescription;
}
if (teamTitle) {
updates.teamTitle = teamTitle;
}
if (team) {
let teamPromises = []; //create an empty array to store promises for updating or creating team members
// updates.team.images = [];
team.forEach((item, i) => {
// item -> current team member being processed, i-> index in the array
let teamMember = {
_id: item._id,
memberName: item.memberName,
memberJobTitle: item.memberJobTitle,
memberDescription: item.memberDescription,
images: item.images,
};
if (files && files[i]) {
let file = files[i];
let img = fs.readFileSync(file.path);
let decode_image = img.toString("base64");
teamMember.images = [
{
filename: file.originalname,
contentType: file.mimetype,
imageBase64: decode_image,
},
];
} else {
teamMember.images = item.images;
}
teamPromises.push(
Section.updateOne(
{ pageId: pageId, "team._id": item._id },
{ $set: { "team.$": teamMember } },
{ new: false }
)
);
});
Promise.all(teamPromises)
.then((result) => {
res.status(200).json({ message: "Section updated successfully!" });
})
.catch((error) => {
res.status(500).json({ error: error.message });
});
} else {
//update other fields if no team member provided
Section.findOneAndUpdate({ pageId: pageId }, updates).then(() => {
res.status(200).json({ message: "Section updated successfully!" });
});
}
};

nodeJS : Connection between tables within a JSON structure

I have a Question object and a Tags object
Tags contains the topics that match the question
Each question can have several tags
I want to connect them in a one-to-many relationship but I dont know how.
my code:
Question.Json:
`[
{
"id": "0f16b716-450c-4468-8096-74e6e4d4c16f",
"lastUpdate": "13/01/2023 09:13:15",
"title": "aaa",
"typeId": 1,
"textBelow": "aaa",
"tags": [
{
"id": "57f2aa4c-5ad2-438a-a1b4-9c5c018d66b4",
"name": "math"
}
]
}
]`
Tags.Json:
`[
{
"id": "57f2aa4c-5ad2-438a-a1b4-9c5c018d66b4",
"name": "math"
}
]
`
my Repositorey:
const { readFile, updateItem, removeItem, insertItem } = require('../fs/fs');
const { v4 } = require('uuid');
class DBQuestionsRepository {
async getAllQuestions() {
const data = await readFile('./data/jsonAsDb.json');
const types = await readFile('./data/questionType.json');
data.forEach((data) => {
data.type = types.find((type) => type.id === Number(data.typeId));
delete data.typeId;
});
return data;
}
async addQuestion(body) {
const currentDate = new Date();
const formattedDate = `${currentDate.getDate().toString().padStart(2, '0')}/${(currentDate.getMonth() + 1).toString().padStart(2, '0')}/${currentDate.getFullYear()} ${currentDate.getHours().toString().padStart(2, '0')}:${currentDate.getMinutes().toString().padStart(2, '0')}:${currentDate.getSeconds().toString().padStart(2, '0')}`;
const item = insertItem
('./data/jsonAsDb.json', { id: v4(), lastUpdate: formattedDate,...body });
return item;
}
async getQuestionById(id) {
const data = await readFile('./data/jsonAsDb.json');
const item = data.find(i => i.id === id);
return item;
}
I tried to connect the tables

How to do pagination with a mongodb aggregate?

While working on MongoDB.I have a problem with doing Pagination.When I'm trying to include Paginaiton with aggerate.I'm also trying to include facets in this.
My code: Just for doing search
app.get("/search", async(req, res) => {
try {
const text = req.query.text
let result = await collection.aggregate([
{
'$search': {
'text': {
'query': `${text}`,
'path': 'title'
}
}
}
]).toArray();
res.send(result)
} catch (error) {
console.error(error)
}
})
This works for both search as well as pagination.
like this, see, It doesn't require any optional request.query.page.
http://localhost:4000/search?text=mango
http://localhost:4000/search?text=mango?page=1
Now, I want to include the pagination with facets search as well...So,
server.get("/search", async(req, res) => {
try {
const key = req.query.key;
const value = req.query.value;
const text = req.query.text;
const page = req.query.page; //Page query create
let result = await collection.aggregate([
{
'$search': {
'text': {
'query': `${text}`,
'path': 'title'
}
}
},
{
'$match': {
[key]: `${value}`
}
}
]).toArray();
res.send(result)
} catch (error) {
console.error(error)
}
})
work for this: without no.of Pages
http://localhost:4000/search?text=Mango&key=Brand&value=rasna
Doesn't work for Pagination:
http://localhost:4000/search?text=Mango&key=Brand&value=rasna&page=2
where I'm wrong here? Do I need to create any additional function to make this works or Something else?
you can use both $skip and $limit aggregation pipelines to achieve this purpose. imagine that we want to have only 20 items per page. so our code looks like this:
server.get("/search", async(req, res) => {
try {
const key = req.query.key;
const value = req.query.value;
const text = req.query.text;
const page = req.query.page - 1; //We subtract one because we don't want skip first twenty items in first page
let result = await collection.aggregate([
{
'$search': {
'text': {
'query': `${text}`,
'path': 'title'
}
}
},
{
'$match': {
[key]: `${value}`
}
},
{ $skip: page * 20 },
{ $limit: 20 }
]).toArray();
res.send(result)
} catch (error) {
console.error(error)
}
})

How to update subdocument value through mongoose?

I am trying to update the value of a nested document through a PUT request. It is working for values in the document, but not in the subdocument.
const AnotherSchema = new Schema ({
Name: String,
Age: Number,
Appearance: {
Hair: String, Eyes: String, Height: Number};
My route looks like this
router.put("/looks/:id/edit", function(req, res) {
var Name= "blob";
var Hair= "blue";
AnotherSchema.findByIdAndUpdate(req.params.id, {Name, Hair}, function(err, feedback){
if (err){
res.send("error");
} else {
res.redirect("/looks");
}
});
});
This route works for updating Name, but not Hair. I have tried Appearance.Hair, but this throws an error in the console for an unexpected . I have also tried [], () and {} but none of these do the trick, nor do " " and this issue does not seem to appear in the docs.
You should be providing the paths via the object notation to the props you want to update:
router.put("/looks/:id/edit", function(req, res) {
AnotherSchema.findByIdAndUpdate(req.params.id, {
Name: "blob",
Appearance: {
Hair: "blue"
}
}, function(err, feedback) {
if (err) {
res.send("error");
} else {
res.redirect("/looks");
}
});
});
The above findByIdAndUpdate is equivalent to:
{ $set: { Name: "blob", Appearance: { Hair: "blue" } } } as per the docs
You should use the $set operator, otherwise you will replace the whole record with the object provided as argument.
var updateObj = {
{ $set: { Name: "blob", Appearance: { Hair: "blue" } } }
};
AnotherSchema.findByIdAndUpdate(req.params.id, updateObj, function (err, feedback) { ... });

return value of chrome.webRequest based chrome.storage

Save and restore options!
I'm trying to block some sites through WebRequest, but when the ckeckbox this false even still blocking the site, anyone can help, this is the code that I have
Options.js
function save_options(){
var blockurl_1 = document.getElementById("blockurl_1").checked;
var blockurl_2 = document.getElementById("blockurl_2").checked;
chrome.storage.sync.set({
blockurl_1: blockurl_1,
blockurl_2: blockurl_2
}, function() {
var status = document.getElementById('status');
status.textContent = 'Block';
});
}
function restore_options() {
chrome.storage.sync.get({
blockurl_1: false,
blockurl_2: false
}, function(items) {
document.getElementById('blockurl_1').checked = items.blockurl_1;
document.getElementById('blockurl_2').checked = items.blockurl_2;
});
}
document.addEventListener('DOMContentLoaded', restore_options);
var checkcontent = document.getElementsByClassName("save")[0];
checkcontent.addEventListener("click",save_options);
I need to do this myself, but with chrome.storage
chrome.webRequest.onBeforeRequest.addListener(function(details) {
return {
cancel: ( localStorage["block_chat_seen"] == 'true' ) ? true : false
}
}, { urls: ['*://*.facebook.com/'] }, ['blocking'])
...
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
chrome.storage.sync.get(null, function(items) {
if (items.blockurl_1) {
chrome.webRequest.onBeforeRequest.addListener(function(details) {
var state = (blockurl_1 === true) ? 'true' : 'false';
return { cancel: state }; }, {
urls: ["*://www.google.com.co/*"]
},
["blocking"]);
}
if (items.blockurl_2) {
chrome.webRequest.onBeforeRequest.addListener(function(details) {
var state = (blockurl_2 === true) ? 'true' : 'false';
return { cancel: state }; }, {
urls: ["*://www.youtube.com.co/*"]
},
["blocking"]);
}
});
});
You are adding listeners each time.
You need to clear existing ones before adding a new one.

Resources