Avoiding same chat when using Firestore - android

I'm making now a chat app by using the Firestore database and im thinking how to avoid the same chat to be created twice.
For example, if person A sends message to person B, I had like the message to enter the same chat collection as if person B send the message to person A.
I have found the following suggestion here
It recommends to compare the users UID which I know and construct some chatID based on the combination of those string.
private String setOneToOneChat(String uid1, String uid2)
{
if(uid1 <uid2){
return uid1+uid2;
}
else{
return uid2+uid1;
}
}
comparing the length of both UID doesn't work since all (or at least all I have seen are from the same length).
However, first I didn't really understand how to use math operator such as < on a string, second im not sure if it really catches all cases.
If there are any suggestions to implement such thing I would like to hear.
Thank you

The ordering should be based on the actual content of the strings, not just on their length.
So in Java/Android:
if(uid1.compareTo(uid2) > 0){
return uid1+uid2;
}
else{
return uid2+uid1;
}

Related

Flutter-Dart Firebase Get a Summe from Docs

I wanna get a Summe from Firebase with Flutter-Dart, i can with Stream Builder all Datas receiving but i wanna get a Summe-Total from Orders. For Example how much money did i earn for this month ? or How much money i have to Pay to my Owner ? My Firebase Database look like this,
[Firebase Struktur][1]
[1]: https://i.stack.imgur.com/E5zsM.png
I wanna get the Grand Total for all "betrag", i tried this like it , but its coming a null, how can i all Documents as List with choosed Feld with For Loop Grand total Calculating and show in the Text ?
var rechnungider = 0;
firestoreInstance.collection("rechnungen").doc().get().then((value) {
for (int i = value.data()!.length; i > value.data()!.length; i++) {
print(value.data());
rechnungider += int.parse(value.data()!["betrag"]);
}
box.write("rechnunggider", rechnungider.toString());
print("Rechnunggider Yenilendi:${box.read("rechnunggider")}");
//print(value.data.toString());
});
}
I wanna show without Document name, i mean all betrag data from all Documents.Total Value, Summe.
Thanks
Please do not calculate the sum of collections on the frontend. I can't warn you enough!
I would recommend to create a cloud function that saves the sum on another place in your database. It is the recommended approach for getting the sum for a value from a collection.
Let me know if you need an example for the functions to write.

How to reference one Node value in Other Node in firebase Android [duplicate]

I've read the Firebase docs on Stucturing Data. Data storage is cheap, but the user's time is not. We should optimize for get operations, and write in multiple places.
So then I might store a list node and a list-index node, with some duplicated data between the two, at very least the list name.
I'm using ES6 and promises in my javascript app to handle the async flow, mainly of fetching a ref key from firebase after the first data push.
let addIndexPromise = new Promise( (resolve, reject) => {
let newRef = ref.child('list-index').push(newItem);
resolve( newRef.key()); // ignore reject() for brevity
});
addIndexPromise.then( key => {
ref.child('list').child(key).set(newItem);
});
How do I make sure the data stays in sync in all places, knowing my app runs only on the client?
For sanity check, I set a setTimeout in my promise and shut my browser before it resolved, and indeed my database was no longer consistent, with an extra index saved without a corresponding list.
Any advice?
Great question. I know of three approaches to this, which I'll list below.
I'll take a slightly different example for this, mostly because it allows me to use more concrete terms in the explanation.
Say we have a chat application, where we store two entities: messages and users. In the screen where we show the messages, we also show the name of the user. So to minimize the number of reads, we store the name of the user with each chat message too.
users
so:209103
name: "Frank van Puffelen"
location: "San Francisco, CA"
questionCount: 12
so:3648524
name: "legolandbridge"
location: "London, Prague, Barcelona"
questionCount: 4
messages
-Jabhsay3487
message: "How to write denormalized data in Firebase"
user: so:3648524
username: "legolandbridge"
-Jabhsay3591
message: "Great question."
user: so:209103
username: "Frank van Puffelen"
-Jabhsay3595
message: "I know of three approaches, which I'll list below."
user: so:209103
username: "Frank van Puffelen"
So we store the primary copy of the user's profile in the users node. In the message we store the uid (so:209103 and so:3648524) so that we can look up the user. But we also store the user's name in the messages, so that we don't have to look this up for each user when we want to display a list of messages.
So now what happens when I go to the Profile page on the chat service and change my name from "Frank van Puffelen" to just "puf".
Transactional update
Performing a transactional update is the one that probably pops to mind of most developers initially. We always want the username in messages to match the name in the corresponding profile.
Using multipath writes (added on 20150925)
Since Firebase 2.3 (for JavaScript) and 2.4 (for Android and iOS), you can achieve atomic updates quite easily by using a single multi-path update:
function renameUser(ref, uid, name) {
var updates = {}; // all paths to be updated and their new values
updates['users/'+uid+'/name'] = name;
var query = ref.child('messages').orderByChild('user').equalTo(uid);
query.once('value', function(snapshot) {
snapshot.forEach(function(messageSnapshot) {
updates['messages/'+messageSnapshot.key()+'/username'] = name;
})
ref.update(updates);
});
}
This will send a single update command to Firebase that updates the user's name in their profile and in each message.
Previous atomic approach
So when the user change's the name in their profile:
var ref = new Firebase('https://mychat.firebaseio.com/');
var uid = "so:209103";
var nameInProfileRef = ref.child('users').child(uid).child('name');
nameInProfileRef.transaction(function(currentName) {
return "puf";
}, function(error, committed, snapshot) {
if (error) {
console.log('Transaction failed abnormally!', error);
} else if (!committed) {
console.log('Transaction aborted by our code.');
} else {
console.log('Name updated in profile, now update it in the messages');
var query = ref.child('messages').orderByChild('user').equalTo(uid);
query.on('child_added', function(messageSnapshot) {
messageSnapshot.ref().update({ username: "puf" });
});
}
console.log("Wilma's data: ", snapshot.val());
}, false /* don't apply the change locally */);
Pretty involved and the astute reader will notice that I cheat in the handling of the messages. First cheat is that I never call off for the listener, but I also don't use a transaction.
If we want to securely do this type of operation from the client, we'd need:
security rules that ensure the names in both places match. But the rules need to allow enough flexibility for them to temporarily be different while we're changing the name. So this turns into a pretty painful two-phase commit scheme.
change all username fields for messages by so:209103 to null (some magic value)
change the name of user so:209103 to 'puf'
change the username in every message by so:209103 that is null to puf.
that query requires an and of two conditions, which Firebase queries don't support. So we'll end up with an extra property uid_plus_name (with value so:209103_puf) that we can query on.
client-side code that handles all these transitions transactionally.
This type of approach makes my head hurt. And usually that means that I'm doing something wrong. But even if it's the right approach, with a head that hurts I'm way more likely to make coding mistakes. So I prefer to look for a simpler solution.
Eventual consistency
Update (20150925): Firebase released a feature to allow atomic writes to multiple paths. This works similar to approach below, but with a single command. See the updated section above to read how this works.
The second approach depends on splitting the user action ("I want to change my name to 'puf'") from the implications of that action ("We need to update the name in profile so:209103 and in every message that has user = so:209103).
I'd handle the rename in a script that we run on a server. The main method would be something like this:
function renameUser(ref, uid, name) {
ref.child('users').child(uid).update({ name: name });
var query = ref.child('messages').orderByChild('user').equalTo(uid);
query.once('value', function(snapshot) {
snapshot.forEach(function(messageSnapshot) {
messageSnapshot.update({ username: name });
})
});
}
Once again I take a few shortcuts here, such as using once('value' (which is in general a bad idea for optimal performance with Firebase). But overall the approach is simpler, at the cost of not having all data completely updated at the same time. But eventually the messages will all be updated to match the new value.
Not caring
The third approach is the simplest of all: in many cases you don't really have to update the duplicated data at all. In the example we've used here, you could say that each message recorded the name as I used it at that time. I didn't change my name until just now, so it makes sense that older messages show the name I used at that time. This applies in many cases where the secondary data is transactional in nature. It doesn't apply everywhere of course, but where it applies "not caring" is the simplest approach of all.
Summary
While the above are just broad descriptions of how you could solve this problem and they are definitely not complete, I find that each time I need to fan out duplicate data it comes back to one of these basic approaches.
To add to Franks great reply, I implemented the eventual consistency approach with a set of Firebase Cloud Functions. The functions get triggered whenever a primary value (eg. users name) gets changed, and then propagate the changes to the denormalized fields.
It is not as fast as a transaction, but for many cases it does not need to be.

Possible to query against the count of an included key?

I have an application where I need to return the first user found that meets certain criteria, some of that criteria is having a certain number of objects stored.
For example, let's say I want to return the first store I can find that has at-least 3 employees with atleast two children. I know, what an odd-ball example. So I would have a query something like this:
PFUser.query()?
.whereKey("objectId", notEqualTo: PFUser.currentUser()?.objectId!)
.includeKey("stores.employees.children")
// .whereCountForkey("stores.employees", greaterThan: 2)
// .whereCountForKey("stores.employees.children", greaterThan: 1)
.getFirstObject();
Notice the commented out lines, I'm trying to find a way to do soemthing like this in a single query. I'm using parse, which I believe uses MongoDB on the back end, but I don't believe you can execute custom database queries..?
This is a mobile application for both iOS and Android, although the code shown is in SWIFT I have two variations of the project. Examples in either swift, obj-C, Java, or C# will be fine.
Also more than happy with Cloud-code solutions.
There is an example in the documentation
var Team = Parse.Object.extend("Team");
var teamQuery = new Parse.Query(Team);
teamQuery.greaterThan("winPct", 0.5);
var userQuery = new Parse.Query(Parse.User);
userQuery.matchesKeyInQuery("hometown", "city", teamQuery);
userQuery.find({
success: function(results) {
// results has the list of users with a hometown team with a winning record
}
});

Clearer Format Firebase Android retrieved data

When I retrieve my data it contains brackets { and unique id such as - JSDHGJDGJJSKA ... I want to make it cleaner and get rid of the brackets for e.g. my output is:
{-JfFQQRYnhiKeuN5ERGX={msg=Monday},-JfFQAhQQWIFAUuV1nD4={msg=this is test}}
I want to get rid of the brackets and the word msg and retrieve just one of the message at random.
I want my output to be if I pick up a random message:
Monday
if I pick up another at random
this is test
Any ideas on how to achieve this will be greatly appreciated.
This will retrieve a random message from the object you've shown in your question.
function getRandomMessage(data) {
if( !data ) { return null; }
var keys = Object.keys(data);
var randomKey = keys[ Math.floor(Math.random()*keys.length) ];
return data[randomKey];
}
Keep in mind that this assumes you have a small number of records. If you start getting into the thousands, you'll need a more robust solution than just grabbing the entire data set.
When I used this I was able to retrieve my data for eg. I save as Book -> title: "The book of death"
here is the code to retrieve the title:Retrieve data-
String title = (String) snapshot.child("title").getValue();
It worked after I used and I didnt used push since push creates its unique ID and its complex for my level to deal with it so I used:Saving data-
Map<String, Object> title= new HashMap<String, Object>();
title.put("title", "This is a working message");
f.child("Book").updateChildren(title);
and everything worked out. I hope it helps everyone who has having these issues. With update children you can use auto increment for your id.
are you getting data in string ? and If you are using string then it is easy , you can use the method of replace eg: yourString.replace("a","b")

How to check for type?

In an Android app I've got a couple contacts from my contacts list. They can be either emails, phone numbers, or even other things. I now want to check which type it is and bind specific actions to them.
For example, if it is a type vnd.android.cursor.item/email_v2, I want to send a POST message with just the email field, and if it is a type vnd.android.cursor.item/phone_v2 I want to send a POST message with just the phone field.
Any ideas how I could check this?
I guess the way to go would be using overloading:
You implement multiple methods with different input parameters but the same name, such as:
checkContact(email_v2 email){ do things with email }
checkContact(phone_v2 phone){ do things with phone }
checkContact(String s){do things with random string }
I think you get my point.
If you want a simple if-statement, though:
if (contact instanceof vnd.android.cursor.item/email_v2){ do send }
You could try checking the class constant CONTENT_ITEM_TYPE for your different types, something like:
contact.CONTENT_ITEM_TYPE.equals("vnd.android.cursor.item/email_v2");

Categories

Resources