处理由于 Firebase 查询中的高元素计数而导致的内存使用情况

Posted

技术标签:

【中文标题】处理由于 Firebase 查询中的高元素计数而导致的内存使用情况【英文标题】:Dealing with memory usage due to high element count in Firebase query 【发布时间】:2017-08-15 20:35:17 【问题描述】:

我正在编写将元素从 firebase 附加到数组的代码,以使用文本字段执行简单搜索。

该方法的代码如下:

 var filteredEvents = [Event]()
var eventsArray = [Event]()
fileprivate func fetchEvents()
    print("Fetching events....")
    //create a reference to the location in the database that you want to pull from and observe the value there
    let ref = Database.database().reference().child("events")
    // this will retur a snapshot with all the data at that location in the database and cast the results as a dictionary for later use
    ref.observe(.value, with:  (snapshot) in
        guard let dictionaries = snapshot.value as? [String: Any] else
            return
        
        //does the job of sorting dictionary elements by key and value
        //displaying the key and each corresponding value
        dictionaries.forEach( (key,value) in
           // print(key, value)
            //creating an eventDictionary to store the results of previous call
            guard let eventDictionary = value as? [String: Any] else
                return
            
            //will cast each of the values as an Event based off my included struct
            //Make sure to create a model it is the only way to have the data in the format you want for easy access
             let events = Event(currentEventKey: key, dictionary:eventDictionary)
            // appends that to the dictionary to create the dictionary of events
            self.eventsArray.append(events)
        )
        // will sort the array elements based off the name
        self.eventsArray.sort(by:  (event1, event2) -> Bool in
            return event1.currentEventName.compare(event2.currentEventName) == .orderedAscending
        )
        // will again reload the data
        self.collectionView?.reloadData()

    )  (err) in
        print("Failed to fetch events for search")
    

我个人没有想到我可能有很多活动的机会。我将无法将所有 1000 多个事件都附加到字典中。那会让我的记忆力更上一层楼。无论如何我可以让查询响应文本字段。任何人都可以帮助我执行此操作但不会破坏我的记忆的查询行吗?

"events" : 
"CCDS" : 
  "attend:count" : 1,
  "event:date" : 
    "end:date" : "08/09/2017",
    "end:time" : "7:00 PM",
    "start:date" : "08/09/2017",
    "start:time" : "5:00 PM"
  ,
  "event:description" : "Happy hour is more joyful in the summer thanks to Center City District Sips, which offers discounted drinks and appetizers every Wednesday evening.  Catch up with old friends and make a few new ones as Center City’s best bars and restaurants host the summer’s happiest hour every Wednesday from 5-7 p.m.  Enjoy $5 cocktails, $4 wine, $3 beers and half-price appetizers at dozens and dozens of bars and restaurants.",
  "event:imageURL" :someURL",
  "event:location" : 
    "event:city" : "Philadelphia",
    "event:state" : "PA",
    "event:street:address" : "660 Chestnut St",
    "event:zip" : 19106
  ,
  "event:name" : "Center City District Sips"
,
"MIA" : 
  "attend:count" : 1,
  "event:date" : 
    "end:date" : "09/03/2017",
    "end:time" : "7:00 PM",
    "start:date" : "09/02/2017",
    "start:time" : "12:00 PM"
  ,
  "event:description" : "Budweiser Made in America Festival is an annual music festival held in Philadelphia and formerly simultaneously held in Los Angeles.Sponsored by Anheuser–Busch and produced by Live Nation, the event features several stages that continuously host live music from a wide range of genres including hip hop, rock, pop, R&B, and EDM.",
  "event:imageURL" : "someURL",
  "event:location" : 
    "event:city" : "Philadelphia",
    "event:state" : "PA",
    "event:street:address" : "Ben Franklin Parkway",
    "event:zip" : 19130
  ,
  "event:name" : "Made In America"

  ,

例如,我想提取有关我使用查询搜索过的事件的所有信息。因此,如果我开始输入 Made in America,它将从事件选项卡中提取有关该事件的所有相关信息

这是我目前拥有的

fileprivate func fetchEvents(searchString: String)
    print("Fetching events....")
    //create a reference to the location in the database that you want to pull from and observe the value there
    let ref = Database.database().reference().child("events")
    // this will retur a snapshot with all the data at that location in the database and cast the results as a dictionary for later use
  let query = ref.queryOrdered(byChild: "event:name").queryEqual(toValue: searchString)
    print(query)
    query.observeSingleEvent(of: .value, with:  (snapshot) in
        guard let dictionary = snapshot.value as? [String: Any] else
            print(snapshot.value)
            return
        
        print(snapshot.value)
    )  (err) in
        print("Failed to fetch event data", err)
    


返回这个

(/events ep = Made In America; i = "event:name"; sp = Made In America; )

【问题讨论】:

澄清一下,您是否在问是否有办法让您输入一些值的文本字段,然后查询数据库以查找该值?或者您是否希望执行类似无限滚动的操作,一次下载几个孩子? @JenPerson 我正在寻找一种方法来拥有一个文本字段,我在其中输入一些值,然后查询 firebase 以查找该值 通知每个询问/编辑过您的问题的用户并不是很好。请耐心等待,因为这是一个问答社区,而不是付费支持论坛。请参阅 SO 的 "Be Nice" policy。 您需要提供更多上下文。为什么需要下载数百个对象?如果用户对“房屋”一词进行搜索,您将根据某种排名算法(就像周围的每个搜索引擎)下载一些。在我看来,解决方案是使用基于页面的方法,并在用户查询新术语时删除旧结果。 @nathan 我错过了什么上下文。我想减少我的问题将面临多个用户和房屋的潜在内存问题 【参考方案1】:

看来问题是

"How can I query for a value contained in a child node?"

给定一个与原始结构相似的结构

"events" : 
  "CCDS" : 
    "attend:count" : 1,
    "event:imageURL" :"someURL",
    "event:name" : "Center City District Sips"
  "MIA" : 
    "attend:count" : 1,
    "event:imageURL" : "someURL",
    "event:name" : "Made In America"

Firebase 查询将返回您想要的节点。

如果用户输入 Made In America 并点击搜索按钮,则查询会在快照中返回该节点。

let searchString = "Made In America"
let ref = self.ref.child("events")
let query = ref.queryOrdered(byChild: "event:name").queryEqualTo(searchString)
query.observeSingleEvent(of: .value, with:  (snapshot) in
    for child in snapshot.children 
        let snap = child as! DataSnapshot
        let eventDict = snap.value as! [String: Any]
        let attendCount = eventDict["attend:count"] as! String
        let url = eventDict["event:imageURL" as! String
    
)

如果你想进行部分字符串匹配,用户可以输入几个字符,如 Made,代码类似,但你需要让 firebase 返回所有以 Made 开头的匹配...

查询如下所示

let startString = "Made"
let endString = "Made" + "\\uf8ff"
let query = ref.queryOrdered(byChild: "event:name")
                        .queryStarting(atValue: startString)
                        .queryEnding(atValue: endString")

“\uf8ff”是 Unicode 中代码级别非常高的字符 - 因为它包含所有前面的字符。

但是,“即时”查询可能会导致 UI 反应迟钝或缓慢,因此不建议这样做。

另一种方法是创建一个单独的节点,该节点包含的信息少得多,并且包含用户将搜索的元素以及对事件节点的引用。

所以包含所有数据的主节点是这样的

"events" : 
  "-uyuh8s8j8jsdas" : 
    "event": "CCDS"
    "attend:count" : 1,
    "event:imageURL" : "someURL",
  "-y88jsijsijjids" : 
    "event": "MIA"
    "attend:count" : 1,
    "event:imageURL" : "someURL",

“更小”的节点看起来像这样

   events_for_searching
       -uyuh8s8j8jsdas
         event:name: "Center City District Sips"
       -y88jsijsijjids
         event:name: "Made In America"

这样,您可以将 events_for_searching 中的所有节点加载到一个数组中(然后根据用户类型过滤该数组),这将使 UI 响应迅速,当用户选择名称时,您可以使用键从该节点作为参考,通过 observeSingleEvent 函数从事件节点加载数据。

编辑

作为对评论的回应,我想在代码中添加更多细节。

这是我的结构

  "events" : 
    "event_0" : 
      "event:name" : "An Event"
    ,
    "event_1" : 
      "event:name" : "Made In America"
    
  ,

以及查询事件的代码:名称:美国制造

let searchString = "Made In America"
let ref = self.ref.child("events")
let query = ref.queryOrdered(byChild: "event:name").queryEqual(toValue: searchString)

query.observeSingleEvent(of: .value, with:  (snapshot) in
    guard let dictionary = snapshot.value as? [String: Any] else
        print(snapshot.value)
        return
    
    print(snapshot.value)
)  (err) in
    print("Failed to fetch event data", err)

和输出

Optional(
    "event_1" =     
        "event:name" = "Made In America";
    ;
)

【讨论】:

我不确定这是什么意思(/events ep = Apples; i = "event:name"; sp = Apples; ) @SJackson5 什么不起作用,您的第二条评论是什么意思?该代码或结构出现在哪里? 打印查询结果时在控制台中 就像我打印 snapshot.value 以检查它从不打印的内容 嗯。我的答案中的代码是从一个工作项目中复制和粘贴的,所以我知道它可以工作。我的回答中也有两个建议的选项,所以我不知道您实施了哪一个。另外,我在你的结构中没有看到任何关于苹果的东西,所以我不知道它来自哪里。我建议使用您的新代码创建一个新问题,并确保包含包含 Apples 的 Firebase 结构。

以上是关于处理由于 Firebase 查询中的高元素计数而导致的内存使用情况的主要内容,如果未能解决你的问题,请参考以下文章

Flutter firebase 查询快照错误:“未处理的异常:错误状态:无元素”

Firebase:按儿童计数排序的查询

Firebase Analytics 应用程序和大查询计数用户

Firebase 数据库快速入门处理计数的方式是不是安全?

Firebase first_open 计数变少

运行查询以从大查询中获取事件计数