Firebase ensuring a record is not repeated - java

I have this code:
public static void insertvote(String userkey, String categ, String candId) {
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference totalVotesRef = rootRef.child("votes").child(categ).child(candId);
Vote vote = new Vote(userkey);
totalVotesRef.push().setValue(vote.getVoterEmail());
}
It is supposed to insert votes into a firebase database, in a votes collections as below:
The idea is to ensure a voter only votes once due to integrity by checking if their email exists in the votes. However, as you can see, a voter can vote twice.
Is there a way I can make a function that checks whether the email exixts in the database, and if it does exist, tell the voter they cant vote twice?
Thanks in advance.

You should use the Firebase Auth UID of the current user as the name of the child key.
String uid = FirebaseAuth.getInstance().getCurrentUser().getUid();
totalVotesRef.child(uid).setValue(vote.getVoterEmail());

I will give you an idea, think about it. If you use
Shared Preferences, you can put every e-mail address with a boolean flag(True/False - Voted/not). Then, you can call the Shared Preferences and check the boolean value of the flag for every e-mail address. So, if it’s true - don’t allow him to vote.
Wish I helped :)

Related

How to update user data without changes into Firestore when it's checking for already existence in database?

I recently implemented unique username into my app when registering, all good far here, by the way I also need to set it to when the user is editting it's profile.
I tried to do the same thing, but I'm facing an issue here. The app don't let save the profile, because it's checking if the username's taken and, as we're already using one, it won't let us do it.
Ex.: My username is "bob", I changed my profile pic or my display name, so when I click to save, the app will do a username checking in the background and will not let me save it because the username is already taken, but the problem is that it's already my user.
I've tried to set this, but failed:
if (document.equals(setup_username.getText().toString()) || document.isEmpty()){
updateProfile();
Here's my code:
setup_progressbar.setVisibility(View.VISIBLE);
FirebaseFirestore.getInstance().collection("Users").whereEqualTo("username",setup_username.getText().toString()).get().addOnCompleteListener((task) -> {
if (task.isSuccessful()){
List<DocumentSnapshot> document = task.getResult().getDocuments();
if (document.equals(setup_username.getText().toString()) || document.isEmpty()){
updateProfile();
} else {
setup_progressbar.setVisibility(View.INVISIBLE);
setup_username.setError(getString(R.string.username_taken));
return;
}
} else {
setup_progressbar.setVisibility(View.INVISIBLE);
String error = task.getException().getMessage();
FancyToast.makeText(getApplicationContext(), error, FancyToast.LENGTH_SHORT, FancyToast.ERROR, false).show();
}
});
So how to get around this and only forbid it when I try to change my username to another that is taken? Like: "bob" to "bill", but "bill" is already taken, so it won't allow.
You'll need to have some indication in each Users document to indicate which user has claimed that specific name. Given that you store the username inside the document, ownership would typically be established by using the UID of the user as the ID of the document.
Once you have run your query to find the document for the username, you can then check the UID of the owner of that username against the currently signed in user. If the two UIDs are the same, the current user owns the username and is allowed to update the document.
Compare new username with previous username(store it in a variable while displaying user profile data), if both are same don't update it all else check for its uniqueness.
or if you don't have existing username data create relationship with that document and fetch previous username first.

Firebase insertion overwites instead of appending

I have this database structure in firebase:
Its supposed to collect votes. I have several categories such as president, minister...etc. All people who vie for a "President" seat are listed using their unique keys, and Im trying to collect all voters emails.
This is my code:
public static void insertvote(String userkey, String categ, String candId) {
System.out.println("Returned candidate's userkey or ID: "+userkey);
System.out.println("Returned category: "+categ);
DatabaseReference rootRef = FirebaseDatabase.getInstance().getReference();
DatabaseReference totalVotesRef = rootRef.child("votes").child(categ).child(candId);
Vote vote = new Vote(userkey);
totalVotesRef.setValue(vote.getVoterEmail());
}
The problem I have is that, when another user logs in and votes, instead of appending their email, its being overwritter to the existing email.
How can I resolve this?
Thanks
Instead of using setValue() at the location to collect emails, which always overwrites existing data, you should look into using push() first, which generates a new child using a random ID. You will want to become familiar with the documentation on working with lists of data, especially appending to a list of data.
totalVotesRef.push().setValue(vote.getVoterEmail())
Each email will appear under that randomly generated child value.

how can i update another user balance base on id in firebase?

i am doing android studio project using Firebase,currently i had done with the registration and login,but my function is to transfer amount to another users with balance.
So what i need to do is to find the way to modify/update the balance content(value) after transferring the amount to another user.
but i do some researched they say Firebase only can only read/write with own user account.
assume i have already login to user1,and want to do the transfer to user2.
thanks.
-Users
-AsjdnskkhdiioAndmmnekwas
Name:"User1"
email:"user1#hotmail.com"
uid: "Adjshdkjwwnekwihwoi4kdnw4l2"
age:20
Balance:100
-DdmkenklahaoinskaAnmdmls
Name:"User2"
email:"user2#hotmail.com"
uid: "Cbdnaknekmmalsmdlen1qnio2"
age:21
Balance:100
-------------------------------------------------------------------
**how to become:**
-Users
-AsjdnskkhdiioAndmmnekwas
Name:"User1"
email:"user1#hotmail.com"
uid: "Adjshdkjwwnekwihwoi4kdnw4l2"
age:20
Balance:50
-DdmkenklahaoinskaAnmdmls
Name:"User2"
email:"user2#hotmail.com"
uid: "Cbdnaknekmmalsmdlen1qnio2"
age:21
Balance:150
Remember that you are manager all information of all users (in your database).
Use DatabaseReference setValue() method to update information inside database like this:
private DatabaseReference mDatabase;
mDatabase = FirebaseDatabase.getInstance().getReference();
String senderId = "sample_id_sender";
String receiverId = "sample_id_receiver";
mDatabase.child("users").child(senderId ).child("Balance").setValue(new_balance1);
mDatabase.child("users").child(receiverId).child("Balance").setValue(new_balance2);
How to know id of receiver??? it depends on your situation, example, you want to send 1$ to user whose name is "User2". You must find user2 id in list of users, in you database, I think these guide is good for you:
https://firebase.google.com/docs/database/android/read-and-write
https://firebase.google.com/docs/database/android/lists-of-data
Interesting problem. I think you can use a combination of a modified data model, a transaction and server-side security rules to implement this.
Step-wise:
Since you'll need to know the current balance of both users in order to determine their new balance, you'll need to use a transaction.
You'll need to store the actual transaction in some way, as otherwise the server won't be able to validate the write. The minimum info here is from UID, to UID and amount.
In your security rules you should then modify that the total balance across the two accounts is unmodified across the transaction. For this you need the data from step 2, as you otherwise can't know the to account.
Finally, in your security rules you'll want to validate that the only account whose balance can go down is the one of which the current user is the owner. In rules something like "balance": { ".validate": "data.child('uid').val() == auth.uid || newData.child('Balance').val() > data.child('Balance').val()" }
Scalability may be an issue though, as it's modifying nodes in multiple child nodes which means the transactions run across the entire top-level branch.
Also see:
Is the way the Firebase database quickstart handles counts secure?

Filtering by grandchild property in firebase [in android]

CONTEXT :
Hi, I'm currently working on an Android project backed by Firebase.
I have set up a denormalized data structure that relates polls to users (many-to-many relationship) via way of votes. Here is an image displaying the votes path of my database. The structure is as follows :
votes -> [pollUid] -> [votePushuid] -> vote object
So in this example we have a single poll that has 4 votes.
I want to run a check to see if a user has already voted on a poll. In order to do this I fetch the pollsUid, then run through its votes to see if any of them contain the voterUid property == to their user uid.
This is done as follows :
FirebaseHandler.getInstance().getMainDatabaseRef()
.child(FirebaseConstants.VOTES) //votes root
.child(pollKey) //polluid
.orderByChild("voterUid")
.equalTo(FirebaseHandler.getInstance().getUserUid())
.limitToFirst(1)
.addListenerForSingleValueEvent(new ValueEventListener() {
#Override
public void onDataChange(DataSnapshot dataSnapshot) {
if(!dataSnapshot.exists()) {
If the datasnaptshot exists then we know that the user has already voted on this poll and can handle it in the Java logic.
PROBLEM :
The datasnapshot received by onDataChange is always null (ie does not exist) when searching for a specific user's vote on a specific poll. I know for a fact that the vote exists in the db through inspecting the data, and that the userUid is correct via debugging. Removing the equalTo and limitToFirst returns all of the votes for the poll without a problem so clearly the stem of the ref is correct. This implies to me that the issue is created by one of the two methods just mentioned. Even stranger is the fact that this approach does work at certain times, but not at others.
QUESTION :
How do I return a list of firebase stored objects filtered by a grandchild property? If this is not possible what would be the more appropriate datastructure for this problem?
On a further note I've seen people taking the approach of using Query instead of Databasereferences. Perhaps this might have something to do with the current issue.
Your query is correct. I have no problem running that query using my own DB. It's probably the userId doesn't match. DatabaseReference extends Query, that's why you can access Query's methods.
A database structure alternative would be
{ "users_votes": {
"<userId>": {
"<pollId1>" : true,
"<pollId2>" : true,
"<pollId3>" : true
}
}
}
Set the value to that node once the user voted to a poll.
To check if the user has voted for a poll
usersVotesRef.child(FirebaseHandler.getInstance().getUserUid())
.child(pollKey).addListenerForSingleValueEvent(valueEventListener);

Google App Engine HRD query without ancestor

I have a GAE project written in Java and I have some thoughts about the HRD and a problem that I'm not sure how to solve.
Basically I have users in my system. A user consists of a userid, a username, an email and a password. Each time I create a new user, I want to check that there isn't already a user with the same userid (should never happen), username or email.
The userid is the key, so I think that doing a get with this will be consistent. However, when I do a query (and use a filter) to find possible users with the same username or email, I can't be sure that the results are consistent. So if someone has created a user with the same username or email a couple of seconds ago, I might not find it with my query. I understand that ancestors are used to work around this problem, but what if I don't have an ancestor to use for the query? The user does not have a parent.
I'd be happy to hear your thoughts on this, and what is considered to be best practice in situations like these. I'm using Objectify for GAE if that changes anything.
I wouldn't recommend using email or any other natural key for your User entity. Users change their email addresses and you don't want to end up rewriting all the foreign key references in your database whenever someone changes their email.
Here's a short blurb on how I solve this issue:
https://groups.google.com/d/msg/google-appengine/NdUAY0crVjg/3fJX3Gn3cOYJ
Create a separate EmailLookup entity whose #Id is the normalized form of an email address (I just lowercase everything - technically incorrect but saves a lot of pain when users accidentally capitalize Joe#example.com). My EmailLookup looks like this:
#Entity(name="Email")
public class EmailLookup {
/** Use this method to normalize email addresses for lookup */
public static String normalize(String email) {
return email.toLowerCase();
}
#Id String email;
#Index long personId;
public EmailLookup(String email, long personId) {
this.email = normalize(email);
this.personId = personId;
}
}
There is also a (not-normalized) email field in my User entity, which I use when sending outbound emails (preserve case just in case it matters for someone). When someone creates an account with a particular email, I load/create the EmailLookup and the User entities by key in a XG transaction. This guarantees that any individual email address will be unique.
The same strategy applies for any other kind of unique value; facebook id, username, etc.
A way around the HRD's eventual consistency, is to use get instead of query. To be able to do this is you need to generate natural IDs, e.g. generate IDs that consists of data you receive in request: email and username.
Since get in HRD has strong consistency, you will be able to reliably check if user already exists.
For example a readable natural ID would be:
String naturalUserId = userEmail + "-" + userName;
Note: in practice emails are unique. So this is a good natural ID on it's own. No need to add a made-up username to it.
You may also enable cross-group transactions (see https://developers.google.com/appengine/docs/java/datastore/overview#Cross_Group_Transactions) and then in one transaction look for the user and create a new one, if that helps.
Recommend avoiding an indexed field and query unless you have other uses for it. Here is what I have done before (Python) using key_name (since entity ids need to be ints). Easy to use either the key_name or id for other entities that need to link to user:
username = self.request.get('username')
usernameLower = username.lower()
rec = user.get_by_key_name(usernameLower)
if rec is None:
U = user(
key_name = usernameLower,
username = username,
etc...)
U.put()
else:
self.response.out.write(yourMessageHere)

Categories