Firestore查询在一个字段上排序,同时筛选另一个字段。

9

我在 Google Cloud Firestore 中遇到了查询条件的问题。

有人能帮助我吗?

这是我的收藏和文档

以下是获取以 HA_ 开头并按 ID 倒序排列的第一个文档的代码:

public Article getLastArticleProvider() {
    ApiFuture<QuerySnapshot> query = firebaseDB.getTblArticles()
            .whereGreaterThanOrEqualTo("articleId", "HA_")
            .orderBy("id", Query.Direction.DESCENDING)
            .limit(1)
            .get();

    QuerySnapshot snapshotApiFuture;
    Article article = null;

    try {
        snapshotApiFuture = query.get();
        List<QueryDocumentSnapshot> documents = snapshotApiFuture.getDocuments();
        for (QueryDocumentSnapshot document : documents) {
            article = document.toObject(Article.class);
        }
    } catch (InterruptedException | ExecutionException e) {
        return null;
    }
    return article;
}

我想获取文章ID以“HA_”或“XE_”开头的最后一篇文章的ID。

例如上图:

  • 如果文章ID以“HA_”开头,则返回ID为831的对象
  • 如果文章ID以“XE_”开头,则返回ID为833的对象

现在我遇到了一个错误:

java.util.concurrent.ExecutionException: com.google.api.gax.rpc.InvalidArgumentException: io.grpc.StatusRuntimeException: INVALID_ARGUMENT: inequality filter property and first sort order must be the same: articleId and id

3个回答

9

简短总结:

在不等式过滤属性后添加一个虚拟订单,并根据您的需求更改查询优先级。

我在JavaScript上遇到了相同的问题,但我认为这也是一个Java解决方案。

完整解决方案:

当我运行我的查询:

module.exports.getLastGroupChat = async function (groupId) {
    let colRef = db.collection('messages')
    colRef = colRef.where('groupId', '==', groupId)
    colRef = colRef.where('userId', '!=', '')
    colRef = colRef.orderBy('timestamp', 'desc').limit(1)
    const doc = await colRef.get()
    return doc

}

并收到:

不等式过滤属性和第一个排序顺序必须相同:userId和时间戳

为了解决这个问题,首先我需要在我的不等式过滤器之后添加一个相同不等式属性的排序顺序。

此外,我需要改变我的查询优先级,以实现不等式属性的虚拟排序顺序。

注意:您可以在同一查询上运行 where -> order -> where -> order

module.exports.getLastGroupChat = async function (groupId) {
    let colRef = db.collection('messages')
    colRef = colRef.where('userId', '!=', '')
    colRef = colRef.orderBy('userId', 'desc')
    colRef = colRef.where('groupId', '==', groupId)
    colRef = colRef.orderBy('timestamp', 'desc').limit(1)
    const doc = await colRef.get()
    return doc
}

这个查询在我的本地调试Firestore上完美运行。将更改推送到Firebase云函数并触发函数。查看函数日志,你可能会得到一个索引错误。

该查询需要一个索引。您可以在此处创建它:https://console.firebase.google.com/v1/r/project..

确保进入链接并建立索引。它将需要大约五到十分钟才能生效。然后再次运行您的函数,一切应该都正常了。

祝您愉快!:)


我在安卓上尝试了你的解决方案,但是不同字段的双重where子句无法工作。你能再检查一下吗?我认为它在你的情况下工作是因为你使用了“==”运算符。错误:所有带有不等式(notEqualTo、notIn、lessThan、lessThanOrEqualTo、greaterThan或greaterThanOrEqualTo)的where过滤器必须在同一个字段上。.whereGreaterThanOrEqualTo("current_date", timestamp) .orderBy("current_date", Query.Direction.DESCENDING) .whereNotEqualTo(country, 0) .orderBy(country) - Raghav Satyadev
有几个(但重要的)Firestore查询限制。您可以在以下文档中查看所有内容:https://firebase.google.com/docs/firestore/query-data/queries。每当您面临复合查询时,请使用该文档。 - genericUser
1
在您的情况下,您在同一查询中组合了不同字段的不等式过滤器,这是firestore不支持的。我真的建议您阅读我给您的文档,它将帮助您了解firestore查询的限制,并希望找到解决方案来解决您的问题。 - genericUser
我昨天刚读完它,现在不得不回到MySQL了。 - Raghav Satyadev
1
Firestore有很多限制,这导致查询非常昂贵和低效。谷歌在云数据库方面明显落后于其他云服务提供商,仅适用于创建小型虚拟应用并放置在PlayStore上。 - genericUser

4

也许自此问题被提出以来,Firebase 已经进行了更改。

答案是你可以在不同的字段上进行筛选和排序。

你可以链接多个 orderby 和 filter,但是如果你首先进行筛选,那么在可以对其他字段进行 orderby 之前,你必须按照该筛选条件进行排序。

例如:

citiesRef.where('population', '>', 2500000).orderBy('population').orderBy('country');

这个可以在他们的文档中找到。 https://firebase.google.com/docs/firestore/query-data/order-limit-data#order_and_limit_data

However, if you have a filter with a range comparison (<, <=, >, >=), your first ordering must be on the same field, see the list of orderBy() limitations below.

您可以在不同字段上链接orderBys,我只是在我的一个查询中这样做了。(您第一次运行它时可能会出现错误,它会要求您创建索引,甚至有一个链接可以创建索引)
await db.collection('daily_equipments').where('date', '<', tomorrow._d).where('date', '>=', date).orderBy('date').orderBy('order','asc').get();

3
如下错误提示所述:

不等式过滤属性和第一个排序顺序必须相同: articleId 和 id

因此,在您的情况下,不能同时使用不同的属性对元素进行过滤和排序,即使用 articleIdid
官方文档中还有一个如何不要这样做的示例。
  • Range filter and first orderBy on different fields

     citiesRef.whereGreaterThan("population", 100000).orderBy("country"); //Invalid
    
因此,为了解决这个问题,您应该按照同一文档属性进行过滤和排序。

你有什么建议吗? - LongLv
1
我不确定我理解你的问题。建议是不要在同一属性上进行过滤和排序,因为这是不可能的,对吧? - Alex Mamo
抱歉我的英文不好,但我想要一个像SQL查询一样的结果:选择*从文章中,其中articleId类似于'%HA_%',按id降序排列限制1。 - LongLv
1
很遗憾,在Firestore文档路径中没有通配符。因此,像那样的查询是不存在的。你唯一的选择是使用whereGreaterThan和whereLessThan方法。 - Alex Mamo
我能为您提供其他信息吗? - Alex Mamo

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