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

时间:2016-07-10 21:59:43

标签: ios swift database-design firebase 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
      } ]
    }
  }
}

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

示例:

获取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
  • 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

我的问题:

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

1 个答案:

答案 0 :(得分:9)

答案是:您无法在Firebase中搜索此类型。

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

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

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

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

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

您的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。现在你有几千条记录可以轻松加载并在代码中进一步过滤

我希望其中一个或两者的组合有所帮助。