使用NSFetchedResultsController搜索每个字符CoreData非常慢,有大量记录

时间:2016-11-04 08:51:07

标签: ios search core-data

我有一个预装了20000条记录的数据库。

基本上有两个实体在其中进行搜索,City Entity而另一个实体是street Entity。 City有“名称”属性,Street Entity也有名称属性,city and street都在one-to-many relationship。因为一个城市可以有很多街道。

现在,当用户可以使用“a或b”之类的任何字符进行搜索时,它应该向用户显示相应的城市和/或街道.Eveytime我必须重新加载并重新配置NSFetchedResultsController。

我已经实施了NSPredicate来过滤掉记录。

let predicate = NSPredicate(format:"(name CONTAINS[c] %@) OR (SELF.streets.name CONTAINS[c] %@)",text,text)
 predicateArray.append(predicate)

这个谓词用于过滤记录。我也使用MagicalRecord作为coreData的包装器。搜索过去就像我在寻找每个角色一样。

非常感谢任何帮助。

3 个答案:

答案 0 :(得分:1)

我试着在内存20000行中进行过滤。它速度超快!完全没有滞后。你似乎不止一次去持久性商店,这就是我的建议:

  • 停止使用NSFetchedResultController(在此之前尝试第三点可行)
  • 创建两个NSFetchRequest:其中一个将为您提供城市,另一个将为您提供街道。因此,您将获得一系列城市和一系列街道。 这种方法可以帮助您避免获取非常低效的关系city.streets。如果你可以将两者组合并映射到单个字符串数组,那就更好了。
  • 非常重要:设置request.returnsObjectsAsFaults = false。否则,您将继续访问数据库以获取city.name或street.name。

搜索文本字段更改时:

  • 首先过滤城市然后过滤街道。
  • 重新加载tableView

编辑:测试示例 我测试了下面的代码

  • iPhone 6:第一个字符在100ms-170ms之间。然后它变得更快。 (2x)我觉得有任何滞后。

  • iPad mini 2:第一个字符在300ms到350ms之间。然后它变得更快。 (2x)有一些滞后。

    class ViewController: UIViewController, UISearchBarDelegate, UITableViewDataSource{
    
    @IBOutlet weak var tableView: UITableView!
    @IBOutlet weak var searchBar: UISearchBar!
    
    var streets = [Street]()
    var cities = [City]()
    
    var filteredStreets: [Street] = []
    var filteredCities: [City] = []
    
    override func viewDidLoad() {
        super.viewDidLoad()
        cities = findAllCities()
        streets = findAllStreets()
        filteredCities = cities
        filteredStreets = streets
    }
    
    func searchBar(searchBar: UISearchBar, textDidChange searchText: String) {
        filterItemsWithCurrentSearchText(searchText)
    }
    
    func filterItemsWithCurrentSearchText(text: String){
        if text.isEmpty {
            filteredStreets = streets
            filteredCities = cities
        } else {
            NSLog("1")
            filteredStreets = streets.filter{ $0.name.rangeOfString(text, options: .CaseInsensitiveSearch) != nil }
            NSLog("2")
            filteredCities = cities.filter{ $0.name.rangeOfString(text, options: .CaseInsensitiveSearch) != nil }
            NSLog("3")
        }
        tableView.reloadData()
        NSLog("4")
    }
    
    
    func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return filteredStreets.count + filteredCities.count
    }
    
    func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
        let cell = tableView.dequeueReusableCellWithIdentifier("Cell")!
    
        if indexPath.row < filteredCities.count{
            cell.textLabel!.text = "City:" + filteredCities[indexPath.row].name
        }else{
            cell.textLabel!.text = "Street:" + filteredStreets[indexPath.row-filteredCities.count].name
        }
        return cell
    }
    
    func numberOfSectionsInTableView(tableView: UITableView) -> Int {
        return 1
    }
    
    func findAllCities() -> [City]{
        let request = City.MR_createFetchRequest()
        request.returnsObjectsAsFaults = false
        return City.MR_executeFetchRequest(request) as! [City]
    }
    
    func findAllStreets() -> [Street]{
        let request = Street.MR_createFetchRequest()
        request.returnsObjectsAsFaults = false
        return Street.MR_executeFetchRequest(request) as! [Street]
    }
    }
    

答案 1 :(得分:0)

由于您正在使用如此庞大的数据库,因此将数据库编入索引可能是一个好主意。

这样可以提高搜索速度,因为您只需查看匹配的索引,而不必每次都查看数据库中的所有20,000条记录。可以把它想象成在书的索引中查找某些内容,而不必阅读每一页以找到您正在寻找的内容。

Database indexing - Wikipedia

iOS中的核心数据内置了索引。尝试在要编制索引的属性上启用它,看看是否有助于加快搜索时间。

Core Data attribute indexing

答案 2 :(得分:0)

您可以尝试使用以下选项来提高提取速度。

在您的情况下,以下内容可能有助于检索更快的结果,

  • fetchBatchSize :设置批量大小会限制一次从数据存储中检索的数据量。如果批量大小设置为20,结果集为1,000,则结果数组最多只能填充为20.当我们迭代20时,将检索下一批。

如果您需要

,可以使用以下内容
  • fetchLimit::有时我们只想要返回特定数量的对象。当有很多对象时,这很有用。在获取完成之前,UI不会更新,因此限制获取是必要的。

详细参考:CoreData