【问题标题】:Firebase and UISearchbarController searching via the server and not the client -Swift iOSFirebase 和 UISearchbarController 通过服务器而不是客户端搜索 -Swift iOS
【发布时间】:2017-02-16 00:47:51
【问题描述】:

有没有人知道如何将 Firebase 整合到 UISearchController 委托中?我找不到任何可靠的信息。可能有数千名员工。

我知道如何使用搜索控制器委托updateSearchResultsForSearchController 并使用NSPredicate 来过滤我正在寻找的内容,如果我使用的是 NSUserDefaults 但我不确定是否使用 Firebase。

我在我的问题中添加了更多代码

我有一个保存在 FirebaseDatabase 中的自定义数据模型对象,我想在该对象中搜索以下所有属性。

lastName
idNumber
deptNumber
position

搜索这些属性中的任何一个都应该首先在表格单元格中显示部分字符串,直到显示我要查找的整个字符串。因此,如果我输入字母“S”,则应显示所有以“S”开头的员工姓氏。如果我输入“Sa”,in 将过滤到这些字母“。据我了解,我应该使用“\u{f8ff}”来获取部分搜索字符串,但不返回任何数据。

所有的代码都在这里

我的对象是:

class Employee{
var firstName: String?
var lastName: String?
var idNumber: String?
var deptNumber: String?
var position: String?
}

我的路

-root
  -users
    -uid
      |_"email":"emailAddress"
      |_"userID":"uid"
      |_"firstName":"firstName"
      |_"lastName":"lastName"
  -employees
    -hireDate
      -uid //this is the same uid from the users node so I know who's who
        |_"firstName":"firstName"
        |_"lastName":"lastName"
        |_"idNum":"idNumber"
        |_"deptNumber":"deptNumber"
        |_"position":"position"

我的规则:

这里发生的是员工被雇用的那一天,他们被要求使用他们的电子邮件地址和密码创建一个公司帐户。 同时创建了一个“员工”路径,其中一个孩子是“hireDate”路径,最后是员工“uid”路径。这个员工“uid”是我想从“员工”节点搜索的路径

{
  "rules": {
      "users" : {
          "$uid" : {
              ".read": true,
              ".write": "auth != null && auth.uid == $uid"
          }
      },
      "employees": {
          "$hireDate": {
              "$uid": {
                 ".read": true,
                 ".indexOn": ["lastName", "idNumber", "deptNumber", "position"]
              }
          }
     }
  }
}

我的搜索控制器

import UIKit

class SearchController: UIViewController{

@IBOutlet var tableView: UITableView!

var searchController: UISearchController!
var employeesToFilter = [Employee]()
var filteredSearchResults = [Employee]()


override func viewDidLoad() {
    super.viewDidLoad()
    self.searchController = UISearchController(searchResultsController: nil)

    self.tableView.delegate = self
    //all searchController properties get set here no need to include them though

    let ref = FIRDatabase.database().reference()
    let employeeRef = ref.child("employees")

    employeeRef?.queryOrderedByChild("lastName").queryStartingAtValue("\u{f8ff}").queryLimitedToFirst(20).observeEventType(.ChildAdded, withBlock: {

        (snapshot) in

        if let dict = snapshot.value as? [String:AnyObject]{

            let firstName = dict["firstName"] as! String
            let lastName = dict["lastName"] as! String
            let idNumber = dict["idNumber"] as! String
            let deptNumber = dict["deptNumber"] as! String
            let position = dict["position"] as! String

            let employee = Employee()
            employee.firstName = firstName
            employee.lastName = lastName
            employee.idNumber = idNumber
            employee.deptNumber = deptNumber
            employee.position = position

            self.employeesToFilter.append(employee)
        }
    })
    self.tableView.reloadData()
}

override func viewDidAppear(animated: Bool) {
    self.searchController.active = true
}

deinit {
    self.searchController = nil
}

}


//MARK:- TableView Datasource
extension SearchBuildingController: UITableViewDataSource, UITableViewDelegate{

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
    return self.filteredSearchResults.count
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
    let cell = self.tableView.dequeueReusableCellWithIdentifier("SearchCell", forIndexPath: indexPath) as! SearchCell

    let searchString = self.filteredSearchResults[indexPath.row]

    cell.firstNameLabel.text = searchString.firstName
    cell.lastNameLabel.text = searchString.lastName
    cell.idNumberLabel.text = searchString.idNumber
    cell.deptNumberLabel.text = searchString.deptNumber
    cell.positionLabel.text = searchString.position
    return cell
}
}

//MARK:- SearchController Delegates
extension SearchController: UISearchResultsUpdating, UISearchBarDelegate, UISearchControllerDelegate{

func searchBarTextDidBeginEditing(searchBar: UISearchBar) {
    tableView.reloadData()
}

func updateSearchResultsForSearchController(searchController: UISearchController) {

    self.employeesToFilter.removeAll(keepCapacity: false)
    self.filteredSearchResults.removeAll(keepCapacity: false)

    let searchText = self.searchController.searchBar.text

    let searchPredicate = NSPredicate(format: SELF.lastName CONTAINS [c] %@ OR SELF.idNumber CONTAINS [c] %@ OR SELF.deptNumber CONTAINS[c] %@ OR SELF.position CONTAINS [c] %@", searchText!, searchText!, searchText!, searchText!)

    let array = (self.employeesToFilter as NSArray).filteredArrayUsingPredicate(searchPredicate)
    self.filteredSearchResults = array as! [Employee]

    tableView.reloadData()
}
}

【问题讨论】:

  • 你不会只搜索"\u{f8ff}";这没有任何意义,因为您没有给它“开始于”标准。因此,您需要startingAtValue("S")endingAtValue("S\u{f8ff}"); 之类的东西。请注意,尝试在 Firebase 中实现高级模糊/上下文搜索通常比它的价值更复杂。一个合适的搜索引擎会更加健壮,而且在我看来,启动和运行的复杂性要低得多。有关如何将搜索与 Firebase 集成的示例,请参阅 Flashlight
  • @Kato 谢谢我正在调查它

标签: ios uitableview swift2 firebase-realtime-database uisearchcontroller


【解决方案1】:

以下是我如何使用 Firebase 构建校园列表的示例。此方法会预先加载表格视图中的所有数据,以便于搜索和过滤。

我的校园对象非常简单,只有一个 id 和一个名字。

struct Campus {
    var id: String
    var name: String
}

在视图控制器中,我有两个数组。一个是保存所有返回的校园列表,另一个数组用于过滤后的校园。

let campuses = [Campus]()
let filteredCampuses = [Campus]()

然后我调用了一个我设置的方法来从 Firebase 加载校园。

override func viewDidLoad() {
    ...

    getAllCampusesFromFirebase() { (campuses) in
        self.campuses = campuses
        dispatch_async(dispatch_get_main_queue(), {
            self.tableView.reloadData()
        })
    }
}

然后在执行搜索时,我过滤掉校园,将校园名称与搜索栏中的搜索文本进行比较。

func updateSearchResultsForSearchController(searchController: UISearchController) {
    guard let searchText = searchController.searchBar.text else {
        return
    }

    filteredCampuses = campuses.filter { campus in
        return campus.name.lowercaseString.containsString(searchText.lowercaseString)
    }

    tableView.reloadData()
}

如果您没有预先加载所有数据,那么 Firebase 提供了一些方便的调用方法,您可以使用这些方法根据引用路径过滤数据。 https://firebase.google.com/docs/database/ios/lists-of-data

queryStarting(atValue)queryStarting(atValue:childKey:) 可能是您在这种情况下想要使用的那个。

ref.queryStarting(atValue: Any?)
ref.queryStarting(atValue: Any?, childKey: String?)

【讨论】:

  • 谢谢。我今天早些时候正在搜索。如果数据库中有数十亿个校园对象,我认为将所有 FB 数据加载到数组中会导致严重的性能问题。如果存在大量数据,则通过 queryStart.. 直接从数据库查询似乎是可行的方法。不过,我迷路了两个地方。 1、如何将queryData放入校园数组或过滤后的数组? 2.您如何查询多个属性,例如您将如何查询校园 ID 和名称?另外,如果您有一个 location 属性,您将如何包含它?这些是要查询的 3 个差异值。
  • 1) 要获取 queryData,您将在您正在搜索的路径上执行 observe(FIRDataEventType:with) 方法,例如 ref.queryStarting(atValue).observeEvent(FIRDataEventType:with)。这将为您的查询返回数据,然后使用该数据填充过滤后的数组。 2)要获得多个查询,您可以将查询链接在一起ref.queryStarting(atValue).queryStarting(atValue) 等。虽然我还没有尝试过,所以不确定当它有多个值时它是否将查询视为 AND 或 OR。 3) 对于位置搜索,我之前使用过 GeoFire,效果很好。
  • 我用更多代码更新了我的答案。如果你有机会,请告诉我你的想法。
  • @LanceSamaria 抱歉刚刚回复。一直在旅行。 Kato 提出的建议将成为我的建议,以及startingAtValue 和endingAtValue。希望这对您有所帮助并帮助您完成您想做的事情。
  • 感谢您的帮助。 ElastiSearch/Flaslight 似乎是要走的路。我相信这一切都是预先构建的。让它工作是我的新查询:)
猜你喜欢
  • 2015-06-20
  • 2012-01-12
  • 2010-12-08
  • 2016-03-19
  • 2019-03-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多