How to retrieve an object ID generated by Firebase? - web

I have the following object:
root: {
id1: { /* this is an autogenerated id by Firebase */
name1: "abc",
name2: "xyz"
},
id2: {
name1: "abc",
name2: "xyz"
},
id3: {
name1: "abc",
name2: "xyz"
},
}
My code to retrieve the whole snapshot is:
getRoot () {
firebase.database ().ref ('roots/')
.on('value', function (snapshot) {
console.log (snapshot.val());
})
}
Everything is perfect. I got every object from the root component, but I can't figure out how to access the IDs and their children? Thanks!

I didn't remember I posted this question. I found out how to do this long time ago.
Here is the code that will do the job:
Service:
myFunction () {
var ref = firebase.database ().ref ('roots')
return new Promise ((resolve, reject) => {
ref.on ('value', function (snapshot) {
if (snapshot.val () == null) {
reject (null);
} else {
var list = new Array ();
snapshot.forEach (function (data) {
var item = {
key: data.key, //this is to get the ID, if needed
name1: data.val ().name1,
name2: data.val ().name2,
}
list.push (item);
});
resolve (list);
}
});
});
}
Component:
this.myService.myFunction ().then (objects => {
this.objects = objects;
for (let obj of this.objects) {
console.log (obj.key);
}
}).catch (error => {
alert ('Nothing found');
})
Wish you a happy coding!

Related

Firebase function returning null value when called from iOS app

I am making a call to a callable firebase function from my iOS app and am getting a return value of null. The correct value was returned just a few days ago, but now it's always returning null. The data is logging correctly in the console just before the return line, and there is no error appearing within the iOS call.
exports.startPlaylist = functions.https.onCall((data, context) => {
const uid = context.auth.uid;
const signature = data.signature;
return axios.post('---url----', {
data: signature
}).then(function(response) {
const val = response.data;
const ref = database.ref().push();
ref.set({
host: {
uid: uid
},
users: {
uid: uid
},
books: val
}, function(error) {
if(error) {
console.log('Not set');
} else {
const info = { id: ref.key };
console.log(info) //Correct log value appears in console
return info; //Return null, however
}
});
}).catch(function(err) {
console.log(err);
});
});
Firebase call
Functions.functions().httpsCallable("startPlaylist").call(["signature": signature]) { (result, error) in
guard let result = result, error == nil else { return }
print(result.data) //<-- prints "null"
}
As stated you should work with the promise returned by set:
ref.set({
host: {
uid: uid
},
users: {
uid: uid
},
books: val
}).then(function() {
const info = { id: ref.key };
console.log(info)
return info;
})
.catch(function(error) {
console.log('Not set');
});

How to implement async in for loop?

I have a collection called 'alldetails' which have the details of some collection
{
"name" : "Test1",
"table_name" : "collection1",
"column_name" : "column1"
},
{
"name" : "Test2",
"table_name" : "collection2",
"column_name" : "column2"
},
{
"name" : "Test3",
"table_name" : "collection3",
"column_name" : "column3"
}
I have collection1,collection2 and collection3 which have column1,column2,colum3 respectively
I have to fetch all the name from the 'alldetails' and I have to get the min and max value of other table based on the column name.
So I want the output like below
{name: ["Test1","Test2","Test3"],
date: [{min_date: "2018-12-01", max_date: "2018-12-31", name: "Test1"},
{min_date: "2018-12-01", max_date: "2018-12-31", name: "Test2"},
{min_date: "2018-12-01", max_date: "2018-12-31", name: "Test3"}]
}
I tried the below code because of non blocking its not waiting the response.
alldetails.find({}, { _id: 0 }).then(async function(result) {
let result_data = {};
let resolvedFinalArray = {};
let array = [];
result_data["name"]= [];
result_data["date"] = [];
resolvedFinalArray = await Promise.all(result.map(async value => {
result_data["name"].push(value.name)
getResult(value.table_name,value.column_name,function(response){
result_data["date"].push({min_date: response.minvalue, max_date: response.maxvalue, name:value.name})
});
}));
setTimeout(function()
{
console.log(resolvedFinalArray);
}, 3000);
});
Please suggest me a solution.
If you want to wait for getResult then you need to return Promise from result.map callback.
You are not pushing anything to resolvedFinalArray so why bother with console.log(resolvedFinalArray)
alldetails.find({}, {_id: 0}).then(async (result) => {
let result_data = {};
result_data["name"] = [];
result_data["date"] = [];
await Promise.all(result.map(value => {
// create Promise that resolves where getResult callback is fired
return new Promise((resolve) => {
getResult(value.table_name, value.column_name, (response) => {
result_data["name"].push(value.name);
result_data["date"].push({
min_date: response.minvalue,
max_date: response.maxvalue,
name: value.name
});
resolve();
});
});
}));
console.log(result_data);
});
or using for loop
alldetails.find({}, {_id: 0}).then(async (result) => {
let result_data = {};
result_data["name"] = [];
result_data["date"] = [];
for (let i = 0; i < result.length; i++) {
const value = result[i];
await new Promise((resolve) => {
getResult(value.table_name, value.column_name, (response) => {
result_data["name"].push(value.name);
result_data["date"].push({
min_date: response.minvalue,
max_date: response.maxvalue,
name: value.name
});
resolve();
});
});
}
console.log(result_data);
});
use async.eachOfLimit if you want to apply an async function on all element of an array:
var async = require("async");
var array = [{_id: "...."},{...},{...}];
async.eachOfLimit(array, 1, function(element, index, cb){
myAsyncFunctionWithMyElement(element, function(err){
return cb(err);
});
}, function(err){
// final callback
});
The array forEach method won't work with async function (unless you do deeply evil things like redefining the prototype). This question has a nice insight of the internal.
If you don't want to rely on external libraries, an easy (and my favourite) approach is something like:
for (let i = 0; i < <your array>.length; i++ ) {
await Promise.all( <your logic> );
}
Just adapt it to your need! :)
You might want to use the for await of loop. See this blog post for details.
This, IMHO, is the most modern way to do it, and it doesn't require you to load any external dependencies, since it is built-in to the language itself. It's basically very similar to the classical for of loop.
This should work, if all lexical scope are taken to consideration. Async each is also is better option it would reduce if else blocks and manage promise for you.
alldetails.find({}, { _id: 0 })
.exec((err, result) => {
if (!err) {
let resolvedFinalArray = [];
result.map((value) => {
resolvedFinalArray.push({
name: value.name,
date: []
});
getResult(value.table_name, value.column_name, (err, response) => {
if (!err) {
resolvedFinalArray[resolvedFinalArray.indexOf(value.name)]['date'].push({
min_date: response.minvalue,
max_date: response.maxvalue,
name:value.name
});
} else {
// Send your error messsage.
// res.status(500).send(err);
}
});
});
console.log(resolvedFinalArray);
// res.send(resolvedFinalArray);
} else {
// Send your error messsage.
// res.status(500).send(err);
}
});

Nodejs findOneAndUpdate paramerters

I have a user model with the following schema, all fields required
name:String,
country:String,
age:Number
From frontend i press update button and update only the age field, and on backend i have the following
var body = _.pick(req.body, ["candidateId","name","age","country"]);
var candidateId = mongoose.Types.ObjectId(body.candidateId);
Candidate.findOneAndUpdate({
_id:candidateId
},{
name: body.name,
country: body.country,
age: body.age,
}).then((data) => {
if (!data) {
res.status(400).send('noDataFound');
} else {
res.status(200).send(data);
}
}).catch((e) => {
res.status(500).send(e)
})
Front passes the data as follows
{ "candidateId":"5a9a86c16acff45a8070d2da",
"name":"Superman",
}
I will get get undefined error for body.age and body.country since its not passed from front end.
QUESTION - How can use the previous values if no value is send from front end for some parameter, one approach is to send everything even if its not changed.
It looks like you're using lodash, check out omitBy to remove undefined or null properties:
_.omitBy({ foo: 'foo', bar: undefined, baz: null }, _.isNil)
{foo: "foo"}
Here's a vanilla JS solution:
Object
.entries({ foo: 'foo', bar: undefined, baz: null })
.filter(([_, value]) => value !== null && value !== undefined)
.reduce((acc, [key, value]) => { acc[key] = value; return acc; }, {})
When I'm facing this problem I usually create a parser function as:
var body = _.pick(req.body, ["candidateId","name","age","country"]);
var candidateId = mongoose.Types.ObjectId(body.candidateId);
function createUpdatedValues(params) {
var condition = {};
if (params.candidateId) condition.candidateId = params.candidateId;
if (params.name) condition.name = params.name;
if (params.age) condition.age = params.age;
if (params.country) condition.country = params.country;
return condition;
}
Candidate.findOneAndUpdate({
_id:candidateId
},createUpdatedValues(body)).then((data) => {
if (!data) {
res.status(400).send('noDataFound');
} else {
res.status(200).send(data);
}
}).catch((e) => {
res.status(500).send(e)
})
Form a new object that includes sent values:
const newObj = {};
if (body.name) {
newObj.name = body.name;
}
if (body.age) {
newObj.age = body.age;
}
if (body.country) {
newObj.country = body.country;
}
Then update using newObj.
Candidate.findOneAndUpdate({
_id:candidateId
}, newObj)...
I am using the following way to go around it , if any other better approach is there let me know ill mark that as answer.
I am now passing all the data to update from frontend in a new object like this
--- Client Data---
{ "candidateId":"5a9a86c16acff45a8070d2da",
"updatedData":{
"firstName":"Jack",
"lastName":"Bauer",
"applicationSource":"consultant",
"skills":["trading","sales"]
}
}
---- Updating logic ----
var body = _.pick(req.body, ["candidateId","updatedData"]);
var candidateId = mongoose.Types.ObjectId(body.candidateId);
var dataToUpdate = _.pick(body.updatedData,["firstName","lastName"]);
Candidate.findOneAndUpdate({
_id:candidateId
},dataToUpdate).then((data) => {
if (!data) {
res.status(400).send('noDataFound');
} else {
res.status(200).send(data);
}
}).catch((e) => {
res.status(500).send(e)
})

Node JS Promise.all and forEach in nested Array

In a Node app with typescript, i need to iterate through a array and i call an asynchronous function inside the loop to get informations about the related items for each item in the array. The function is called for each related item to get its title in relatedItems array.
I'm able to retrieve promises for the 2nd level array (relatedItems) but not sure how to implement a then once top level finishes as well.
How to reach my goal with promise.all.
var Inputarray = [
{ Category: "cat1"
relatedItems: [
{id: "1"},
{id: "2"},
{id: "3"}
]
},
{
Category: "cat2"
relatedItems: [
{id: "1"},
{id: "2"},
{id: "3"}
]
}
];
var wantedresult= [
{ Category: "cat1"
relatedItems: [
{Title: "xxx"},
{Title: "yyy"},
{Title: "zzz"}
]
},
{
Category: "cat2"
relatedItems: [
{Title: "ttt"},
{Title: "kkk"},
{Title: "mmm"}
]
}
];
private GetAllRelattedItems(data: IListItem[]): any{
let rendredItems: RelatedItem[] = new Array();
data.forEach(item => {
let relatedItemsinfos : relatedItemInfos[]=item.Children;
let localTab:relatedItem[]=new Array();
let newItem = {
Category:item.Category,
Children: []
};
var promises = [];
relatedItemsinfos.forEach(relatedItemsInfositem =>{
promises.push(this.GetRelatedItem(relatedItemsInfositem.WebId,relatedItemsInfositem.ListId,relatedItemsInfositem.ItemId));
});
Promise.all(promises).then(function(response) {
response.forEach(obj=>{
let newNode: relatedItem ={
Title :Obj.Title,
};
newItem.Children.push(newNode);
});
rendredItems.push(newItem);
});
});
}
private GetRelatedItem(id:string) : Promise<relatedItem> {
return new Promise((resolve) => {
pnp.sp.site.openWeb()
.then(web => {
//this.getWeb()
//.then((web) => {
return web.web.lists.getList().get(); //w.web.lists.getById("").get().then(r => {
})
.then((list) => {
return this.getItem(list,id);
})
.then((item) => {
resolve(item);
});
});
}
You should use Promise.all at the top level and return the promise for each item in the data array:
private GetAllRelattedItems(data: IListItem[]): any {
//..
let allData = data.map(item => {
//..
return Promise.all(promises).then(function (response) {
//..
});
});
Promise.all(allData).then (_=> { /* After all data is retrieved */})
}
Since you are using Typescript a better approach would be to take advantage of async/await to make the code more readable:
private async GetAllRelattedItems(data: IListItem[]): Promise<RendredItem[]> {
let allData = data.map(async item => {
let relatedItemsinfos: relatedItemInfos[] = item.Children;
let localTab: relatedItem[] = new Array();
let newItem = {
Category: item.Category,
Children: []
};
var response = await Promise.all(relatedItemsinfos.map(relatedItemsInfositem => {
return this.GetRelatedItem(relatedItemsInfositem.WebId);
}));
newItem.Children = response.map(entry => ({
Title: entry.value[0].Title,
FileLeafRef: entry.value[0].FileLeafRef,
Modified: entry.value[0].Modified,
FileRef: entry.value[0].FileRef,
FieldValuesAsText: {
FileRef: entry.value[0].FileRef,
}
}));
return newItem;
});
var result = await Promise.all(allData);
// After all results are done
return result
}
private async GetRelatedItem(id: string): Promise<{ value: relatedItem[] }> {
const web = await pnp.sp.site.openWeb()
const list = await web.web.lists.getList().get(); //w.web.lists.getById("").get().then(r => {
return await this.getItem(list,id);
}
// Placeholder
public getItem(list: any[], id: string ): Promise<{ value: relatedItem[] }>{
return Promise.resolve({
value : []
});
}
Note I guessed some of the types based on usage, you should review to make sure they are correct.
private async GetAllRelattedItems(data: IListItem[]): Promise<RendredItem[]> {
let allData = data.map(async item => {
let relatedItemsinfos: relatedItemInfos[] = item.Children;
let localTab: relatedItem[] = new Array();
let newItem = {
Category: item.Category,
Children: []
};
var response = await Promise.all(relatedItemsinfos.map(relatedItemsInfositem => {
return this.GetRelatedItem(relatedItemsInfositem);
}));
newItem.Children = response.map(entry => ({
Title: entry.value[0].Title,
FileLeafRef: entry.value[0].FileLeafRef,
Modified: entry.value[0].Modified,
FileRef: entry.value[0].FileRef,
FieldValuesAsText: {
FileRef: entry.value[0].FileRef,
}
}));
return newItem;
});
var result = await Promise.all(allData);
// After all results are done
return result
}
private async GetRelatedItem(relatedItemsInfositem) : Promise<any> {
const web = await pnp.sp.site.openWebById(relatedItemsInfositem.WebId);
const list = await web.web.lists.getById(relatedItemsInfositem.ListId).get();
return await this.getItem(list,relatedItemsInfositem.ItemId);
// here i would like to call GetItemSize that is async and that take return the size as a number but i would like to return the whole item model
}
public getItem(list: any, itemId: string): Promise<any>{
let url: string;
if(list.ParentWebUrl !='/'){
url= list.ParentWebUrl + "/_api/web/lists/getbytitle('"+list.Title+"')/items?$select=FileLeafRef,FileRef,Title,File/ServerRelativeUrl,Modified,FieldValuesAsText/FileRef&$expand=File&$expand=FieldValuesAsText&$filter=Id eq "+itemId;
}
else url="/_api/web/lists/getbytitle('"+list.Title+"')/items?$select=FileLeafRef,FileRef,Title,File/ServerRelativeUrl,Modified,FieldValuesAsText/FileRef&$expand=FieldValuesAsText&$expand=File&$filter=Id eq "+itemId;
return this.context.spHttpClient.get(url,SPHttpClient.configurations.v1).then((response: Response)=>{
return response.json();
});
}
private async GetItemSize(title: string): Promise<relatedItem>{
let url:string ;
let response‌​ = await this.context.spHttpClient.get(url ,SPHttpClient.configuration‌​s.v1);
// Asuming the json has the shape of relatedItem : Here the response is just a number and not shaped RelatedItem the goal is to return the whole relatedItem with size information.
return <relatedItem>response.json(); }

Push to second level array in mongodb with node/express

I am working on a chatroom where users can chat with each other filtered on the basis on projects. Users from the same project can talk to each other.
Here is my chat model where each document is based on project ref and has an array for the messages with user refference:
'use strict';
var mongoose = require('bluebird').promisifyAll(require('mongoose'));
var ChatSchema = new mongoose.Schema({
projectid: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Project'
},
messages: [{
userid: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
message: String,
date: {
type: Date,
default: Date.now
},
time: String
}]
});
export default mongoose.model('Chat', ChatSchema);
Now I am trying to update the messages array with new messages but I am unable to do so since past few hours. Here is what I have so far.
To get chat messages based on projects I am using:
routes:
router.get('/projectid/:id', controller.showByProject);
router.post('/projectid/:id', controller.insertMessageByProject);
controller:
// Gets the chat thread based on project id
export function showByProject(req, res) {
Chat.findAsync({projectid: req.params.id})
.then(handleEntityNotFound(res))
.then(respondWithResult(res))
.catch(handleError(res));
}
// Insert a new message in the chat based on projectid
export function insertMessageByProject(req, res) {
if (req.body._id) {
delete req.body._id;
}
Chat.findAsync({projectid: req.params.id})
.then(handleEntityNotFound(res))
.then(saveUpdates({$push: {messages: req.body}}))
.then(respondWithResult(res))
.catch(handleError(res));
}
Json Object I am sending from POSTMAN:
{
"messages":
{
"userid": "56d7967745ab81322a964927",
"message": "This is a meesage"
}
}
OR
{
"userid": "56d7967745ab81322a964927",
"message": "This is a meesage"
}
I am able to update the object if I have the object ID to the chat document itself but inside my application, I do not have the direct reference. I have tried few other ways as well but every time my application returns a 500 error.
Your help would be highly appreciated.
EDIT 1: here are the helping functions I am using generated by the angular full-stack plugin.
function respondWithResult(res, statusCode) {
statusCode = statusCode || 200;
return function(entity) {
if (entity) {
res.status(statusCode).json(entity);
}
};
}
function saveUpdates(updates) {
return function(entity) {
var updated = _.merge(entity, updates);
return updated.saveAsync()
.spread(updated => {
return updated;
});
};
}
function removeEntity(res) {
return function(entity) {
if (entity) {
return entity.removeAsync()
.then(() => {
res.status(204).end();
});
}
};
}
function handleEntityNotFound(res) {
return function(entity) {
if (!entity) {
res.status(404).end();
return null;
}
return entity;
};
}
function handleError(res, statusCode) {
statusCode = statusCode || 500;
return function(err) {
res.status(statusCode).send(err);
};
}
EDIT 2: As I mentioned in the comments, the problem was with _.Merge function which was not merging the object right, although it should have been able to update the object.
So I wrote my own function for saveUpdates as follows:
function saveUpdatesForNewChat(updates) {
return function(entity) {
var temp = entity;
temp[0].messages.push(updates);
console.log('\ntemp:');
console.log(require('util').inspect(temp, { depth: null }));
console.log('\nend of ops\n\n');
var updated = _.merge(entity, temp);
console.log('out of merge');
console.log(require('util').inspect(updated, { depth: null }));
return updated.saveAsync()
.spread(updated => {
return updated;
});
};
}
ok so I have left the console logs inside and it's perfect object to save into the database but the server still returns a 500 errors on update.
OK! So I have found the answer myself.
The problem was that the object returned was a result set and I was calling save on whole result set. I fetched the first element out of the returned resultset, pushed new message to the element and called save on it and it started working.
Here is the code:
function saveUpdatesForNewChat(updates) {
return function(entity) {
var temp = entity[0];
temp.messages.push(updates);
var updated = temp;
return updated.saveAsync()
.spread(updated => {
return updated;
});
};
}

Resources