I am using Firebase Auth and Firebase Database to store student's profiles and students reports.
When the user signs up they input email, password, school name, school year, school discipline. I use the email and password for the Auth but the rest of the info is stored in the Database with a unique ID as shown below:
For the reports, each students can input many entries, each with its unique ID as shown below:
Here are my questions:
Once a student logs in, how can I find their profile info since the parent is a unique ID. In other words is there a way to search through the database for that students email (for example, mido4#hotmail.com, in the image) and from that get the students name (in this case, Emina Osman)?
Once you get the student's name, how can you search for all the entries that student has saved in the database? For each entry the student has, the student name is saved.
Not sure if the way I setup the database is ideal so is there a better way?
Thanks for your time! Any help would be really appreciated!
Yes, it is. The simplest way to achieve this, is to change your database structure a little bit. So instead of using as a unique identifier, the pushed key, generated by the push() method, i suggets you using the email address. It's also unique and easy to use. The benefit is, that is allows you to search your database for that particular email. Your database structure should look like this:
flashscreen-1d252
|
--- Users
|
--- mido4#hotmail,com
| |
| --- //User Details
|
--- mido5#hotmail,com
|
--- //User Details
Note, that Firebase does not allow symbols as . (dot) to be used in a key. So as you probably see, i have changed the . dot with a , (comma). I have achieved this using the below method:
String encodeUserEmail(String userEmail) {
return userEmail.replace(".", ",");
}
To search for an user and get the name, simply add a listener on Users node like this:
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference userRef = rootRef.child("Users").child("mido4#hotmail,com");
ValueEventListener eventListener = new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(dataSnapshot.exists()) {
String firstName = dataSnapshot.child("firstname").getValue(String.class);
String lastName = dataSnapshot.child("lastname").getValue(String.class);
Log.d("TAG", firstName + " " + lastName);
}
}
#Override
public void onCancelled(DatabaseError databaseError) {}
};
usersRef.addListenerForSingleValueEvent(eventListener);
The output will be: Emina Osman
You'll have to search after the email address and not after the name.
Already answered above. This database structure is more efficient.
Edit: There 2 additional questions regarding this answer based on using the email address vs. uid.
What if a user decides to delete his account and after a while decides to return and tries to sign-in again? If we are using the email address, nothing happens. If we are using the uid, when the user signs in for the second time, another uid is generated, which is obvious that is different from the first one and this the moment in which you are in trouble, because even he is the same user, he is treated as a new one.
What if the users email address changes? IMHO, an email can be changed only if you decide to change it. Personally, I haven't changed my email address in years but I have deleted hundreds of accounts from hundreds of applications. Even if you change your email address, there's no much of a problem. You login in your application, change the email address and that's it. You'll have also all your history within that application. Having a unique identifier a uid, in case you delete the account and you come back again, you start from zero.
Once a student logs in, how can I find their profile info since the
parent is a unique ID.
Super easy! When a users account is created initially, Firebase assigns the user a 'random' and unchanging user uid (uid). That uid is what identified that specific user to Firebase. That's what you should store there info under within the users node like this:
users
uid_0
name: "users name"
Then when they authenticate in the future, firebase provides that uid to you in the authentication process. You can then simply get their user information from the users node via that uid. i.e. read the node /users/uid_0
Once you get the student's name, how can you search for all the
entries that student has saved in the database?
Again, super simple. For every entry you make in Firebase, reference that uid. For example, say you want to keep track of each users reports
reports
uid_0
-9i9sdjj3i0a09djads //create with push() or childByAutoId() in swift
reportName: "some report"
-ua9sd9i9i3i0idsfi
reportName: "another report"
Then to get all of their reports, read the node /reports/uid_0
conversely, you can store the reports and then a link to the user
reports
-9i9sdjj3i0a09djads
reportName: "some report"
report_by: "uid_0"
and with that structure a query can be done where report_by is equal to "uid_0" to return all of uid_0's reports.
Not sure if the way I setup the database is ideal so is there a better
way?
there's a number of different ways to achieve what you want but the above is a very common design pattern.
Related
Starting point:
I have a WebSphere with federated security (there is an Active Directory behind it).
I am trying to fetch a VMM user uid by his/her email address, but I a don't know how it's VMM (schema) attributes are mapped to the AD (schema) attributes of the underlying Active Directory entity (person, organizationalPerson objectClass, mail attribute.
(By describing it in a different way: If one have a look at the WAS console, in the "Users and Groups" -> "Manage Users" there is a table where there is an E-Mail column, so it is somehow mapped.
But, by clicking on the ( "Global Security" -> "(federated repositories) configure button" -> (there is a table, you can select the)) LDAP1 row, and checking the table in "Federated repositories property names to LDAP attributes mapping", I don't find that the 'E-Mail' column how has been mapped to the AD attribute. Maybe there is an implicit mapping?)
So, the starting question is this:
How to find this on the WAS console? Or, maybe via wsadmin (scripts)?
So, because of this, I tried to move forward and now I would try to find it using the VMM API, but I don't find in the official documentation the answer to the second question:
Is it possible to fetch somehow the assigned / available attributes of an WebSphere VMM entity (Virtual member manager)?
There is a lot of examples about how to fetch the attributes when you know their name, but there is nothing about this...
Yes, I know that is is a bit XY problem, but please guide me a bit.
Many thanks in advance.
To provide some code sample too, I am trying to fetch the user's uid by using the following code:
public String testFetch(String email) throws Exception
{
String returnAttr = "uid";
// here in the search expression what should I wrire instead of the 'mail'?
String vmmSearchExpr = String.format("#xsi:type='PersonAccount' and mail='%s'", email);
DataObject root = SDOHelper.createRootDataObject();
DataObject searchCtrl = SDOHelper.createControlDataObject(root, null, SchemaConstants.DO_SEARCH_CONTROL);
searchCtrl.setString(SchemaConstants.PROP_SEARCH_EXPRESSION, vmmSearchExpr);
#SuppressWarnings("unchecked")
List<String> props = searchCtrl.getList(SchemaConstants.PROP_PROPERTIES);
props.add(returnAttr);
Service service = new LocalServiceProvider(null);
DataObject searchRoot = service.search(root);
String result = "";
List<?> entities = searchRoot.getList(SchemaConstants.DO_ENTITIES);
if (entities.size() > 1) throw new RuntimeException("multiple users for an identity:" + vmmSearchExpr);
if (entities.size() > 0)
{
DataObject objdo = (DataObject) entities.get(0);
result = objdo.getString(returnAttr);
}else{
log("Got empty list There is no result.");
}
return result;
}
A possible solution is to add a new federal repository supported property (Name: mail, Property name: mail, Entity types: PersonAccount):
After a WAS restart I was able to use the search expression
#xsi:type='PersonAccount' and mail='<email address>'
and the code above to fetch the corresponding uid to the given email address.
It seems there is some info in the c:\IBM\WebSphere\AppServer\etc\wim\setup\wimdbproperties.xml, as if the "ibm-primaryEmail" would be the property that contains the email address, albeit I was not able to find my uid when I specified this instead of the "mail" attribute name.
How can I change multiple documents? I have 2 collections firestore. One (users) is for user account (email, name, etc).
To second collections (dashboard) users can add some message to a board. In this second collection is name, category, time etc.
What I want to do is when someone change his name or faculty in account, it will also change in second collection for each his comment so it will show up to date information
DocumentReference documentReference = fStore.collection("users").document(user.getUid());
Map<String, Object> edited = new HashMap<>();
edited.put("email",email);
edited.put("smallName",StringUtils.unaccent(profileName.getText().toString()).toLowerCase());
edited.put("fullName",profileName.getText().toString());
edited.put("fakulta",mySpinner.getSelectedItem().toString());
documentReference.update(edited).addOnSuccessListener(new OnSuccessListener<Void>() {
#Override
public void onSuccess(Void aVoid) {
Toast.makeText(EditProfile.this, "Profile data are changed", Toast.LENGTH_SHORT).show();
//startActivity(new Intent(getApplicationContext(),MainActivity.class));
finish();
}
Firestore
Is there any way to do it without changing the whole structure of my app?
To update the user names in the comment docs, you'll need to:
Query the collection to find the comments by the user who updated their name. If you stored the UID for each user in the comment document, this would look something like fStore.collection("dashboard").where("uid", "==", "theUidOfTheUserWhoUpdatedTheirName"). If you didn't store their UID, you'll have to query on the old value of their name instead, but the code will be similar.
Loop over the query results and update each document in turn. If you have a lot of these, you may want to read about the fastest way to do this here: What is the fastest way to write a lot of documents to Firestore?
There is a user with the attribute Role, by default TENANT, using a query we set him LANDLORD and in theHOUSE table he adds an apartment with various attributes: description, price, city_id and others. But suddenly this user wanted to remove himself from the status of LANDLORD, delete his apartments from our database and again become justTENANT, how in this case can I delete the information that he has apartments? How to do it, if he has apartments, then they need to be deleted, if not, then just change the user's status to TENANT?
At first there was an idea to assign a zero value, but it seemed strange to me if we just zeroed it out, because then the table will start to get cluttered. There is also a status option: ACTIVE or BANNED, but I don't like this option, because his apartment is still not needed.
The code looks like this:
#PutMapping ("/ {id}")
#PreAuthorize ("hasAuthority ('landlord: write')")
public void TenantPostAdd (#PathVariable (value = "id") Long id) {
User user = userRepository.findById (id) .orElseThrow ();
Role role = Role.TENANT;
user.setRole (role);
House house = houseRepository.findById (id) .orElseThrow ();
house ... // what's here
}
Full Code
To build this level of infrastructure, there are a lot of questions I would have to ask to recommend something. I'd want to see the current database schema as well. Your also requesting the ability to delete which can become problematic. You may want to consider leaving data if you believe that the customer may change roles again. That kind of information is based off of the terms of agreement.
Have you considered building something like this?
Absolute(Numeric) Mode
0 No Permission --- etc...
https://www.guru99.com/file-permissions.html
This could be a prepared statement issue with not the appropriate joins occurring in the statement. I believe you should take another look over your database schema.
I am inserting a data in my Request Node in Firebase database in Android using this,
public void submitRequest(View v) {
Request myUserInsertObj = "Pending";
rootReference.child("Request").child("Pending").child(firebaseuser.getUid()).setValue
(myUserInsertObj);
}
This is my Request Class.
Public class Request{
public String request_status;
public Request(String request_status){
this.request_status = request_status;
}
Request()
}
I found in firebase documentation that I can use firebaseuser.getDisplayName to get the current logged in user's name. But where will the .getDisplayName get the user's name since I created my own login form and user registration in my app.
Question 2:
If I do this, is this possible? Because I want to put a name in requesting guest node so that when I retrieve it in my HTML web admin panel the data will be easier to read.
rootReference.child("Request")
.child("Pending")
.child(firebaseuser.getUid())
.setValue(myUserInsertObj + firebaseuser.getDisplayName);
If so what should I add in my Request Class?
Question 3.
How do I add timestamp I know timestamp is very important in data insertion on every system.
I found in firebase documentation that I can use firebaseuser.getDisplayName to get the current logged in user's name.
Yes, that correct. Calling getDisplayName() on a FirebaseUser object:
Returns the main display name of this user from the Firebase project's user database.
Regarding the second part of your question:
But where will the .getDisplayName get the user's name since I created my own log in form and user registration in my app.
As explained above, getDisplayName() is getting you the name that is coming from the authentication process. If you want to get the user name from your custom user object then you should first get the user object from the database and use it where it is needed.
Because I want to put a name in the request node so that when I retrieve it in my HTML web admin panel the data will be easier to read.
If you want to add the name in your node, you should pass the display name to the child() method and not to the setValue(). Your code should look like this:
rootReference.child("Request")
.child("Pending")
.child(firebaseuser.getUid() + "_" + firebaseuser.getDisplayName())
.setValue(myUserInsertObj);
This code will generate a child that might look like this:
Firebase-root
|
--- Request
|
--- Pending
|
--- SxbVg0...uobvk1_Theodore
|
--- //user details
DatabaseReference class has 4 overloaded setValue() methods but none of this methods allow you to pass an object along with a String as arguments.
Question 3. How do I add timestamp I know timestamp is very important in data insertion on every system.
This is how you add and get the timestamp that you were talking about.
I have generated the push id of the user who have sign up.
String id=databaseUser.push().getKey();
How I can get this id when the user signs in?
Please help me on this.
You can store that id in the database as a property of your User object but a more elegant way would be to use the uid and not that random key provided by the push() method. So once you are authenticated, you can get the uid, using the following line of code:
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();