Meteor React Subscribe to specific collection data - javascript

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.

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.

Firebase retrieve data when certain child value within child changes

So I have a firebase realtime database which keeps users messages. At the moment I have it so when a users 'inbox' child is updated it retrieves the latest data for that child, for example:
firebase.database().ref('users/'+firebase.auth().currentUser.uid+'/inbox/').on('child_changed', function(snapshot) {
// get the latest data
});
But when using this method, if a message is deleted by the user, it will retrieve the child data again. Is there anyway to only retrieve the latest data only if a child inside of that child is updated?
So in my database I have it set out like this:
{
"users" : {
"IPenCEXKICROYWzYtyzybzhem9R2" : {
"inbox" : {
"32tcqIRaG2RAgb9dTwmcgKoVPhd2IPenCEXKICROYWzYtyzybzhem9R2" : {
"lastmessagetimestamp" : 1564139876817,
"conversationmessages" : {
"-LhKZ-MUVxbyRcqp-Q_c" : {
"from" : "32tcqIRaG2RAgb9dTwmcgKoVPhd2",
"messagetext" : "this is a test message",
}
},
},
},
},
}
}
I am saving that timestamp of the last message a user receives. So I would like it to only retrieve data if the 'lastmessagetimestamp' child value is changed.
The simplest way I could think of doing this would be:
firebase.database().ref('users/'+firebase.auth().currentUser.uid+'/inbox/**conversationid**/lastmessagetimestamp/').on('child_changed', function(snapshot) {
// get the latest data
});
But because my 'conversationid' is randomly generated and is not unique, it cannot be done. Is there a way to possibly skip the 'conversationid' and go directly to the 'lastmessagetimestamp'?

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.

Firebase two-way relationships / Retrieving data

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.

Creating a filter in the view using AngularFire/Firebase query

I am building an app in which the user inputs tastings, and those tastings are displayed in a few places - on the user's personal tastings page, and on a "latest tastings" style page.
On the tastings page, when a button is clicked, a "basicTastings()" function is called, and that's where I want to query the DB and for the view to update with only those tastings which have the "Type: Basic" (thee is also type: advanced).
In that function, my test console log is properly outputting that a key() is basic/advanced. Where I'm lost is in then pushing that object into the scope so that only those objects now go through the ng-repeat.
Finally - does it even make sense for me to be doing it this way? I was approaching it in this manner instead of through, say, a jQuery .click() filtering idea because I thought it was a better approach if I end up with large amounts of data in the DB.
My data is set up as:
{
"tastings" : {
"-JlUCGqbLssTeob7weQI" : {
"brewdate" : "2015-03-28T07:07:04.880Z",
"origin" : "Panama",
"overallrating" : 4.5,
"roaster" : "Verve",
"roastname" : "Los Lajones Honey",
"subdate" : 1427526458869,
"thoughts" : "Cup Characteristics: This Honey-Process coffee from Los Lajones is abdundantly juicy and filled with lovely stone fruit notes like Bing cherry and apricot. The body is like a perfectly tempered ganache.",
"type" : "basic",
"user" : "simplelogin:19"
}
},
"simplelogin:19" : {
"date" : 1427086513603,
"email" : "test#test.com",
"regUser" : "simplelogin:19",
"username" : "Tester"
}
}
}
My controller for listing the tastings is:
myApp.controller('TastingsListController',
function($scope, $firebaseArray, $rootScope, $location, Authentication, FIREBASE_URL) {
$scope.$emit('callForAuth'); // Emitter to initiate $onAuth function in Authentication service
var ref = new Firebase(FIREBASE_URL + '/tastings');
var tastingsInfo = $firebaseArray(ref);
$scope.filterBasic = function() {
ref.orderByChild("type").on("child_added", function(tasting) {
console.log(tasting.key() + ' is a ' + tasting.val().type + ' tasting.');
if(tasting.val().type === "basic") {
tastingsInfo.push(tasting);
console.log(tastingsInfo);
}
});
};
});
And finally the relevant ng-repeat and the button to trigger the function:
<md-button class="md-raised md-accent" ng-click="filterBasic()" flex>
Basic
</md-button>
</div>
</md-toolbar>
<md-content class="md-padding">
<div class="basiccontainer" ng-repeat="tasting in tastings | orderBy: 'brewdate'">
EDIT: The data I'm getting in my console.log occurs after each tasting that is "basic", which is where it should be happening, but it contains the entire array of objects, when I want it to contain the object only, and then I want to put that object into an array that I can then iterate on with ng-repeat.
Edit 2: I think I'm approaching this whole thing wrong:
I've been reading and re-reading, and I think I see now where my approach is, basically, exactly what the Firebase docs are saying not to do:
Regarding my data structure and filtering by user for their own tastings: Currently, I'm not indexing anything meaning that, as the data changes, I have to iterate through everything to see who owns what.
What I SHOULD be doing in my case is to, when a user submits a Tasting, to have a user index within the tasting, even if it's just going to ever have one creator, also also have the tasting's key indexed with the user, e.g.:
Tasting:
"tastings" : {
"-JlUCGqbLssTeob7weQI" : {
"brewdate" : "2015-03-28T07:07:04.880Z",
"origin" : "Panama",
"overallrating" : 4.5,
"roaster" : "Verve",
"roastname" : "Los Lajones Honey",
"subdate" : 1427526458869,
"thoughts" : "Cup Characteristics: This Honey-Process coffee from Los Lajones is abdundantly juicy and filled with lovely stone fruit notes like Bing cherry and apricot. The body is like a perfectly tempered ganache.",
"type" : "basic",
"user" : {
"simplelogin:18": true,
},
},
User:
"users" : {
"simplelogin:18" : {
"date" : 1427062799596,
"email" : "test#test.com",
"regUser" : "simplelogin:18",
"username" : "Tester",
"tastings" : {
"-JlUCGqbLssTeob7weQI": true,
"<Tasting 2 Key>": true,
...
},
},
This setup will allow me to actually set up a two-way relationship between a tasting and its creator. I can do this all through the same submit function for when the user presses the "Submit" button - send the data for the tasting, as well use that tasting's key over to the user's tastings index.
Regarding my user of the controller vs a service: What I'm doing right now is trying to do everything in the controller.
What I SHOULD be doing is to, from the controller, call a "listingsfilter" service (or whatever I decide to call it) when the function within my controller is called via the button press. That service itself should be doing the actual work of querying the Firebase for the listings, putting them into an array of some kind, and sending that back out to the controller, who then just feeds that data to the $scope through which the ng-repeat will iterate.

Categories

Resources