按日期范围查询Firestore

144
我需要帮助查询一个包含日期范围的长集合。请参考下面的示例文档。我想使用日期范围来查询startTime字段。
示例文档如下图所示:enter image description here 另外,这里还有一张图片提供参考:enter image description here

所有这些答案都有一个巨大的缺陷:查询只能包含一个字段的“>”或“<”。这意味着,使用此方法来过滤日期,将使在其他字段上进行范围过滤成为不可能。 - Spock
13个回答

196

由于我在Cloud Firestore中将dueDate字段存储为“时间戳”(而不是字符串或数字),因此我这样做来获取截止日期为2017年的发票文档:

let start = new Date('2017-01-01');
let end = new Date('2018-01-01');

this.afs.collection('invoices', ref => ref
  .where('dueDate', '>', start)
  .where('dueDate', '<', end)
);

注意:dueDate字段以一个Date()对象的形式存储在Firebase中,例如:this.doc.dueDate = new Date('2017-12-25')


1
如果您截取了Firestore的屏幕截图,是否可以从中直观地区分日期时间对象或字符串? - DauleDK
是的@DauleDK,看这里:https://imgur.com/a/draQo 第三个字段填充了一个Date() js对象,并将其存储为时间戳(firebase进行了转换)。在图像上,您将看到此时间戳格式化为UTC,但我将其保存为Date对象。最后,要检查这是否真的是时间戳字段,请将鼠标悬停在该字段上。在我的情况下,我看到“Marca de tiempo”,这意味着“时间戳”。 - Capy
3
太棒了!这是一些应该加入到Firestore查询文档中的内容。我有一种感觉,我们将看到firebase-firestore控制台的许多更新。"Marce de tiempo" - 今天学到的最有价值的课程,谢谢 ;) - DauleDK
4
朋友们,我尝试了这个方法,但是没有返回任何结果。 - Nimer Farahty
另外,如果您正在使用额外的等价查询“==”,则可能需要启用复合索引。最好的方法是通过捕获错误来实现,这将输出一个Firebase链接,您可以按照该链接自动设置索引。我的请求看起来像这样,并且需要一个复合索引:.where('team_id', '==', teamId).where('time', '>=', start).where('time', '<=', end) - widavies

46

您可以将datetime对象存储为Unix时间戳(自1970年1月1日以来的秒数)。然后,您可以像这样使用where语句进行选择:

collectionRef.where("startTime", ">=", "1506816000").where("startTime", "<=", "1507593600")

顺便说一下 - 如果您正在使用js或node构建某些内容,您可以使用优秀的(现已停用)库moment将datetime转换为Unix时间戳。


1
谢谢,但我已经有像附加的截图中日期格式的数据了。 - Nimer Farahty
好的。根据Firebase,日期时间是Firestore中支持的数据类型。startTime是字符串还是日期时间对象? - DauleDK
它是一个日期时间对象,不是字符串。 - Nimer Farahty
你能否在Firebase控制台上截取一张屏幕截图,将鼠标悬停在startTime上方,以查看日期时间是否像这里https://imgur.com/a/draQo一样显示? - DauleDK
问题中添加了新的截图,看起来数据时间是使用 moment 模块创建字段时的时间戳。 - Nimer Farahty
显示剩余3条评论

32
    var startfulldate = admin.firestore.Timestamp.fromDate(new Date(1556062581000));
    db.collection('mycollection')
      .where('start_time', '<=', startfulldate)
      .get()
      .then(snapshot => {              
            var jsonvalue: any[] = [];
            snapshot.forEach(docs => {
              jsonvalue.push(docs.data())
               })
                 res.send(jsonvalue);
                 return;
                }).catch( error => {
                    res.status(500).send(error)
                });

2
太棒了!你救了我的命啊,兄弟。我刚把 admin.firestore 替换成 firebase.firestore,它起作用了。 - Fox5150
1
这对我帮助很大。我简直不敢相信 Firebase 没有关于在查询中包含日期的文档... - bdrelling

23
const event = new Date();
const expirationDate = admin.firestore.Timestamp.fromDate(event);
const query = collectionRef.where('startTime', '<=', expirationDate)

13

由于startTime存储为Timestamp,因此您可以执行此查询范围以获得更准确的结果(适用于长日期范围或相同日期范围的两种情况)。

const start = new Date('2021-01-01T00:00:00.000z');
const end = new Date('2021-03-01T23:59:59.000z');

db.collection('Data').where('startTime', '>=', start).where('startTime', '<=', end).get().then(data => {
   //pass your 'data' here
});

我在我的 Node.js 应用中使用了这个。希望它对你有用。


9

最近使用 Firebase Firestore 的所有用户需要注意,根据你的 Firebase 实现设置(根据 firebase 版本)会有一些差异。

之前,Firestore 把 Timestamp 保存为 Date,然而正如文档中所述,这种方式很快就会被 Timestamp 对象取代。请参见此处的 Timestamp 文档

你可以通过在代码中添加以下设置强制 Firebase 使用 Timestamp 对象而不是 Date来实现:

var firebaseApp = firebase.initializeApp({
    apiKey: [APIKEY],
    authDomain: [FIREBASEAPPDOMAIN],
    projectId: [PROJECTID]
});

var firestore = firebase.firestore();
var settings = { timestampsInSnapshots: true }; // force Timestamp instead of Date
firestore.settings(settings);

1
这实际上已经被弃用,并将在未来的版本中被移除。Firebase希望您明确指定Timestamp对象,以便向前兼容。 - ptent

5

解决方案是使用Date.now()。停止使用Firebase的时间戳服务,您需要使用毫秒级别的数字时间值,例如:1514271367000,而不是类似于Firestore使用26/12/2017 1:56:07 GMT-0500(-05)将无法工作的日期格式。查询示例:

this.fsService.afs.collection('chats/4bY1ZpOr1TPq8bFQ3bjS/finance/123+finance/12345'
          , ref => ref.orderBy('hour').startAt(1514184967000).endAt(1514271367000))
          .valueChanges().subscribe(data =>{
            this.mensajes = data;
          })

2
Firestore 把 Date.now() 保存为数字数据没问题吗?我觉得在某些情况下,拥有日期会更好,因为这样更容易理解。 - Telion
1
这似乎是Firebase实时数据库代码(例如,它使用startAtendAt),而不是Firestore代码(后者将使用where,请参见此处)。这两者相似但并不相同。 - robsiemb
1
时区是什么?当我确定数字时,是否应将日期时间转换为GMT? - Csaba Toth

4

像我一样使用PHP访问Firestore的人,可以尝试以下方法:

$startTime = new DateTime('2020-05-23 00:00:00');
$endTime = new DateTime('2020-06-23 23:59:59');

$start = new Google\Cloud\Core\Timestamp($startTime);
$end = new Google\Cloud\Core\Timestamp($endTime);

// fb is a Google\Cloud\Firestore\FirestoreClient object
$this->query = $this->fb->collection('your_collection');

$aux = $this->query;
$aux = $aux->where('startTime', '<', $end);
$aux = $aux->where('startTime', '>', $start);

return $aux->documents();

尽情享受。


0

通用函数,可按特定字段的日期范围查找集合中的文档:

public List<QueryDocumentSnapshot> findDocsByDateRange(
                                          String collection, 
                                          String fieldStartDate,
                                          String fieldEndDate,
                                          Date startDate, 
                                          Date endDate) {
    ApiFuture<QuerySnapshot> querySnapshot = fireStore()
        .collection(collection)
            .whereGreaterThanOrEqualTo(FieldPath.of(fieldStartDate), startDate)
                .whereLessThanOrEqualTo(FieldPath.of(fieldEndDate), endDate)
                    .get();
    return querySnapshot.get().getDocuments();
}

软件包:

import com.google.api.core.ApiFuture;
import com.google.cloud.firestore.DocumentSnapshot;
import com.google.cloud.firestore.FieldPath;
import com.google.cloud.firestore.Firestore;
import com.google.cloud.firestore.QueryDocumentSnapshot;
import com.google.cloud.firestore.QuerySnapshot;

-1
在前端应用程序中,Firebase 时间戳和日期可以用于查询和存储文档。

Firestore date usage


4
你好。虽然外部链接可能很有用,但请直接将你的回答内容插入到 SO 中。 - Maciej Jureczko

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接