I have a Firebase database structuring question. My scenario is close to a chat application. Here are the specifics
- users(node storing several users of the app)
- id1
name: John
- id2
name: Meg
- id2
name: Kelly
- messages(node storing messages between two users)
- message1
from: id1
to: id2
text: ''
- message2
from: id3
to: id1
text: ''
Now imagine building a conversations view for an individual user. So I want to fetch all messages from that particular user
and to that particular user
I am writing it as follows right now:
let fromMessagesRef = firebase.database().ref('messages').orderByChild('from').equalTo(firebase.auth().currentUser.uid)
fromMessagesRef.once("value").then((snapshot) => {/* do something here*/})
let toMessagesRef = firebase.database().ref('messages').orderByChild('to').equalTo(firebase.auth().currentUser.uid)
toMessagesRef.once("value").then((snapshot) => {/* do something here*/})
Questions:
Is this the right way to model the problem?
If yes, is there a way to combine the above 2 queries?
I would store the data like this:
- users(node storing several users of the app)
- id1
name: John
messages
message1: true
message2: true
- id2
name: Meg
messages
message1: true
message3: true
- id3
name: Kelly
messages
message2: true
message3:true
- messages(node storing messages between two users)
- message1
from: id1
to: id2
text: ''
- message2
from: id3
to: id1
text: ''
- message3
from: id2
to: id3
text: ''
Firebase recommends storing things like this. So in your case your query would be
let fromMessagesRef = firebase.database().child('users').child(firebase.auth().currentUser.uid).child('messages')
This allows it to be very fast as there is no orderBy being done. Then you would loop over each message and get it's profile from the messages node.
The structure you have is one possible way to model this data. If you're building an application like this, I would highly recommend the angularfire-slack tutorial. One potentially faster way to model the data would be to model the data like is suggested in this tutorial https://thinkster.io/angularfire-slack-tutorial#creating-direct-messages
{
"userMessages": {
"user:id1": {
"user:id2": {
"messageId1": {
"from": "simplelogin:1",
"body": "Hello!",
"timestamp": Firebase.ServerValue.TIMESTAMP
},
"messageId2": {
"from": "simplelogin:2",
"body": "Hey!",
"timestamp": Firebase.ServerValue.TIMESTAMP
}
}
}
}
}
The one thing you need to watch for in this case if you choose to do it like this is that before your query, you need to sort which user will be the "primary user" under whom the messages will be stored. As long as you make sure that is the same every time, you should be good to go.
One improvement you could make to this structure is something you already pointed out - flattening your data and moving the messages to another node - like you did in your example.
To answer your second question, if you were to keep that structure, I think you would need both of those queries, because firebase does not support a more complicated OR query that would allow you to search both at the same time.
No. Firebase Auth subsystem is where you want to store the email, displayName, password, and photoURL for each user. The function below is how you do it for a password-based user. oAuth-based users are easier. If you have other properties you want to store, like age for example, put those under a users node with each users uid that Firebase Authentication provides you.
function registerPasswordUser(email,displayName,password,photoURL){
var user = null;
//NULLIFY EMPTY ARGUMENTS
for (var i = 0; i < arguments.length; i++) {
arguments[i] = arguments[i] ? arguments[i] : null;
}
auth.createUserWithEmailAndPassword(email, password)
.then(function () {
user = auth.currentUser;
user.sendEmailVerification();
})
.then(function () {
user.updateProfile({
displayName: displayName,
photoURL: photoURL
});
})
.catch(function(error) {
console.log(error.message);
});
console.log('Validation link was sent to ' + email + '.');
}
As for the messages node, get the random id from Firebase Realtime Database's push method and use that as the id of each message under messages. Firebase queries are used:
var messages = firebase.database().ref('messages');
var messages-from-user = messages.orderByChild('from').equalTo('<your users uid>');
var messages-to-user = messages.orderByChild('to').equalTo('<your users uid>');
messages-from-user.once('value', function(snapshot) {
console.log('A message from <your users uid> does '+(snapshot.exists()?'':'not ')+' exist')
});
messages-to-user.once('value', function(snapshot) {
console.log('A message to <your users uid> does '+(snapshot.exists()?'':'not ')+' exist')
});
Define an index for messages-from-user and messages-to-user in your Rules:
{
"rules": {
"messages": {
".indexOn": ["from", "to"]
}
}
}
Below data structure gives you more flexibility with you data. Instead of having to store each messages that user sent back and forth I would suggest to store it in separate node and store the messageID with each user that is involved in the conversation.
Obviously you need to set the security rules, so other user can't see conversation if they are not in the conversation.
By doing this we are not creating deep chain node inside user info
- users(node storing several users of the app)
- id1
name: John
messages: [msID1, msID9]
- id2
name: Meg
messages: [msID1, msID7]
- id3
name: Kelly
messages: [msID9, msID7]
- messages(node storing messages between two users)
- msID1
from: id1
to: id2
text: ''
- msID7
from: id3
to: id2
text: ''
- msID9
from: id3
to: id1
text: ''
Firebase has actually built a demo (and extendible) chat application called Firechat. The source and documentation is provided, and of particular note is the section on their data structures.
Although they've implemented chatrooms, you can see that they've flattened their data structures as in many of the other answers. You can read more about how and why this is done in the Firebase guide.
Related
I am trying to embed docusign api to my application for signing documents from two parties. Until now I have been able to use the docusign "Request a signature through your app (embedded signing)" to allow a specific individual to sign the document through my app.
Now I want to add an additional user's email, who will be notified via email when the first user completes the signing procedure to sign his part of the document.
In the code, I have tried to add a second recipient/signer:
const signer2 = docusign.Signer.constructFromObject({
email: "blah#blah.com",
name: "Blah",
clientUserId: "blah",
recipientId: 2,
routingOrder: "2",
});
add his tabs:
const signHere2 = docusign.SignHere.constructFromObject({
anchorString: "/sn2/",
anchorYOffset: "-10",
anchorUnits: "pixels",
anchorXOffset: "0" });
const signer2Tabs = docusign.Tabs.constructFromObject({
signHereTabs: [signHere2] });
signer2.tabs = signer2Tabs;
and add this signer to the recipients list:
const recipients = docusign.Recipients.constructFromObject({
signers: [signer1, signer2],
});
env.recipients = recipients;
The first signer is able to sign the document embedded through my app and complete the process. However, the second signer never receives the email for signing his part when the first one completes. Any idea?
Remove clientUserId from the second signer like this:
const signer2 = docusign.Signer.constructFromObject({
email: "blah#blah.com",
name: "Blah",
recipientId: 2,
routingOrder: "2",
});
I'm sure this is a trivial question but can't seem to figure out how to access the players id in this firebase array. I need to be able to access all the players id's, and if the current users.id established at login matches one of the players id's firebase array then those games will be looped over with ng-repeat. I know how to accomplish the latter, I just can't figure out to access the players id's inside the unique id's; Hopefully that makes sense. Any help is greatly appreciated, thanks.
JS
(this is some of the code associated with my problem)
game.controller('games.controller', ['$scope', '$state', '$stateParams', 'Auth', '$firebaseArray','Fire', function ($scope, $state, $stateParams, auth, $firebaseArray, fire) {
$scope.games = $firebaseArray(fire.child('games'));
$scope.view = 'listView';
$scope.setCurrentGame = function(game) {
$scope.currentGame = game;
};
$scope.createGame = function() {
if ($scope.format == 'Match Play') {
$scope.skinAmount = 'DOES NOT APPLY';
$scope.birdieAmount = 'DOES NOT APPLY';
}
$scope.games.$add({
name: $scope.gameName,
host: $scope.user.name,
date: $scope.gameDate,
location: {
course: $scope.courseName,
address: $scope.courseAddress
},
rules: {
amount: $scope.gameAmount,
perSkin: $scope.skinAmount,
perBirdie: $scope.birdieAmount,
format: $scope.format,
holes : $scope.holes,
time: $scope.time
}
})
$state.go('games');
};
$scope.addPlayer = function(game) {
$firebaseArray(fire.child('games').child(game.$id).child('players')).$add({
id : $scope.user.id,
name : $scope.user.name,
email : $scope.user.email
});
}
// swap DOM structure in games state
$scope.changeView = function(view){
$scope.view = view;
}
}]);
You're violating two common Firebase rules here:
if an entity has a natural, unique id, store it with that id as its key
don't nest lists
I'm guessing #1 happened because you are storing the players using $firebaseArray.$add(). Instead of repeating myself, I'll list a few questions that deal with the same problem:
Firebase make user object from auth data
Adding users to firebase
How to bypass unique ID and reference child nodes
The nesting of lists is a common mistake. By having the players stored under each game, you can never load the data for a list of games, without also loading all players for all games. For this reason (and others) it is often better to store the nested list under its own top-level:
games
-Juasdui9
date:
host:
-Jviuo732
date:
host:
games_players
-Juasdui9
-J43as437y239
id: "Simplelogin:4"
name: "Anthony"
-Jviuo732
....
users
How do I pick the email address value from meteor Mongo user table?
I have written below query to pick the element:
users=Meteor.users.find({},{emails:1})
This the code I have written to fetch the email address, but I don't know how much it's affecting performance in the code:
users = Meteor.users.find({})
users.forEach(function(key,option){
key.emails.forEach(function (key,option){
console.log(key.address)
});
});
In meteor, you should call:
users = Meteor.users.find({}, { fields: { emails: 1 } })
Reference in docs
EDIT
Please remember users is a cursor object. Cursor objects can be handled directly in templates, and must be the return of publications. You can't iterate a cursor directly in a javascript loop.
Example: (remember authorization in production publications)
Meteor.publish('user-emails', function() {
return Meteor.users.find({}, { fields: { emails: 1 } });
});
If you want to directly access the user instances, for example to iterate them in a javascript code, you need to fetch the cursor (reference in docs).
Example:
var users = Meteor.users.find({}, { fields: { emails: 1 } }).fetch();
Now users is an array of users. Feel free to iterate them.
Example (I'm using underscore.js):
var users = Meteor.users.find({}, { fields: { emails: 1 } }).fetch();
_.each(users, function(user) {
console.log(user.emails);
});
Now, if you need a vector only with emails, one on each index, you can pluck the emails from a fetched array with underscore.js (reference of pluck)
var emails = _.pluck(Meteor.users.find({}, { fields: { emails: 1 } }).fetch(), 'emails');
Hope it works :)
if its not working, dont forget to return
return users
I'm new for mongodb.
i'm trying to understand how user privileges work on mongodb
i have a mongodb data base ( from mongoHQ sandbox 512 Mb free ).
and also i create a database and it contains some collections
here how it looks like
here is my database events and its collections (messages,*provider*)
events
- messages
- provider
what i want is :
i have 3 users with 3 different privileges on events database's different collections
here are 3 users with their privileges
providerUser : readonly access on provider collection and don't have access events database's other collections (and have no access on other data bases also )
eventreadUser : readonly access on message collection on events data base and don't have access to other collections on events database (and have no access on other data bases also )
eventreadWriteUser :only have read,write access on message collection on events data base and don't have access to other collections on events database (and have no access on other data bases also )
so i created a javascript that add these users to events database
addUsers.js
var conn = new Mongo('hostname:port');
var db = conn.getDB("events");
// For authentification on DB
db.auth('username', 'password');
function addCurlUserAndroidUser() {
var providerUser = {
user: 'providerUser',
pwd: 'providerUserPassword',
privileges: [{
resource: {collection: "provider" },
actions: [ "find"]
}],
roles: [ "read"]
};
var eventreadUser = {
user: 'eventreadUser',
pwd: 'eventreadUserPassword',
privileges: [{
resource: { collection: "messages" },
actions: [ "find"]
}],
roles: [ "read"]
};
var eventreadWriteUser = {
user: 'eventreadWriteUser',
pwd: 'eventreadWriteUserPassword',
privileges: [{
resource: { collection: "messages" },
actions: [ "find", "insert","remove","update"]
}],
roles: [ "readWrite"]
};
db.addUser(eventreadUser);
db.addUser(providerUser);
db.addUser(eventreadWriteUser);
}
after i execute this javascript by following command line :
mongo hostname:port/events addUsers.js
the problem is providerUser have access to read messages collection
and eventreadUser , eventreadWriteUser can also have access to read provider collection
please ask more information if needed or if there something not clearly explained
it will be very useful if i get some useful responses very quickly
Thank you
Dinesh
I am building an app using Meteor and need to access the stored email address of a logged-in user.
I am currently using:
var userObj = Meteor.user();
console.log(userObj);
to access the user. However, I am only able to access the id. The email address is stored in a nested object that looks like this:
[Object {address="address#gmail.com", verified=false}]
I have tried various ways to traverse the JSON object but can't figure out how to access the value I need.
Meteor.user().emails[0].address works for me.
Here's what the doc says:
By default the server publishes username, emails, and profile. See
Meteor.users for more on the fields used in user documents.
Example user document:
{
_id: "bbca5d6a-2156-41c4-89da-0329e8c99a4f", // Meteor.userId()
username: "cool_kid_13", // unique name
emails: [
// each email address can only belong to one user.
{ address: "cool#example.com", verified: true },
{ address: "another#different.com", verified: false }
],
createdAt: 1349761684042,
profile: {
// The profile is writable by the user by default.
name: "Joe Schmoe"
},
services: {
facebook: {
id: "709050", // facebook id
accessToken: "AAACCgdX7G2...AbV9AZDZD"
},
resume: {
loginTokens: [
{ token: "97e8c205-c7e4-47c9-9bea-8e2ccc0694cd",
when: 1349761684048 }
]
}
}
}
You don't specify how you are authenticating users. For example, if you were using Google authentication only, the email address would be found only in
Meteor.user().services.google.email
So, it depends.
Try this:
Meteor.user().emails[0].address
Regards,