Swift 中带有过滤器的高级 Firebase 查询

Posted

技术标签:

【中文标题】Swift 中带有过滤器的高级 Firebase 查询【英文标题】:Advanced Firebase Query with filter in Swift 【发布时间】:2016-07-10 21:59:42 【问题描述】:

我刚从关系数据库学校毕业,处理 JSON 数据库对于新手来说并不是一件容易的事。我有这个结构来存储用户:


  "users" : 
    "0CcKvNkOm5fVqL" : 
      "birthday" : 564688000,
      "country" : "US",
      "email" : "email@live.com",
      "firstName" : "John",
      "gender" : "male",
      "isOnline" : true,
      "lastLoginDate" : 1468166460486,
      "lastName" : "Paul",
      "learningLanguages" : [ 
        "language" : "fr_FR",
        "levelID" : 2
       ],
      "profileImage" : "https://firebasestorage.googleapis.com/image.jpg",
      "providerID" : "Firebase",
      "registrationDate" : 1468168460486,
      "speakingLanguages" : [ 
        "language" : "es_ES",
        "levelID" : 7
       ]
    
  

我在我的应用中提供了一个搜索屏幕,用户可以在其中搜索其他用户,并且他们可以组合所有这些过滤器参数:

示例:

获取10用户starting from index 0是谁:

male 来自"US" speaks "da_DK"levelID 2 或/和 "fr_FR"any level 学习“de_DE”withlevel 1**and/or**learns “ar_AR”withlevel 4` age range between 18 and 24 isOnline last login date 订购。

如果有一个名为 users_languages 的表,这对 SQL 来说是一项简单的任务:

SELECT ...
FROM users AS u
JOIN users_languages AS l
  ON u.id = l.id
WHERE u.gender = "male" 
AND u.age BETWEEN 18 AND 24 // need claculation but let's keep it simple
AND u.country = "US"
AND ((l.language = "de_DE" AND l.mode = "learning" AND l.level = 1) OR (l.language = "ar_AR" AND l.mode = "learning" AND l.level = 4))
....
ORDER BY isOnline, lastLoginDate DESC
LIMIT 0,10

我的问题:

    如何使用 Firebase 以实际结构构建上述查询 如果无法针对我的特定用例改进数据库结构(以便能够以更好的方式处理上述查询)

【问题讨论】:

推荐阅读:NoSQL data modeling和my answer here 两件事:请将您的 Firebase 结构发布为文本,而不是图像。它使复制/粘贴到答案中变得更加容易,并且还使其可搜索。 Firebase 控制台具有导出功能。另外,您在任何给定时间都会有多少用户在线? @Jay thx,我已经用文本结构更新了我的问题,我期待作为第一步可能有数百或数十个 .. 但如果解决方案可以扩展,那就太好了,有什么建议吗? @FrankvanPuffelen 甚至阅读了文档,而您之前的回答似乎并没有为我的案例提供答案:( 并且我们将不胜感激 快照包含按正确顺序返回子项所需的信息。但简单地打印快照可能会将其转换为字典,从而丢失此排序信息。见***.com/questions/38266429/… 【参考方案1】:

直截了当的答案是:您不能在 Firebase 中进行搜索。

让我提供一个文本墙的答案,希望能找到解决方案。

坦率地说: 正如弗兰克在他的现场 cmets 和链接中提到的那样,利用 ElasticSearch 等其他产品可能是一种解决方案。虽然它们确实提供了可扩展性,但它为等式增加了另一个产品。我建议进一步探索这些选项。

过滤很酷: 第二种解决方案是在代码中过滤。虽然这对于数千条记录来说是一个很好的解决方案,但它对于数万/数十万条记录是不可扩展的。但是,如果您有复杂的数据结构和有限的数据量,这是最好的解决方案。

请注意,如果 UI 的结构可以使其正常工作,那么即使有数百万条记录,您也可以在代码中进行过滤。决定一到两个主要搜索,例如性别。然后对所有女性执行查询。这将您的数据集减少了一半,并且在代码中更易于管理。您还可以进一步减少数据集 - 请参阅下一节。

改变是好的: 另一种选择是构建数据以匹配您将执行的查询类型。举个简单的例子:假设您要查询三个项目;性别国家年龄

您的 Firebase 结构将是

users
  -Jyiai09jsi
    data: "male_US_40"
  -Jqkjisjida
    date: "male_US_27"
  -JyHYjlkall
    data: "male_US_30"

然后查询美国所有30-40岁的男性用户

usersRef.queryOrderedByChild("data").queryStartingAtValue("male_US_30")
        .queryEndingAtValue("male_US_40").observeSingleEventOfType(
       .Value, withBlock:  snapshot in
    print(snapshot)
)

这里的好处是它具有可扩展性,但缺点是您不能只查询美国用户。另一方面,这是一个小得多的数据集,您可以在代码中进一步过滤。

重复数据是你的朋友: 好消息是也有一个解决方案:磁盘空间很便宜,所以复制你的数据

user_countries
   US
       -Jyiai09jsi: true
       -Jqkjisjida: true
       -JyHYjlkall: true
   UK
      etc etc

user_gender
   male
       -Jyiai09jsi: true
       -Jqkjisjida: true
       -JyHYjlkall: true
   female
       etc etc

user_speaks
   da_UK
      users
   fr_FR
      users

这种结构让您可以超级快速地访问数据组;国家、性别等。我在这里使用 true 作为占位符,但从技术上讲,您也可以在该位置拥有每个用户节点。但是,这将再次在查询期间读取大量数据;一堆“真正的”节点是非常少量的数据,即使有数千个节点。

SQL ftw! 其他需要考虑的是您如何使用 Firebase 的异步特性。您是否真的需要将该数据存储在 Firebase 中,或者您可以将该数据存储在另一个基于云的 SQL 服务器中以进行查询并将指向该数据的链接存储在 Firebase 中。这样您就可以通过 SQL 查询您心中的内容,然后使用 Firebase 进行消息传递、更新等。

最后的想法如果您想进行此类搜索,最好的办法是以尽可能快地减少其占用空间的方式构建数据,然后在代码中过滤其余部分。想象一下有一百万条记录,然后查询male_US_30_FR。现在你有几千条记录可以轻松加载并在代码中进一步过滤

我希望这些中的一个或组合有所帮助。

【讨论】:

感谢杰伊的另一个精彩回答! 现在,Firebase Firestore 允许使用这些高级查询选项! @Eric 太真实了! Firestore 有更强大的查询。但是,如果您仔细查看原始问题,查询包含多个字段的 OR 语句,即使在今天,这对 Firestore 来说也是具有挑战性的。另请注意如果您包含具有范围比较的过滤器,则您的第一个排序必须在同一字段中,并且问题中的排序在 isOnline最后登录日期 但需要按年龄,因为这是范围参数。

以上是关于Swift 中带有过滤器的高级 Firebase 查询的主要内容,如果未能解决你的问题,请参考以下文章

firebase 云功能中带有 typescript 的 firebase-admin

如何在swift ios中通过键值读取子数据或过滤firebase数据

Firebase Swift - 当搜索控制器过滤结果时,实时更新不起作用

swift Swift中带有协议的构建器模式示例

Swift 中带有选择器的协议扩展

Swift中带有按钮的UITableViewCell [重复]