I have the following data structure , users and services have many to many
relationship
users : {
user1 : {
name : blah,
email : a#a.com,
services : {
servicekey1 : true,
servicekey9 : true
}
}
}
services : {
servicekey1 : {
name : blahserve,
category : blahbers,
providers : {
user1 : true,
user7 : true
}
}
}
I want to get the list of user objects for a service. How can this be done with and without angularfire.
I came up with this query (without angularfire) -
var refu = new Firebase("https://myapp.firebaseio.com/users");
var refs = new Firebase("https://myapp.firebaseio.com/services");
refs.child("servicekey1/providers").once("value",function(s){
var users = s.val();
angular.forEach(users, function(v,k){
refu.child(k).once("value", function(su){
console.log(su.val());
})
})
});
This solves my purpose but I feel there should be a better way to do it , may be with angular fire. Please suggest if there are any other/ better ways to achieve this?
Related
so what i am doing is sending this data to the the firebase firestore here's my code its working fine,
final data = {
"sent_requests" : {
"sender" : "${loggedInUser.uid}",
"receiver" : args.uid,
"details" : {
"date" : detail_class.datetime,
"total_dishes" : detail_class.total_dishes,
"total_people" : detail_class.total_people,
"meals" : detail_class.meals,
"location" : detail_class.location,
}
}
};
dynamic db = FirebaseFirestore.instance;
db.collection("users").doc("${loggedInUser.uid}").set(data, SetOptions(merge: true));
This result is fine, but this code just overwrites the previous request. I instead want to add more requests in it,
when the sender and receiver are different.
Field names are unique in Firestore, so you can't have (for example) two receiver fields.
What you can have is a single field that is an array of values. I'd typically call that receivers (plural) to indicate that it's a multi-value field, and you'd write that from your code with:
final data = {
"sent_requests" : {
...
"receivers" : [args.uid],
...
}
};
This sets the receivers field to an array with just the value or args.uid. If you want to merge the args.uid with any existing values in the database already, you can use an array-union operator:
final data = {
"sent_requests" : {
...
"receivers" : FieldValue.arrayUnion([args.uid]),
...
}
};
Now the args.uid value will be added to the receivers array, unless it's already in there.
I have a class User that embeds a JsonObject to represent the user's fields. This class looks like that:
class User {
private JsonObject data;
public User(...) {
data = new JsonObject();
data.put("...", ...).put(..., ...);
}
public String getID() { return data.getString("_id"); }
// more getters, setters
// DB access methods
public static userSave(MongoClient mc, User user){
// some house keeping
mc.save("users", user.jsonObject(), ar -> {
if(ar.succeeded()) { ... } else { ... }
});
}
}
I've just spent more than half a day trying to figure out why a call to user.getID() sometimes produced the following error: ClassCastException: class io.vertx.core.json.JsonObject cannot be cast to class java.lang.CharSequence. I narrowed down to the userSave() method and more specifically to MongoClient::save() which actually produces a side effect which transforms the data._id from something like
"_id" : "5ceb8ebb9790855fad9be2fc"
into something like
"_id" : {
"$oid" : "5ceb8ebb9790855fad9be2fc"
}
This is confirmed by the vertx documentation which states that "This operation might change _id field of document parameter". This actually is also true for other write methods like inserts.
I came with two solutions and few questions about doing the save() properly while keeping the _id field up to date.
S1 One way to achieve that is to save a copy of the Json Object rather than the object itself, in other words : mc.save("users", user.jsonObject().copy(), ar -> {...});. This might be expensive on the long run.
S2 An other way is to "remember" _id and then to reinsert it into the data object in the if(ar.succeeded()) {data.put("_id", oidValue); ...} section. But as we are asynchronous, I don't think that the interval between save() and the data.put(...) is atomic ?
Q1: Solution S1 make the assumption that the ID doesn't change, i.e., the string 5ceb8ebb9790855fad9be2fc will not change. Do we have a warranty about this ?
Q2: What is the right way to implement the saveUser() properly ?
EDIT: The configuration JSON object user for the creation of the MongoClient is as follows (in case there is something wrong) :
"main_pool" : {
"pool_name" : "mongodb",
"host" : "localhost",
"port" : 27017,
"db_name" : "appdb",
"username" : "xxxxxxxxx",
"password" : "xxxxxxxxx",
"authSource" : "admin",
"maxPoolSize" : 5,
"minPoolSize" : 1,
"useObjectId" : true,
"connectTimeoutMS" : 5000,
"socketTimeoutMS" : 5000,
"serverSelectionTimeoutMS" : 5000
}
I do have a Mongo collection that stores albums with predefined "slot" for its images and feel a bit stuck if there a way to loop over the properties of the collection in order to display images in separated divs.
I did used this code for mapping over the album covers and it worked great:
albums() {
return Albums.find().fetch();
}
{this.albums().map( (album) => {
return <div key={album._id}><img src={album.cover} /></div>
})}
But now I ask you to help, is it possible to loop over photoOne, photoTwo, etc... and skip/don't display data if it is empty like in photoThree for example.
{
"_id" : "CHMHbNWWwZGaLGvB6",
"title" : "Text",
"cover" : "link",
"createdAt" : date,
"photoOne" : {
"titleOne" : "Text",
"coverOne" : "link"
}
"photoTwo" : {
"titleTwo" : "Text",
"coverTwo" : "link"
}
"photoThree" : {
"titleThree" : "",
"coverThree" : ""
}
}
I'm not a Mongo user, but in the map function you can check for the existing values, and handle it there. Something like (there is surely a cleaner way, though):
this.albums().map( (album) => {
for (key in album){
if (key.startsWith('photo')){
var title = Object.keys(album[key])[0];
if (album[key][title].length != 0){
console.log("Can use: " + Object.keys(album[key])[0])
}
}
}
})
Results in:
Can Use This: titleOne
Can Use This: titleTwo
Hope that helps, but it seems like having the photos with photoOne, photoTwo you are limiting the number of photos to use and requiring the need to use Object.keys to get the values out (without specifically using album.photoOne, album.photoTwo, etc.
If the album photos were stored in an embedded document, you could just include the photos and titles that existed and avoid having to check for empty ones. You would just loop through the photos that are present....if that makes sense.
I have this helper
myClub: function(){
var currentUserId = Meteor.userId();
var user = Meteor.users.findOne({_id: currentUserId});
return user;
}
I want it to return user.role
Here is my user in MongoDB
{
"_id" : "RdirmrLG3t8qBk4js",
"createdAt" : ISODate("2016-04-17T19:40:56.877Z"),
"services" : {
"password" : {
"bcrypt" : "$2a$10$cPe92XR9DT238bH/RanYEu.J6K2ImvAEbWOcVq6j9luI0BH08Qdly"
},
"resume" : {
"loginTokens" : [
{
"when" : ISODate("2016-04-17T19:51:49.474Z"),
"hashedToken" : "uVKUj/7JEkkOuizXhjl212Z38E47HXCex+D4zRikQ1k="
}
]
}
},
"username" : "worker",
"role" : "worker",
"club" : "hzSKAJfPXo7hSpTYS"
}
The code above works just fine. So it finds the current user and outputs info about it. But when I change user to user.role I get the following errormessage.
TypeError: Cannot read property 'role' of undefined
at Object.myClub
How can it be undefined? Is my syntax incorrect?
Template helpers are reactive, which means they update themselves as the app state changes or new data appears. In your case, the helper is called immediately when the template is rendered and before the Meteor.users collection is filled. Therefore, the .findOne() method returns undefined. It will be corrected in the second pass after new data arrives.
The simple fix here is to check whether the data is present inside the helper:
myClub: function(){
var currenUserId = Meteor.userId();
var user = Meteor.users.findOne({_id: currenUserId});
if(!user) return 'NO DATA';
return user.role;
},
In real life you'll probably want to wait for the basic data to be loaded before you render the template. That is usually done on the controller level.
Try:
myClub: function(){
return Meteor.user() && Meteor.user().role;
}
This is shorthand for return the role if there's a user.
As far as the role field not showing up, make sure that you are publishing that key from the server and subscribing to it. For example:
Meteor.publish('me',function(){
return Meteor.users.find(this.userId,{fields: {role: 1, username: 1, profile: 1, emails: 1}});
});
And on the client:
var me = Meteor.subscribe('me');
if ( me.ready() ) console.log("Ta-da! The role is: "+Meteor.user().role);
make sure that you subscribed to all data you need.
By the way, you can try following:
role: function(){ return (Meteor.user() || {}).role; }
Cheers
I'm dont think that this is a Meteor specific question, but rather around mongo and building mongo queries.
If I have the following structure,
{
username : someName,
contacts : [
{
userid : asdfae33rtqqxxx,
name : contactName,
status : friend
}
{
userid : asdfae33rtqqxxx,
name : anotherName,
status : pending
}
{
userid : asdfae33rtqqxxx,
name : contactName,
status : blocked
}
]
}
How could I pass in values from this array into a query against the users collection, to a) get the users in this array, or b) get all users in this array from the users collection with a particular status.
If this is not possible, how should I adjust my schema in order to make these sorts of queries possible?
This function will return a Meteor.users cursor based on an array of contacts and an optionally required status:
var usersByContacts = function(contacts, requiredStatus) {
var userIds = _.chain(contacts)
.map(function(c) {
if (requiredStatus) {
if (c.status === requiredStatus)
return c.userid;
} else {
return c.userid;
}
})
.compact()
.value();
return Meteor.users.find({_id: {$in: userIds}});
};
You can use it like this:
var users1 = usersByContacts(thing.contacts);
var users2 = usersByContacts(thing.contacts, 'pending');
This assumes thing has the schema referenced in your question. Also note that if you find this is a common pattern in your code, you should consider turning usersByContacts into a transform.