I was just wondering if someone could help me with an issue I'm having in my Ionic app.
I'm having trouble getting my head around nested firebase queries and being able to update the ionic view asynchronously. I'm not using angularfire or anything like that.
My database structure is the following...
"posts" : {
"-KguJ95gTreWH5AT5lQ1" : {
"category" : "Example",
"description" : "This is a test description!!!",
"userID" : "SeMpRKxczcSCFpnkVDTU2pxCDV12"
},
"-Kh--WhNsWAXqs1jLfXu" : {
"category" : "Example 2",
"description" : "Test description .....",
"userID" : "SeMpRKxczcSCFpnkVDTU2pxCDV12"
}
},
"user-posts" : {
"SeMpRKxczcSCFpnkVDTU2pxCDV12" : { // User ID
"-KguJ95gTreWH5AT5lQ1" : { // Post
"category" : "Example",
"description" : "This is a test description!!!",
"userID" : "SeMpRKxczcSCFpnkVDTU2pxCDV12"
},
"-Kh--WhNsWAXqs1jLfXu" : { // Post
"category" : "Example 2",
"description" : "Test description .....",
"userID" : "SeMpRKxczcSCFpnkVDTU2pxCDV12"
}
}
}
And my call to the database is the following...
const posts: Post[] = [];
this.database.ref('posts').orderByChild('category').equalTo(this.category.name).once('value')
.then(
(posts) => {
//console.log(posts.val());
posts.forEach(function(childPost) {
var post = childPost.val();
var user = {};
firebase.database().ref('users/'+post.userID).on('value', function(snapshot){
user = snapshot.val();
// console.log(user);
});
var newPost = new Post(childPost.key, post.category, post.description, user );
//console.log(JSON.stringify(newPost));
posts.push(newPost);
});
}
).catch(
(err) => {
console.log(err);
}
);
The problem I'm having is that my view is rendered before the data from the second query is resolved. I have to leave the view and re-enter for the change detection to update my view.
I've read about things such as nested observables etc, but that just seems like overkill for what I'm trying to do ( Or I may be wrong ). Any help would be appreciated?
Related
I have a problem with GiftedChat, the messages appear completely disorganized in the app and even looking for messages directly from the firebase (where it is correct), the app does not get a logical order. When sending is organized, however the problem is when you load the messages. I'm completely lost
loadMessages = async () => {
const { user } = this.props;
const matchId = this.props.navigation.getParam('matchId');
const data = (await firebase.database().ref(`matchs/${matchId}/messages`).limitToLast(300).once('value')).val();
let messages = [];
if(data){
Object.keys(data)
.forEach(messageId => {
let message = data[messageId];
if(_.get(message, 'user._id') !== user.uid) _.push(message);
messages.push(message);
});
}
this.setState(() => ({
messages,
}));
}
My JSON:
{
"-LkAMYoS3fySk46Pbpan" : {
"_id" : "f5ba3d9a-c346-4f79-b371-c5d54798567e",
"createdAt" : 1563558815857,
"text" : "First message",
"user" : {
"_id" : "BVY4MDwSaaSDI2bAGjwkZlYktsK2",
"avatar" : "https://firebasestorage.googleapis.com/v0/b/wefound-760f2.appspot.com/o/users%2FBVY4MDwSaaSDI2bAGjwkZlYktsK2%2Fphotos%2Fk1xuqv26wdrjxoxmp8m.jpg?alt=media&token=7c16a0e4-2cb8-45a5-83a4-635d49c71180",
"name" : "Rafael"
}
},
"-LkAMZiITDxHE1WfCBGC" : {
"_id" : "c2755b48-136d-4a68-b283-377ebac7df8e",
"createdAt" : 1563558819564,
"text" : "Second message",
"user" : {
"_id" : "BVY4MDwSaaSDI2bAGjwkZlYktsK2",
"avatar" : "https://firebasestorage.googleapis.com/v0/b/wefound-760f2.appspot.com/o/users%2FBVY4MDwSaaSDI2bAGjwkZlYktsK2%2Fphotos%2Fk1xuqv26wdrjxoxmp8m.jpg?alt=media&token=7c16a0e4-2cb8-45a5-83a4-635d49c71180",
"name" : "Rafael"
}
},
"-LkAM_l4o_w_QeCsYRc8" : {
"_id" : "65772152-afd9-4353-b752-ac65978a536d",
"createdAt" : 1563558823838,
"text" : "Third message",
"user" : {
"_id" : "BVY4MDwSaaSDI2bAGjwkZlYktsK2",
"avatar" : "https://firebasestorage.googleapis.com/v0/b/wefound-760f2.appspot.com/o/users%2FBVY4MDwSaaSDI2bAGjwkZlYktsK2%2Fphotos%2Fk1xuqv26wdrjxoxmp8m.jpg?alt=media&token=7c16a0e4-2cb8-45a5-83a4-635d49c71180",
"name" : "Rafael"
}
},
"-LkAMcSSTOP7L1CwyiU4" : {
"_id" : "e69f3a72-0f4e-4c06-a763-518ef1984aa0",
"createdAt" : 1563558834859,
"text" : "Fourth message",
"user" : {
"_id" : "BVY4MDwSaaSDI2bAGjwkZlYktsK2",
"avatar" : "https://firebasestorage.googleapis.com/v0/b/wefound-760f2.appspot.com/o/users%2FBVY4MDwSaaSDI2bAGjwkZlYktsK2%2Fphotos%2Fk1xuqv26wdrjxoxmp8m.jpg?alt=media&token=7c16a0e4-2cb8-45a5-83a4-635d49c71180",
"name" : "Rafael"
}
},
"-LkAMduvBrEnUG6POGKt" : {
"_id" : "897b2042-25dc-46ec-a5f3-5bdc1fc355dd",
"createdAt" : 1563558840853,
"text" : "Fifth message",
"user" : {
"_id" : "BVY4MDwSaaSDI2bAGjwkZlYktsK2",
"avatar" : "https://firebasestorage.googleapis.com/v0/b/wefound-760f2.appspot.com/o/users%2FBVY4MDwSaaSDI2bAGjwkZlYktsK2%2Fphotos%2Fk1xuqv26wdrjxoxmp8m.jpg?alt=media&token=7c16a0e4-2cb8-45a5-83a4-635d49c71180",
"name" : "Rafael"
}
}
}
I gave console.tron.log () in the messages and they appear disorganized exactly the same is in the app, the problem is in the component?
1 - refers to the function that loads the messages.
2 - JSON file
There are two steps to ordering the data:
Telling the Firebase Database server to return the child nodes in the correct order.
Maintaining that order in your client-side code.
As far as I can tell your code does neither of these, which means the nodes end up in whatever order your client uses for JSON properties (which are by definition unordered).
Let's first see how to retrieve the data in the correct order from Firebase:
const snapshot = (await firebase.database().ref(`matchs/${matchId}/messages`).orderByChild('createdAt').limitToLast(300).once('value'));
The above orders all child nodes by the value of their createdAt property, then returns the last 300 in order to the client.
You'll note that I don't call val() here yet. The reason for that is that snapshot.val() returns a JSON object, and properties in a JSON object have no defined order. So if you call .val() too early, you lose the ordering information that the server returned.
Next up is processing them in the client-side code to not lose that order, which depends on using DataSnapshot.forEach():
data.forEach((message) => {
messages.push(message.val());
})
Finally, I am able to solve this problem by sorting the JSON which is coming to from the server based on the date and time(CreatedAT).
If the JSON data stored in a variable called discussion then your code should be
discussion.sort(function compare(a, b) {
var dateA = new Date(a.createdAt);
var dateB = new Date(b.createdAt);
return dateB - dateA;
});
In your case, data or messages is the one which holds the JSON. Add this code once you get the code in JSON format.
Thank you.
I'm new to MongoDB/Mongoose and have recently started a project using MongoDB, Node and Express and I have run into an issue where when trying to query with mongoose on an _id that exists the result is null. I've found similar issues but most state that mongoose is querying on a pluralized collection name, I have confirmed that is not my issue here by turning on debugging for mongoose and verifying the collection is properly named.
My document lives in the report db and customer collection, there currently is only one document in that collection.
{
"_id" : ObjectId("5c64881002ea07789e9444aa"),
"fields" : {
"Customer ID" : "_id",
"First Name" : "first_name",
"Last Name" : "last_name",
"Contact Type" : "opt_history.contact_type",
"Opt In Date" : "opt_history.opt_in_date",
"Opt Out Date" : "opt_history.opt_out_date",
"Location Opt In" : "opt_history.location_opt_in",
"Location Opt Out" : "opt_history.location_opt_out"
},
"filters" : {
"company" : {
"validation" : {
"required" : true
},
"query_value" : "company:%company_id%"
},
"opt_history" : {
"validation" : {
"required" : true
},
"query_value" : {
"opt_in" : {
"if" : {
"start_date" : {
"if" : {
"end_date" : "{$and: [{\"opt_history.opt_in_date\":{$gte: %start_date%, $lte: %end_date%}}, {\"opt_history.opt_out_date\":{$eq: null}}]}"
},
"else" : "{$and: [{\"opt_history.opt_in_date\":{$eq: %start_date%}}, {\"opt_history.opt_out_date\":{$eq: null}}]}"
}
},
"else" : "{$and: [{\"opt_history.opt_in_date\":{$ne: null}}, {\"opt_history.opt_out_date\":{$eq: null}}]}"
},
"opt_out" : {
"if" : {
"start_date" : {
"if" : {
"end_date" : "opt_history.opt_out_date:{$gte: %start_date%, $lte: %end_date%}"
},
"else" : "opt_history.opt_out_date:{$eq: %start_date%}"
}
},
"else" : "opt_history.opt_out_date:{$ne: null}"
},
"no_opt" : "{$or: [{\"opt_history\":null}, {\"opt_history.opt_out_date\":{$nin:[null]}}]}"
}
}
}
}
The code used to fetch the document is as follows (FWIW I've tried both the string _id and the ObjectId and the results are the same):
exports.run = function(req, res, next) {
console.log(req.query.id);
const report = require('../models/Report');
report.findById("5c64881002ea07789e9444aa").exec(function(err, result) {
console.log(err);
console.log(result);
res.send(result);
});
};
I've turned on debug for mongoose and can see that the query that is being formed is customer.findOne({ _id: ObjectId("5c64881002ea07789e9444aa") }, { projection: {} }) and I have attempted to run that query in mongodb directly and get the following error.
db.customer.findOne({ _id: ObjectId("5c64881002ea07789e9444aa") }, { projection: {} })
2019-02-15T11:27:51.425-0700 E QUERY [js] Error: error: {
"ok" : 0,
"errmsg" : ">1 field in obj: {}",
"code" : 2,
"codeName" : "BadValue"
} :
_getErrorWithCode#src/mongo/shell/utils.js:25:13
DBCommandCursor#src/mongo/shell/query.js:708:1
DBQuery.prototype._exec#src/mongo/shell/query.js:113:28
DBQuery.prototype.hasNext#src/mongo/shell/query.js:288:5
DBCollection.prototype.findOne#src/mongo/shell/collection.js:260:10
#(shell):1:1
It seems that the problem is with the "projection: {}" that mongoose appears to be injecting into my query. When I remove that piece the query will run fine in mongodb. So the question is, why does mongoose do this and what can I do to suppress it? I want the entire document so I don't need to project any fields.
I figured it out, it turns out I was trying to improperly switch dbs on my mongoose connection so the document I was searching for never actually existed because I was searching in the wrong db (at the time both DB's had a collection with the same name but that is changing).
I am working on a web application that allows some users to send others push notifications. The list of users "subscribed" to any one user is stored in a firebase database, the structure is as follows;
senderUserID
senderSubscribers
subscriberOneID
token: subToken
subscriberTwoID
token: subToken
subscriberThreeID
token: subToken
Basically I need to store the list of tokens in an array in order to use each of them to specify the clients receiving a push notification.
Here is my javaScript that returns the subscriber list 'Node'. In my case senderSubscribers is actually 'players'
firebase.database().ref('/users/'+currentUserId+'/players/').once('value').then(function(snapshot){
var allPlayersNode = snapshot.val();
console.log(allPlayersNode);
});
Console.log looks something like
firstUserID:Object
regID: the first subscribers token
secondUserID:Object
regID: the second subscribers token
JSON code:
"emails" : {
"kylebehiels#hotmail" : "vlHVHcIetlZp7zS8mm6GcwoFRsB3",
"test#gmail" : "hJuVfpGTJvRyP4qsA6xyyvgQDay1"
},
"users" : {
"hJuVfpGTJvRyP4qsA6xyyvgQDay1" : {
"email" : "test#gmail.com",
"first_name" : "kyle",
"games" : {
"placeholder" : "placeholder"
},
"last_name" : "behiels",
"practices" : {
"placeholder" : "placeholder"
},
"regid" : "c_z_KWeWorU:APA91bHORVZqSph-HikfxtO2XjvMnwudIymORoSm9t3gupKZ1fFIZCLxGUbX5dsrYooHHpeEiPXhcXEMu5Eo5-nwF8bBjw-OTwSbn_rujTuPVaCDOXUKp9mzIYqeZq7SFMaWssZfyKj7",
"workouts" : {
"placeholder" : "placeholder"
}
},
"vlHVHcIetlZp7zS8mm6GcwoFRsB3" : {
"email" : "kylebehiels#hotmail.com",
"first_name" : "kyle",
"games" : {
"placeholder" : "placeholder"
},
"last_name" : "behiels",
"players" : {
"hJuVfpGTJvRyP4qsA6xyyvgQDay1" : {
"playerRegID" : "c_z_KWeWorU:APA91bHORVZqSph-HikfxtO2XjvMnwudIymORoSm9t3gupKZ1fFIZCLxGUbX5dsrYooHHpeEiPXhcXEMu5Eo5-nwF8bBjw-OTwSbn_rujTuPVaCDOXUKp9mzIYqeZq7SFMaWssZfyKj7"
},
"vlHVHcIetlZp7zS8mm6GcwoFRsB3" : {
"playerRegID" : "cvZj3hb_zkk:APA91bEcmKlDlC5VKOI6wc1BvRI5mGgmWFA3QuTR3jH48l9fz565RhY2PEXE2GkXyhKXgb67qu7ieRlWF403q6rQPi0-xgGIbfvOkhGzopfyTFLNQg7ADgNHFAd1YfwwesbHjL5IHLZd"
}
},
"practices" : {
"placeholder" : "placeholder"
},
"regid" : "cvZj3hb_zkk:APA91bEcmKlDlC5VKOI6wc1BvRI5mGgmWFA3QuTR3jH48l9fz565RhY2PEXE2GkXyhKXgb67qu7ieRlWF403q6rQPi0-xgGIbfvOkhGzopfyTFLNQg7ADgNHFAd1YfwwesbHjL5IHLZd",
"workouts" : {
"placeholder" : "placeholder"
}
}
}
}
I have the collection "menu" with structure:
{
"_id" : "vJQr7EuieDHMuX2nx" "section" : "vine" "price" : "200" "category" : "bar"
"_id" : "vJQr7EuieDHMuX4rf" "section" : "beer" "price" : "200" "category" : "kitchen"
"_id" : "vJQr7EuieDHMuX5tg" "section" : "milk" "price" : "200" "category" : "kbar"
"_id" : "vJQr7EuieDHMuX4sc" "section" : "beer" "price" : "200" "category" : "kitchen"
"_id" : "vJQr7EuieDHMuX2xs" "section" : "vine" "price" : "200" "category" : "bar"
}
I want to show the user only "section" without repeating
For this I used
Menu.aggregate([{$group:{_id:"$section"}}])
but it's not working
How can I do this? May be to use a different method?
Server side:
Meteor.methods({
'getSections': function () {
var pipeline = [{$group:{_id:"$section"}}];
var result = Menu.aggregate(pipeline);
var sections = [];
for (i = 0; i < result.length; i++) {
sections.push(result[i]._id);
};
console.log(sections);
return sections;
}
});
result in the server: [ 'vine', 'beer', 'milk' ]
Client side:
Template.MobBarMenu.helpers({
'list': function(){
this.render('MobBarMenu', {});
},
'listSections': function () {
Meteor.call('getSections', function (err, data) {
console.log(data);
// return data;
});
}
});
template name="MobBarMenu"
{{#each listSections}}
{{this}}
{{/each}}
template
I don't know where to find the solution
You probably want an advanced publication. On your template code, add a named collection (only on client). Then you can create a helper to get the data and subscribe to the publication.
const Sections = new Mongo.Collection('sections');
Template.Menu.onCreated(function () {
this.subscribe('sections')
})
Template.Menu.helpers({
sections() {
return Sections.find();
}
});
Then, create a publication that writes to this.
Meteor.publish('sections', function () {
const handle = Menu.find({}, {
fields: {
_id: 0
section: 1
}
}).observe({
added: doc => this.added('sections', doc.section, doc),
changed: doc => this.changed('sections', doc.section, doc),
removed: doc => this.removed('sections', doc.section, doc)
});
this.ready();
this.onStop(() => handle.stop())
})
Meteor uses MiniMongo - it's own wrapper of nodejs MongoDB driver.
And by default collecions has no method aggregate. You can find it in a few packages, like monbro:mongodb-mapreduce-aggregation.
Or (which better imo) you can get access to raw collection with native to Meteor rawCollection method:
Menu.rawCollection().aggregate(pipeline, function(err, res) {
//do stuff here
})
Hey guys actually i got two question.
1.Im tying to get user info here is a list of what info i want (using facebook API):
user_likes, friends_about_me, user_birthday, email, user_location, user_work_history, read_friendlists, friends_groups, user_groups
here is my code for that:
Template.user_loggedout.events({
"click #fb":function(e,tmp){
Meteor.loginWithFacebook({
requestPermissions :
['user_likes',
'friends_about_me',
'user_birthday',
'email',
'user_location',
'user_work_history',
'read_friendlists',
'friends_groups',
'user_groups']
},function (err){
if(err){
console.log("error when login with facebook " + err);
} else {
console.log("login with facebook succeeded");
}
});
},
})
but my i ends up creating a user object with just some of the fields (user JSON object from mongoDB, i inserted "xxx" at some fields just for security):
{
"createdAt" : 1378842117154,
"_id" : "mW7urf5yZPCm6HhNK",
"services" : {
"facebook" : {
"accessToken" : "xxxx",
"expiresAt" : 1383945305007,
"id" : "xxxxxx",
"email" : "xxxx",
"name" : "Boaz",
"first_name" : "Boaz",
"last_name" : "xxx",
"link" : "https://www.facebook.com/xxxx",
"username" : "boazmier",
"gender" : "male",
"locale" : "he_IL"
},
"resume" : {
"loginTokens" : [
{
"token" : "TcLnp9GSbDasNZNCj",
"when" : 1378842117154
}
]
}
},
"profile" : {
"name" : "Boaz xxxx"
}
}
clearly you can see that there is no record for friends_list, user_birthday and much more.
second question:
same thing with github - i requset this: user, public_repo, avatar_url, gist
but end up with:
{
"createdAt" : 1378843359664,
"_id" : "pJGwTepYe2Ps7hhnS",
"services" : {
"github" : {
"id" : xxxx,
"accessToken" : "xxxxx",
"email" : "xxxxx",
"username" : "boazhoch"
},
"resume" : {
"loginTokens" : [
{
"token" : "hbNLcuC85MKwBJBfb",
"when" : 1378843359664
}
]
}
},
"profile" : {
"name" : "xxxx"
}
}
so i end up with no avatar but when changing my code on server to:
Accounts.onCreateUser(function(options,user){
var accessToken = user.services.github.accessToken,result,profile;
result = Meteor.http.get("https://api.github.com/user", {
params: {
access_token: accessToken
}
});
if(result.error){
throw result.error
}
profile = _.pick(result.data,
"login",
"name",
"avatar_url",
"url",
"company",
"blog",
"location",
"email",
"bio",
"html_url");
user.profile = profile;
return user;
});
my user object get the avatar_url why is that? i know that meteor ships out with Account.createUser and by doing onCreateUser im overriding it but then, what is the porpse of requestPermissions? also i can have only one Account.onCreateUser function, so how do i make this function request different data from each service? ( i want to include facebook,google,github,twitter and meetup)
Do you want the data to be available on the client to anyone with JS skills? If so, on saving things to the user record: The easiest thing to do would be to add any new information to the profile object, possibly grouped under a facebook object when from facebook. The profile object already is all published to the client. http://docs.meteor.com/#meteor_user
user.profile.prefs.color = 'red'
To keep it confidential, as you find things that should not make it to the client, write and read them from routines on the server. See
Meteor.call()
You can find code for how to get more data in the user record from the server to the client and back, where you essentially publish more. I wouldn't worry until there is a reason the profile object is insufficient.