【Firebase】它要怎麼排序呢?(補充)

在上集的庫存APP中,雖然把東西做出來了,但熊熊發現我明明就有寫排序的Code呀,卻完全沒有反應,排序在資料庫中是很正常的功能吧?不然要資料庫幹嘛呢?如果利用手機小小的CPU去排序,一下子就沒電了呀,我想我的人品應該沒有問題吧?所以就去問問高高手同事,也查了一下,發現…果然是我的問題呀…XD,現在我們就來排序吧。

排序

測試資料

  • 就一個很簡單的書籍列表,資料都是從博客來Copy來的,主要要注意的地方是,BarCode是為了測試才使用A~E的值
{
  "Books": {
    "BarCode": {
      "E": {
        "ISBN": 9789863843344,
        "Title": "世界大局.地圖全解讀",
        "URL": "https://www.books.com.tw/products/0010822566",
        "Icon": null,
        "Count": 0,
        "Timestamp": 9789863843344
      },
      "D": {
        "ISBN": 9789578787322,
        "Title": "一日三餐減醣料理:單週無壓力消失2kg的美味計劃,72道低醣速瘦搭配餐",
        "URL": "https://www.books.com.tw/products/0010789764",
        "Icon": null,
        "Count": 0,
        "Timestamp": 9789863843344
      },
      "C": {
        "ISBN": 9789573285304,
        "Title": "表裡不一的動物超棒的!圖鑑 (電子書)",
        "URL": "https://www.books.com.tw/products/E050045960",
        "Icon": null,
        "Count": 0,
        "Timestamp": 9789863843344
      },
      "B": {
        "ISBN": 9789571377995,
        "Title": "愛對了,每天都是情人節:以「16型愛情氣質」探尋屬於你的美好伴侶",
        "URL": "https://www.books.com.tw/products/0010822829",
        "Icon": null,
        "Count": 0,
        "Timestamp": 9789863843344
      },
      "A": {
        "ISBN": 9787111617976,
        "Title": "Flutter技術入門與實戰",
        "URL": "https://www.books.com.tw/products/CN11615679",
        "Icon": null,
        "Count": 0,
        "Timestamp": 9789863843344
      }
    }
  }
}

排序跟沒排序

  • 其實在官方文件是有說明的,但是…寫得實在是有看沒有懂(其實是我的英文太爛了),重點就在下面,不過要注意的是它只會順著由小到大排序,如果要反過來的話,就要在程式內自己去把Array倒過來排了…
datasnapshot.value      // 沒有排序的 (DataSnapshot)
datasnapshot.children   // 有排過序的 (NSEnumerator)
// MARK: - 小工具
extension ViewController {
    
    /// 產生[book]
    private func booksMaker(withChildren children: NSEnumerator) -> [Book] {
        
        var books = [Book]()
        
        for child in children {
            
            if let child = child as? DataSnapshot {
                
                let bookData = child.value as? [String: Any]
                if let book = self.bookMaker(withValue: bookData) { books.append(book) }
            }
        }
        
        return books
    }
    
    /// 產生book
    private func bookMaker(withValue value: [String: Any]?) -> Book? {
        
        guard let value = value,
              let isbn = value["ISBN"] as? Int,
              let title = value["Title"] as? String
        else {
            return nil
        }
        
        let book = Book(ISBN: isbn, Title: title)
        
        return book
    }
}

排序程式

  • 其實就只是把排序的Code加上去而已,另外不得不說,Swift的Enum真的是太好用了,可以加上變數…XD
import UIKit
import FirebaseDatabase

// MARK: - Firebase工具
class FIRDatabase: NSObject {
    
    /// 排序的類型
    enum OrderType {
        case none
        case queryOrderedByKey
        case queryOrderedByValue
        case queryOrderedByPriority
        case queryOrderedByChild(field: Book.Field)
    }
    
    public static let shared = FIRDatabase()
    private let reference = Database.database().reference()
    private override init() { super.init() }
}

// MARK: - 小工具
extension FIRDatabase {

    /// 取得資料 (及時)
    func childValueForRealtime(withPath path: String, orderType type: OrderType ,result: @escaping (Result<DataSnapshot?, Error>) -> Void) -> UInt? {
        
        let _reference = reference.child(path)
        var queryReference: DatabaseQuery?
        
        switch type {
        case .none: queryReference = _reference
        case .queryOrderedByKey: queryReference = _reference.queryOrderedByKey()
        case .queryOrderedByValue: queryReference = _reference.queryOrderedByValue()
        case .queryOrderedByPriority: queryReference = _reference.queryOrderedByPriority()
        case .queryOrderedByChild(let field): queryReference = _reference.queryOrdered(byChild: field.rawValue)
        }
        
        let handleNumber = queryReference?.observe(.value, with: { (snapshot) in
            result(.success(snapshot))
        }, withCancel: { (error) in
            result(.failure(error))
        })
        
        return handleNumber
    }
}

根據主Key做排序

  • 就是queryOrderedByKey() => BarCode (A~E)
// MARK: - 小工具
extension ViewController {

    /// 取得書本資料
    private func booksForRealtime() {
        
        let path = "Books/BarCode"
        
        _ = FIRDatabase.shared.childValueForRealtime(withPath: path, orderType: .queryOrderedByKey) { (result) in
            
            switch(result) {
            case .failure(let error): print(error)
            case .success(let snapshot):
                guard let snapshot = snapshot else { return }
                self.books = self.booksMaker(withChildren: snapshot.children)
                self.myTableView.reloadData()
            }
        }
    }
}

根據內容的Key做排序

  • 就是queryOrdered(byChild:) => { ISBN: }
// MARK: - 小工具
extension ViewController {
    
    /// 初始化設定
    private func initSetting() {
        myTableView.delegate = self
        myTableView.dataSource = self
    }
    
    /// 取得書本資料
    private func booksForRealtime() {
        
        let path = "Books/BarCode"
        
        _ = FIRDatabase.shared.childValueForRealtime(withPath: path, orderType: .queryOrderedByChild(field: .ISBN)) { (result) in
            
            switch(result) {
            case .failure(let error): print(error)
            case .success(let snapshot):
                guard let snapshot = snapshot else { return }
                self.books = self.booksMaker(withChildren: snapshot.children)
                self.myTableView.reloadData()
            }
        }
    }
}

範例程式碼下載

後記

  • 其實排序這件事情,我一開始也是把資料傳回來之後,在手機上去做排序,後來想想太笨了,果然是『不經一坑,不長一智』啊。