在Swift中使用过滤器的高级Firebase查询

iOS*_*eek 5 database-design ios firebase swift firebase-realtime-database

我刚刚来自关系数据库学校,处理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
      } ]
    }
  }
}
Run Code Online (Sandbox Code Playgroud)

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

例:

获取以下10用户starting from index 0:

  • male
  • "US"
  • speaks "da_DK"levelID 2 或/和 "fr_FR"any level
  • 并且学习"de_DE" with等级1 **and/or**学习"ar_AR" with等级4`
  • age range between 18 and 24
  • ORDER BY 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
Run Code Online (Sandbox Code Playgroud)

我的问题:

  1. 如何使用Firebase和实际结构构建上面的查询
  2. 如果不可能如何改进我的特定用例的数据库结构(以更好的方式处理上面的查询)

Jay*_*Jay 9

最重要的答案是:你不能在Firebase中搜索这种类型.

让我提出一个墙上的答案,希望能够找到解决方案.

坦率地说: 正如弗兰克在他的论点和链接中提到的那样,利用ElasticSearch等其他产品可以成为一种解决方案.虽然它们确实提供了可扩展性,但它增加了另一种产品.我建议进一步探讨这些选择.

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

在这方面,如果UI的结构使其工作,您甚至可以使用数百万条记录来过滤代码.确定一到两个主要搜索,例如性别.然后对所有女性执行查询.这会将您的数据集减少一半,并且在代码中更易于管理.您还可以进一步减少数据集 - 请参阅下一节.

变化是好的: 另一种选择是构建数据以匹配您将要执行的查询类型.举个简单的例子:假设您有三个要查询的项目; gender_country_age

您的Firebase结构将是

users
  -Jyiai09jsi
    data: "male_US_40"
  -Jqkjisjida
    date: "male_US_27"
  -JyHYjlkall
    data: "male_US_30"
Run Code Online (Sandbox Code Playgroud)

然后查询年龄在30到40岁之间的美国所有男性用户

usersRef.queryOrderedByChild("data").queryStartingAtValue("male_US_30")
        .queryEndingAtValue("male_US_40").observeSingleEventOfType(
       .Value, withBlock: { snapshot in
    print(snapshot)
})
Run Code Online (Sandbox Code Playgroud)

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

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

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
Run Code Online (Sandbox Code Playgroud)

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

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

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

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