how to fetch subcollection's data in firestore? - node.js

I have built a webapp using node express to backend and for frontend I used Reactjs .
In firestore database has a "users" collection in there have many documents for every users. For each document have fields and have subcollections.
1st view
2nd view (in a subcollection view)
this is the sample database like my real database structure .I want fetch all users(inside the users table documents) andalso with subcollections fields.
For every users have same subcollections.(Like as this image user have 4 subcollections andaslo another user also have that same subcollections.)
For this I write the code like this.
model class
class Users {
constructor(id,name,email,provider,firstWord,leda,age,birthday,district,gender,familyChildren,fatherEducation,monthlyIncome,motherEducation,whichChild,awaSE,awaUN,kathakaraaSE,kathakaraaSE,kathakaraaUN) {
this.id = id;
this.name = name;
this.email = email;
this.provider = provider;
this.email = firstWord;
this.email = leda;
this.age = age;
this.birthday = birthday;
this.district = district;
this.gender = gender;
this.familyChildren = familyChildren;
this.fatherEducation = fatherEducation;
this.monthlyIncome = monthlyIncome;
this.motherEducation = motherEducation;
this.whichChild = whichChild;
this.awaSE = awaSE;
this.awaUN = awaUN;
this.kathakaraaSE = kathakaraaSE;
this.kathakaraaUN = kathakaraaUN;
}
}
module.exports = Users;
controller
'use strict';
const firebase = require('../db');
const Users = require('../models/users');
const firestore = firebase.firestore();
const getAllUsers = async (req, res, next) => {
try {
const users = await firestore.collection('users');
const data = await users.get();
const userArray = [];
if(data.empty) {
res.status(404).send('No user found');
}else {
data.forEach(doc => {
const users = new Users(
doc.id,
doc.data().name,
doc.data().email,
doc.data().provider,
doc.data().firstWord,
doc.data().leda,
doc.data().age,
doc.data().birthday,
doc.data().district,
doc.data().gender,
doc.data().familyChildren,
doc.data().fatherEducation,
doc.data().monthlyIncome,
doc.data().motherEducation,
doc.data().whichChild,
doc.data().awaSE,
doc.data().awaUN,
doc.data().kathakaraaSE,
doc.data().kathakaraaUN,
);
userArray.push(users);
});
res.send(userArray);
}
} catch (error) {
res.status(400).send(error.message);
}
}
module.exports = {
getAllUsers,
}
router class
const router = require("express").Router();
const { getAllUsers } = require('../controllers/userscontroller.js')
router.get('/AllUsers', getAllUsers);
module.exports = router;
model class image
1.users collection fields
2.childGrow collection fields
3.childPrivateDetails collection fields
4.familyDetails collection fields
5.wenath collection fields
but out put is
in there not display other collections fields.
How I do that using node express?

You can do something like this:
const getAllUsers = async (req, res, next) => {
try {
const users = await getFirestore().collection('users');
const data = await users.get();
const userArray = [];
if (data.empty) {
res.status(404).send('No user found');
} else {
for (let doc of data.docs) {
let firstSubCollectionData = await getFirestore().collection('users').doc(doc.id).collection('firstSubCollection').get();
let secondSubCollectionData = await getFirestore().collection('users').doc(doc.id).collection('secondSubCollectionData').get();
let thirdSubCollectionData = await getFirestore().collection('users').doc(doc.id).collection('thirdSubCollectionData').get();
let forthSubCollectionData = await getFirestore().collection('users').doc(doc.id).collection('forthSubCollectionData').get();
// construct your object here
// add it to list
}
res.send(userArray);
}
} catch (error) {
res.status(400).send(error.message);
}
}
BUT
sub-collections can be potentially a list of values. If not I think you have to redesign your data model.

Related

"Function DocumentReference.set() called with invalid data. Data must be an object, but it was" How I solve it? In nodejs and firestore

I tried to add data getting reference with another table Id but when I run then show this error.
I used MVC architecture
controller.js
const addProduct = async (req, res, next) => {
try {
const {categoryID,name} = req.body;
await firestore.collection('product').doc().set(categoryID,name);
res.send('Word saved successfully');
} catch (error) {
res.status(400).send(error.message);
}
}
model.js
class Product {
constructor(id, name,categoryID ) {
this.id = id;
categoryID = {
type : firefose.Schema.Types.ObjectId,
ref : 'category',
required : true
},
this.name = name;
}
}
module.exports = Product;
router.js
const router = require("express").Router();
const { addProduct } = require("../controllers/words12controller.js");
router.post('/addProduct', addProduct);
module.exports = router;
Change .set(categoryID,name); to :
.set({ category: categoryID, name: name});
.set expects an object with key/value pairs otherwise it doesn't know which value to update.

Multi-tenancy with mongoose and express

I am working on a MERN SaaS application and I have read all manner of documents on multi-tenancy, where the goal is to create a layer of data isolation above the level of the user. putting all the info together I came about this solution which suggests making use of "continous-local-storage" However, after implementing it, I can't really get it to work, all I have seems logically right, I really can't figure out the issue. After implementation, my application refuses to load data from the database...
//lib/storage.js
const createNamespace = require('continuation-local-storage').createNamespace;
const namespaceName = ('request');
const ns = createNamespace(namespaceName);
const bindCurrentNamespace=function(req, res, next){
ns.bindEmitter(req);
ns.bindEmitter(res);
ns.run(() => {
next();
});
}
const setCurrentTenantId=function(tenantId){
return ns.set('tenantId', tenantId);
}
const getCurrentTenantId=function(){
return ns.get('tenantId');
}
module.exports ={
bindCurrentNamespace:function(){},
setCurrentTenantId:function(){},
getCurrentTenantId:function(){}
}
Sever.js
**********
// some code above
const storage= require('./lib/storage')
// multitenant logic
const BindCurrentNamespace = storage.bindCurrentNamespace
app.use(BindCurrentNamespace);
app.use((req, res, next) => {
// Get current user from session or token
const user = req.user
// Get current tenant from user here
// Make sure its a string
const tenantId = user.organization._id.toString()
setCurrentTenantId(tenantId);
next();
});
/lib/multiTenant.js
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const storage = require('./storage');
const GetCurrentTenantId = storage.getCurrentTenantId
const tenantModel = function (name, schema, options) {
return (props = {}) => {
schema.add({ tenantId: String });
const Model = mongoose.model(name, schema, options);
const { skipTenant } = props;
if (skipTenant) return Model;
Model.schema.set('discriminatorKey', 'tenantId');
const tenantId = GetCurrentTenantId;
const discriminatorName = `${Model.modelName}-${tenantId}`;
const existingDiscriminator = (Model.discriminators || {})[discriminatorName];
return existingDiscriminator || Model.discriminator(discriminatorName, new Schema({}));
};
}
const tenantlessModel = function (name, schema, options) {
return () => mongoose.model(name, schema, options);
}
module.exports={
tenantModel:function(){},
tenantlessModel:function(){}
}
Modified Schema
// Employee schema
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const Tenant= require('../lib/multiTenant')
const TenantModel=Tenant.tenantModel;
//create Schema
const EmployeeSchema = new mongoose.Schema({
name: String,
department: String,
origin: String,
wages:Number,
overtime:{type:Number,
default:0},
joinDate: String,
attendances: Object
},{timestamps:true});
const Employee=TenantModel("Employee", EmployeeSchema,'employee001');
module.exports=Employee
Usage
// #desc Get all Employee
//#routes Get/api/v1/employee
//#acess Public
exports.getAllEmployee = asyncHandler(async (req, res, next) => {
Employee().find({}, null, {sort: {name: 1}}, function(err, employees){
if(err){
res.status(500);
res.send(err);
} else {
res.json(employees);
}
});
});

How to achieve a one to many relationship with GraphQL & DataLoader

I'm having a hard time figuring out why my graphQL & DataLoader setup isn't working and could use some help.
I have a User and a Orchestra type, and I would like to transform a User to populate its createdOrchestras field and do the same thing with Orchestra and an owner field.
EDITED.
The following code causes an infinite loop:
Here are the DataLoaders, which are passed to the resolvers via context:
const DataLoader = require('dataloader');
const Orchestra = require('../models/orchestras');
const User = require('../models/users');
const batchOrchestras = async ids => {
const orchestras = await Orchestra.find({ _id: { $in: ids } });
const orchestraMap = {};
orchestras.forEach(o => {
orchestraMap[o.id] = o;
});
return ids.map(id => orchestraMap[id] || new Error(`No result for ${id}`));
}
const batchUsers = async ids => {
const users = await User.find({ _id: { $in: ids } });
const userMap = {};
users.forEach(u => {
userMap[u.id] = u;
});
return ids.map(id => userMap[id] || new Error(`No result for ${id}`));
};
module.exports = () => new DataLoader(batchUsers)
module.exports = () => new DataLoader(batchOrchestras);
Here are the transform functions which should be capable of fetching data for nested fields via data loaders and modify sensitive fields like the user password.
async function transformUser(userId, loaders) {
const user = await loaders.userLoader.load(userId.toString());
return {
...user._doc,
createdOrchestras: await Promise.all(
user._doc.createdOrchestras.map(orchestra =>
transformOrchestra(orchestra, loaders)
)
)
}
}
async function transformOrchestra(orchestraId, loaders) {
const orchestra = await loaders.orchestraLoader.load(orchestraId.toString());
return {
...orchestra._doc,
owner: transformUser(orchestra._doc.owner, loaders)
}
}
module.exports = {
transformUser,
transformOrchestra
}
How should I restructure the code to prevent an infinite loop but keeping the transform functions as the final providers of data for a particular field ?

NodeJS botbuilder SDKv4

How can I store the values of the user input? For example I've created this dialog and I want to create an email and in the same time store them in a DB, but I am not sure where to add the functions.
Thanks
Constructor:
constructor(conversationState) {
this.dialogStateAccessor = conversationState.createProperty(DIALOG_STATE_ACCESSOR);
this.holidayAccessor = conversationState.createProperty(HOLIDAY_ACCESSOR);
this.conversationState = conversationState;
this.dialogSet = new DialogSet(this.dialogStateAccessor);
this.dialogSet.add(new ChoicePrompt(MONTH_PROMPT));
this.dialogSet.add(new ChoicePrompt(START_DATE_PROMPT));
this.dialogSet.add(new ChoicePrompt(END_DATE_PROMPT));
this.dialogSet.add(new WaterfallDialog(HOLIDAY_DIALOG, [
this.promptForMonth.bind(this),
this.promptForstartDate.bind(this),
this.promptForendDate.bind(this),
]));
}
TurnContext:
case ActivityTypes.Message:
const holiday = await this.holidayAccessor.get(turnContext, null);
const dc = await this.dialogSet.createContext(turnContext);
if (!dc.activeDialog) {
if (!holiday) {
await dc.beginDialog(HOLIDAY_DIALOG);
}
else {
await turnContext.sendActivity(
`An email was sent to your manager for approval`);
}
}
To start, you need to create and pass the userState store in your index.js file first.
const { ConversationState, MemoryStorage, UserState } = require('botbuilder');
[...]
const conversationState = new ConversationState(memoryStorage);
const memoryStorage = new MemoryStorage();
const userState = new UserState(memoryStorage);
[...]
const bot = new ExampleBot(conversationState, userState);
In your bot.js file, include and instantiate userState and assign a user profile:
class ExampleBot {
constructor(conversationState, userState) {
[...]
const USER_PROFILE = 'userProfile';
this.userProfile = userState.createProperty(USER_PROFILE);
this.userState = userState;
[...]
}
Now you can access the userState. You can do so as part of the OnTurn:
async onTurn(turnContext) {
if (turnContext.activity.type === ActivityTypes.Message) {
const userProfile = await this.userProfile.get(turnContext, {});
const conversationData = await this.conversationData.get(
turnContext, { promptedForUserName: false });
if (!userProfile.name) {
if (conversationData.promptedForUserName) {
userProfile.name = turnContext.activity.text;
await turnContext.sendActivity(`Thanks ${userProfile.name}.`);
conversationData.promptedForUserName = false;
} else {
await turnContext.sendActivity('What is your name?');
conversationData.promptedForUserName = true;
}
await this.userProfile.set(turnContext, userProfile);
await this.userState.saveChanges(turnContext);
}
}
}
Or as part of the waterfall / step:
async someWaterfallStep(step) {
const user = await this.userProfile.get(step.context, {});
if (!user.name) {
[do something]
} else {
await step.context.sendActivity(`Hello ${user.name}. Nice to meet you!`);
return step.endDialog()
}
}
You can read more about setting user state in this doc.
Hope of help!

How to Loop Data and Validate in Mongodb

I have a dynamic input field where user can add multiple category at once. Data sent at backend is like
['ELECTRONIC','TOYS','GAMES']
Now I want to check for each element of the array ,if they are already present on mongodb . If its present i want to store it in errors object as
errors={ 0: 'Duplicate Data found'}
I am attaching my code for validation which is not working please help . .
const Category = require('../../models/Category');
const fieldCheck = (req, res, next) => {
const data = req.body;
const errors = [];
for( i = 0; i < data.length ; i++){
Category.findOne({ category_name : data[i]})
.then(user => {
if(user){
// # If a reqistered User ID is found ,then move ahead
errors[i] = 'Duplicate Entry Found';
errors.push(errors[i]);
}
}).catch(err =>{
return res.json(err);
}
)
}
console.log(errors);
};
module.exports = fieldCheck;
My Category Schema is ....
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const categorySchema = new Schema ({
category_name:{
type:String,
unique:true,
isRequired:true,
},
date:{
type:Date,
default:Date.now()
}
});
module.exports = mongoose.model('Category',categorySchema);
You are trying to call an asynchronous method (findOne) inside a synchronous loop (for). As you experience, this is like oil and water.
An easy fix is to make your method asynchronous and use the await keyword, example:
const fieldCheck = async (req, res, next) => {
const data = req.body;
const errors = [];
try {
for( i = 0; i < data.length ; i++) {
let user = await Category.findOne({ category_name : data[i]});
if (user) {
// # If a reqistered User ID is found ,then move ahead
errors[i] = 'Duplicate Entry Found';
errors.push(errors[i]);
}
}
// I assume you wanted to respond to res.json here?
console.log(errors);
} catch (err) {
return res.json(err);
}
};
module.exports = fieldCheck;

Resources