1

I am trying developing an application like Taxi booking and storing data on Firebase.

But, I am facing problem while querying data for RideDetail(History) from Firebase.

I want to fetch ride_detail for specific "customer_id" in pagination form.

My Firebase DataStructure:

{
  "ride_details": {
    "NuEoP2WNPwigsbY1FQy9M150131918189233": {
      "customer_id": "tstebwLlf4OCRdWhNKO9XCO08xY2",
      "destination_address": "New Ranip\nNew Ranip\nAhmedabad\nGujarat 380081\nIndia",
      "destination_lang": 72.55924470000001,
      "destination_latg": 23.0930152,
      "discount": "10%",
      "driver_id": "cIyZQIJ7tsdvF1a9KpRrKucF2o62",
      "drop_time": "2017-07-29 09:12:21 +0000",
      "fare": "13.16 Rs.",
      "payment_time": 150149034812771,
      "pickup_time": "2017-07-29 09:10:38 +0000",
      "priceperkm": "10.00 Rs.",
      "ride_confirm_time": "2017-07-29 09:06:21 +0000",
      "source_address": "Vastrapur\nVastrapur\nAhmedabad\nGujarat\nIndia",
      "source_lang": 72.5293244,
      "source_latg": 23.0350073,
      "tax": "10%"
    },
    "RH0oZ0Ypbkur3wJM3HMvM150147833457957": {
      "customer_id": "aYQFbwLlf4OCRdWhNKO9XCO08xY2",
      "destination_address": "Sarovar Park Plaza Hotels and Resorts Private Limted\nNo 1\nSector 10\nCBD Belapur\nWadala West\nWadala\nMumbai\nMaharashtra 400614\nIndia",
      "destination_lang": 72.8561644,
      "destination_latg": 19.0176147,
      "discount": 0,
      "driver_id": "cIyZQIJ7tsdvF1a9KpRrKucF2o62",
      "drop_time": "",
      "fare": 0,
      "payment_time": 150149034812772,
      "pickup_time": "",
      "priceperkm": 0,
      "ride_confirm_time": "2017-07-31 05:18:54 +0000",
      "source_address": "Smokin Joe's Fresh Pizza\nShop No. 2\n3\nGround Floor\nAbhiman II\nWadala West\nThane West\nMumbai\nMaharashtra 400602\nIndia",
      "source_lang": 72.8561644,
      "source_latg": 19.0176147,
      "tax": 0
    }
  }
}

Here "payment_time" is timestamp when payment done.

And the response I want is like:

{
    "RH0oZ0Ypbkur3wJM3HMvM150147833457957": {
      "customer_id": "aYQFbwLlf4OCRdWhNKO9XCO08xY2",
      "destination_address": "Sarovar Park Plaza Hotels and Resorts Private Limted\nNo 1\nSector 10\nCBD Belapur\nWadala West\nWadala\nMumbai\nMaharashtra 400614\nIndia",
      "destination_lang": 72.8561644,
      "destination_latg": 19.0176147,
      "discount": 0,
      "driver_id": "cIyZQIJ7tsdvF1a9KpRrKucF2o62",
      "drop_time": "",
      "fare": 0,
      "payment_type": 150149034812772,
      "pickup_time": "",
      "priceperkm": 0,
      "ride_confirm_time": "2017-07-31 05:18:54 +0000",
      "source_address": "Smokin Joe's Fresh Pizza\nShop No. 2\n3\nGround Floor\nAbhiman II\nWadala West\nThane West\nMumbai\nMaharashtra 400602\nIndia",
      "source_lang": 72.8561644,
      "source_latg": 19.0176147,
      "tax": 0
    },
    "1trcf0Ypbkur3wJM3HMvM150147833457957": {
      "customer_id": "aYQFbwLlf4OCRdWhNKO9XCO08xY2",
      "destination_address": "Sarovar Park Plaza Hotels and Resorts Private Limted\nNo 1\nSector 10\nCBD Belapur\nWadala West\nWadala\nMumbai\nMaharashtra 400614\nIndia",
      "destination_lang": 72.8561644,
      "destination_latg": 19.0176147,
      "discount": 0,
      "driver_id": "cIyZQIJ7tsdvF1a9KpRrKucF2o62",
      "drop_time": "",
      "fare": 0,
      "payment_type": 150149034812778,
      "pickup_time": "",
      "priceperkm": 0,
      "ride_confirm_time": "2017-07-31 05:18:54 +0000",
      "source_address": "Smokin Joe's Fresh Pizza\nShop No. 2\n3\nGround Floor\nAbhiman II\nWadala West\nThane West\nMumbai\nMaharashtra 400602\nIndia",
      "source_lang": 72.8561644,
      "source_latg": 19.0176147,
      "tax": 0
    } 
}

I want first 10 records for specific "customer_id" that I pass in query orderedBy "payment_time". Also I want to do pagination for the same. i.e. in second query call, it must return 11-20 records and so on.

Frank van Puffelen
  • 565,676
  • 79
  • 828
  • 807
DJ1
  • 936
  • 15
  • 29
  • Please post your firebase structure as text, not an image. Images are not searchable and if we need to use it in an answer it has to be retyped. Can you clarify your query? What are your expected results? – Jay Aug 01 '17 at 10:34
  • @Jay Please check again I have edited Question. – DJ1 Aug 01 '17 at 12:08
  • The question is still unclear; is the dataset you want X number of records for a particular customer that has a certain payment type? If that's correct then your 'response I want' doesn't match as it's two different payment types for the same customer. Maybe it's something else? *OrderBy "payment_time" and "customer_id" Equal to "value".* isn't clear. Do you want to have the returned values sorted by payment_type and the ones you want returned are customer_id = value? – Jay Aug 01 '17 at 12:18
  • Are the filters that you will be adding Dynamic or Static?. If static you will have to play around with the JSON Database Structure and you will be good to go . If Dynamic.. you can apply filter on one of them and retrieve and then apply the other filter on the frontend... – Dravidian Aug 01 '17 at 12:18
  • @Jay please check edited question. – DJ1 Aug 01 '17 at 12:33
  • @Dravidian please check edited question. – DJ1 Aug 01 '17 at 12:34
  • Your initial question asks about *multiple filter* but the details are the query should return the results for a single customer_id and you want them sorted by payment_type. That's conflicting info so can you clarify?You also want information about pagination which appears to be a separate question. – Jay Aug 01 '17 at 13:58
  • There are a few questions where this topic has been covered before: https://stackoverflow.com/search?tab=votes&q=%5bswift%5d%5bfirebase-database%5d%20pagination Did you try anything yet? – Frank van Puffelen Aug 01 '17 at 14:02
  • @FrankvanPuffelen I know how to do pagination and I have already done that part but my query is different as I mentioned in the question. I want to fetch all records with specific "customer_id" or "driver_id". Also, want in an ordered manner by "payment_time". If the question is still unclear for you, I can explain well if we can chat over skype or StackOverflow. Is it possible? – DJ1 Aug 02 '17 at 04:06
  • 1
    It sounds like you're trying to combine multiple calls to `orderBy...` in a single query, which is not possible in Firebase Database. See http://stackoverflow.com/questions/26700924/query-based-on-multiple-where-clauses-in-firebase – Frank van Puffelen Aug 02 '17 at 04:19

2 Answers2

3

The question and comments have some different criteria but let me address it at a high level;

The first answer is: Firebase cannot be queried for the value of one child and then ordered by another.

The simple query function expresses that:

let query = ridesRef.queryOrdered(byChild: "cust_id").queryEqual(toValue: "cust id 4")

To accomplish that task, query for the child data you want, in this case all customer id 4 nodes, and then order in code. Here's an example

class RideClass {
    var key = ""
    var cust_id = ""
    var pay_time = ""

    init(key: String, cust_id: String, pay_time: String) {
        self.key = key
        self.cust_id = cust_id
        self.pay_time = pay_time
    }
}

var rideArray = [RideClass]()

func populateRideArray() {
    let usersRef = self.ref.child("ride_details")
    let query = usersRef.queryOrdered(byChild: "cust_id").queryEqual(toValue: "cust id 4") 
    query.observeSingleEvent(of: .value, with: { snapshot in 
        for child in snapshot.children {
            let snap = child as! DataSnapshot
            let dict = snap.value as! [String: Any]
            let key = snap.key
            let custId = dict["cust_id"] as! String
            let payTime = dict["pay_time"] as! String
            let ride = RideClass(key: key, cust_id: custId, pay_time: payTime)
            self.rideArray.append(ride)
        }

        for ride in self.rideArray {  //unsorted example
            print(ride.pay_time)
        }

        self.rideArray.sort { $0.pay_time < $1.pay_time } //sort

        for ride in self.rideArray {  //sorted example
            print(ride.pay_time)
        }
    })
}

In this example, we create a RideClass which stores some info about the ride, and then an array of rides which could be used as a tableView dataSource.

Then query for all rides that are for cust id 4. We have a loop to show what was retreived unsorted and then this little gem

self.rideArray.sort { $0.pay_time < $1.pay_time }

which sorts the ride array in place by pay_time, which answers the question.

Suppose though, there are 100,000 ride child nodes. Loading in all of that data and sorting in code could be challenging memory wise. What do you do?

We leverage a compound value; along with the child nodes of cust_id and pay_time, we also include id_time. Here's a possible structure:

  "ride_details" : {
    "ride_0" : {
      "cust_id" : "cust id 4",
      "id_time" : "cust id 4_172200",
      "pay_time" : "172200"
    },
    "ride_1" : {
      "cust_id" : "cust id 2",
      "id_time" : "cust id 2_165500",
      "pay_time" : "165500"
    },
    "ride_2" : {
      "cust_id" : "cust id 1",
      "id_time" : "cust id 1_182300",
      "pay_time" : "182300"
    },
    "ride_3" : {
      "cust_id" : "cust id 3",
      "id_time" : "cust id 3_131800",
      "pay_time" : "131800"
    },
    "ride_4" : {
      "cust_id" : "cust id 4",
      "id_time" : "cust id 4_132200",
      "pay_time" : "132200"
    }
  },

and then some code to read in the cust id 4 nodes in the correct order

    let ridesRef = self.ref.child("ride_details")
    let query = ridesRef.queryOrdered(byChild: "id_time")
                        .queryStarting(atValue: "cust id 4_")
                        .queryEnding(atValue: "cust id 4_\\uf8ff")
    query.observeSingleEvent(of: .value, with: { snapshot in
        for child in snapshot.children {
            let snap = child as! DataSnapshot
            let dict = snap.value as! [String: Any]
            let key = snap.key
            let custId = dict["cust_id"] as! String
            let payTime = dict["pay_time"] as! String
            let ride = RideClass(key: key, cust_id: custId, pay_time: payTime)
            self.rideArray.append(ride)
        }

        for ride in self.rideArray {  //unsorted example
            print(ride.pay_time)
        }
    })

Two things to note:

The snapshot must be iterated over to maintain the child sequence

"\uf8ff" is a character at a very high code level in Unicode - because of that it encompasses all of the preceeding characters.

Jay
  • 34,438
  • 18
  • 52
  • 81
0

There are two basic approaches that may work for your requirements:

  1. construct a query that orders by payment_time, limits to include only the first 10 records, then holds a reference to the 10th record so that you can make subsequent pagination calls with the queryStartingAtValue filter.

  2. Setup a firebase cloud function with a database trigger listening to the payment_time node, such that every time your empty sting is updated with a value in the database you transform the data so that it's organized in such a way that makes it trivial to consume for your needs here. For example - I would organize the new data at a path that looks like this: customer_ride_details/{{customer_id}}/{{ride_id}}. And since you are triggering the function when you replace the payment_time empty string with a timestamp. The keys should be ordered already for you to consume. You will still need to manage your pagination like we did with option 1.

Doug
  • 794
  • 7
  • 12