NodeJS/Mongoose - How to reference individual models after concatenation? - node.js

I am concatenating two models (Blogs and Events) so I can display all records from both collections in a single forEach loop:
const blogs = await Blog.find({});
const events = await Event.find({});
const blogsAndEvents = blogs.concat(events);
blogsAndEvents.forEach(function(blogOrEvent) { etc. etc.
However, within the forEach loop, in some cases, I want to still reference the original model it comes from, e.g. 'if Blog, title equals BLOG, if Event, title equals event). I have a workaround for this where I am using a unique field from each model to determine the underlying model, like this:
if (blogOrEvent.blogPost) {
title = "BLOG"
} elseIf (blogOrEvent.eventDate) {
title = "EVENT"
}
This approach doesn't seem very clean to me though as it relies on their being a unique field in each model. It there a better way I can access the original underlying model within the loop? Thanks.

With mongoose documents it can be distinctly verified which Model they belong to using instance of :
const blogs = await Blog.find({});
const events = await Event.find({});
const blogsAndEvents = blogs.concat(events);
blogsAndEvents.forEach(function (blogOrEvent) {
if (blogOrEvent instanceof Blog) {
title = "BLOG";
} else if (blogOrEvent instanceof Event) {
title = "EVENT";
}
});

Related

Firebase function - how to create multiple documents from different collections

I am fairly new to writing functions and cannot figure out how to solve my issue or even search for it properly.
I have three collections:
current_projects
vendors
vendor_projects
when a new project is created I want the function to take all documents in vendors, add certain fields from them to vendor_projects, and include the project_id field that is created in current_projects.
Do I need a for loop to accomplish this or is there other syntax that could be utilized?
My current function is below. This creates on document using the new project_id field but doesnt take any of the fields from vendors. Any input is greatly appreciated.
exports.createProjVen = functions.firestore.document("/Current_projects/{id}")
.onCreate((snap, context)=>{
console.log(snap.data());
const id = snap.data().id;
// const collection = context.params.Current_projects;
// const id = context.params.id;
const projectVendors = admin.firestore().collection("project_vendors");
// const vendors = admin.firestore().collection("vendors");
return projectVendors.doc(id).set({
actual: "",
budget: "",
id: "23121",
project_id: id,
toggle: "true",
type: "Fixtures",
vendor_company: "tes",
vendor_contact: "tes",
vendor_email: "jj#j.com",
vendor_id: "test",
vendor_phone_number: "test"});
});
Adding more details:
When a new project is added it creates a record in current_projects.
I want the function to be able to query all documents in the vendor collection when a new record is created in current_projects.
From there, I want to grab the highlighted fields from the vendor documents, add the id from that was created from current_projects (highlighted in the first screen shot), and create a new document in project_vendors (third screen shot below) for every document in the vendors table.
If you are trying to created a document in project_vendors collection for every vendor in vendors after a project is created then you can map an array of promises and then use Promise.all() as shown below:
exports.createProjVen = functions.firestore.document("/Current_projects/{id}")
.onCreate((snap, context) => {
const docSnap = snap.data();
const id = context.params.id;
const vendorsSnap = await admin.firestore().collection("vendors").get();
const vendorsData = vendorsSnap.docs.map((d) => ({ id: d.id, ...d.data() }))
const promises = [];
const vendorPrsCol = admin.firestore().collection("project_vendors");
vendorsData.forEach((vendor) => {
const data = {
projectId: id,
email: vendor.email,
// other vendor fields
}
promises.push(vendorPrsCol.add(data));
})
await Promise.all(promises);
console.log("Vendor Projects added");
return null;
});

how to push object ids to another schema

I have a schema like this:
var MusicSchema = new Schema({
music_genres:[{
type:Schema.Types.ObjectId,
ref:'music_genres',
}],
music: {
type:String,
required:true,
},
});
now, in front-end, i have a multi select where user chooses one or many different music_genres.
so when clicked submit, sometimes I get (if user chose only one genre)
- 5cab466ed076761558a76148 or if multiple - [ '5cab466ed076761558a76148', '5cab4915d076761558a7614a' ].
So, if user chose only 1 genre, it's string but if user chose multiple, it's array.
router.post('/',async (req,res)=>{
const newMusic = new Music();
if(typeof req.body.music_genres === "string") req.body.music_genres = [req.body.music_genres];
for(var i in req.body.music_genres) newMusic.music_genres.push(req.body.music_genres[i]);
await newMusic.save();
Question: I hate when I wrote if statement and checking if it's string, make it array. I also hate for statement. Is there any way to make this code better without if and for loop?
You could use a ternary with the spread operator instead of a loop withpush()
router.post('/',async (req,res)=>{
const newMusic = new Music();
typeof req.body.music_genres === "string" ? newMusic.music_genres =
[req.body.music_genres]: newMusic.music_genres = [...req.body.music_genres];
await newMusic.save();
Example
let foo = 'foo';
let arr = ['a', 'b'];
let bar = [];
a = foo;
typeof a == 'string' ? bar = [a] : bar = [...a];
console.log(bar);
If you want to remove for loop from the code you should use mongoose insertMany function as you can see in its official document here doc, you can pass an array to this function then it will handle the bulk creation.
but for your first issue I think the best approach is make the request body integrated from client side so you can easily make the string an array with one element in it, I think it will be better for the consistency of server side code.

how to pass fieldname dynamically in mogodb

I want to pass field name dynamically in Mondgodb while query the collection. e.g.
emp.ply = function(res,res) {
Employee.find({area: 'Plymouth'}).exec(function(err,PLY) {
res.render("../index", {resultset: PLY})
here I'm doing query on field name area which is hard code. Instead of i want to pass fieldname dynamically. How we can achieve it in nodeJS
So, conceptually you just need to build the query object before sending it to the .find() function. Perhaps something like this:
const query = {}
const fieldname = 'area'
const fieldvalue = 'Plymouth'
query[fieldname] = fieldvalue
Employee.find(query).exec((err, ply) => {
// do whatever
});
You could conceptually extract both fieldname and fielvalue from req.body or req.query depending on what you're doing.
If your code supports ES6 or (any latest browser except IE), we can do this:
emp.ply = function(res,res) {
let searchField = 'area'; // this can be anything dynamically
Employee.find({[searchField]: 'Plymouth'}).exec(function(err,PLY) {
res.render("../index", {resultset: PLY})
})
}
You can refer to MDN docs about Computed property names

Cloud Functions & Firestore: Iterate over document.data

The database structure looks like this:
User {id}
Settings (Collection)
device_uids(document)
{device_uid_1}: Boolean
(...)
{device_uid_n}: Boolean
I want to get the document and access all of the device_uids within that document.
I tried like this, however the console logs, that forEach is not definded:
const settings_ref = admin.firestore().collection('User').doc(uid).collection('Settings').doc('device_uids');
settings_ref.get()
.then(snap =>{
let uids = snap.data();
uids.array.forEach(element => {
let device = element.key;
if(device != device_uid){
//GO ON
}
});
})
How can I access the values individually?
You don't have a field called array in your document, so uids.array will always be undefined. If you just want to iterate all the properties of the document, it's just like iterating all the properties of a plain old JavaScript object:
const data = snap.data();
for (const key in data) {
const value = data[key];
// now key and value are the property name and value
}

MongoDB race conditions or concurency issues

I have the following code in my chat application based on NodeJS and MongoDB to change admin for room:
export function setAdmin(room, user) {
const userGuid = getGuid(user);
if (room.users && room.users.length) {
// filter current and new
room.users = room.users.filter(guid =>
guid !== room.adminGuid && guid !== userGuid
);
} else {
room.users = [];
}
room.users.push(userGuid);
room.adminGuid = userGuid;
return roomsCollection.save(room, { w: 1 });
}
Each room have only 2 users: admin and customer.
To update several rooms:
const rooms = await roomsCollection.find({ adminGuid: currentAdminGuid }).toArray();
for (const room of rooms) {
await setAdmin(room, newAdminUser);
}
I had some problems under highload with my application. They were resolved with help of indexes.
But after, working with MongoDB dump I found out that I have rooms with 3 users guids in room.users array. I think that save works as update for exist document, but how it updates array? If $set, why I have such 3 users rooms. Any thoughts how it would be possible?
save() behaves as insert if document does not exist. You have to use update() with {upsert: false}
some resources to update array: Array Query, array Operator
simple tutorial to nest array inside document, here
advanced example of dealing with multiple nested array, here

Resources