Firebase数据结构查询

Firebase data structure query

阅读文档后,他们建议不要使用嵌套数据结构。我认为这是处理我的数据的最佳方式,但我不确定如何将 tripId 添加到酒店集合中。

我的另一个选择是让用户为每个酒店条目输入“BOL”...我对该方法的问题是如何查询日期以查看与“行程”匹配的所有酒店?

编辑:::::::: 我想要完成的是,当用户选择一次旅行时,它会运行一个查询并显示与该旅行相关的酒店的所有夜晚总数。

users:
    - gCFiwSLwBeg87N6GfeofFxIAZyi2
        - UID: gCFiwSLwBeg87N6GfeofFxIAZyi2
        - firstName: "James"
        - email: "a@email.com"

trips:
    - "random document id"
        - bol: "1234567"
        - startDate: "1/3/2022"
        - destination: "Sacramento, CA"
        - userID: "Users UID"
        - foodTotal: "100"
        - hotelTotal: "240"
    - "random document id"
        - bol: "256976"
        - startDate: "2/5/2022"
        - destination: "Orlando, FL"
        - userID: "Users UID"
        - foodTotal: "150"
        - hotelTotal: "400"

hotels:
    - "random document id"
        - tripId: ?
        - date: "1/4/2022"
        - destination: "Sacramento, CA"
        - userID: "Users UID"
        - name: "Hotel Inn"
        - cost: "120"
    - "random document id"
        - tripId: ?
        - date: "1/5/2022"
        - destination: "Sacramento, CA"
        - userID: "Users UID"
        - name: "Hotel Inn"
        - cost: "120"

这是我目前用来将酒店添加到 firebase 的函数。

class FirebaseHotelRepository: HotelRepositoryProtocol {
    private let db = Firestore.firestore()

func addHotel(hotel: Hotel, completion: @escaping (Result<Hotel?, Error>) -> Void) {
        do {
            var addedHotel = hotel
            addedHotel.userId = Auth.auth().currentUser?.uid
            let _ = try db.collection("hotels").addDocument(from: addedHotel)
        } catch {
            fatalError("Unable to encode trip: \(error.localizedDescription)")
        }
    }
}

class AddHotelViewModel: ObservableObject {
    private let repo: HotelRepositoryProtocol

    var name: String = ""
    var location: String = ""
    var date = Date()
    var cost: String = ""

    @Published var saved: Bool = false
    
    init(repo: HotelRepositoryProtocol) {
        self.repo = repo
    }
    
    func addHotel() {
        let hotel = Hotel(bol: bol,
                          name: name,
                          location: location,
                          date: date,
                          cost: cost,
                          color: UIColor(color).hexStringFromColor())
        
        repo.addHotel(hotel: hotel) { result in
            switch result {
            case .success(let savedHotel):
                DispatchQueue.main.async {
                    self.saved = savedHotel == nil ? false : true
                }
            case .failure(let error):
                print(error.localizedDescription)
            }
        }
    }
}

从这个documentation:

In some cases, it can be useful to create a document reference with an auto-generated ID, then use the reference later. For this use case, you can call doc():

这里有一个示例代码,您可以参考:

let trips = db.collection("trips").addDocument(data: [
 // trips' data
])

// This will append tripId to the `addedHotel`
addedHotel["tripId"] = trips.documentID

let hotels = db.collection("hotels").addDocument(from: addedHotel)

以上代码应获取旅行的文档 ID,您可以手动附加或构建应传递给酒店文档的数据。


您可以找到更多相关信息here

让我们从澄清这一点开始

not using nested data structure

Firebase 中的一切都是嵌套的! - 这就是它的工作方式。这是关于深度嵌套数据的参考,因为它很难获得 to/query 等,因此对数据进行非规范化或扁平化是一种常用的做法。

有多种方法可以获取您想要的数据,但让我 re-state 这个问题

I have trips and each trip has a number of associated hotels. My goal is to read the hotels for each trip to calculate a total cost for those hotels.

TL;DR 跳到底部:一个快速的事情部分

这是一种结构

trip_0
   trip_name: "Napa trip"
   hotels:
     hotel_0: true
     hotel_1: true
     hotel_2: true

使用该结构,阅读 trip_0 将提供关联的酒店。然后您遍历该数组(映射)并读取 hotel_0 并获取它的成本,然后 hotel_1 并获取它的成本。把成本加起来,你就完成了。无需查询。

第二个选项是反向方法,使用链接酒店和旅行的单独集合 - 这需要查询以获取与每次旅行关联的酒店

hotels_trips
   hotel_0
      trip_0: true
      trip_1: true
      trip_2: true
   hotel_1
      trip_1: true

根据以上内容,运行 对 hotels_trips 集合中的酒店的查询 trip_1 为真。获得酒店列表后,您可以阅读每个酒店以了解其成本并将其相加。

另一种解决方案是使用 arrayContains 的结构和查询:

hotels
   hotel_0
      hotel_name: "Hotel California"
      cost: 500
      trip_array
         0: trip_0
         1: trip_1

然后是查询酒店集合中包含 trip_0

的所有酒店的代码
func queryHotels() {
    let hotelCollection = self.db.collection("hotels")
    hotelCollection.whereField("trip_array", arrayContains: "trip_0").getDocuments { querySnapshot, error in
        if let err = error {
            print(err.localizedDescription)
            return
        }

        guard let docs = querySnapshot?.documents else { return }

        for doc in docs {
            let name = doc.get("name") as! String
            print(name)
        }
    }
}

一件小事:数组通常很难在像 Firebase 这样的 NoSQL 数据库中使用,所以我通常会避免使用它们。您可能要考虑使用 Map 而不是数组,然后在路径 trip_map/trip_0 上查询以查看它是否为真。酒店结构将包括此而不是 trip_array

trip_map
   trip_0: true
   trip_1: true

并且查询将测试路径以查看其值是否为真

hotelCollection.whereField("trip_map.trip_0", isEqualTo: true).getDocuments { querySnapshot, error in