Firebase two-way relationships / Retrieving data - javascript

I would like some explanations about data retrieving in firebase. I'm not an expert in NoSql data structure and something is missing in my mind. It's not natural for me. However, I think i've understood the basics of Two-way relationships in this kind of structure.
Here is my data structure :
{
// Two-way relationships between accounts & logements
"accounts" : {
"uniqueId1" : {
"nom" : 'My name 1',
"email" : 'My email 1',
"logements" : {
uniqueIdLogement1 : true,
// do I have to list relationships with images & geofire here ?
uniqueIdLogement2 : true
},
"favorites" : {
uniqueIdLogement2 : true,
uniqueIdLogement25 : true,
uniqueIdLogement32 : true
}
},
....
},
// Each logement has his own "images" & "geofire" data
"logements" : {
"uniqueIdLogement1" : {
"nom_du_logement" : 'My logement name 1',
"accounts" : {
uniqueId1 : true
},
"images" : {
uniqueIdImages1 : true,
uniqueIdImages2 : true,
uniqueIdImages3 : true,
},
"geofire" : {
uniqueIdGeofire1 : true
},
},
...
},
"images" : {
"uniqueIdImages1" : {
"image" : 'My image URL 1',
"logements" : {
uniqueIdLogement1 : true
}
},
...
},
"geofire" : {
"uniqueIdGeofire1" : {
"g" : 'My geofire Data,
"l" : {
0 : '-44.34',
1 : '-3.2'
},
"logements" : {
uniqueIdLogement1 : true
}
},
...
}
}
I think everyone has his own point of view about data structure but in my opinion (in reference of Firebase Docs) it has to be quite like that. But if you see some updates, don't hesitate !
So, In angularJs, i would like to list each "logements" for account with "uniqueId1" for exemple and display their own "images" & geofire data (and check if they are my user's favorites logements). Is it possible to do that ?
// list every logements for account ID1
// for each logement take images data & geofire data & check if favorites
// push infos in $scope.items & display with ng-repeat
Another question relative to that : When i remove a "logements" for User ID1, i want to remove also all images & geofire references ... ! Is it possible to do that too ?
// remove uniqueIdLogement1 from account ID 1
// remove images where "logements" = uniqueIdLogement1
// remove goofier where "logements" = uniqueIdLogement1
I think that if i understand that correctly, it will be okay for me ! I just can't see right now how it works and it's frustrating because i know there is a lot of potential with this kind of database. Can you explain me please some details about that . Thank you very much

With any database you'll often need to join data from multiple locations to build your view.
In relational databases, you can get the data from these multiple locations (tables in that case) with a single statement, by using a JOIN clause.
In Firebase (and many other NoSQL databases) there is no built-in way to join data from multiple locations. So you will have to do that in your code.
var ref = firebase.database().ref();
var accountRef = ref.child('accounts/uniqueId1');
accountRef.on('value', function(accountSnapshot) {
var logementCount = accountSnapshot.child('logements').numChildren;
var logementLoadedCount = 0;
accountSnapshot.child('logements').forEach(function(logementKey) {
var logementRef = ref.child('logements').child(logementKey.key);
logementRef.once('value', function(logementSnapshot) {
var logement = logementSnapshot.val();
logementLoadedCount = logementLoadedCount + 1;
if (logementLoadedCount == logementCount) {
console.log('We've loaded all logements');
}
});
});
});
Many developers from a SQL and traditional web development background worry about the performance of all those nested calls. In Firebase that is not needed (for reasonable amounts of data), since Firebase pipelines the requests over a single connection. See Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly.
In general I highly recommend reading this article on NoSQL data modeling for a good introduction to the general topic.

Related

Update database entry using mongoose

Hello i am using mongoose.
I have built this query that finds my desired project :
const projects = await ClientManagers.findOne({'project.contactPerson.work_email' : 'testing#email.com'} , { 'project.$.companyName': 1 });
this returns an object from my database like this :
{
'projectName' : 'x',
'companyName' : 'x bv'
}
How can i update the company name to be 'Y bv' instead of 'x bv'.
Assuming this is your document structure,
{
"_id" : ObjectId("5f2ae5a4b1549ac0460920dd"),
"projectName" : "A",
"project" : [
{
"companyName" : "T1",
"contactPerson" : {
"work_email" : "t1#gmail.com"
}
},
{
"companyName" : "T2",
"contactPerson" : {
"work_email" : "t2#gmail.com"
}
}
]
}
Single Update updateOne()
If you know email will be unique and want to update single document then use updateOne().
first is query part to find condition, email t1#gmail.com
second is set/update part, here $ is for array because project is an array, update companyName to T1 Company
await ClientManagers.updateOne(
{ 'project.contactPerson.work_email': 't1#gmail.com' },
{
$set: { "project.$.companyName": "T1 Companmy" }
}
)
Multiple Update updateMany()
If email is not unique and want to update everywhere then use updateMany(), it will update every matching documents.
await ClientManagers.updateMany(
{ 'project.contactPerson.work_email': 't1#gmail.com' },
{
$set: { "project.$.companyName": "T1 Company" }
}
)
Not suggesting update() method to use, because its deprecated in mongoose and will give Deprecation Warnings
, this function is replaced with updateOne(), updateMany() and replaceOne() methods.
Good start. Mongo has better documentation with examples. I suggest you to refer that also.
use update
db.collection.update({companyName:'x bv'}, {"$set":{"companyName":y}})
Mongo is case sensitive. So name should match exactly.
update updates one document. To update multiple, use updateMany or multi:true option with update or findOneAndMondify for one update for find and update case.

Restricting writing in firebase to both "mainsibbling" and "slavesibbling" nodes (if "slavesibbling" has a variable key)?

I am trying to restrict writing to two related firebase nodes. I have a the following data structure on firebase:
"mainsibblings" : {
"-L9ygIWI-TKeNZvQ-TmP" : {
"MACaddress" : "111111111111",
},
},
"slavesibbling" : {
"111111111111" : {
"onMainSibling" : "-L9ygIWI-TKeNZvQ-TmP",
}
}
The slavesibbling pushkey is a unique MAC Address (freely created and updated by the user).
Conditions:
1) user should have permission to write a new mainsibbling and slavesibbling
2) shouldn't have permission to overwrite neither (mainsibbling & slavesibbling) if slavesibbling key (in this case: "111111111111") already exists.
3) the "owner" should be able to update both node even after he creates them
So if someone types a MAC Address that is registered already, the child nodes can't be updated, but if the user (referenced in ownerID) want to update a MAC Address he has created he should be able to do it.
How do I write the firebase database rules to control this?
I am trying this to prevent duplicates but it's not working:
"mainsibblings": {
".read": true,
"$pushKey": {
".write": "data.child('MACaddress').val() != newData.child('MACaddress').val()",
}
},
"slavesibbling": {
"$MACaddress":{
".read": true,
".write": "!data.hasChild(newData.val())",
}
}
This is the write operation I want to allow/disallow (I have replaced the MACaddress variable for a hardcoded MACaddress):
function AddSibblings(newMainSib, newSlaveSib) {
var MACaddress = "111111111111";
var ownerID = "QXXds7d33ceyecc4inoe33p_3";
mainRef.child(MACaddress).set(this.newMainSib);
otherRef.child(MACaddress).set(this.newSlaveSib)
mainRef.child(MACaddress).child('ownerID').set(ownerID);
otherRef.child(MACaddress).child('ownerID').set(ownerID);
otherRef.child(MACaddress).child('onMainSibbling').set(MACaddress);
}
There is no way in Firebase security rules to find whether a specific value exists in a list of children. If you want to ensure that a value is unique, you should use that value as the key of a collection.
For example in your case you can easily change the data to use the MAC address as the key for both mainsiblings and slavesiblings:
"mainsiblings" : {
"111111111111" : {
"MACaddress" : "111111111111",
},
},
"slavesibbling" : {
"111111111111" : {
"onMainSibling" : "111111111111",
}
}
And
"mainsiblings" : {
"111111111111" : {
"MACaddress" : "111111111111",
},
"223344556677" : {
"MACaddress" : "223344556677",
}
},
"slavesibbling" : {
"111111111111" : {
"onMainSibling" : "111111111111",
},
"223344556677" : {
"onMainSibling" : "223344556677",
}
}
Since property names are by definition unique in JSON, there is no way to have duplicate MAC addresses in this data structure.
Also see:
Firebase android : make username unique
How do you prevent duplicate user properties in Firebase?
Firebase security rules to check unique value of a child #AskFirebase
unique property in Firebase
Enforcing unique usernames with Firebase simplelogin

Only download Firebase snapshots that have child key

My data structure looks like this (removed unnecessary parts):
{
"threads" : {
"PUSHID" : {
"info" : {
"members" : {
"uid" : true,
"uid2" : true
}
}
}
}
}
I'm trying to write some javascript to pull snapshots of threads a user is in, but I can't figure out a way for it to work without pulling snapshots of each thread. This is my code now that pulls each thread snapshot.
firebase.database().ref('threads').on('child_added', function(snapshot) {
if (snapshot.hasChild('info/members/' + userUid)) {
// Display thread info
}
});
I tried to make a query with .orderByChild('info/members/' + userUid) and removing null snapshots, but I would have to add a .indexOn for each userUid which is obviously not practical.
Your current structure makes it easy/efficient to look up the users for a thread. But your use case is to look up the threads for a user. You'll need to augment your data model to allow the use-case:
{
"user_threads" : {
"uid": {
"PUSHID": true,
"PUSHID2": true
},
"uid2": {
"PUSHID": true,
"PUSHID3": true
}
}
}
And then read it with:
firebase.database().ref('user_threads/'+userUid).on('child_added', function(snapshot) {
...
});
Modifying/expanding your data model to match the use-cases of your app is quite common when using NoSQL databases.

Meteor React Subscribe to specific collection data

I am building a React - Meteor web app and I need to get access to a specific piece of data from a collection.
There are three main components, the company list, the project list and the task list. When I list all of the companies, I can select one and then display all of the project associated with that company. What I want to then do is click on a project and see all of the tasks which are associated with that project. My data structure is as follows (as you can see, projects are an array of objects):
{
"_id" : "aQnrkqMi6ugEvav4a",
"owner" : "7Gp49ZCtGC9oEx3jN",
"createdAt" : ISODate("2017-05-08T15:52:27.777Z"),
"data" : {
"name" : "lkhgb",
"contacts" : [
{
...
}
],
"projects" : [
{
...
},
{
"name" : "ljhvljhvblhkjvblhkj",
"id" : "258757206",
"tasks" : [
"task1",
"task2"
]
}
]
}
}
In my TaskList Component, I'm exporting it like so:
export default createContainer((props) => {
const {companyId} = props.match.params;
Meteor.subscribe('company');
return {project: Company.findOne(companyId)};
}, TaskList)
Where I'm pulling the companyId off the props. I am also pulling off the projectId from props, but when I query the collection it's just returning undefined. I have published the GitHub repo as live here - https://github.com/GlueDigiStu/ClientManager and would appreciate any help.
You did indeed subscribe to the "company" publication on the server.
Although that publication does not exist.
Meteor.publish('company', function (companyId) {
const publications = [];
publications.push(Company.find(
{
_id: companyId,
},
));
return publications;
});
This will publish the id you want from the server.
Then the subscription from the client would be:
Meteor.subscribe('company', props.companyId);
Let me know if you have any further questions.
If I may I'll also give you some suggestions. Having documents in documents in documents isn't really great for publications.
I would suggest to place projects and tasks into new collections and only subscribe to tasks once their project is selected.

Extjs 4 gridrow draws blank after model save

I have a couple of grids, divided in an accordion layout. They basicly show the same kind of data so an grouped grid should do the trick, however it looks really good this way and so far it works good too.
Left of the grids there is a form panel which is used to edit grid records, when I click on a record in the grid the appropriate data shows up in the form. I can edit the data, but when I click the save button, which triggers an 'model'.save() action, the related grid row draws blank and a dirty flag appears. I checked the model and the 'data' attribute doesn't contain any data but the id, the data is present in the 'modified' attribute.
I read that the red dirty flag means that the data isn't persisted in the back-end, but in this case it is. The request returns with a 200 status code and success : true.
The onSave method from the controller:
onSave : function() {
// Get reference to the form
var stepForm = this.getStepForm();
this.activeRecord.set( stepForm.getForm().getValues() );
this.activeRecord.save();
console.log( this.activeRecord );
}
The step store:
Ext.define( 'Bedrijfsplan.store.Steps', {
extend : 'Ext.data.Store',
require : 'Bedrijfsplan.model.Step',
model : 'Bedrijfsplan.model.Step',
autoSync : true,
proxy : {
type : 'rest',
url : 'steps',
reader : {
type : 'json',
root : 'steps'
},
writer : {
type : 'json',
writeAllFields : false,
root : 'steps'
}
}
} );
Step model:
Ext.define( 'Bedrijfsplan.model.Step', {
extend : 'Ext.data.Model',
fields : [ 'id', 'section_id', 'title', 'information', 'content', 'feedback' ],
proxy : {
type : 'rest',
url : 'steps',
successProperty : 'success'
}
} );
Step grid
Ext.define( 'Bedrijfsplan.view.step.Grid', {
extend : 'Ext.grid.Panel',
alias : 'widget.stepsgrid',
hideHeaders : true,
border : false,
columns : [ {
header : 'Titel',
dataIndex : 'title',
flex : 1
} ]
} );
I spend a couple of hours searching and trying, but I still haven't found the solution. Some help on this matter would be appreciated :)
Your model updating code:
this.activeRecord.set( stepForm.getForm().getValues() );
Should work, but I might try splitting it into two lines and setting a breakpoint to verify that getValues() is returning what you're expecting.
Also ensure that you have the name attribute set for each field in your form and that it matches exactly to the names of fields in your model.
Finally, it's better to call .sync() on the store rather than .save() on the model when you're working with a model that belongs to a store. They option autoSync: true on the store will make this happen automatically each time you make a valid update to one of its models.
The BasicForm.loadRecord and BasicForm.updateRecord methods provide a nice wrapper around the functionality you're seeking that may work better:
onRowSelected: function(activeRecord) {
stepForm.getForm().loadRecord(activeRecord);
}
onSaveClick: function() {
var activeRecord = stepForm.getForm().getRecord();
stepForm.getForm().updateRecord(activeRecord);
activeRecord.store.sync();
}
The only oddity I see is with your: this.activeRecord.set( stepForm.getForm().getValues() );
I've always used .set() on the store never on the record. e.g.:
myDataStore.set( stepForm.getForm().getValues() );

Categories

Resources