I am using Firebase and I have had no problems getting data in alphabetical order until recently. I never used queries, I always just used snapshots of data and sorted through them one-by-one. Recently, the data has not been always coming in alphabetical order in the snapVal. How do I make it so I get a snapVal of data sorted alphabetically, like it is in the snapshot from the database?
Real Example: there are 4 messages, id1-id4 (in that order). They contiain the message "1"-"4". The snapshot comes looking correct. But the snapVal (snapshot.value) looks like this:
["id2": {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 2;
TIME = "8:12 PM";
}, "id4": {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 4;
TIME = "8:12 PM";
}, "id1": {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 1;
TIME = "8:12 PM";
}, "id3": {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 3;
TIME = "8:12 PM";
}]
What the snapshot looks like:
["id1": {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 1;
TIME = "8:12 PM";
}, "id2": {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 2;
TIME = "8:12 PM";
}, "id3": {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 3;
TIME = "8:12 PM";
}, "id4": {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 4;
TIME = "8:12 PM";
}]
To get the snapVal, I use this:
if let snapVal = snapshot.value as? [String: AnyObject] {
// Comes out of order..
}
To clarify:
Snapshot (this ends up coming out correct):
Snap (CHAT) {
id1 = {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 1;
TIME = "8:12 PM";
};
id2 = {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 2;
TIME = "8:12 PM";
};
id3 = {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 3;
TIME = "8:12 PM";
};
id4 = {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 4;
TIME = "8:12 PM";
};
}
This is the output for print(snapVal.keys) inside if let snapVal = snapshot.value as? [String: AnyObject]:
LazyMapCollection<Dictionary<String, AnyObject>, String>(_base: ["id2": {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 2;
TIME = "8:12 PM";
}, "id4": {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 4;
TIME = "8:12 PM";
}, "id1": {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 1;
TIME = "8:12 PM";
}, "id3": {
DATE = "10/20/16";
"FIRST_NAME" = first;
ID = userID;
"LAST_NAME" = last;
MESSAGE = 3;
TIME = "8:12 PM";
}], _transform: (Function))
My Code:
self.firebase.child("Chats").child(chatID).queryOrderedByKey().observeSingleEvent(of: .value, with: { (snapshot) in
print(snapshot)
if let snapVal = snapshot.value as? [String: AnyObject] {
print(snapVal)
for c in snapVal {
print("checking Message as child")
let message = c.value["MESSAGE"] as? String
let fn = c.value["FIRST_NAME"] as? String
let ln = c.value["LAST_NAME"] as? String
let USER_ID = c.value["ID"] as? String
if let userID = USER_ID {
if let msg = message {
if let firstName = fn {
if let lastName = ln {
let username = "\(firstName) \(lastName)"
self.addMessage(userID, text: msg, name: username)
print("Message added! \nMessage Info:")
print("User ID: \(userID)")
print("text: \(msg)")
print("Username: \(username)")
} else {
print("LN did not pass")
}
} else {
print("FN did not pass")
}
} else {
print("Msg did not pass")
}
} else {
print("User ID did not pass")
}
}
}
})
Since you haven't shared the necessary code, I'll assume you're doing something along these lines:
ref!.queryOrdered(byChild: "text").observe(.value, with: { (snapshot) in
print("\(snapshot.value)")
})
When you execute a query on a Firebase location, the data is returned with information about the order of the items according to the query. When you observe a value event, the snapshot contains the keys, the values and the order of the children.
But when you convert request the snapshot.value property, all information has to be converted into a dictionary. The keys and the values of each child survive this conversion, but the information on ordering is lost.
For this reason, you'll have to use the children property of the snapshot to iterate over the children in the correct order:
ref!.queryOrdered(byChild: "text").observe(.value, with: { snapshot in
for child in snapshot.children {
print("child \(child)")
}
})
Solution: After very extensive searching and attempting, the problem still persisted that once the snapshot was converted to a snapVal (snapshot.value), the order often rearranged. My (working) solution:
for child in snapshot.children {
let child = child as! FIRDataSnapshot
if let childVal = child.value as? [String: AnyObject] {
let childMessage = childVal["MESSAGE"] as! String
// more code for each child. This child might be a post of a sort, which you can access properties of in a way like I did in the line above
}
}
Process:
Loop through each child in snapshot
Convert the child to a FIRDataSnapshot so it is not an element
Get the value of the particular child to access properties from
Add in the respective code for the child following NSDictionary principles.
Why this solution is solid
Receiving snapshots in the proper order is very simple. The issue I faced was getting data in the correct order when I got the snapshot.value. This solution fixes that because the values of each child are only accessed when looping through the children of snapshot, which is sorted. This leaves the order of children still in the control of the snapshot.
I also like the snapshot.value approach by using [String: AnyObject] because it is very close to the old functionality of Firebase implementation in Swift: Simple and very clean. I actually think that using NSDictionary in this way is really a way to save time in the long run because it is not verbose in any way.
From my experience with Firebase you cannot guarantee the order that the data is being returned in the snapshot value. Firebase provides a couple of functions that you can add to your reference query though to order and sort the data based on key queryOrderedByKey, value queryOrderedByValue, or child queryOrderedByChild.
From your description it sounds like you may want to use queryOrderedByChild to sort you snapshots correctly.
Here's the documentation on those functions and scroll down to the sort data section. https://firebase.google.com/docs/database/ios/lists-of-data
The problem as you can see is not that Firebase is not giving you the response ordered by as requested, the problem is that you are parsing the response into an Dictionary and Dictionaries are no ordered list. The simplest way to fix it is to order your dictionary using something like this:
self.messages.sortInPlace({ ($0.date.compare($1.date) == NSComparisonResult.OrderedAscending)})
Or a even better way is to add the result directly into an array.
You can find more about this problem in this question in my course, there is a long discussion about it there (https://www.udemy.com/firebase/learn/v4/questions/1341056).
Let me know if that helps you.
Related
In my project I used to have more than 6.000 points of interest (pois). Now I need to access for example the title of one poi with specific ID. Normally it works fine in my app when I need some specific from user for example.
I do the same steps like I usually do. But when I try to access poi data, it only give the whole "poi" data (>6.000 pois). I tried getData even observeSingeEvent.
This is my database structure:
{
"pois" : {
"0015CF32-D5B2-481A-8DA2-877E621A4E82" : {
city = a,
"country" = a,
creator = System,
id = "0015CF32-D5B2-481A-8DA2-877E621A4E82",
latitude = "1",
longitude = "1",
street = "a",
title = "a",
type = 0,
zip = 1
}, + 6.000 more...
"users" : {
"6EA555FA-BAA8-4EDE-BD05-5D290B953CAC" : {
"City" : "Motown",
"Country" : "Anywhere",
"Forename" : "Fred",
"Surename" : "Feuerstein",
"onTour" : "false",
"registeredEmail" : "fred#feuerstein.com",
"userId" : "6EA555FA-BAA8-4EDE-BD05-5D290B953CAC",
"userImagePath" : "6EA555FA-BAA8-4EDE-BD05-5D290B953CAC.jpg",
"username" : "Tom"
}
}
}
My request and result for user data: (I reduced the whole code because it doesn't matter what happens within the completion)
database.child("users/6EA555FA-BAA8-4EDE-BD05-5D290B953CAC/registeredEmail").getData { error, snap in
print(snap)
}
results: Snap (registeredEmail) fred#feuerstein.com
My request and result for user data.
database.child("pois/42E11715-B3B0-46D9-A568-E578870EF9A4/title/").getData { error, snap in
print(snap)
}
results (Shorten):
Snap (title) {
"0015CF32-D5B2-481A-8DA2-877E621A4E82" = {
city = a;
"country" = a;
creator = System;
id = "0015CF32-D5B2-481A-8DA2-877E621A4E82";
latitude = "1";
longitude = "1";
street = "a";
title = "a";
type = 0;
zip = 1;
};
"002A3A1C-DE5C-4CB4-AD36-1B4495B77F22" = {
city = "b";
"country" = b;
creator = System;
id = "002A3A1C-DE5C-4CB4-AD36-1B4495B77F22";
latitude = "1";
longitude = "1";
rating = 0;
street = "b";
title = "b";
type = 0;
zip = 1;
}; + 6.000 more pois
I guess this coherent with the amount of data in "pois". Someone an idea how to solve the problem?
I know this question has been ask several times but none of the solutions work for me. here is my snap shot
Snap (CaseExtend) {
NYxdSlq8yOgQd3phssRlD = {
CaseMinute = {
WMKuImjuH0qPIGJhHHJKv = {
caseMinute = "";
dateTime = "Dec 1, 2021 12:20 PM";
minuteId = WMKuImjuH0qPIGJhHHJKv;
userId = OtRFaqilTigWvEkUCASvCoEegny1;
};
};
CaseParticipant = {
3f7nqmcWFTenePkEB9KoMHLzVtk2 = {
userId = 3f7nqmcWFTenePkEB9KoMHLzVtk2;
};
OtRFaqilTigWvEkUCASvCoEegny1 = {
userId = OtRFaqilTigWvEkUCASvCoEegny1;
};
};
caseId = Oj7TwzDC3HQMJKyTb8Ebi;
caseNumber = TEST;
court = "Labour Tribunal";
courtRoomNo = "";
date = "2021-12-01";
extendId = NYxdSlq8yOgQd3phssRlD;
extendStatus = "Not Extend";
location = "";
participantCount = 2;
profilePicture = "https://firebasestorage.googleapis.com/v0/b/lege-155e1.appspot.com/o/Profile%20Pictures%2FOtRFaqilTigWvEkUCASvCoEegny1.jpg?alt=media&token=676226fa-638b-4175-a8d5-247095f6b86a";
step = TBM;
time = "";
userId = OtRFaqilTigWvEkUCASvCoEegny1;
};
}
and i retrieve the snapshot using following code,
ref.child("CaseExtend").queryOrdered(byChild: "userId").queryEqual(toValue: "OtRFaqilTigWvEkUCASvCoEegny1" ).observe(.value, with: { (DataSnapshot) in
})
i want to add the caseID, caseNumber, date.. and other values for different variables. is there anyway i can do this?
first of all you are getting all data in casetExtend node all the variables you want to assign is not under that node. for that you need to navigate one level of child node.
ref.child("CaseExtend").queryOrdered(byChild: "userId").queryEqual(toValue: "OtRFaqilTigWvEkUCASvCoEegny1" ).observe(.value, with: { (snapshot) in
for caseSnapshot in snapshot.children.allObjects as! [DataSnapshot] {
let dict = caseSnapshot.value as? [String: AnyObject] // assign the snapshot to a dictionary
let court = dict!["court"] as? String ?? "unknown"; // assign the variable
}
})
I am trying to do a simple ordered query request based on epoch timestamps:
guard let user = Auth.auth().currentUser?.uid else {return}
let queryRef = Database.database().reference().child("ordersBackup").child(user).queryOrdered(byChild: "pickupCompleteAfter").queryLimited(toFirst: 1)
queryRef.observeSingleEvent(of: .childAdded, with: { (snapshot) in
print(snapshot.value)
})
Structure:
{
"UserId" : {
"UniqueId1" : {
"pickupCompleteAfter" : 1568314979000,
"name" : "Jeff"
},
"UniqueId2" : {
"pickupCompleteAfter" : 1557687779000,
"name" : "Stig"
},
"UniqueId3" : {
"pickupCompleteAfter" : "1578855779000",
"name" : "Ali"
}
}
}
It should return UniqueId2. However, I am always retrieving the same order as the structure i.e UniqueId1, UniqueId2, UniqueId3. It doesn't take the timestamp into consideration at all. What am I doing wrong?
Output:
LQ8nHi
{
orderInfo = {
deliveryCompleteAfter = 1552917600000;
deliveryCompleteBefore = 1552924800000;
pickupCompleteAfter = 156249280000;
pickupCompleteBefore = 1552492800000;
};
}
YQxeKv
{
orderInfo = {
deliveryCompleteAfter = 1552917600000;
deliveryCompleteBefore = 1552924800000;
pickupCompleteAfter = 1557687779000;
pickupCompleteBefore = 1552492800000;
};
}
ibIPO9
{
orderInfo = {
deliveryCompleteAfter = 1553090400000;
deliveryCompleteBefore = 1553097600000;
pickupCompleteAfter = 1578855779000;
pickupCompleteBefore = 1552665600000;
};
}
The pickupCompleteAfter is nested under the orderInfo node of each child. You're current not telling the database about that full path, so it only checks for a pickupCompleteAfter right under each child.
To order/filter on orderInfo/pickupCompleteAfter, use:
let query = Database.database().reference().child("ordersBackup").child(user)
.queryOrdered(byChild: "orderInfo/pickupCompleteAfter").queryLimited(toFirst: 1)
query.observeSingleEvent(of: .childAdded, with: { (snapshot) in
print(snapshot.value)
I am using firebase and I am trying to retrieve data from Firebase Database which is working well however when I call "postDict" I get.
["-KPZAOg58kUdqwYMzlDJ":
{
description = jpeg;
listingImageURL = "https://firebasestorage.googleapis.com/v0/b/hype-central.appspot.com/o/lidsafstings%2FC73FdfB8A0-968df7-44DB-B96B-A10DC1E3A2B7.png?alt=media&token=b6e296f2-8854-4b76-9bfc-470dfd69d11f2f";
location = jpeg;
price = 54;
title = jpeg;
userDisplayName = "Brandon YOIO";
userid = pRuwvL7WyzQpY0G22ZLYCmTTemB3;
},
"-KPZ0yLhcfLvP9YWNjbC": {
description = "Description ofvaroe";
listingImageURL = "https://firebadfasestorage.googleapis.com/v0/b/hype-central.appspot.com/o/listings%2F0ACdasfCD645-2E2C-4936-B3DF-37EC55AA0157.png?alt=media&token=b321dasff42f-75b5-4eb3-b129-b0a902cc5926";
location = Vancouver;
price = 34;
title = eclipse;
userDisplayName = "Brandon YOIO";
userid = pRuwvL7WyzQpY0G22ZLYCmTTemB3;
},
"-KPoCsNS63JZdbTJIdaP": {
description = Brandon;
listingImageURL = "https://firebadasfsestorage.googleapis.com/v0/b/hype-central.appspot.com/o/listings%2FOptional(%22pRuwvL7WyzQpY0G22ZLYCmTTemB3%22)%2F3EACE28E-D28A-464A-B6D0-FB502E9dafsB4775.jpeg?alt=media&token=1d45bc7b-7273-4d33-8ccd-755f3102a23c";
location = BRANDON;
price = BRANDON;
title = BRANDON;
userDisplayName = "Brandon YOIO";
userid = pRuwvL7WyzQpY0G22ZLYCmTTemB3;
},
"-KP_Y7ug7hwrHtW6VNqV": {
description = "Description ofvaroe";
listingImageURL = "https://firebaseddasfstorage.googleapis.com/v0/b/hype-central.appspot.com/o/listings%2FC5daf7991BA-D7E0-4C8A-96E7-0DB17EEBABD7.jpeg?alt=media&token=efd03b4d-6964-4685-9b48-5ed36a4ceb59";
location = "";
price = "";
title = "";
userDisplayName = "Brandon Mayhew";
userid = pRuwvL7WyzQpY0G22ZLYCmTTemB3;
}]
If I call "postDict["-KPZAOg58kUdqwYMzlDJ"]["title"]" It will print the title which is exactly what I want however how do I get an array of all the titles in this JSON format (not just the title of "-KPZAOg58kUdqwYMzlDJ").
How do I get all the title's?
Use :-
rootRef.observeEventType(.Value, withBlock: {(postDictionary) in
if let postDict = postDictionary.value as? [String:AnyObject]{
for each in postDict as [String:AnyObject]{
let autoID = each.0
rootRef.child(autoID).observeEventType(.Value, withBlock: {(specificPost) in
//RETRIEVE YOUR DATA
})
}
}
})
PS:- I have used rootRef as you have not specified your parent nodes in your JSON structure :)
I am trying to hook into the onCreateUser function with the accounts-password package to add information into another collection when an account is created. I have added this piece of code into the server.js
//server.js
var ShopDetails = {
name = "",
postCode = "",
type = "";
openTime = "",
closeTime = "",
phoneNum = 0,
dashboardDetails = false //until shop populates these fields
createdBy = user._id //_id taken from function below
}
Accounts.onCreateUser(function(options,user){
var userId = user._id;
ShopList.insert(shopDetails);
return user;
});
The two collections I have in the app are -
ShopList = new Mongo.Collection("shopList") //held in a collections folder
user collection that comes bundled with accounts-password
Once a new user has created an account, I wanted to insert an object with all the required fields (shopDetails) into the ShopList collection (For the purpose of creating a personal dashboard for each shop at a later point) . However after creating a few test accounts, I cannot see them in the ShopList collection in RoboMongo, I know the user accounts are being inserted as I can see them in the user collection. Can anyone tell me where I am going wrong? Thanks
What do you get if you print
console.log(shopDetails) inside the hook?
Also try to use callbacks on the insert as a second parameter, so we can help you like this
ShopList.insert(ShopDetails,function(error,result){
if(error){
console.log(error); //should print the error and this help us more to help you =D
}else{
console.log(result);
}
});
also why don you better do something like
insertShopDetails = function(userId){
var ShopDetails = {
name = "",
postCode = "",
type = "";
openTime = "",
closeTime = "",
phoneNum = 0,
dashboardDetails = false,
createdBy = userId
}
ShopList.insert(ShopDetails,function(error,result){
if(error){
console.log(error);
}else{
console.log(result);
}
});
}
And then just call it inside the onCreatedHook
Accounts.onCreateUser(function(options,user){
var userId = user._id;
insertShopDetails(userId);
return user;
});
The above thing should work, but why dont you better do this using a hook?
meteor add matb33:collection-hooks
Meteor.users.after.insert(function(userId, doc, modifier) {
var ShopDetails = {
name = "",
postCode = "",
type = "";
openTime = "",
closeTime = "",
phoneNum = 0,
dashboardDetails = false
createdBy = userId
}
ShopList.insert(shopDetails);
});