9

我来自 SQL 背景,所以我在设计 NoSQL firebase 架构时遇到了问题。我习惯于使用“WHERE”子句查询任何内容,而在 firebase 中这样做似乎更困难(尽管性能很容易弥补它!)。

我正在为歌曲存储“轨道”对象。这些对象具有键/值对,例如艺术家名称、曲目标题、流派、评级、创建日期等,如下所示:

tracks
|_____-JPl1zwOzjqoM8xDTFll
          |____ artist: "Bob"
          |____ title: "so long"
          |____ genre: "pop"
          |____ rating: 52
          |____ created: 1403129692781
|
|_____ -JPv7KnVi8ASQJjRDpvh
          |____ artist: "Mary"
          |____ title: "im alright now"
          |____ genre: "rock"
          |____ rating: 70
          |____ created: 1403129692787

我网站上的默认行为是列出所有这些曲目,最新添加的曲目出现在列表顶部。我可以将我的 $priority 设置为要创建,然后将其变为负数(已创建 * -1)以实现我相信的这种效果。

但在未来,我希望能够通过其他方式过滤/查询列表,例如:

  • 检索具有摇滚、流行或嘻哈流派的所有曲目。

  • 检索评分为 80 或更高且在过去 7 天内添加的所有曲目。

如何在firebase中实现这一点?我的理解是,实际上只有两种方式来订购数据:

  1. 通过“ID”值,它具有“firebaseURL.firebaseio.com/tracks/id”的物理位置,在我的情况下,当我添加轨道时,它会自动为我选择。这没关系(我认为),因为我有用于列出详细信息的各个跟踪页面的页面,并且我网站上的 URL 类似于“www.mysite.com/tracks/-JPl1zwOzjqoM8xDTFll”。

  2. 通过使用 $priority,在我的例子中,我使用了“created”值,以便按正确的日期顺序排列我的列表。

鉴于我设置的方式(如果有更好的方法,请告诉我),有没有一种方法可以轻松查询特定类型或特定评级?

我读了博客“非规范化你的数据是正常的”(https://www.firebase.com/blog/2013-04-12-denormalizing-is-normal.html),我想我明白了。根据 Anant 的描述,实现我想要的一种方法可能是在 firebase 中为一个流派创建一个新对象并列出那里的所有曲目,如下所示:

tracks
|______ All
        |_____ -JPlB34tJfAJT0rFT0qI
        |_____ -JPlB32222222222T0qI
        |_____ -JPlB34wefwefFT0qI

|______ Rock
        |_____ -JPlB32222222222T0qI
        |_____ -JPlB34tJfAJT0rFT0qI

|______ Pop
        |_____ -JPlB34wefwefFT0qI

博客中的前提是硬盘空间很便宜,但用户的时间却不是。因此,存在重复数据是可以的,因为它允许更快的读取。

这是有道理的,我不介意这种方法。但这只有在用户只想从一种流派中选择所有曲目时才有效。如果他们想从摇滚和流行音乐中获取所有曲目怎么办?每次有人提交任一类型的歌曲时,我是否必须存储另一个名为 Rock&Pop 的对象并在其中存储一首曲目?

genre
|_______pop-rock
         |_________ -JPlB34tJfAJT0rFT0qI (a rock song)
         |_________ -JPlB34wefwefFT0qI (a pop song)
         |_________ -JPlB32222222222T0qI (a rock song)

此外,存储整个轨道对象或仅使用 trackid 的引用是否更有意义?例如,在 /genre/pop 下:

Should I store just the reference?
genre
|______ pop
        |______ -JPlB34wefwefFT0qI

Or, Should I store the entire track?
genre
|______ pop
        |______ -JPlB34wefwefFT0qI
                    |___ artist: "bob"
                    |___ title: "hello"
                    |___ genre: pop
                    |___ etc..

两种方法之间有性能差异吗?我在想也许后者会更快,因为我不需要查询每个单独的曲目以获取其他详细信息,但我只是想确定一下。

我已经多次重做我的 firebase 架构。我做了一些改进,但是随着我的应用程序变得越来越大,更改它变得更加昂贵并且消耗更多时间。如果我能在花大量时间重做其余代码以再次匹配之前最后一次解决这些问题,那就太好了。

感谢您对此的任何帮助,非常感谢。如果您需要更多信息,请告诉我。

4

2 回答 2

5

Firebase 将在明年为查询 API 推出大量新增功能。上下文搜索(其中 foo 类似于bar)可能永远不会在实时数据中大受欢迎——它既慢又麻烦。

Firebase 中有一篇关于 sql 查询和等效模式的两部分博客文章。我建议你通读一遍。第 2 部分特别讨论了Flashlight

为什么选择 ElasticSearch 和服务?与实时数据存储和同步一样,搜索是一个复杂的主题,具有大量样板和可发现的复杂性。在 SQL 中编写 where 子句很容易,这将为您提供一些方法,但它很快就达不到用户的期望。

ES 可以快速与 Firebase 集成(我上次尝试时,Flashlight 服务与应用程序集成不到 5 分钟),并提供强大而全面的搜索功能。

因此,在 Firebase 围绕查询推出一些改变游戏规则的功能之前,我建议一开始就检查这种方法,而不是尝试通过其他方式来增强搜索功能。

于 2014-06-25T20:37:07.127 回答
0

在上面的示例中,您构建了不同的层次结构并存储了一些数据,但只需将 ID 作为键放入。所以当你把它放到客户端上时,你可能最终还是会按一些数据字段进行排序。

我喜欢让 Firebase 使用多部分键为我处理排序。

例如,如果我需要按流派和艺术家姓名访问曲目,我将创建一个名为tracksByGenreAndArtist 的平面索引节点,其键由genre_name + artist_name + track_name + track_id 组成。该值将是一个具有艺术家姓名、艺术家 ID、曲目名称和曲目 ID 的对象。添加 id 只是为了确保它是唯一的。

现在所有数据都可以按流派、艺术家和曲目名称的顺序访问。你甚至可以对它进行预测搜索,它是如此之快。

假设用户选择了流派“摇滚”,并且她在搜索框中键入了“B”。您可以通过抓取名称以“B”开头的艺术家的前十首曲目来填充预测下拉列表:

indexRef.orderByKey().startAt('Rock'+'B').limitToFirst(10);

使用您存储在该位置的部分数据对象在下拉列表中显示艺术家的姓名和曲目。

如果用户选择预测,则使用 track id 从您的轨道节点检索完整的轨道对象,并使用艺术家 id 从艺术家节点检索完整的艺术家对象。

如果用户输入了不同的字母,那么只需扔掉你的预测并进行另一个预测获取,例如,

indexRef.orderByKey().startAt('Rock'+'Br').limitToFirst(10);

另外,如果您需要同时搜索摇滚和流行音乐流派怎么办?好吧,你可以很快地做两个像上面那样的查询

indexRef.orderByKey().startAt('Rock'+'Br').limitToFirst(10);

indexRef.orderByKey().startAt('Pop'+'Br').limitToFirst(10);

您可以在预测下拉列表中将它们分别分组:前十个来自 Rock,然后是前十个来自流行音乐。如果这对您来说还不够高效,我想您总是可以使用相同的微小数据对象和可以选择作为搜索过滤器的每个独特的流派组合来制作大量组合索引。尽管如此,“磁盘很便宜,但用户的时间不便宜”是您的指导格言。

于 2017-02-20T20:14:07.287 回答