@@ -378,3 +378,182 @@ spec = describe "related queries" $ do
378378 , matchHeaders = [ matchContentTypeJson
379379 , " Content-Range" <:> " 0-1/952" ]
380380 }
381+
382+ context " related conditions using the embed(column) syntax" $ do
383+ it " works on a many-to-one relationship" $ do
384+ get " /projects?select=name,clients()&clients(name)=eq.Microsoft" `shouldRespondWith`
385+ [json |[
386+ {"name":"Windows 7"},
387+ {"name":"Windows 10"}
388+ ]|]
389+ { matchStatus = 200
390+ , matchHeaders = [matchContentTypeJson]
391+ }
392+ get " /projects?select=name,computed_clients()&computed_clients(name)=eq.Apple" `shouldRespondWith`
393+ [json |[
394+ {"name":"IOS"},
395+ {"name":"OSX"}
396+ ]|]
397+ { matchStatus = 200
398+ , matchHeaders = [matchContentTypeJson]
399+ }
400+
401+ it " works on a one-to-many relationship" $ do
402+ get " /entities?select=name,child_entities()&child_entities(name)=like.child*" `shouldRespondWith`
403+ [json |[
404+ {"name":"entity 1"},
405+ {"name":"entity 2"}
406+ ]|]
407+ { matchStatus = 200
408+ , matchHeaders = [matchContentTypeJson]
409+ }
410+ get " /entities?select=name,child_entities()&child_entities(name)=like(any).{*1,*2}" `shouldRespondWith`
411+ [json |[
412+ {"name":"entity 1"}
413+ ]|]
414+ { matchStatus = 200
415+ , matchHeaders = [matchContentTypeJson]
416+ }
417+
418+ it " works on a many-to-many relationship" $ do
419+ get " /users?select=name,tasks()&tasks(id)=eq.1" `shouldRespondWith`
420+ [json |[
421+ {"name":"Angela Martin"},
422+ {"name":"Dwight Schrute"}
423+ ]|]
424+ { matchStatus = 200
425+ , matchHeaders = [matchContentTypeJson]
426+ }
427+ get " /users?select=name,tasks()&tasks(id)=eq.7" `shouldRespondWith`
428+ [json |[
429+ {"name":"Michael Scott"}
430+ ]|]
431+ { matchStatus = 200
432+ , matchHeaders = [matchContentTypeJson]
433+ }
434+ {- - TODO(draft): make nesting work
435+ it "works on nested embeds" $ do
436+ get "/entities?select=name,child_entities(name,grandchild_entities())&child_entities(grandchild_entities(name))=like.*1" `shouldRespondWith`
437+ [json|[
438+ {"name":"entity 1","child_entities":[{"name":"child entity 1"}, {"name":"child entity 2"}]}]|]
439+ { matchStatus = 200
440+ , matchHeaders = [matchContentTypeJson]
441+ }
442+ --}
443+ it " can do an or across embeds" $
444+ get " /client?select=*,clientinfo(),contact()&or=(clientinfo(other).like.*Main*,contact(name).like.*Tabby*)" `shouldRespondWith`
445+ [json |[
446+ {"id":1,"name":"Walmart"},
447+ {"id":2,"name":"Target"}
448+ ]|]
449+ { matchStatus = 200
450+ , matchHeaders = [matchContentTypeJson]
451+ }
452+
453+ it " works when the embedding uses the column name" $ do
454+ get " /projects?select=name,c_id:client_id,client_id(name)&client_id(name)=like.Apple" `shouldRespondWith`
455+ [json |[
456+ {"name":"IOS","c_id":2,"client_id":{"name":"Apple"}},
457+ {"name":"OSX","c_id":2,"client_id":{"name":"Apple"}}
458+ ]|]
459+ { matchStatus = 200
460+ , matchHeaders = [matchContentTypeJson]
461+ }
462+ get " /projects?select=name,client_id(name)&client_id(name)=like.Apple" `shouldRespondWith`
463+ [json |[
464+ {"name":"IOS","client_id":{"name":"Apple"}},
465+ {"name":"OSX","client_id":{"name":"Apple"}}
466+ ]|]
467+ { matchStatus = 200
468+ , matchHeaders = [matchContentTypeJson]
469+ }
470+
471+ -- "?table=not.is.null" does a "table IS DISTINCT FROM NULL" instead of a "table IS NOT NULL"
472+ -- https://github.com/PostgREST/postgrest/issues/2800#issuecomment-1720315818
473+ it " embeds verifying that the entire target table row is not null" $ do
474+ get " /table_b?select=name,table_a(name)&table_a(id)=eq(any).{1,2}" `shouldRespondWith`
475+ [json |[
476+ {"name":"Test 1","table_a":{"name":"Not null 1"}},
477+ {"name":"Test 2","table_a":{"name":null}}
478+ ]|]
479+ { matchStatus = 200
480+ , matchHeaders = [matchContentTypeJson]
481+ }
482+
483+ it " works with count=exact" $ do
484+ request methodGet " /projects?select=name,clients(name)&clients(id)=gt.0"
485+ [(" Prefer" , " count=exact" )] " "
486+ `shouldRespondWith`
487+ [json |[
488+ {"name":"Windows 7", "clients":{"name":"Microsoft"}},
489+ {"name":"Windows 10", "clients":{"name":"Microsoft"}},
490+ {"name":"IOS", "clients":{"name":"Apple"}},
491+ {"name":"OSX", "clients":{"name":"Apple"}}
492+ ]|]
493+ { matchStatus = 200
494+ , matchHeaders = [ matchContentTypeJson
495+ , " Content-Range" <:> " 0-3/4" ]
496+ }
497+ request methodGet " /client?select=*,clientinfo(),contact()&or=(clientinfo(other).ilike.*main*,contact(name).ilike.*tabby*)"
498+ [(" Prefer" , " count=exact" )] " "
499+ `shouldRespondWith`
500+ [json |[
501+ {"id":1,"name":"Walmart"},
502+ {"id":2,"name":"Target"}
503+ ]|]
504+ { matchStatus = 200
505+ , matchHeaders = [ matchContentTypeJson
506+ , " Content-Range" <:> " 0-1/2" ]
507+ }
508+
509+ it " works with count=planned" $ do
510+ request methodGet " /projects?select=name,clients(name)&clients(id)=gt.0"
511+ [(" Prefer" , " count=planned" )] " "
512+ `shouldRespondWith`
513+ [json |[
514+ {"name":"Windows 7", "clients":{"name":"Microsoft"}},
515+ {"name":"Windows 10", "clients":{"name":"Microsoft"}},
516+ {"name":"IOS", "clients":{"name":"Apple"}},
517+ {"name":"OSX", "clients":{"name":"Apple"}}
518+ ]|]
519+ { matchStatus = 206
520+ , matchHeaders = [ matchContentTypeJson
521+ , " Content-Range" <:> " 0-3/400" ]
522+ }
523+ request methodGet " /client?select=*,clientinfo(),contact()&or=(clientinfo(other).ilike.*main*,contact(name).ilike.*tabby*)"
524+ [(" Prefer" , " count=planned" )] " "
525+ `shouldRespondWith`
526+ [json |[
527+ {"id":1,"name":"Walmart"},
528+ {"id":2,"name":"Target"}
529+ ]|]
530+ { matchStatus = 206
531+ , matchHeaders = [ matchContentTypeJson
532+ , " Content-Range" <:> " 0-1/952" ]
533+ }
534+
535+ it " works with count=estimated" $ do
536+ request methodGet " /projects?select=name,clients(name)&clients(id)=gt.0"
537+ [(" Prefer" , " count=estimated" )] " "
538+ `shouldRespondWith`
539+ [json |[
540+ {"name":"Windows 7", "clients":{"name":"Microsoft"}},
541+ {"name":"Windows 10", "clients":{"name":"Microsoft"}},
542+ {"name":"IOS", "clients":{"name":"Apple"}},
543+ {"name":"OSX", "clients":{"name":"Apple"}}
544+ ]|]
545+ { matchStatus = 206
546+ , matchHeaders = [ matchContentTypeJson
547+ , " Content-Range" <:> " 0-3/400" ]
548+ }
549+ request methodGet " /client?select=*,clientinfo(),contact()&or=(clientinfo(other).ilike.*main*,contact(name).ilike.*tabby*)"
550+ [(" Prefer" , " count=estimated" )] " "
551+ `shouldRespondWith`
552+ [json |[
553+ {"id":1,"name":"Walmart"},
554+ {"id":2,"name":"Target"}
555+ ]|]
556+ { matchStatus = 206
557+ , matchHeaders = [ matchContentTypeJson
558+ , " Content-Range" <:> " 0-1/952" ]
559+ }
0 commit comments