0

I'm currently working on a project which let users to rent extra spaces. You can think clone of airbnb. In this iOS app, I'm using Firebase for Back-end as a Service.

I want to retrieve the flats that satisfy the filtering options like, bed count, bedroom count, Smoking etc. Logically, I'm grouping all flats by time slots first, then fetching the flats satisfies filtered city, then i'm fetching the flats which satisfies the filters. Then I'm iterating all the flats one by one to fetch their images.

My Json tree is like:

filter_flats/FlatCity/FlatID
time_slots/timestamp/flatID:true
flat_images/FlatID

Here is what I'm trying:

import Foundation
import Firebase
import FirebaseStorage

protocol QuerymasterDelegate :class
{
    func getFilteredFlats(filter:FilterModel, completion: @escaping ([FilteredFlat]) -> ())
}



class Querymaster:QuerymasterDelegate
{
    var flatEndpoint = FIRFlat()

    var returningFlats = [FilteredFlat]()
    /////////////////////////////////////////////////////////////
    //MARK: This method pulls all flats with required timeslot.//
    /////////////////////////////////////////////////////////////
    internal func getFilteredFlats(filter: FilterModel, completion: @escaping ([FilteredFlat]) -> ()) {

        var zamanAraliginaUygunFlatlar = [String]()

        FIRREF.instance().getRef().child("time_slots").queryOrderedByKey().queryStarting(atValue: filter.fromDate?.toTimeStamp()).queryEnding(atValue: filter.toDate?.toTimeStamp()).observeSingleEvent(of: .value, with: { (ss) in
            for ts in ss.children.allObjects
            {
                let timeslotFlatObj = ts as! FIRDataSnapshot
                let timeslotForFlat = timeslotFlatObj.value as! [String:Bool]
                for x in timeslotForFlat
                {
                    if (x.value == true && !zamanAraliginaUygunFlatlar.contains(x.key))
                    {
                        zamanAraliginaUygunFlatlar.append(x.key)
                    }

                }
            }

            /////////////////////////////////////////////////////////
            //MARK: This method pulls all flats with filtered city.//
            /////////////////////////////////////////////////////////

            FIRREF.instance().getRef().child("filter_flats/" + filter.city!).observe(.value, with: { (ss) in

                var sehirdekiFlatler = [String:Flat]()

                for i in ss.children.allObjects
                {
                    let flt = Flat()
                    let flatObject = i as! FIRDataSnapshot
                    let mainDict = flatObject.value as! [String:Any]
                    flt.userID = (mainDict["userId"] as? String)!
                    flt.id = flatObject.key
                    flt.city = filter.city
                    flt.title = mainDict["title"] as? String
                    flt.bathroomCount = mainDict["bathroomCount"] as? Int
                    flt.bedCount = mainDict["bedCount"] as? Int
                    flt.bedroomCount = mainDict["bedroomCount"] as? Int
                    flt.internet = mainDict["internet"] as? Bool
                    flt.elevator = mainDict["elevator"] as? Bool
                    flt.heating = mainDict["heating"] as? Bool
                    flt.gateKeeper = mainDict["gateKeeper"] as?Bool
                    flt.parking = mainDict["parking"] as? Bool
                    flt.pool = mainDict["pool"] as? Bool
                    flt.smoking = mainDict["smoking"] as? Bool
                    flt.tv = mainDict["tv"] as? Bool
                    flt.flatCapacity = mainDict["capacity"] as? Int
                    flt.cooling = mainDict["cooling"] as? Bool
                    flt.price = mainDict["price"] as? Double
                    flt.washingMachine = mainDict["washingMachine"] as? Bool

                    sehirdekiFlatler[flt.id] = flt

                    if(!(zamanAraliginaUygunFlatlar).contains(flt.id))
                    {
                        if(filter.bathroomCount == nil || filter.bathroomCount! <= flt.bathroomCount!)
                        {
                            if(filter.bedCount == nil || filter.bedCount! <= flt.bedCount!)
                            {
                                if(filter.bedroomCount == nil || filter.bedroomCount! <= flt.bedroomCount!)
                                {
                                    if(filter.internet == false || filter.internet! == flt.internet!)
                                    {
                                        if(filter.elevator == false || filter.elevator! == flt.elevator!)
                                        {
                                            if(filter.heating == false || filter.heating! == flt.heating!)
                                            {
                                                if(filter.gateKeeper == false || filter.gateKeeper! == flt.gateKeeper!)
                                                {
                                                    if(filter.parking == false || filter.parking! == flt.parking!)
                                                    {
                                                        if(filter.pool == false || filter.pool! == flt.pool!)
                                                        {
                                                            if(filter.smoking! == false || filter.smoking! == flt.smoking!)
                                                            {
                                                                if(filter.tv! == false || filter.tv! == flt.tv!)
                                                                {
                                                                    if(filter.capacity == nil || filter.capacity! <= flt.flatCapacity!)
                                                                    {
                                                                        if(filter.cooling == false || filter.cooling! == flt.cooling!)
                                                                        {
                                                                            if(filter.priceFrom == nil || filter.priceFrom! <= flt.price!)
                                                                            {
                                                                                if(filter.priceTo == nil || filter.priceTo! >= flt.price!)
                                                                                {
                                                                                    if(filter.washingMachine == false || filter.washingMachine! == flt.washingMachine!)
                                                                                    {
                                                                                        let filteredFlat = FilteredFlat()
                                                                                        filteredFlat.flatCity = flt.city
                                                                                        filteredFlat.flatID = flt.id
                                                                                        filteredFlat.flatPrice = flt.price
                                                                                        filteredFlat.flatTitle = flt.title
                                                                                        filteredFlat.userID = flt.userID
                                                                                        self.returningFlats.append(filteredFlat)


                                                                                    }
                                                                                }
                                                                            }
                                                                        }
                                                                    }
                                                                }
                                                            }
                                                        }
                                                    }
                                                }
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                ////////////////////////////////////////////////////////////////
                //MARK: This method pulls thumbnail image for returning Flats.//
                ////////////////////////////////////////////////////////////////
                if self.returningFlats.count > 0
                {
                    for a in self.returningFlats
                    {
                        FIRREF.instance().getRef().child("flat_images/" + a.flatID!).observeSingleEvent(of: .value, with: { (ss) in
                            let dict = ss.children.allObjects[0] as! FIRDataSnapshot
                            let obj = dict.value as! [String:String]
                            let flatImageDownloaded = FlatImageDownloaded(imageID: dict.key, imageDownloadURL: obj["downloadURL"]!)
                            a.flatThumbnailImage = flatImageDownloaded
                            completion(self.returningFlats)
                        })
                    }
                }
            })
        })
    }
}

The marked code This method pulls thumbnail image for returning Flats. is usually fetching data slower because of jumping among nodes of flats.

I didn't realized that what i'm doing wrong. Can you suggest more efficient method?

Thanks in advance.

Community
  • 1
  • 1
L'lynn
  • 167
  • 1
  • 13
  • Downloading data from the Firebase Database is typically a simple matter of your available bandwidth and the amount of data you're downloading. Latency is often less of an issue, since [Firebase pipelines requests where possible](http://stackoverflow.com/questions/35931526/speed-up-fetching-posts-for-my-social-network-app-by-using-query-instead-of-obse/35932786#35932786). How much data are you downloading over what bandwidth and what's the time it takes? Can you [reproduce with a simpler piece of code and share the JSON (as text)](http://stackoverflow.com/help/mcve)? – Frank van Puffelen Dec 20 '16 at 15:45

2 Answers2

0

Firebase uses NoSQL database, so the typical querying would be very inefficient. As far as I understand, your app requires a lot of query for the data, so Firebase itself does not the perfect solution for your app I guess..

Anyway, in the code, you can just request images in Dispatch async block, and that would make the process faster. Also, fyi, storing images in FirebaseStorage and store the url in the FirebaseDatabase will help you reduce a lot of money per data.

Gavin Kwon
  • 126
  • 4
0

This sounds like you are working with a significant amount of data, and we don't know your current UI but that may be the place to start to help reduce the bandwidth and speed up performance.

For example, in the UI there are three buttons that activate as the user selects the prior one.

Select City <- highlighted and ready to be tapped
Select Bedrooms <-Dimmed and not available yet
Select Internet <-Dimmed and not available yet

When the user taps Select City, they are presented a list of Cities available. this is easy and a fast Firebase query.

They tap Select Bedrooms and select 2. No real query needed here yet since it's a number between 1 and 5 (for example).

They tap Select Internet and this is a Yes/No or maybe None/Dial up/Broadband.

Now here's the key - the Structure.

City0
  name: "some city"

Flats
  flat_0
    location: "City0"
    bedrooms: "2"
    internet: "Broadband"
    filter: "City0_2_Broadband"
  flat_1
    location: "City0"
    bedrooms: "4"
    internet: "Dial up"
    filter "City0_4_Dial up"

with this, you can search for any city, or any number of bedrooms or any internet as separate queries.

The real power is having the ability to perform an SQL-like 'and' query when you want to get very specific data; which flats are available and 4 bedrooms and dial up and in city0

query for:  "City0_4_Dial up"

very fast and flexible and you can build the criteria for the query within the UI which limits the amount of data flowing into the app from Firebase, which makes it very fast.

If this is too far off base, let me know and I will edit and provide another option.

Jay
  • 34,438
  • 18
  • 52
  • 81
  • This apporach quite flexible which is thinked already by my team. But there is a trick about filtering. For example, if we are searching a query like, City0_4_Dialup, our UI must provide flats thats satisfy flats which are satisfy the query parameters of city0_4_dialup but our ui must provide the flats if the user does not select for example bedrooms or internet, so we take them as "Does not matter" and will display the flats whether they does have internet , bedrooms or not. For a simply understanding, our logic is appends, true or does not matter like in Airbnb. – L'lynn Dec 21 '16 at 22:44
  • Also if user search flats that have 4 bedrooms, the flats which have 5,6,7,8,9...n bedrooms must be presented on our UI. This querying requirements gets us confusing about Firebase which has no a powerful query motor unlike classic relational motors. – L'lynn Dec 21 '16 at 22:48