How to get child node names from Parent ref or parent node from firebase database - firebase-realtime-database

like in the realtime database my data is like this
room
1)child1
2)child2
3)child3
i want to get only the name child1, child2, child3 not all the data of child1,child2 and child3.
i try like this
getNode = (snapshot) => {
let keys = Object.keys(snapshot);
let json1 = {}
let arr = keys.map((k1) => {
json1 = {...json1, [k1]: "" }
return ""
});
return json1;
};
exports.getNodeNameFromDatabase = functions.https.onRequest((request, response) => {
let nodeRef = request.query.nodeId;
let ref = db.ref(`${nodeRef}`);
ref.once("value", (snapshot) => {
let data = getData(snapshot.val());
console.log(snapshot.val());
response.send(data);
});
});
by doing this i have to fetch all the data of that ref and then i get node from that data. But it not work when data size is big. So is there any other method to get only clild node name of that level of the given ref.

Related

Firebase - Nested observeSingleEvent query in for loop callback too many time update

I am facing a serious callback hell on firebase realtime database update.
Situation:
I have comments node which store all the comment's detail information, such as belong to whose userId (uid) , message, and post id (pid). Please see image below.
I have another post-comment nodes, which store comments key under each post id key. Please see image below.
Finally the third nodes is user-comment, which store all comments key under unique user account id key. Please see image below.
Problem:
Everything work fine on "Write comment" function, because it just create a comment key and update comment data to these nodes.
But, when user call "Delete post" function, which will delete all the comments data belong to this post id. Therefore, I have this code logical to loop all the comments data. The whole point is that first I have to get the post-comment snapshot in order to limitation the query amount on comments node (because comments node store all the app user's comment detail data. Without knowing the quantity of comment belong to the target post, it will need to for loop all over the comments node, it is too overload.)
For looping the post-comment will get the commentKey, then I can set Null on comments node and post-comment node.
But the issues happen on I need to use comments node to find out the userId, in order to set NSNull on user-comment. When I calling the event below:
commentsRef.child((snap as AnyObject).key).observeSingleEvent(of:
.value, with: { (commentSnapshot) in
})
The commentsRef callback scope become another thread. Therefore, if I call rootRef.updateChildValues out side of this scope and in the end of for loop (post-comment) which will only update comments node and post-comment node. The user-comment updates data will still assign key:value on the other thread.
updates["user-comment/(userId)/comments/(commentKey)"] = NSNull()
I have to put the rootRef.updateChildValue in the
commentsRef.child((snap as AnyObject).key).observeSingleEvent(of:
.value, with: { (commentSnapshot) in
...
rootRef.updateChildValues(updates)
})
This logic will cause updateChildValues being called too many time if the comments over 10,000 or more than 1 million, because it is in the for looping. I use count down method try to call update only once on the for loop end. But the count number always be 0 in the commentRef scope... I don't know why...
Please help me out with a better solution to dealing with this nested observeSingleEvent update issues without changing the current nodes structure. My goal is to only call rootRef.updateChildValue one time.
Thanks for your help.
Demo code:
func deleteAllCommentsRelateTo(postId: String, callback: ((CommentServiceError?) -> Void)?) {
var error: CommentServiceError?
guard session.isValid else {
error = .authenticationNotFound(message: "Authentication not found.")
callback?(error)
return
}
let uid = session.user.id
let rootRef = Database.database().reference()
let path1 = "posts/\(postId)/comments_count"
let path2 = "posts/\(postId)/uid"
let commentCountRef = rootRef.child(path1)
let authorRef = rootRef.child(path2)
authorRef.observeSingleEvent(of: .value, with: { authorSnapshot in
guard let authorId = authorSnapshot.value as? String else {
error = .failedToDelete(message: "Author not found")
callback?(error)
return
}
if uid != authorId {
error = .failedToDelete(message: "User has no permission to delete this post comments")
callback?(error)
return
}
commentCountRef.runTransactionBlock({ (data) -> TransactionResult in
if let _ = data.value as? Int {
data.value = 0
}
return TransactionResult.success(withValue: data)
}) { (err, committed, snapshot) in
guard err == nil, committed else {
error = .failedToDelete(message: "Unable to delete a comment")
callback?(error)
return
}
var updates: [AnyHashable: Any] = [:]
/**
* [CHECKED] Set NSNull() on comments, post-comment, and user-comment nodes.
*/
let commentsRef = rootRef.child("comments")
let postCommentRef = rootRef.child("post-comment")
let query = postCommentRef.child(postId).child("comments").queryOrderedByKey()
query.observeSingleEvent(of: .value, with: { (data) in
guard data.hasChildren() else {
error = .failedToDelete(message: "No comments data")
callback?(error)
return
}
var count = data.childrenCount
print("post-comment count!!!!!!!: ", data.childrenCount)
for snap in data.children {
guard let commentKeySnap = snap as? DataSnapshot else {
continue
}
count -= 1
let commentKey = commentKeySnap.key
if count == 0 {
print("this is totally not right!!!!!")
}
updates["comments/\(commentKey)"] = NSNull()
updates["post-comment/\(postId)/comments/\(commentKey)"] = NSNull()
commentsRef.child((snap as AnyObject).key).observeSingleEvent(of: .value, with: { (commentSnapshot) in
guard let userId = commentSnapshot.childSnapshot(forPath: "uid").value as? String else {
return
}
updates["user-comment/\(userId)/comments/\(commentKey)"] = NSNull()
print("In this observeSingleEvent will always be 0 count::::: ", count)
if count == 0 {
rootRef.updateChildValues(updates, withCompletionBlock: { err, ref in
guard err == nil else {
error = .failedToDelete(message: "Failed to delete comment")
callback?(error)
return
}
})
print("deleteAllComments: ", updates)
callback?(nil)
}
})
print("count down: ", count)
}
})
})
}
})
}
Solution:
I accidentally found out the correct place to put count -= 1. Originally I put it in the for loop scope, but the count did not decrease in the commentRef in scope. Therefore, I put count -= 1 in the commentRef scope which success count to zero and only call rootRef.update one time.

Pagination in firebase with identical child values

My data structure is as follows:
users:
user1:
-carModel: evo x
-username: importguy
-region: north east
user2:
-carModel: evo x
-username: evoguy
-region: north east
user3:
-carModel: mustang gt
-username: muscleguy
-region: south east
I want the user to be able to search for a car, say evo, and display results of users who own those particular cars. I need to paginate these results for my app. The problem is I can't figure out how to properly query this. Here is what i have so far.
func fetchUsersBy(car: String) {
if self.carCurrentKey == nil {
let ref = USER_REF.queryOrdered(byChild: "carModel").queryStarting(atValue: car).queryLimited(toFirst: 3)
ref.observeSingleEvent(of: .value, with: { (snapshot) in
guard let snap = snapshot.children.allObjects as? [FIRDataSnapshot] else { return }
guard let last = snapshot.children.allObjects.last as? FIRDataSnapshot else { return }
snap.forEach({ (snapshot) in
guard let userDict = snapshot.value as? Dictionary<String, AnyObject> else { return }
guard let carModel = userDict["carModel"] as? String else { return }
if carModel.contains(car) {
print(snapshot)
}
})
self.carCurrentKey = last.key
self.carCurrentValue = last.childSnapshot(forPath: "carModel").value as? String
})
} else {
// where to start next query?
let ref = USER_REF.queryOrder(byChild: "carModel").queryStarting(atValue: self.carCurrentValue)
}
}
I have to order the query by carModel, in order to group all of the users with that particular car type together in a snapshot. Since all the car models are the same value, I cannot figure out where to start or end the next query for the pagination. Using the reference i have in the else block starts the query at the same place as the block above. Any help or advice would be much appreciated.
I considered doing a fan out, and making a separate structure for car types. This would be difficult though.
For both startAt() and endAt(), you can pass a second value, childKey as shown here.
So your query will look something like this:
let ref = USER_REF.queryOrdered(byChild: "carModel").queryStarting(atValue: self.carCurrentValue, childKey: self.carCurrentKey).queryLimited(toFirst: 3+1)
Note that I used toFirst: 3+1. That's because, annoyingly, startAt() is inclusive and there's no way to skip the first record. So, since we started from the last record retrieved on the previous page, you will want to query for one extra record and discard the first result.
Here's a more complete example in JavaScript. Not familiar enough to translate this to Swift, but it should give you the algorithm in completion.
class Cursor {
constructor(baseRef, pageSize) {
this.baseRef = baseRef;
this.lastKey = null;
this.lastValue = null;
this.pageSize = pageSize;
}
next() {
let ref = this.baseRef;
if( this.lastValue !== null ) {
// a previous page has been loaded so get the next one using the previous value/key
// we have to start from the current cursor so add one to page size
ref = ref.startAt(this.lastValue, this.lastKey).limitToFirst(this.pageSize+1);
}
else {
// this is the first page
ref = ref.limitToFirst(this.pageSize);
}
return ref.once('value').then(snap => {
const keys = [];
const data = []; // store data in array so it's ordered
snap.forEach(ss => {
data.push(ss.val());
keys.push(ss.key);
});
if( this.lastValue !== null ) {
// skip the first value, which is actually the cursor
keys.shift();
data.shift();
}
// store the last loaded record
if( data.length ) {
const last = data.length - 1;
this.lastKey = keys[last];
this.lastValue = data[last].author;
}
return data;
});
}
}
And here's a working fiddle.
Keep in mind that this is a realtime data stream. So pagination is tricky. It's generally easier to just do infinite scroll than to try and maintain a realistic cursor on a moving data set (records can reorder when data changes, get deleted, added in the middle, etc).

iOS - RealmSwift

I have created in my application to the database using Realmsvift. Here is the output in the console. Please tell me, how do I read data from it in the application? For example, I want to display key-values: Oslo - 2.89. Thanks for the help.
class ViewController: UIViewController {
var city_list: [String] = ["Moscow", "London", "Oslo", "Paris"]
let realm = try! Realm()
override func viewDidLoad() {
super.viewDidLoad()
let manager: ManagerData = ManagerData()
for name in city_list {
manager.loadJSON(city: name)
}
let localWeather = realm.objects(WeatherData.self)
print(localWeather)
/////////////////////////////////////////
Results<WeatherData> (
[0] WeatherData {
city_name = Moscow;
tempList = RLMArray <0x6080002e2b00> (
[0] Temp {
temp = -4.25;
}
);
},
[1] WeatherData {
city_name = London;
tempList = RLMArray <0x6000002e4700> (
[0] Temp {
temp = 9.630000000000001;
}
);
},
[2] WeatherData {
city_name = Paris;
tempList = RLMArray <0x6000002e4800> (
[0] Temp {
temp = 6.59;
}
);
},
[3] WeatherData {
city_name = Oslo;
tempList = RLMArray <0x6000002e4900> (
[0] Temp {
temp = -2.89;
}
);
}
If I understand your question correctly, you want to get properties from each model.
If that is the case, you're almost there. localWeather is like an array in the Realm's world (e.g., type of Results<WeatherData>).
You can just access it like normal swift's array/objects:
let firstWeather = localWeather[0]
let name = firstWeather.city_name
let temp = firstWeather.tempList[0].temp
// do what you want with 'name' and 'temp', e.g. key-value
print("\(name) - \(temp)")

How can I find a specific Product id by sending Product Name?

I use Firebase For My Store App. I want to find a Product's Details by taking a product name for the user. My JSON format looks like this:
{
product :
electronic =
a = {
pname = "iphone 5"
pprice = "20000"
pdescription = "Details....." }
b = {
pname = "iphone 6"
pprice = "30000"
pdescription = "Details....." }
}
cloths =
a = pname = "shirt"
pprice = "200"
pdescription = "Details....." }
b = {
pname = "pents"
pprice = "300"
pdescription = "Details....." }
}
Now, suppose I have the name iphone 5, then how can I find out the other details of the product?
Try this :-
FIRDatabase.database().reference().child("product/electronic").queryOrderedByChild("pname").queryEqualToValue("iphone 5").observeSingleEventOfType(.Value , withBlock : {(snap) in
if let snapDict = snap.value as? [String:AnyObject]{
for each in snapDict{
print(each.0) // product key
print(each.1) //product details
}
}
})
import Firebase
FIRApp.configure()
ref = FIRDatabase.database().reference()
let prod_query = "iphone 5"
ref.observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let product_enum = snapshot.children
while let product = product_enum.nextObject() as? FDataSnapshot {
product.queryEqualToValue(child:"\(prod_query)").observeSingleEventOfType(.Value, withBlock: { (snap) in
let pid = snap.key as! String
let pprice = snap.value!["pprice"] as! Int
let pdescription = snap.value!["pdescription"] as! String
})
}
})
This implies that you know what the product letter is so that you can pull the correct name, price, and description.
The while loop will iterate through the different types of products (electronics, cloths, etc) and perform a query searching for a product ID that contains the child with the pname you're looking for.
Firebase suggests that instead of using .Value, it's better to use .ChildAdded since it accomplishes the same goal while managing new objects added. But since it appears you are trying to view static data, .Value works just fine.
This should serve as an excellent example as to how you can retrieve data using Firebase. But I suggest checking out the documentation on your own just in case you have further questions.
While I really don't mind looking this information up... this site is used in order to gain a better understanding of code, rather than existing as a collection of personal assistants.
Showing research efforts within your question can go a long way.

Realm queries to extract data

I have 2 Realm Models:
class CourseModel: Object {
dynamic var coursename = ""
dynamic var par3field = 0
dynamic var par4field = 0
dynamic var par5field = 0
let scoreModels: List<ScoresModel> = List<ScoresModel>()
override internal static func primaryKey() -> String? { return "coursename" }
}
class ScoresModel: Object {
dynamic var dateplayed = ""
var courseModel: CourseModel? {
return linkingObjects(CourseModel.self, forProperty: "scoreModels").first
}
}
The app user will first add a new course for which I use CourseModel. As the user plays a course they enter scores for that course, for which I use ScoresModel, hence the primary key 'coursename'.
I query the CourseModel with
let realm = try Realm()
let results = realm.objects(CourseModel)
return results
and it produces the following result
Results<CourseModel> (
[0] CourseModel {
coursename = First Course;
par3field = 4;
par4field = 10;
par5field = 4;
scoreModels = RLMArray <0x797a36d0> (
[0] ScoresModel {
dateplayed = Apr 5, 2016; },
[1] ScoresModel {
dateplayed = Mar 3, 2016; }
);
},
[1] CourseModel {
coursename = Second Course;
par3field = 4;
par4field = 10;
par5field = 4;
scoreModels = RLMArray <0x7a046f40> (
[0] ScoresModel {
dateplayed = Apr 5, 2016; }
);
}
)
The ScoresModel produces a similar result but without the CourseModel data.
The ScoresModel has a lot of data in it, I only showed 'dateplayed' here to keep it short.
My question is this; when I've extracted the data from Realm how can I access a particular field to work with that data, i.e. how do I get the par5field data to do calculations with it, and also the 2nd question how do I get to the scoreModels data, for example 'dateplayed' to list the dates in a table for example?
When you perform a query against Realm, the results are returned in a Results object that behaves exactly like an array. So you need to iterate through each object to access the properties you want for each one.
To answer your first question, to access the par5field property (From just the first object):
let firstObject? = results.first
let par5field = firstObject.par5field
// Do calculations with it
For your second question, scoreModels is just a standard array object, so you can just insert the values it into a table view as you would a standard Array object.
If you wanted to list ALL of the ScoreModel objects, regardless of which CourseModel objects they belong to, you can perform a Realm query to get them directly.
let realm = try! Realm()
let results = realm.objects(ScoreModel)
return results

Resources