FIRDatabaseQuery: how to do an inner join - ios

I'm trying to do an inner join on a FIRDatabaseQuery object.
below is the database structure. I have some posts that are linked to post-comments. I am trying to get all the posts that a specific user added a comment on:
{
"posts" : {
"-KIycKhZU55dmKnHbbxv" : {
"author" : "John Doe",
"body" : "This is a post test",
"title" : "test",
"uid" : "SHaH36BLwgPvwgi9cDmRnsnONFB2"
},
"-KIyg_ks1O5QL4_4dfq_" : {
"author" : "Jane Doe",
"body" : "This is a post test",
"title" : "test2",
"uid" : "x5leSBGArnd10JilD9YDyNBNfZ03"
},...
}
"post-comments" : {
"-KIycKhZU55dmKnHbbxv" : {
"-KIycMyL0Vy1BHVdI4zc" : {
"author" : "toto",
"text" : "test",
"uid" : "SHaH36BLwgPvwgi9cDmRnsnONFB2"
},
"-KIyg_ks1O5QL4_4dfq_" : {
"author" : "toto",
"text" : "test",
"uid" : "SHaH36BLwgPvwgi9cDmRnsnONFB2"
}
},...
}
in SQL this will be translated into a inner join query similar to:
Select * from post inner join post-comments on post-id where post-comments.uid = "user id"
Does anybody know how o get something similar to an inner join in firebase?
Thanks a lot,
Yacine

You may want to consider
"posts" : {
"-KIycKhZU55dmKnHbbxv" : {
"author" : "John Doe",
"body" : "This is a post test",
"title" : "test",
"uid" : "SHaH36BLwgPvwgi9cDmRnsnONFB2"
"commented_by"
"SHaH36BLwgPvwgi9cDmRnsnONFB2": true
Then you can simply query for
posts.child("commented_by/uid").queryEqualToValue(true)
or, change the post-comments around to better match the queries you want to do:
"post-comments" : {
"-KIycMyL0Vy1BHVdI4zc" : {
"author" : "toto",
"post_data":
post_id: "-KIycKhZU55dmKnHbbxv",
post_title: "test"
"text" : "test",
"uid" : "SHaH36BLwgPvwgi9cDmRnsnONFB2"
},
That makes the query a snap as the post-comments node can be queried for the uid which return all of the post_id's of the posts they commented on. It wouldn't return the post itself, however, you may just be looking for the title so you can populate a list. Once the user taps it clicks it you can retrieve the actual data.

You will need to work with nested firebase calls. You can find a javascript example in this question but your swift code should look like the following:
ref.child("posts").observeEventType(.ChildAdded, withBlock: { (snapshot) in
if let postId = snapshot.key as! String {
let commentsRef = ref.child("post-comments")
commentsRef.child(postId).queryOrderedByChild("uid").queryEqualToValue(userId).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
for child in snapshot.children.allObjects as [FDataSnapshot] {
print(child.value)
}
}) { (error) in
print(error.localizedDescription)
}
}
})

Related

How to retrieve the User info?

I want to retrieve data uid in User table data but for a specific user , I have 2 users, and it seems that it grabs the 2 users uid but I want to grab with i speficy not both of them just one.
Thank You In advance
let specificDatabase = Database.database().reference()
specificDatabase.queryOrdered(byChild: "User/FirstName").queryEqual(toValue: "The user first name")
specificDatabase.observeSingleEvent(of: .value) { (snapShot: DataSnapshot) in
for child in snapShot.children {
print(snapShot.key)
}
}
Firebase Data Structure
"User" : {
"ez8sTAsqXTWfnuzizUXU69VS4qM2" : {
"FirstName" : "other",
"LastName" : "Martin",
"uid" : "ez8sTAsqXTWfnuzizUXU69VS4qM2"
},
}
"Data" : {
"ez8sTAsqXTWfnuzizUXU69VS4qM2" : {
"-Ll7jUYg6BxRAhWPLskg" : {
"Name" : "other Martin",
"Data1" : "data"
},
"-Ll7jW_elQIPTLESwDYD" : {
"Name" : "other Martin",
"Data1" : "data "
}
},
}
You're telling Firebase to order each child node of the root by its User/FirstName property and then filter on that. Since the child nodes of the root don't have a property at that path, the query returns no results.
Instead you want to order/filter each child node of /User by itsFirstName property, which you can do with:
let specificDatabase = Database.database().reference(withPath: "User")
specificDatabase.queryOrdered(byChild: "FirstName").queryEqual(toValue: "other")

Firebase, get property by email

I have this database on Firebase:
{
"issues" : {
"-L04771_EjrLlv5u1-GU" : {
"issue" : "Test insert 1",
"last_edit" : "d8QICgTG5xR20RBzAXfzfu8gLgw2",
"owner" : "d8QICgTG5xR20RBzAXfzfu8gLgw2",
"owner_email" : "example1#gmail.com",
"status" : 1,
"url" : "http://www.example.com/example.html"
},
"-L047pIoqxkj4saaTYyQ" : {
"issue" : "Test insert 2",
"last_edit" : "d8QICgTG5xR20RBzAXfzfu8gLgw2",
"owner" : "d8QICgTG5xR20RBzAXfzfu8gLgw2",
"owner_email" : "example2#gmail.com",
"status" : 1,
"url" : "http://www.example.com/example.html"
}
}
}
I have to extract only those who have owner_email "example1#gmail.com".
Is possible?
You're probably looking for Firebase Queries and especially the equalTo-Query.
Your code would be something like this:
// Find all issues with owner_email = example1#gmail.com
var ref = firebase.database().ref("issues");
ref.orderByChild("owner_email").equalTo("example1#gmail.com").on("value", function(snapshot) {
// Loops through the matching issues
snapshot.forEach(function(child) {
console.log(child.key);
});
});

Query object in child array

I am trying to use a query to retrieve data of student who are only 4 years old from my database but i cant figure out how to use Firebase to query the age (The array in each child).
{
"student" : {
"-Kv2RVDDsI-v6V7g_LBn" : [ {
"name" : "sam",
"age" : "6"
}, {
"name" : "tom",
"age" : "4"
}
],
"-hguyu-v6V7g_LBn" : [ {
"name" : "Tim",
"age" : "12"
}, {
"name" : "tom",
"age" : "4"
}
]
}
}
This is my code but it does no return anything.
ref.child("student").queryOrdered(byChild: "age").queryEqual(toValue: 4).observe(.childAdded, with: { (snapshot) in
let value = snapshot.value
print(value)
}) { (error) in
print(error.localizedDescription)
}
However, if i remove the queryOrdered(byChild: "age") it works.
Thanks.
You are referencing "Student" when actually your JSON database format child path name is "Students".
Also make sure that you are querying by key. Refer to the Firebase documentation to see how to query by key. I don't understand your structure though why you have 2 children per key.
Why not have a new key for each child?

How do I associate a firebase sub-entry to a main entry? (explanation in comments)

The JSON:
{
"coffee-shops" : {
"-KJ4I4D-Jrqrzc9wP42C" : {
"coffeeShopName" : "Starbucks",
"coffeeShopRating" : 3.5,
"-KJ4VVB51wx9NpEKtjxQ" : {
"coffeeShopName" : "Starbucks",
"coffeeShopRating" : 1,
"coffeeShopReview" : "R",
"coffeeShopReviewerName" : "Charles"
},
"-KJEJ6MpQwOHcay_9k6v" : {
"coffeeShopName" : "Starbucks",
"coffeeShopRating" : 4,
"coffeeShopReview" : "B",
"coffeeShopReviewerName" : "Charles"
}
}
},
"users" : {
"02a54e06-9635-4e22-9bb7-c0ddcd9c6f4f" : {
"email" : "charles#gmail.com",
"provider" : "password",
"username" : "thecoffeeguy"
},
"03fe2c17-3c66-442c-a63d-4a1e02fd660c" : {
"email" : "test#gmail.com",
"provider" : "password",
"username" : "Charles"
},
"16a7279f-5478-4f3f-b5f8-2f261d166d92" : {
"email" : "tester#gmail.com",
"provider" : "password",
"username" : "haha"
},
"23275f65-8e16-4ede-9236-21485b7493b9" : {
"email" : "boo#gmail.com",
"provider" : "password",
"username" : "boo"
},
"a5ed6962-76bc-476a-b432-6787e45badfc" : {
"email" : "mesbekmek#gmail.com",
"provider" : "password",
"username" : "mesbekmek"
}
}
}
Some context: I'm making a coffee app and I need to have the reviews be specific to the coffee shop a user is at. Right now, all reviews that have ever been made will show up on my tableview.
This isn't so much a "how to code" question, but me wondering how to approach this and how I might solve it.
This is what I think I should do:
get a specific coffee shop's uuid
iterate over the reviews because they are sub-entries in my coffee shop model
get data from iterating over the reviews, then see if the uuid of the cell(?) selected matches the review uuid
This doesn't really sound right to me, so any help would be great.
You can organize your db in a different way.
coffee-shops:
|--coffeeId1
|--coffeeId2
|--coffeeId3
reviews:
|--coffeeId1
|----reviewId1
|----reviewId2
|--coffeeId2
|----reviewIdX
|----reviewIdXx
When you insert a review you can use the same key of the coffee-shop.
In this way all the reviews of the same coffee shop is under the same ref.
You can achieve it using something like this:
ref(reviews).child(coffeeshop.getKey()).push();

How to join query in mongodb?

I have user document collection like this:
User {
id:"001"
name:"John",
age:30,
friends:["userId1","userId2","userId3"....]
}
A user has many friends, I have the following query in SQL:
select * from user where in (select friends from user where id=?) order by age
I would like to have something similar in MongoDB.
To have everything with just one query using the $lookup feature of the aggregation framework, try this :
db.User.aggregate(
[
// First step is to extract the "friends" field to work with the values
{
$unwind: "$friends"
},
// Lookup all the linked friends from the User collection
{
$lookup:
{
from: "User",
localField: "friends",
foreignField: "_id",
as: "friendsData"
}
},
// Sort the results by age
{
$sort: { 'friendsData.age': 1 }
},
// Get the results into a single array
{
$unwind: "$friendsData"
},
// Group the friends by user id
{
$group:
{
_id: "$_id",
friends: { $push: "$friends" },
friendsData: { $push: "$friendsData" }
}
}
]
)
Let's say the content of your User collection is the following:
{
"_id" : ObjectId("573b09e6322304d5e7c6256e"),
"name" : "John",
"age" : 30,
"friends" : [
"userId1",
"userId2",
"userId3"
]
}
{ "_id" : "userId1", "name" : "Derek", "age" : 34 }
{ "_id" : "userId2", "name" : "Homer", "age" : 44 }
{ "_id" : "userId3", "name" : "Bobby", "age" : 12 }
The result of the query will be:
{
"_id" : ObjectId("573b09e6322304d5e7c6256e"),
"friends" : [
"userId3",
"userId1",
"userId2"
],
"friendsData" : [
{
"_id" : "userId3",
"name" : "Bobby",
"age" : 12
},
{
"_id" : "userId1",
"name" : "Derek",
"age" : 34
},
{
"_id" : "userId2",
"name" : "Homer",
"age" : 44
}
]
}
Edit: this answer only applies to versions of MongoDb prior to v3.2.
You can't do what you want in just one query. You would have to first retrieve the list of friend user ids, then pass those ids to the second query to retrieve the documents and sort them by age.
var user = db.user.findOne({"id" : "001"}, {"friends": 1})
db.user.find( {"id" : {$in : user.friends }}).sort("age" : 1);
https://docs.mongodb.org/manual/reference/operator/aggregation/lookup/
This is the doc for join query in mongodb , this is new feature from version 3.2.
So this will be helpful.
You can use in Moongoose JS .populate() and { populate : { path : 'field' } }.
Example:
Models:
mongoose.model('users', new Schema({
name:String,
status: true,
friends: [{type: Schema.Types.ObjectId, ref:'users'}],
posts: [{type: Schema.Types.ObjectId, ref:'posts'}],
}));
mongoose.model('posts', new Schema({
description: String,
comments: [{type: Schema.Types.ObjectId, ref:'comments'}],
}));
mongoose.model('comments', new Schema({
comment:String,
status: true
}));
If you want to see your friends' posts, you can use this.
Users.find(). //Collection 1
populate({path:'friends', //Collection 2
populate:{path:'posts' //Collection 3
}})
.exec();
If you want to see your friends' posts and also bring all the comments, you can use this and too, you can indentify the collection if this not find and the query is wrong.
Users.find(). //Collection 1
populate({path:'friends', //Collection 2
populate:{path:'posts', //Collection 3
populate:{path:'commets, model:Collection'//Collection 4 and more
}}})
.exec();
And to finish, if you want get only some fields of some Collection, you can use the propiertie select Example:
Users.find().
populate({path:'friends', select:'name status friends'
populate:{path:'comments'
}})
.exec();
MongoDB doesn't have joins, but in your case you can do:
db.coll.find({friends: userId}).sort({age: -1})
one kind of join a query in mongoDB, is ask at one collection for id that match , put ids in a list (idlist) , and do find using on other (or same) collection with $in : idlist
u = db.friends.find({"friends": ? }).toArray()
idlist= []
u.forEach(function(myDoc) { idlist.push(myDoc.id ); } )
db.friends.find({"id": {$in : idlist} } )
Only populate array friends.
User.findOne({ _id: "userId"})
.populate('friends')
.exec((err, user) => {
//do something
});
Result is same like this:
{
"_id" : "userId",
"name" : "John",
"age" : 30,
"friends" : [
{ "_id" : "userId1", "name" : "Derek", "age" : 34 }
{ "_id" : "userId2", "name" : "Homer", "age" : 44 }
{ "_id" : "userId3", "name" : "Bobby", "age" : 12 }
]
}
Same this: Mongoose - using Populate on an array of ObjectId
You can use playOrm to do what you want in one Query(with S-SQL Scalable SQL).
var p = db.sample1.find().limit(2) ,
h = [];
for (var i = 0; i < p.length(); i++)
{
h.push(p[i]['name']);
}
db.sample2.find( { 'doc_name': { $in : h } } );
it works for me.
You can do it in one go using mongo-join-query. Here is how it would look like:
const joinQuery = require("mongo-join-query");
joinQuery(
mongoose.models.User,
{
find: {},
populate: ["friends"],
sort: { age: 1 },
},
(err, res) => (err ? console.log("Error:", err) : console.log("Success:", res.results))
);
The result will have your users ordered by age and all of the friends objects embedded.
How does it work?
Behind the scenes mongo-join-query will use your Mongoose schema to determine which models to join and will create an aggregation pipeline that will perform the join and the query.

Resources