swift Firestore搜索数组包含多个值

nr7wwzry  于 2023-05-27  发布在  Swift
关注(0)|答案(6)|浏览(224)

如果我有一个简单的集合,例如:

Fruits:
  Banana:
   title: "Banana"
   vitamins: ["potassium","B6","C"]
  Apple:
   title: "Apple"
   vitamins: ["A","B6","C"]

如果我想搜索含有维生素B6的水果,我会做以下事情:

db.collection("Fruits").whereField("vitamins", arrayContains: "A").getDocuments() { (querySnapshot, err)  in
        if let err = err {
            print("Error getting documents: \(err)")
        } else {
            for document in querySnapshot!.documents {
                print("\(document.documentID) => \(document.data())")
            }
        }
    }

这样我就可以看到我收藏的所有含有维生素A的水果了。然而,我如何能够搜索含有多种维生素的水果,比如维生素B6和C?我不能简单地搜索["B6","C"],因为它将寻找一个数组,而不是独立的字符串。
这在云之火中是可能的吗?如果没有,是否有其他替代方法?

4c8rllxm

4c8rllxm1#

通过在查询中链接条件,可以很容易地查找匹配多个条件的文档:

db.collection("Fruits")
    .whereField("vitamins", arrayContains: "B6")
    .whereField("vitamins", arrayContains: "C")

但是关于复合查询的文档建议,当前不能在单个查询中包含多个arrayContains条件。
所以,这是不可能的与你今天所拥有的。考虑使用map结构而不是数组:

Fruits:
  Banana:
   title: "Banana"
   vitamins: {
     "potassium": true,
     "B6": true,
     "C": true
   }

然后你可以这样查询:

db.collection("Fruits")
    .whereField("vitamins.B6", isEqualTo: true)
    .whereField("vitamins.C", isEqualTo: true)
kd3sttzy

kd3sttzy2#

Firestore在2019年底引入了whereIn、arrayContains和arrayContainsAny方法。这些方法执行逻辑“或”查询,但您可以传入的子句限制为10个(因此您最多可以搜索10种维生素)。
一些解决方案已经提到了重组数据。利用重组方法的另一个解决方案是不将Vitamins包括到Fruit文档中,而是相反。这样你就得到了所有的文件'香蕉'的一部分。

let vitaminsRef = db.collection('Vitamins').where('fruits', arrayContains: 'banana');

此解决方案允许您规避10个子句的限制。它在一次读取操作中获取所有在其“Fruits”数组中包含“Banana”的“Vitamin”文档(如果太多,请考虑分页)。

gcmastyq

gcmastyq3#

在Firestore中无法执行多个数组包含查询。但是,您可以合并尽可能多的where条件。因此,请考虑以下情况:

[Fruits]
    <Banana>
        title: "Banana"
        vitamins:
            potassium: true
            b6: true
            c: true

Firestore.firestore().collection("Fruits").whereField("vitamins.potassium", isEqualTo: true).whereField("vitamins.b6", isEqualTo: true)

但是,这仍然不允许一个干净的OR搜索。要查询含有维生素x或维生素y的水果,你必须更有创造力。此外,如果有很多可能的值,并且它们需要与复合查询组合,则不应使用此方法。这是因为Firestore不允许超过200个综合指数,每个综合指数都需要指定确切的维生素。这意味着您必须为vitamins.b6vitamins.potassium等创建单独的复合索引。而你总共只有200美元

js5cn81o

js5cn81o4#

在Firestore中无法查询包含多个arrayContains的Firestore数据库。如果您使用多个,则可能会出现以下错误:
无效查询。查询仅支持具有单个数组包含筛选器。
如果需要过滤多个维生素,则需要更改数据库的结构逻辑,方法是为每个维生素创建属性,然后链接.whereField()函数调用。我知道这听起来有点奇怪,但这就是Cloud Firestore的工作原理。
你的schema应该是这样的:

vitamins: { 
    "potassium": true,
    "B6": true,
    "C": true
}

要查找含有potassiumB6C维生素的所有水果,您应该使用如下所示的查询:

let fruitsRef = db.collection("Fruits")
    .whereField("vitamins.potassium", isEqualTo: true)
    .whereField("vitamins.B6", isEqualTo: true)
    .whereField("vitamins.C", isEqualTo: true)
dxxyhpgq

dxxyhpgq5#

它已经被评论过了,但是如果你的查询要求你创建一个复合索引,那么这个被接受的答案是不可行的,因为它可能会导致你的复合索引计数很快达到200的限制。这是一个很麻烦的方法,但我能想到的最好的解决方案是以某种有保证的顺序存储,例如。按字母顺序-给定水果的维生素的每个组合的数组,每个组合被连接为字符串。例如,由于香蕉含有B6,C和钾,香蕉的一系列维生素组合将是:

  • B6
  • B6||C
  • B6||C||potassium
  • B6||potassium
  • C
  • C||potassium
  • potassium

然后,如果你的用户正在搜索每一种同时含有B6和钾的水果,你的查询将是:

db.collection("Fruits").where("vitamins", "array-contains", "B6||potassium")
1aaf6o9v

1aaf6o9v6#

我想我找到了一种很好的方法,避免了为map值创建所有索引
如果你create all array combinations possible from an array of values

[a, b] => [[a],[b],[a,b]]

您可以排序并使用逗号或其他分隔符将它们连接起来

[[a],[b],[a,b]].map => ['a','b','a,b']

,然后存储字符串数组(必须排序),并使用排序的分隔搜索查询执行array-contains

where('possible_value_array', 'array-contains', 'a,b')

好奇我是不是漏掉了什么

相关问题