I'm developing an Android app that uses the Parse.com online database, and i'm having some difficulty wrapping my head around the relationship between classes.
I have the following classes defined: User (default), PrivateInfo, Relationships.
The relationships class is a many-to-many class between users (users stored as pointers), and the PrivateInfo class contains information specific to a user (stored as a pointer) to be accessed only by said user or users present in a relationship. For example, if user A is in a relationship with user B, then A should be able to access this privateInfo.
I have an activity where the private info from all users is to be shown. How can I build a query that will fetch the privateInfo from all users in a relationship with the current user? I've seen the usage of innerqueries in the documentation and I think that's what I should be using, but the usage of ParseObjects is making this confusing to me, since i'd have to work with PrivateInfo and Relationship ParseObjects, and not a User parseObject, which is what is being pointed by the privateInfo class.
An easily missed part of the documentation is the Relations Guide that explains the different types of relationships in much more detail than I can in an answer.
In regards to doing a query for "userA", looking to get all PrivateInfo objects that are for users that have a Relationship record that points to "userA":
ParseQuery<ParseObject> relationQuery = ParseQuery.getQuery("Relationship");
relationQuery.whereEqualTo("user", userA);
ParseQuery<ParseObject> privateInfoQuery = ParseQuery.getQuery("PrivateInfo");
// find records where PrivateInfo.user == Relationship.relatedToUser
privateInfoQuery.whereMatchesKeyInQuery("user", "relatedToUser", relationQuery);
// if you also need the User so you can show info about them
privateInfoQuery.include("user");
This assumes your classes have the following columns:
PrivateInfo
user : Pointer<_User>
Relationship
user : Pointer<_User>
relatedToUser : Pointer<_User>
If you need to get Private info for reverse relationships too, it gets trickier. I can provide a sample OR query if needed.
You might want to look over the REST Api docs on ACL and on security..
Wnen you have User A in relation w/ B, you can UPD ACL READ on B's privateInfo , giving A read access.
Then you can just do an 'include=ClassPointedTO' on any query and the extra class ( private info will be inlined in response to flat query with the sensitive data ).
If u drop the relation you must keep info consistent by ALSO updating the ACL element to drop read on the dropped target of the dropped relation.
--EDIT--
So when the Privateinfo entry is created, you can grant ACL READ to the creating user if you want. Or you can grant ACL READ to a Role entry that the creating user is in. Or you can do both. You would also need ACL WRITE by the creating user.
After that, in any query by any user you would get the following on a direct Query on Privateinfo or on a query utilizing "?include=Privateinfo" ...
Users with READ ( either direct or via a Role which the user belongs to ) will have in the response those Privateinfo entries where they have READ.
Related
I wanna ask about the concept and logically ways to give another user the privilege to access other's users' data. What I want to do exactly is like this :
Basically, collection 1 contains several Users ID (UID) from authentication, then the user will have their own data collected in collection 2 which contain the data ID.
So, it's like giving access to another user to collaborate with the data like Google Docs Apps where we can add another user to edit our documents. I've been thinking of how to do this, but still, I got stuck.
My question is, how can I possibly do this? cause from what I've read, cloud firestore don't use such a foreign key like MySQL. Thank You
haven't tried something like this but i think this approch overcomes your problem.
modify your structure according to above image. userID collection will contain userIds which are allowed to edit their parent collection.and create firestore rules according to your use to check weather the userId is allowed to edit the Collection or not.
in your case when 'user 2' will have reference to 'collection 2', he/she will try to change data. firebase rule will check if auth.userId is inside the 'collection2.UserIDs' or not and will allow according that.
My idea is to create a like&unlike system for posts (a user can like and unlike a post) and to show a list of users in a fragment, ordered this way: the top user is the one for which there are the most numerous likes.
In my Firebase Firestore database:
I have a collection of posts. Each document (which is a post) contains the ID of the user that created it, and a counter of likes (I call it "A").
I have another collection, of users. Each document (which is a user) contains a counter (I call it "B") that counts for the number of likes, all posts combined.
For the moment, I use "B" to order the users in the list of users I've described above. I use "B" this way: return FirebaseFirestore.getInstance().collection("users").orderBy("likes", Query.Direction.DESCENDING);.
However, due to some technical facts in relation to Firebase Security Rules, and to simplify the database structure, I would want to only use "A", and remove "B". More precisely, I would want to get all the users, and order them according to "A". However, the counter "A" is defined in the documents of the collection of posts, whereas the counter "B" was defined in the documents of the collection of users: that makes it easier to use.
So my question is: for each user, I would have to get all their posts, then sum up their counter "A", then get all the users, then order them according to this sum. However, I would want to know: is it possible to do this using Firebase Firestore Android API? (something like the instruction given before) In others words: How to order documents from a collection, according to the field of documents of another collection? But note that in fact it's more difficult, since I must sum up the counter "A" of each post created by the user.
What you're trying to do is essentially a "join" type operation, which is not supported by Firestore. A Firestore query can only consider documents in a single collection at a time. If you have data from two collections to use for the purpose of generating query results, you will have at least two queries to perform.
The only real alternative you have is to introduce a third collection with data already merged from the other two collections that supports the query you want to make. This is common in NoSQL type databases, and is called "denormalization".
I am attempting to search for all users who are a part of a given club/organization. I would have both the clubsOrgsId and clubsOrgsName. In the list of users these two data points are in an object named by the clubsOrgsId in a list called clubsOrgs, as seen below. Is this possible to do with a Firebase query; I would rather not run the search locally.
This app is being written in Kotlin but an answer is appreciated regardless of programming language.
Your current data structure allows your to efficiently find the club orgs for a given user. It does not allow you to efficiently find the users for a given club org. If you want that, consider adding an inverted data structure to allow it:
CLUBORGS
$cluborgid
users
$uid: true
With this additional structure in place, the lookups are super easy.
Also see:
Firebase query if child of child contains a value
Firebase Query Double Nested
Many to Many relationship in Firebase
Ok, so in this scenario we have three users that download my app: Mike, Suzy, and John. I want to store an Entity object on the Datastore using this code, for each user:
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
Entity e = new Entity("Person",someKey);
e.setProperty("name",name);
e.setProperty("age",age);
ds.put(e);
So when the three users named above use my app for the first time, my Google Cloud Datastore shows three entities of kind, "Person" with name properties: "Mike", "Suzy", and "John", and the cooresponding age properties.
Lets say that John opens the app and wants to see who else is using the app, how can I get those entities and display them to john so that he sees Suzy and Mike's names?
Basically, I want to be able to share data between all users by storing entities on the Datastore, and granting access to other users' entities to every user.
Another scenario could be an implementation of a High Scores list: Let's say I only want to keep the top 5 all time high scores across all users:
Entity e2 = new Entity("HiScore",aKey);
e.setProperty("score",score);
ds.put(e);
So when a user gets a new score, I would pull all existing entities, check the score properties on all of them and update the datastore accordingly by replacing a score. At the same time, I'd also want this data update pushed to other devices.
So here are two general examples along the lines of what I would like to accomplish (I know about google-play API that handles high scores for you, so don't tell me to use that because I'm not necessarily trying to implement high scores). In short I want to create a feed of the current entities that exist on my Datastore that can be seen by all users
Is using datastore entities the right way to go for this?
This is a very broad question (or set of questions), but to answer some of your individual points:
Lets say that John opens the app and wants to see who else is using the app, how can I get those entities and display them to john so that he sees Suzy and Mike's names?
That's a very simple query, like this:
DatastoreService ds = DatastoreServiceFactory.getDatastoreService();
Query q = new Query("Person");
List<Entity> persons = ds.prepare(q).asList(FetchOptions.Builder.withDefaults());
You then say
I want to be able to share data between all users by storing entities on the Datastore, and granting access to other users' entities to every user
Users don't own entities; your Cloud Platform project owns entities. So it's up to your application to determine who can see what data. There's nothing inherent in Datastore preventing Suzy from seeing Mike's Person entity unless your application puts rules in place.
Finally,
Another scenario could be an implementation of a High Scores list: Let's say I only want to keep the top 5 all time high scores across all users...
... So when a user gets a new score, I would pull all existing entities, check the score properties on all of them and update the datastore accordingly by replacing a score.
Depending on your traffic you may find that solution doesn't scale well; I would recommend reading this case study.
I'm new to Parse.com and was having trouble designing the structure of my database, and how to retrieve the desired data.
In the database, each user (primary identifier as email) has a list of friends and a status boolean. The friend list contains the email of other users in the database. I need to get the status boolean for each of the friends in a particular users list, and preferably in a single query to the server.
What would be a good way to design our structure and retrieve this data. Currently, I made two data classes (tables), one containing each user with their boolean status, and another containing each user and their list of friends. Firstly I was not sure if this structure is the correct way to go. Secondly, I don't know how to retrieve the status boolean for each user in a single users friend list.
Edit I actually discovered the relation column type just yesterday, but I was unable to figure out how to use it. 1) How do I link a Persona to a User in code? I understand I need to use ObjectID here, but how?
2) How do I add other Personae (friends) to a relation of a single Persona (the user). I was unable to populate this relation column. I understand query can be used on the relation column, but I couldn't reach that far ahead without populating the relation column.
3) In my query to the server, am I pulling the entire table? Lets say a user has 2 friends. Is there a way for me to fetch only the current user, and the two friends, or am I pulling the entire table, and then doing my filtering on it. I am concerned with the network being burdened if my table of users grows big.
Edit Well I couldn't figure out relational queries perfectly just yet, however, I found a good solution to my problem. Since the list of friends changes very rarely, I'll be maintaining this list offline, resulting in a single query to the server of pulling in the status of my friends. Along with this list, I may or may not also decide to pull in my own data and get an updated friend list. Thank you for your help though.
The way to model many-to-many relations in parse is with the relation column type. This is the best choice to describe how a user has many friends who are users. If this is a social-network-like app, another good bit of advice is to create a class -- distinct from the parse User -- that describes users' public personae.
This is so you can have the parse User class remain as the private, customer relationship between your app and a real person (there are built in security constraints here). This other table, say we call it Persona, can have a pointer-typed column to its user, keep such things as nickname, profile image, etc. and also keep your boolean status.
_User class - default stuff that comes standard with parse, plus anything pertaining to the customer relationship with your app.
Persona - pointer to _User table, boolean status, other public info, relation called "friends" relating this to other Persona.
So, given a logged in user and his/her currently selected persona (your choice whether users may have more than one personae), you can get friends' personae as follows (in pseudo code):
friendsRelation <- myPersona.friends
friendsQuery <- friendsRelation.query // query is a method on relation
run friendsQuery asynch, then the result will be allFriendsPersonae
for each persona in allFriendsPersonae
status <- persona.status
If you choose not to take the persona class advice, the "code" above is the same, just replace persona with user.
Edit - in response to question edit:
1) Link a persona the user by setting the persona's user column (pointer type) to the user object. To get that persona later, when you only have a user, query the persona table where "user" column equals user.
2) Relation implements an add() method. If you have a personaA, and want to add personaB as a friend, you getRelation("friends") on personaA, and send it add(personaB).
3) The query you get from a relation is a query only for members of that relation. So if personaA has two friends personaB and personaC, you'll get only B and C when you run personaA's friends query.