首页 > 解决方案 > 按 desc 排序的时间范围内的 Firebase 查询值

问题描述

我的目标: 我想从按喜欢(降序)排序的无限集合中检索项目。我还想按日期和结果计数限制这些结果,例如前 10 名。

我无法以一种很好的方式妥善处理这个案子。这是我到目前为止得到的,但我只想要前 10 个结果,并且缺少这个限制。如果我limit()在查询中放置一个,我不会得到正确的结果。

Timestamp get timeLimit {
    final limit = DateTime.now().subtract(const Duration(days: 30));
    return Timestamp.fromDate(limit);
  }

QuerySnapshot snapshot = 
    await FirebaseFirestore.instance
    .collection('data')
    .where('date', isGreaterThanOrEqualTo: timeLimit)
    .orderBy('date', descending: true)
    .get();

snapshot.docs.forEach((document) {
              Data data = Data.fromJson(document.data());
              resultList.add(data);
              resultList.sort((a, b) => b.likes.compareTo(a.likes));
            });

标签: firebasefluttergoogle-cloud-firestore

解决方案


您正在达到 Cloud Firestore 的限制。如果您有一个具有范围比较(<、<=、>、>=)的过滤器,则您的第一个排序必须在同一字段上。[参考:https://cloud.google.com/firestore/docs/query-data/order-limit-data]

在您的情况下,第二个 orderBy (on likes) 只会在完全相同的组中产生影响date,这将是非常无用的。


头脑风暴解决方案。

(欢迎任何建议!)

如果您的目标是跟踪每日/每周/每月 TOP 10,您可能应该在 Firestore 中维护该列表,而不是计算每个访问者的列表。

您可以让每个 [待定义的持续时间] 运行一个云函数来重置 TOP 10 列表。

这是有代价的,但read对于每位游客来说,运营成本会大大降低。

!!!注意,以下内容未经测试。

这样的函数可以写成:

const admin = require('firebase-admin');
admin.initializeApp();

const db = admin.firestore();


exports.computeTopTenLists = functions.pubsub.schedule('every 2 hours').onRun((context) => {
  var millisecondsInOneDay = 24 * 60 * 60 * 1000;
  var dailyStart = new Date(Date.now() - millisecondsInOneDay);
  var weeklyStart = new Date(Date.now() - 7 * millisecondsInOneDay);
  var monthlyStart = new Date(Date.now() - 30 * millisecondsInOneDay);
  date.setDate(date.getDate() - 7);

  // Top 10 list (sorted by likes, descending)
  var dailyTopTen = [];
  var weeklyTopTen = [];
  var monthlyTopTen = [];

  db.collection('data').where("date", ">=", monthlyStart ).get().then((querySnapshot) => {
    [
      { list: dailyTopTen, start: dailyStart },
      { list: weeklyTopTen, start: weeklyStart },
      { list: monthlyTopTen, start: monthlyStart },
    ].forEach((curr) => {
      curr.list.push(querySnapshot[0].data());
      querySnapshot.slice(1).forEach((doc) => {
        var data = doc.data();
        data['id'] = doc.id;
        if (data['date'] > curr.start && data['likes'] > curr.list[curr.list.length-1]) {
          curr.list.push(data);
          curr.list.sort((a, b) => (a.likes > b.likes ? -1 : 1));
          curr.list = curr.list.slice(0, 10);
        }
      });
    });
  })

  db.collection('top-ten').document('lists').set({
    daily: dailyTopTen.map(data => ({id: data['id'], title: data['title']})),
    weekly: weeklyTopTen.map(data => ({id: data['id'], title: data['title']})),
    monthlyly: monthlyTopTen.map(data => ({id: data['id'], title: data['title']})),
  });

  return null;
});

您只需阅读一份文档即可获得三个 Top 10 列表。


推荐阅读