QueryIntegrationTests.swift 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. /*
  2. * Copyright 2023 Google LLC
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. import FirebaseFirestore
  17. import Foundation
  18. class QueryIntegrationTests: FSTIntegrationTestCase {
  19. func testOrQueries() throws {
  20. let collRef = collectionRef(
  21. withDocuments: ["doc1": ["a": 1, "b": 0],
  22. "doc2": ["a": 2, "b": 1],
  23. "doc3": ["a": 3, "b": 2],
  24. "doc4": ["a": 1, "b": 3],
  25. "doc5": ["a": 1, "b": 1]]
  26. )
  27. // Two equalities: a==1 || b==1.
  28. let filter1 = Filter.orFilter(
  29. [Filter.whereField("a", isEqualTo: 1),
  30. Filter.whereField("b", isEqualTo: 1)]
  31. )
  32. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1),
  33. matchesResult: ["doc1", "doc2", "doc4", "doc5"])
  34. // (a==1 && b==0) || (a==3 && b==2)
  35. let filter2 = Filter.orFilter(
  36. [Filter.andFilter(
  37. [Filter.whereField("a", isEqualTo: 1),
  38. Filter.whereField("b", isEqualTo: 0)]
  39. ),
  40. Filter.andFilter(
  41. [Filter.whereField("a", isEqualTo: 3),
  42. Filter.whereField("b", isEqualTo: 2)]
  43. )]
  44. )
  45. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2),
  46. matchesResult: ["doc1", "doc3"])
  47. // a==1 && (b==0 || b==3).
  48. let filter3 = Filter.andFilter(
  49. [Filter.whereField("a", isEqualTo: 1),
  50. Filter.orFilter(
  51. [Filter.whereField("b", isEqualTo: 0),
  52. Filter.whereField("b", isEqualTo: 3)]
  53. )]
  54. )
  55. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter3),
  56. matchesResult: ["doc1", "doc4"])
  57. // (a==2 || b==2) && (a==3 || b==3)
  58. let filter4 = Filter.andFilter(
  59. [Filter.orFilter(
  60. [Filter.whereField("a", isEqualTo: 2),
  61. Filter.whereField("b", isEqualTo: 2)]
  62. ),
  63. Filter.orFilter(
  64. [Filter.whereField("a", isEqualTo: 3),
  65. Filter.whereField("b", isEqualTo: 3)]
  66. )]
  67. )
  68. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter4),
  69. matchesResult: ["doc3"])
  70. // Test with limits without orderBy (the __name__ ordering is the tie breaker).
  71. let filter5 = Filter.orFilter(
  72. [Filter.whereField("a", isEqualTo: 2),
  73. Filter.whereField("b", isEqualTo: 1)]
  74. )
  75. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter5).limit(to: 1),
  76. matchesResult: ["doc2"])
  77. }
  78. func testOrQueriesWithCompositeIndexes() throws {
  79. // TODO(orquery): Enable this test against production when possible.
  80. try XCTSkipIf(!FSTIntegrationTestCase.isRunningAgainstEmulator(),
  81. "Skip this test if running against production because it results in" +
  82. "a 'missing index' error. The Firestore Emulator, however, does serve these queries.")
  83. let collRef = collectionRef(
  84. withDocuments: ["doc1": ["a": 1, "b": 0],
  85. "doc2": ["a": 2, "b": 1],
  86. "doc3": ["a": 3, "b": 2],
  87. "doc4": ["a": 1, "b": 3],
  88. "doc5": ["a": 1, "b": 1]]
  89. )
  90. // with one inequality: a>2 || b==1.
  91. let filter1 = Filter.orFilter(
  92. [Filter.whereField("a", isGreaterThan: 2),
  93. Filter.whereField("b", isEqualTo: 1)]
  94. )
  95. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1),
  96. matchesResult: ["doc5", "doc2", "doc3"])
  97. // Test with limits (implicit order by ASC): (a==1) || (b > 0) LIMIT 2
  98. let filter2 = Filter.orFilter(
  99. [Filter.whereField("a", isEqualTo: 1),
  100. Filter.whereField("b", isGreaterThan: 0)]
  101. )
  102. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2).limit(to: 2),
  103. matchesResult: ["doc1", "doc2"])
  104. // Test with limits (explicit order by): (a==1) || (b > 0) LIMIT_TO_LAST 2
  105. // Note: The public query API does not allow implicit ordering when limitToLast is used.
  106. let filter3 = Filter.orFilter(
  107. [Filter.whereField("a", isEqualTo: 1),
  108. Filter.whereField("b", isGreaterThan: 0)]
  109. )
  110. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter3)
  111. .limit(toLast: 2)
  112. .order(by: "b"),
  113. matchesResult: ["doc3", "doc4"])
  114. // Test with limits (explicit order by ASC): (a==2) || (b == 1) ORDER BY a LIMIT 1
  115. let filter4 = Filter.orFilter(
  116. [Filter.whereField("a", isEqualTo: 2),
  117. Filter.whereField("b", isEqualTo: 1)]
  118. )
  119. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter4).limit(to: 1)
  120. .order(by: "a"),
  121. matchesResult: ["doc5"])
  122. // Test with limits (explicit order by DESC): (a==2) || (b == 1) ORDER BY a LIMIT_TO_LAST 1
  123. let filter5 = Filter.orFilter(
  124. [Filter.whereField("a", isEqualTo: 2),
  125. Filter.whereField("b", isEqualTo: 1)]
  126. )
  127. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter5).limit(toLast: 1)
  128. .order(by: "a"),
  129. matchesResult: ["doc2"])
  130. }
  131. func testOrQueriesWithIn() throws {
  132. let collRef = collectionRef(
  133. withDocuments: ["doc1": ["a": 1, "b": 0],
  134. "doc2": ["b": 1],
  135. "doc3": ["a": 3, "b": 2],
  136. "doc4": ["a": 1, "b": 3],
  137. "doc5": ["a": 1],
  138. "doc6": ["a": 2]]
  139. )
  140. // a==2 || b in [2,3]
  141. let filter = Filter.orFilter(
  142. [Filter.whereField("a", isEqualTo: 2),
  143. Filter.whereField("b", in: [2, 3])]
  144. )
  145. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter),
  146. matchesResult: ["doc3", "doc4", "doc6"])
  147. }
  148. func testOrQueriesWithArrayMembership() throws {
  149. let collRef = collectionRef(
  150. withDocuments: ["doc1": ["a": 1, "b": [0]],
  151. "doc2": ["b": 1],
  152. "doc3": ["a": 3, "b": [2, 7]],
  153. "doc4": ["a": 1, "b": [3, 7]],
  154. "doc5": ["a": 1],
  155. "doc6": ["a": 2]]
  156. )
  157. // a==2 || b array-contains 7
  158. let filter1 = Filter.orFilter(
  159. [Filter.whereField("a", isEqualTo: 2),
  160. Filter.whereField("b", arrayContains: 7)]
  161. )
  162. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1),
  163. matchesResult: ["doc3", "doc4", "doc6"])
  164. // a==2 || b array-contains-any [0, 3]
  165. let filter2 = Filter.orFilter(
  166. [Filter.whereField("a", isEqualTo: 2),
  167. Filter.whereField("b", arrayContainsAny: [0, 3])]
  168. )
  169. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2),
  170. matchesResult: ["doc1", "doc4", "doc6"])
  171. }
  172. func testMultipleInOps() throws {
  173. let collRef = collectionRef(
  174. withDocuments: ["doc1": ["a": 1, "b": 0],
  175. "doc2": ["b": 1],
  176. "doc3": ["a": 3, "b": 2],
  177. "doc4": ["a": 1, "b": 3],
  178. "doc5": ["a": 1],
  179. "doc6": ["a": 2]]
  180. )
  181. // Two IN operations on different fields with disjunction.
  182. let filter1 = Filter.orFilter(
  183. [Filter.whereField("a", in: [2, 3]),
  184. Filter.whereField("b", in: [0, 2])]
  185. )
  186. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1).order(by: "a"),
  187. matchesResult: ["doc1", "doc6", "doc3"])
  188. // Two IN operations on same fields with disjunction.
  189. // a IN [0,3] || a IN [0,2] should union them (similar to: a IN [0,2,3]).
  190. let filter2 = Filter.orFilter(
  191. [Filter.whereField("a", in: [0, 3]),
  192. Filter.whereField("a", in: [0, 2])]
  193. )
  194. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2),
  195. matchesResult: ["doc3", "doc6"])
  196. }
  197. func testUsingInWithArrayContainsAny() throws {
  198. let collRef = collectionRef(
  199. withDocuments: ["doc1": ["a": 1, "b": [0]],
  200. "doc2": ["b": [1]],
  201. "doc3": ["a": 3, "b": [2, 7], "c": 10],
  202. "doc4": ["a": 1, "b": [3, 7]],
  203. "doc5": ["a": 1],
  204. "doc6": ["a": 2, "c": 20]]
  205. )
  206. let filter1 = Filter.orFilter(
  207. [Filter.whereField("a", in: [2, 3]),
  208. Filter.whereField("b", arrayContainsAny: [0, 7])]
  209. )
  210. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1),
  211. matchesResult: ["doc1", "doc3", "doc4", "doc6"])
  212. let filter2 = Filter.orFilter(
  213. [Filter.andFilter(
  214. [Filter.whereField("a", in: [2, 3]),
  215. Filter.whereField("c", isEqualTo: 10)]
  216. ),
  217. Filter.whereField("b", arrayContainsAny: [0, 7])]
  218. )
  219. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2),
  220. matchesResult: ["doc1", "doc3", "doc4"])
  221. }
  222. func testUseInWithArrayContains() throws {
  223. let collRef = collectionRef(
  224. withDocuments: ["doc1": ["a": 1, "b": [0]],
  225. "doc2": ["b": [1]],
  226. "doc3": ["a": 3, "b": [2, 7]],
  227. "doc4": ["a": 1, "b": [3, 7]],
  228. "doc5": ["a": 1],
  229. "doc6": ["a": 2]]
  230. )
  231. let filter1 = Filter.orFilter(
  232. [Filter.whereField("a", in: [2, 3]),
  233. Filter.whereField("b", arrayContainsAny: [3])]
  234. )
  235. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter1),
  236. matchesResult: ["doc3", "doc4", "doc6"])
  237. let filter2 = Filter.andFilter(
  238. [Filter.whereField("a", in: [2, 3]),
  239. Filter.whereField("b", arrayContains: 7)]
  240. )
  241. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter2),
  242. matchesResult: ["doc3"])
  243. let filter3 = Filter.orFilter(
  244. [Filter.whereField("a", in: [2, 3]),
  245. Filter.andFilter(
  246. [Filter.whereField("b", arrayContains: 3),
  247. Filter.whereField("a", isEqualTo: 1)]
  248. )]
  249. )
  250. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter3),
  251. matchesResult: ["doc3", "doc4", "doc6"])
  252. let filter4 = Filter.andFilter(
  253. [Filter.whereField("a", in: [2, 3]),
  254. Filter.orFilter(
  255. [Filter.whereField("b", arrayContains: 7),
  256. Filter.whereField("a", isEqualTo: 1)]
  257. )]
  258. )
  259. checkOnlineAndOfflineCollection(collRef, query: collRef.whereFilter(filter4),
  260. matchesResult: ["doc3"])
  261. }
  262. func testOrderByEquality() throws {
  263. // TODO(orquery): Enable this test against production when possible.
  264. try XCTSkipIf(!FSTIntegrationTestCase.isRunningAgainstEmulator(),
  265. "Skip this test if running against production because order-by-equality is not supported yet.")
  266. let collRef = collectionRef(
  267. withDocuments: ["doc1": ["a": 1, "b": [0]],
  268. "doc2": ["b": [1]],
  269. "doc3": ["a": 3, "b": [2, 7], "c": 10],
  270. "doc4": ["a": 1, "b": [3, 7]],
  271. "doc5": ["a": 1],
  272. "doc6": ["a": 2, "c": 20]]
  273. )
  274. checkOnlineAndOfflineCollection(
  275. collRef,
  276. query: collRef.whereFilter(Filter.whereField("a", isEqualTo: 1)),
  277. matchesResult: ["doc1", "doc4", "doc5"]
  278. )
  279. checkOnlineAndOfflineCollection(
  280. collRef,
  281. query: collRef.whereFilter(Filter.whereField("a", in: [2, 3])).order(by: "a"),
  282. matchesResult: ["doc6", "doc3"]
  283. )
  284. }
  285. }