【问题标题】:Do an inequality and multiple "array-contains", or array-contains-like filters执行不等式和多个“数组包含”或类似数组包含的过滤器
【发布时间】:2021-05-17 17:28:14
【问题描述】:

我正在尝试运行一个不等式和多个 array-contains 的查询。

为简单起见,我用假想数据简化了数据结构。假设在一个名为grocerie_packages 的集合中,我有很多文档。我希望能够根据包裹的名称、包裹的价格以及其中包含的蔬菜和水果来证明搜索。目前的数据结构如下:

{
    price: 10,
    contained_fruits: ["apple", "pear", "grape"],
    contained_vegetables: ["tomato", "cucumber", "carrot"],
    package_name: ["f", "fa", "fam", "fami", "famil", "family", "family_", "family_s", "family_si", "family_siz", "family_size"]
}

一开始,我使用如下代码根据名字进行查询:

.where("package_name", ">=", searchString)
.where("package_name", "<=", searchString + "\uf8ff");

但后来,当我添加按价格查询的功能时,我遇到了以下问题:

查询无效。带有不等式( 或 >=)的所有 where 过滤器必须位于同一字段中。

我通过创建package_name 的子字符串数组解决了这个问题。我使用的代码是这样的:

  .where("package_name", "array-contains", package_name_intput)
  .where("price", "<", max_price);

我现在遇到的问题是,因为我对查询做了一个array-contains,所以我无法根据包含的蔬菜和水果进行查询。我不想在客户端上进行排序,因为在某些情况下,我可能会返回超过所需的数千个项目。我也不能将三个数组合并为一个,因为我只想返回一个文档,如果它匹配查询的所有条件。例如,在有人要求applecarrot 的查询中,我只想返回包含两者的文档。如果它是一个数组,即使在包中只找到一项,它也会返回文档。我只需要为每个蔬菜和水果提供一项(如apple)搜索。

对我来说,运行这样的查询的最佳方式是什么?我正在尝试找到一个解决方案,在该解决方案中,我不会仅仅为了查询 porpuses 而用许多标志填充文档。例如:

{
   flags: ["apple_tomato_f", "apple_tomato_fa", "apple_tomato_fam", ] //and so on
 }

【问题讨论】:

    标签: javascript firebase google-cloud-firestore


    【解决方案1】:

    让我先说...

    我只想补充一点,我不会在实际项目中这样做。相反,我会在 Cloud Run 上创建一个 API,它可以执行多个查询、构建数据、临时缓存它并以我想要的任何形式返回结果。

    但是,为了让我们的头脑灵活,我将按照以下要求来完成此任务:

    1. 必须直接查询 Firestore。
    2. 只有一个查询。
    3. 没有客户端排序。

    脑筋急转弯

    首先重新考虑您的数据库结构。要在单个查询中完成所有这些操作,您需要存储数据,以便可以使用所有可用的一次性运算符。为此,请将您所有的水果和蔬菜存储为 {[key: string]: boolean},如下所示:

    {"grocerie_packages": {
            "abc1": {
                "price": 10,
                "tomato": true,
                "package_name": ["s", "si", "sin", "sing", "singl", "single", "single_s", "single_si", "single_siz", "single_size"]
            },
            "abc2": {
                "price": 20,
                "cucumber": true,
                "tomato": true,
                "package_name": ["f", "fa", "fam", "fami", "famil", "family", "family_", "family_s", "family_si", "family_siz", "family_size"]
            },
            "abc3": {
                "price": 30,
                "package_name": ["s", "si", "sin", "sing", "singl", "single", "single_s", "single_si", "single_siz", "single_size"]
            },
            "abc4": {
                "price": 40,
                "apple": true,
                "tomato": true,
                "package_name": ["f", "fa", "fam", "fami", "famil", "family", "family_", "family_s", "family_si", "family_siz", "family_size"]
            }
        }
    }
    

    这使得以下所有查询成为可能:

    包名包含“fam”,有西红柿,价格

        db.collection('grocerie_packages')
            .where('tomato', '==', true)
            .where('package_name', 'array-contains', 'fam')
            .where('price', '<', 40).get()
            .then((snap) => console.log("Results:", snap.docs.map(s => s.id).join(',')));
    

    Results: abc2

    包名包含“fam”,有西红柿,价格

        db.collection('grocerie_packages')
            .where('tomato', '==', true)
            .where('package_name', 'array-contains', 'fam')
            .where('price', '<', 100).get()
            .then((snap) => console.log("Results:", snap.docs.map(s => s.id).join(',')));
    

    Results: abc2,abc4

    包裹有黄瓜,费用>10

        db.collection('grocerie_packages')
            .where('cucumber', '==', true)
            .where('price', '>', 10).get()
            .then((snap) => console.log("Results:", snap.docs.map(s => s.id).join(',')));
    

    Results: abc2

    包名含“sing”,有西红柿、黄瓜、苹果,价格>100

        db.collection('grocerie_packages')
            .where('tomato', '==', true)
            .where('cucumber', '==', true)
            .where('apple', '==', true)
            .where('package_name', 'array-contains', 'sing')
            .where('price', '>', 100).get()
            .then((snap) => console.log("Results:", snap.docs.map(s => s.id).join(',')));
    

    Results:

    【讨论】:

    • 我想到了这个:) 唯一的问题是,在这样的数据结构中,由于基于不等式的搜索,我需要为每个字段创建一个 composite index...一个痛苦的流浪汉,我只能创造这么多(200)。如果卖家添加了一种新的、以前没有索引的蔬菜怎么办?那么查询必须等到我为它们创建适当的索引。
    • 您正在尝试从 NoSQL 数据库中获得 SQL 查询的灵活性,因此以上是满足您所有要求的唯一解决方案。当卖家添加新蔬菜时,您需要立即创建新索引并且排序选项有限。唯一的其他选择是进行客户端排序或添加 Algolia 或 ElasticCache 并使用它来执行您的搜索,但我没有提到它,因为这些不是您原始问题的答案。
    • 是的,我正在尝试:)我正在寻找解决方法,或现实世界应用程序会使用的解决方案。
    • 好吧,让我们看看是否有人能想出一些真正聪明的东西或一些开箱即用的想法。我不能,这就是我提供赏金的原因:-)
    • @BudaÖrs 您正在寻找的是一个提供全文搜索元素的解决方案,它也知道本身就是一个问题,数据存储围绕该问题(elasticsearch、solr 等)构建,然后是常规的Firestore 提供的 SQL 样式查询功能,但有一些限制。鉴于这两件事,您显然遇到了 firebase 平台的限制。我花了相当多的时间使用 firestore、solr 和 mysql 等常规数据库。我认为 Brian 提供的解决方案是保持上述限制的最佳选择。
    【解决方案2】:

    我最终放弃了查询的不等式部分,而不是让用户给出具体的价格,我只允许用户选择一个价格范围,例如:

    他们可以选择$5-$10 之类的范围,而不是将最高价格设置为$10

    在生产中,解决方案稍微复杂一些,但为了简化,这是核心思想。

    因此,不等式和composite index 问题已解决,使用新数据结构进行查询相当简单。新的数据结构:

    {
        price: "$5-$10",
        contained_fruits: {
          peach: true,
          melon: true,
          cherry: true
        },
        contained_vegetables: {
            cucumber: true,
            tomato: true,
        },
        package_name: {
          b: true,
          bi: true,
          big: true, 
          big_: true,
          big_s: true,
          big_si: true,
          big_siz: true,
          big_size: true,
            
        }
    }
    

    Firebase 允许多个基于相等性的查询,所以这...:

    .where("price", "==", price_input)
    .where(`package_name.${package_name_intput}`, "==", true)
    .where(`contained_vegetables.${vegetable_input}`, "==", true)
    .where(`contained_fruits.${fruit_input}`, "==", true)
    

    ...工作并且完全没问题。这样做的一个很好的优势是我可以让用户在每个查询中搜索多个水果和蔬菜项目,并且我可以相当容易地管理基于价格的查询,尽管不是最初想要的。我也不必理会composite indexes,这也很好。

    话虽如此,如果有人对我最初的问题提出了可行的解决方案,他们将获得赏金奖励。

    希望对您有所帮助,祝您有美好的一天!

    【讨论】:

      【解决方案3】:

      您的查询真的不可能在查询中使用关键字 OR 吗?

      对于您的多重包含,在水果中使用“番茄”还是在蔬菜中使用“番茄”? 像这样的子句应该处理你的两种情况

      在我看来,您的问题确实是布尔逻辑的问题。

      【讨论】:

      • 您能详细说明一下吗?还有一些代码示例?
      猜你喜欢
      • 1970-01-01
      • 2022-11-17
      • 2019-10-30
      • 2021-03-17
      • 2019-07-30
      • 2019-01-20
      • 2019-10-25
      • 2021-12-10
      • 1970-01-01
      相关资源
      最近更新 更多