@@ -22,6 +22,7 @@ import { Deferred } from '../../util/promise';
22
22
import { EventsAccumulator } from '../util/events_accumulator' ;
23
23
import {
24
24
addDoc ,
25
+ and ,
25
26
Bytes ,
26
27
collection ,
27
28
collectionGroup ,
@@ -36,10 +37,14 @@ import {
36
37
endBefore ,
37
38
GeoPoint ,
38
39
getDocs ,
40
+ getDocsFromCache ,
41
+ getDocsFromServer ,
39
42
limit ,
40
43
limitToLast ,
41
44
onSnapshot ,
45
+ or ,
42
46
orderBy ,
47
+ Query ,
43
48
query ,
44
49
QuerySnapshot ,
45
50
setDoc ,
@@ -54,6 +59,7 @@ import {
54
59
apiDescribe ,
55
60
toChangesArray ,
56
61
toDataArray ,
62
+ toIds ,
57
63
withTestCollection ,
58
64
withTestDb
59
65
} from '../util/helpers' ;
@@ -1288,6 +1294,189 @@ apiDescribe('Queries', (persistence: boolean) => {
1288
1294
expect ( toDataArray ( snapshot ) ) . to . deep . equal ( [ { map : { nested : 'foo' } } ] ) ;
1289
1295
} ) ;
1290
1296
} ) ;
1297
+
1298
+ if ( ! persistence ) {
1299
+ return ;
1300
+ }
1301
+
1302
+ // TODO(orquery): Enable this test when prod supports OR queries.
1303
+ // eslint-disable-next-line no-restricted-properties
1304
+ it . skip ( 'can use or queries' , ( ) => {
1305
+ const testDocs = {
1306
+ doc1 : { a : 1 , b : 0 } ,
1307
+ doc2 : { a : 2 , b : 1 } ,
1308
+ doc3 : { a : 3 , b : 2 } ,
1309
+ doc4 : { a : 1 , b : 3 } ,
1310
+ doc5 : { a : 1 , b : 1 }
1311
+ } ;
1312
+
1313
+ return withTestCollection ( persistence , testDocs , async coll => {
1314
+ // Two equalities: a==1 || b==1.
1315
+ await checkOnlineAndOfflineResultsMatch (
1316
+ query ( coll , or ( where ( 'a' , '==' , 1 ) , where ( 'b' , '==' , 1 ) ) ) ,
1317
+ 'doc1' ,
1318
+ 'doc2' ,
1319
+ 'doc4' ,
1320
+ 'doc5'
1321
+ ) ;
1322
+
1323
+ // with one inequality: a>2 || b==1.
1324
+ await checkOnlineAndOfflineResultsMatch (
1325
+ query ( coll , or ( where ( 'a' , '>' , 2 ) , where ( 'b' , '==' , 1 ) ) ) ,
1326
+ 'doc5' ,
1327
+ 'doc2' ,
1328
+ 'doc3'
1329
+ ) ;
1330
+
1331
+ // (a==1 && b==0) || (a==3 && b==2)
1332
+ await checkOnlineAndOfflineResultsMatch (
1333
+ query (
1334
+ coll ,
1335
+ or (
1336
+ and ( where ( 'a' , '==' , 1 ) , where ( 'b' , '==' , 0 ) ) ,
1337
+ and ( where ( 'a' , '==' , 3 ) , where ( 'b' , '==' , 2 ) )
1338
+ )
1339
+ ) ,
1340
+ 'doc1' ,
1341
+ 'doc3'
1342
+ ) ;
1343
+
1344
+ // a==1 && (b==0 || b==3).
1345
+ await checkOnlineAndOfflineResultsMatch (
1346
+ query (
1347
+ coll ,
1348
+ and ( where ( 'a' , '==' , 1 ) , or ( where ( 'b' , '==' , 0 ) , where ( 'b' , '==' , 3 ) ) )
1349
+ ) ,
1350
+ 'doc1' ,
1351
+ 'doc4'
1352
+ ) ;
1353
+
1354
+ // (a==2 || b==2) && (a==3 || b==3)
1355
+ await checkOnlineAndOfflineResultsMatch (
1356
+ query (
1357
+ coll ,
1358
+ and (
1359
+ or ( where ( 'a' , '==' , 2 ) , where ( 'b' , '==' , 2 ) ) ,
1360
+ or ( where ( 'a' , '==' , 3 ) , where ( 'b' , '==' , 3 ) )
1361
+ )
1362
+ ) ,
1363
+ 'doc3'
1364
+ ) ;
1365
+
1366
+ // Test with limits (implicit order by ASC): (a==1) || (b > 0) LIMIT 2
1367
+ await checkOnlineAndOfflineResultsMatch (
1368
+ query ( coll , or ( where ( 'a' , '==' , 1 ) , where ( 'b' , '>' , 0 ) ) , limit ( 2 ) ) ,
1369
+ 'doc1' ,
1370
+ 'doc2'
1371
+ ) ;
1372
+
1373
+ // Test with limits (explicit order by): (a==1) || (b > 0) LIMIT_TO_LAST 2
1374
+ // Note: The public query API does not allow implicit ordering when limitToLast is used.
1375
+ await checkOnlineAndOfflineResultsMatch (
1376
+ query (
1377
+ coll ,
1378
+ or ( where ( 'a' , '==' , 1 ) , where ( 'b' , '>' , 0 ) ) ,
1379
+ limitToLast ( 2 ) ,
1380
+ orderBy ( 'b' )
1381
+ ) ,
1382
+ 'doc3' ,
1383
+ 'doc4'
1384
+ ) ;
1385
+
1386
+ // Test with limits (explicit order by ASC): (a==2) || (b == 1) ORDER BY a LIMIT 1
1387
+ await checkOnlineAndOfflineResultsMatch (
1388
+ query (
1389
+ coll ,
1390
+ or ( where ( 'a' , '==' , 2 ) , where ( 'b' , '==' , 1 ) ) ,
1391
+ limit ( 1 ) ,
1392
+ orderBy ( 'a' )
1393
+ ) ,
1394
+ 'doc5'
1395
+ ) ;
1396
+
1397
+ // Test with limits (explicit order by DESC): (a==2) || (b == 1) ORDER BY a LIMIT_TO_LAST 1
1398
+ await checkOnlineAndOfflineResultsMatch (
1399
+ query (
1400
+ coll ,
1401
+ or ( where ( 'a' , '==' , 2 ) , where ( 'b' , '==' , 1 ) ) ,
1402
+ limitToLast ( 1 ) ,
1403
+ orderBy ( 'a' )
1404
+ ) ,
1405
+ 'doc2'
1406
+ ) ;
1407
+
1408
+ // Test with limits without orderBy (the __name__ ordering is the tie breaker).
1409
+ await checkOnlineAndOfflineResultsMatch (
1410
+ query ( coll , or ( where ( 'a' , '==' , 2 ) , where ( 'b' , '==' , 1 ) ) , limit ( 1 ) ) ,
1411
+ 'doc2'
1412
+ ) ;
1413
+ } ) ;
1414
+ } ) ;
1415
+
1416
+ // TODO(orquery): Enable this test when prod supports OR queries.
1417
+ // eslint-disable-next-line no-restricted-properties
1418
+ it . skip ( 'can use or queries with in and not-in' , ( ) => {
1419
+ const testDocs = {
1420
+ doc1 : { a : 1 , b : 0 } ,
1421
+ doc2 : { b : 1 } ,
1422
+ doc3 : { a : 3 , b : 2 } ,
1423
+ doc4 : { a : 1 , b : 3 } ,
1424
+ doc5 : { a : 1 } ,
1425
+ doc6 : { a : 2 }
1426
+ } ;
1427
+
1428
+ return withTestCollection ( persistence , testDocs , async coll => {
1429
+ // a==2 || b in [2,3]
1430
+ await checkOnlineAndOfflineResultsMatch (
1431
+ query ( coll , or ( where ( 'a' , '==' , 2 ) , where ( 'b' , 'in' , [ 2 , 3 ] ) ) ) ,
1432
+ 'doc3' ,
1433
+ 'doc4' ,
1434
+ 'doc6'
1435
+ ) ;
1436
+
1437
+ // a==2 || b not-in [2,3]
1438
+ // Has implicit orderBy b.
1439
+ await checkOnlineAndOfflineResultsMatch (
1440
+ query ( coll , or ( where ( 'a' , '==' , 2 ) , where ( 'b' , 'not-in' , [ 2 , 3 ] ) ) ) ,
1441
+ 'doc1' ,
1442
+ 'doc2'
1443
+ ) ;
1444
+ } ) ;
1445
+ } ) ;
1446
+
1447
+ // TODO(orquery): Enable this test when prod supports OR queries.
1448
+ // eslint-disable-next-line no-restricted-properties
1449
+ it . skip ( 'can use or queries with array membership' , ( ) => {
1450
+ const testDocs = {
1451
+ doc1 : { a : 1 , b : [ 0 ] } ,
1452
+ doc2 : { b : [ 1 ] } ,
1453
+ doc3 : { a : 3 , b : [ 2 , 7 ] } ,
1454
+ doc4 : { a : 1 , b : [ 3 , 7 ] } ,
1455
+ doc5 : { a : 1 } ,
1456
+ doc6 : { a : 2 }
1457
+ } ;
1458
+
1459
+ return withTestCollection ( persistence , testDocs , async coll => {
1460
+ // a==2 || b array-contains 7
1461
+ await checkOnlineAndOfflineResultsMatch (
1462
+ query ( coll , or ( where ( 'a' , '==' , 2 ) , where ( 'b' , 'array-contains' , 7 ) ) ) ,
1463
+ 'doc3' ,
1464
+ 'doc4' ,
1465
+ 'doc6'
1466
+ ) ;
1467
+
1468
+ // a==2 || b array-contains-any [0, 3]
1469
+ await checkOnlineAndOfflineResultsMatch (
1470
+ query (
1471
+ coll ,
1472
+ or ( where ( 'a' , '==' , 2 ) , where ( 'b' , 'array-contains-any' , [ 0 , 3 ] ) )
1473
+ ) ,
1474
+ 'doc1' ,
1475
+ 'doc4' ,
1476
+ 'doc6'
1477
+ ) ;
1478
+ } ) ;
1479
+ } ) ;
1291
1480
} ) ;
1292
1481
1293
1482
function verifyDocumentChange < T > (
@@ -1302,3 +1491,25 @@ function verifyDocumentChange<T>(
1302
1491
expect ( change . oldIndex ) . to . equal ( oldIndex ) ;
1303
1492
expect ( change . newIndex ) . to . equal ( newIndex ) ;
1304
1493
}
1494
+
1495
+ /**
1496
+ * Checks that running the query while online (against the backend/emulator) results in the same
1497
+ * documents as running the query while offline. If `expectedDocs` is provided, it also checks
1498
+ * that both online and offline query result is equal to the expected documents.
1499
+ *
1500
+ * @param query The query to check
1501
+ * @param expectedDocs Ordered list of document keys that are expected to match the query
1502
+ */
1503
+ async function checkOnlineAndOfflineResultsMatch (
1504
+ query : Query ,
1505
+ ...expectedDocs : string [ ]
1506
+ ) : Promise < void > {
1507
+ const docsFromServer = await getDocsFromServer ( query ) ;
1508
+
1509
+ if ( expectedDocs . length !== 0 ) {
1510
+ expect ( expectedDocs ) . to . deep . equal ( toIds ( docsFromServer ) ) ;
1511
+ }
1512
+
1513
+ const docsFromCache = await getDocsFromCache ( query ) ;
1514
+ expect ( toIds ( docsFromServer ) ) . to . deep . equal ( toIds ( docsFromCache ) ) ;
1515
+ }
0 commit comments