如何查询变量字段并应用排序而不先创建索引?

时间:2018-01-31 15:33:22

标签: firebase google-cloud-firestore

  

编辑:我简化了问题和示例,因为这个问题只是   在您开始使用orderBy时适用。

我有一个用户集合,每个用户订阅了许多变量,如下所示:

user {
    var1: true,
    var2: true,
    var2: true,
    metric: 10
}

我这样做是因为我需要查询类似的内容:

async function getUsersForVar(varId) {
  const snapshot = await db.collection('users')
    .where(varId, '==', true)
    .get()
  ...
}

这很好,直到我需要根据指标选择一组用户:

async function getTopUsersForVar(varId) {
  const snapshot = await db.collection('users')
    .where(varId, '==', true)
    .orderBy('metric')
    .limit(100)
    .get()
  ...
}

执行此操作时,Firestore会告诉您需要创建特定于已使用的varId加上度量标准的索引。但由于项目是可变的,我无法预先知道或索引每个组合。

这个document描述了如何对属性进行编码以便对键值进行排序,但对我来说这不是一个选项,因为度量标准会随着时间的推移而变化。

这是不可能的吗?

如果度量标准发生变化,可能期望数据库能够有效地执行这样的查询,这简直太过分了。

从查询返回的用户文档数量将达到数千。有什么办法可以防止查询所有数据并对客户端进行排序吗?

AFAIK Firestore将获取每个查询的完整文档。此查询将每分钟执行多次,因此如果每次执行此查询时我都会获取数千个用户文档,那么将会产生大量开销。

如果没有其他方法,我应该将用户的所有相关数据隔离到单独的模型/根集合中,这样就不会获取所有其他用户数据来进行此选择。

使用Firebase Realtime DB时,这会有什么不同吗?

2 个答案:

答案 0 :(得分:2)

所以这是交易:

如果要在单个字段上执行查询 - 不等式或相等 - 则不需要索引。 (即使该字段是地图字段中的属性,也就像您的顶级示例一样。)

如果要跨多个字段执行相等查询,那么您也不需要自定义索引。

如果要执行包含1个或多个相等搜索的查询,然后执行不等式搜索或不同字段上的“order by”,则需要自定义索引。目前,您在同一个数据库中仅限于125个不同的自定义索引。

因此,考虑到这一点,我不确定您是否可以完成您正在寻找的那种查询而无需为每个查询创建自定义索引。

一种解决方法是在不对结果进行排序的情况下运行查询,然后在客户端上对它们进行排序。

或者正如nshmura建议的那样,您可以尝试的另一种解决方法是将“指标”值添加到您要查询的变量中。因此,而不是像以下那样构建数据:

user {
    var1: true,
    var2: true,
    var3: true,
    metric: 10
}

您可以考虑将数据结构化为:

user {
    var1: 10,
    var2: 10,
    var3: 10,
    metric: 10
}

然后你可以执行像

这样的搜索
 const snapshot = await db.collection('users')
    .where(varId, '>', 0)
    .limit(100)
    .get()

它将自动按该值排序。这实际上是否有效可能取决于您的具体用例。

答案 1 :(得分:1)

This document描述了类似的问题。

如上所述,您可以通过存储以下指标值来解决这个问题:

user {
  var1: {
      metric1: 10,
      metric2: 1502144665
  }
  var2: {
      metric1: 10,
      metric2: 1502144665
  }
  var3: {
      metric1: 10,
      metric2: 1502144665
  }
  metric1: 10
  metric2: 1502144665
}

现在您可以在没有任何自定义索引的情况下进行查询,如下所示:

const column = `${varId}.${metric}`;
const snapshot = await db.collection("users")
    .where(column, ">", 0) // check dose user subscribe varId 
    .orderBy(column)
    .limit(100)
    .get()