diff --git a/server/src-lib/Hasura/GraphQL/Resolve/BoolExp.hs b/server/src-lib/Hasura/GraphQL/Resolve/BoolExp.hs index 20e90466d96c1..1b00ccd7b0642 100644 --- a/server/src-lib/Hasura/GraphQL/Resolve/BoolExp.hs +++ b/server/src-lib/Hasura/GraphQL/Resolve/BoolExp.hs @@ -35,27 +35,35 @@ parseOpExps parseOpExps annVal = do opExpsM <- flip withObjectM annVal $ \nt objM -> forM objM $ \obj -> forM (Map.toList obj) $ \(k, v) -> case k of - "_eq" -> fmap RA.AEQ <$> asPGColValM v - "_ne" -> fmap RA.ANE <$> asPGColValM v - "_neq" -> fmap RA.ANE <$> asPGColValM v - "_is_null" -> resolveIsNull v + "_eq" -> fmap RA.AEQ <$> asPGColValM v + "_ne" -> fmap RA.ANE <$> asPGColValM v + "_neq" -> fmap RA.ANE <$> asPGColValM v + "_is_null" -> resolveIsNull v - "_in" -> fmap (RA.AIN . catMaybes) <$> parseMany asPGColValM v - "_nin" -> fmap (RA.ANIN . catMaybes) <$> parseMany asPGColValM v + "_in" -> fmap (RA.AIN . catMaybes) <$> parseMany asPGColValM v + "_nin" -> fmap (RA.ANIN . catMaybes) <$> parseMany asPGColValM v - "_gt" -> fmap RA.AGT <$> asPGColValM v - "_lt" -> fmap RA.ALT <$> asPGColValM v - "_gte" -> fmap RA.AGTE <$> asPGColValM v - "_lte" -> fmap RA.ALTE <$> asPGColValM v + "_gt" -> fmap RA.AGT <$> asPGColValM v + "_lt" -> fmap RA.ALT <$> asPGColValM v + "_gte" -> fmap RA.AGTE <$> asPGColValM v + "_lte" -> fmap RA.ALTE <$> asPGColValM v - "_like" -> fmap RA.ALIKE <$> asPGColValM v - "_nlike" -> fmap RA.ANLIKE <$> asPGColValM v + "_like" -> fmap RA.ALIKE <$> asPGColValM v + "_nlike" -> fmap RA.ANLIKE <$> asPGColValM v - "_ilike" -> fmap RA.AILIKE <$> asPGColValM v - "_nilike" -> fmap RA.ANILIKE <$> asPGColValM v + "_ilike" -> fmap RA.AILIKE <$> asPGColValM v + "_nilike" -> fmap RA.ANILIKE <$> asPGColValM v + + "_similar" -> fmap RA.ASIMILAR <$> asPGColValM v + "_nsimilar" -> fmap RA.ANSIMILAR <$> asPGColValM v + + -- jsonb related operators + "_contains" -> fmap RA.AContains <$> asPGColValM v + "_contained_in" -> fmap RA.AContainedIn <$> asPGColValM v + "_has_key" -> fmap RA.AHasKey <$> asPGColValM v + "_has_keys_any" -> fmap RA.AHasKeysAny <$> parseMany asPGColText v + "_has_keys_all" -> fmap RA.AHasKeysAll <$> parseMany asPGColText v - "_similar" -> fmap RA.ASIMILAR <$> asPGColValM v - "_nsimilar" -> fmap RA.ANSIMILAR <$> asPGColValM v _ -> throw500 $ "unexpected operator found in opexp of " diff --git a/server/src-lib/Hasura/GraphQL/Resolve/InputValue.hs b/server/src-lib/Hasura/GraphQL/Resolve/InputValue.hs index f387306c1180a..aa0a653052fa7 100644 --- a/server/src-lib/Hasura/GraphQL/Resolve/InputValue.hs +++ b/server/src-lib/Hasura/GraphQL/Resolve/InputValue.hs @@ -16,6 +16,7 @@ module Hasura.GraphQL.Resolve.InputValue , withArray , withArrayM , parseMany + , asPGColText ) where import Hasura.Prelude @@ -108,3 +109,12 @@ parseMany parseMany fn v = case v of AGArray _ arrM -> mapM (mapM fn) arrM _ -> tyMismatch "array" v + +asPGColText + :: (MonadError QErr m) + => AnnGValue -> m Text +asPGColText val = do + (_, pgColVal) <- asPGColVal val + case pgColVal of + PGValText t -> return t + _ -> throw500 "expecting text for asPGColText" diff --git a/server/src-lib/Hasura/GraphQL/Schema.hs b/server/src-lib/Hasura/GraphQL/Schema.hs index 6eedafeb69074..f4333995c307c 100644 --- a/server/src-lib/Hasura/GraphQL/Schema.hs +++ b/server/src-lib/Hasura/GraphQL/Schema.hs @@ -157,6 +157,7 @@ mkCompExpInp colTy = [ map (mk colScalarTy) typedOps , map (mk $ G.toLT colScalarTy) listOps , bool [] (map (mk $ mkScalarTy PGText) stringOps) isStringTy + , bool [] (map jsonbOpToInpVal jsonbOps) isJsonbTy , [InpValInfo Nothing "_is_null" $ G.TypeNamed $ G.NamedType "Boolean"] ] where @@ -191,6 +192,35 @@ mkCompExpInp colTy = , "_similar", "_nsimilar" ] + isJsonbTy = case colTy of + PGJSONB -> True + _ -> False + + jsonbOpToInpVal (op, ty, desc) = InpValInfo (Just desc) op ty + + jsonbOps = + [ ( "_contains" + , G.toGT $ mkScalarTy PGJSONB + , "does the column contain the given json value at the top level" + ) + , ( "_contained_in" + , G.toGT $ mkScalarTy PGJSONB + , "is the column contained in the given json value" + ) + , ( "_has_key" + , G.toGT $ mkScalarTy PGText + , "does the string exist as a top-level key in the column" + ) + , ( "_has_keys_any" + , G.toGT $ G.toLT $ G.toNT $ mkScalarTy PGText + , "do any of these strings exist as top-level keys in the column" + ) + , ( "_has_keys_all" + , G.toGT $ G.toLT $ G.toNT $ mkScalarTy PGText + , "do all of these strings exist as top-level keys in the column" + ) + ] + mkPGColFld :: PGColInfo -> ObjFldInfo mkPGColFld (PGColInfo colName colTy isNullable) = ObjFldInfo Nothing n Map.empty ty diff --git a/server/src-lib/Hasura/RQL/GBoolExp.hs b/server/src-lib/Hasura/RQL/GBoolExp.hs index 713712b5f6b1a..fd2ba4d45d7c6 100644 --- a/server/src-lib/Hasura/RQL/GBoolExp.hs +++ b/server/src-lib/Hasura/RQL/GBoolExp.hs @@ -1,9 +1,9 @@ -{-# LANGUAGE FlexibleContexts #-} -{-# LANGUAGE FlexibleInstances #-} -{-# LANGUAGE LambdaCase #-} -{-# LANGUAGE MultiWayIf #-} -{-# LANGUAGE OverloadedStrings #-} -{-# LANGUAGE TypeSynonymInstances #-} +{-# LANGUAGE FlexibleContexts #-} +{-# LANGUAGE FlexibleInstances #-} +{-# LANGUAGE LambdaCase #-} +{-# LANGUAGE MultiWayIf #-} +{-# LANGUAGE OverloadedStrings #-} +{-# LANGUAGE TypeSynonymInstances #-} module Hasura.RQL.GBoolExp where @@ -40,6 +40,12 @@ data AnnValOpExpG a | ASIMILAR !a -- similar, regex | ANSIMILAR !a-- not similar, regex + | AContains !a + | AContainedIn !a + | AHasKey !a + | AHasKeysAny [Text] + | AHasKeysAll [Text] + | ANISNULL -- IS NULL | ANISNOTNULL -- IS NOT NULL @@ -434,23 +440,31 @@ mkBoolExpBuilder => (a -> m S.SQLExp) -> BoolExpBuilder m a mkBoolExpBuilder rhsBldr lhs = \case - AEQ val -> mkSimpleBoolExpBuilder equalsBoolExpBuilder val - ANE val -> mkSimpleBoolExpBuilder notEqualsBoolExpBuilder val - AIN vals -> mkInOrNotBoolExpBuilder True vals - ANIN vals -> mkInOrNotBoolExpBuilder False vals - AGT val -> mkSimpleBoolExpBuilder (S.BECompare S.SGT) val - ALT val -> mkSimpleBoolExpBuilder (S.BECompare S.SLT) val - AGTE val -> mkSimpleBoolExpBuilder (S.BECompare S.SGTE) val - ALTE val -> mkSimpleBoolExpBuilder (S.BECompare S.SLTE) val - ALIKE val -> mkSimpleBoolExpBuilder (S.BECompare S.SLIKE) val - ANLIKE val -> mkSimpleBoolExpBuilder (S.BECompare S.SNLIKE) val - AILIKE val -> mkSimpleBoolExpBuilder (S.BECompare S.SILIKE) val - ANILIKE val -> mkSimpleBoolExpBuilder (S.BECompare S.SNILIKE) val - ASIMILAR val -> mkSimpleBoolExpBuilder (S.BECompare S.SSIMILAR) val - ANSIMILAR val -> mkSimpleBoolExpBuilder (S.BECompare S.SNSIMILAR) val - ANISNULL -> return $ S.BENull lhs - ANISNOTNULL -> return $ S.BENotNull lhs + AEQ val -> mkSimpleBoolExpBuilder equalsBoolExpBuilder val + ANE val -> mkSimpleBoolExpBuilder notEqualsBoolExpBuilder val + AIN vals -> mkInOrNotBoolExpBuilder True vals + ANIN vals -> mkInOrNotBoolExpBuilder False vals + AGT val -> mkSimpleBoolExpBuilder (S.BECompare S.SGT) val + ALT val -> mkSimpleBoolExpBuilder (S.BECompare S.SLT) val + AGTE val -> mkSimpleBoolExpBuilder (S.BECompare S.SGTE) val + ALTE val -> mkSimpleBoolExpBuilder (S.BECompare S.SLTE) val + ALIKE val -> mkSimpleBoolExpBuilder (S.BECompare S.SLIKE) val + ANLIKE val -> mkSimpleBoolExpBuilder (S.BECompare S.SNLIKE) val + AILIKE val -> mkSimpleBoolExpBuilder (S.BECompare S.SILIKE) val + ANILIKE val -> mkSimpleBoolExpBuilder (S.BECompare S.SNILIKE) val + ASIMILAR val -> mkSimpleBoolExpBuilder (S.BECompare S.SSIMILAR) val + ANSIMILAR val -> mkSimpleBoolExpBuilder (S.BECompare S.SNSIMILAR) val + AContains val -> mkSimpleBoolExpBuilder (S.BECompare S.SContains) val + AContainedIn val -> mkSimpleBoolExpBuilder (S.BECompare S.SContainedIn) val + AHasKey val -> mkSimpleBoolExpBuilder (S.BECompare S.SHasKey) val + AHasKeysAny keys -> return $ S.BECompare S.SHasKeysAny lhs $ toTextArray keys + AHasKeysAll keys -> return $ S.BECompare S.SHasKeysAll lhs $ toTextArray keys + ANISNULL -> return $ S.BENull lhs + ANISNOTNULL -> return $ S.BENotNull lhs where + toTextArray arr = + S.SETyAnn (S.SEArray $ map (txtEncoder . PGValText) arr) S.textArrType + mkSimpleBoolExpBuilder beF pgColVal = beF lhs <$> rhsBldr pgColVal diff --git a/server/src-lib/Hasura/SQL/DML.hs b/server/src-lib/Hasura/SQL/DML.hs index c6c503b58ca08..aef43d2c2e795 100644 --- a/server/src-lib/Hasura/SQL/DML.hs +++ b/server/src-lib/Hasura/SQL/DML.hs @@ -449,37 +449,49 @@ instance ToSQL BinOp where toSQL AndOp = BB.string7 "AND" toSQL OrOp = BB.string7 "OR" -data CompareOp = SEQ - | SGT - | SLT - | SIN - | SNE - | SLIKE - | SNLIKE - | SILIKE - | SNILIKE - | SSIMILAR - | SNSIMILAR - | SGTE - | SLTE - | SNIN - deriving (Eq) +data CompareOp + = SEQ + | SGT + | SLT + | SIN + | SNE + | SLIKE + | SNLIKE + | SILIKE + | SNILIKE + | SSIMILAR + | SNSIMILAR + | SGTE + | SLTE + | SNIN + | SContains + | SContainedIn + | SHasKey + | SHasKeysAny + | SHasKeysAll + deriving (Eq) instance Show CompareOp where - show SEQ = "=" - show SGT = ">" - show SLT = "<" - show SIN = "IN" - show SNE = "<>" - show SGTE = ">=" - show SLTE = "<=" - show SNIN = "NOT IN" - show SLIKE = "LIKE" - show SNLIKE = "NOT LIKE" - show SILIKE = "ILIKE" - show SNILIKE = "NOT ILIKE" - show SSIMILAR = "SIMILAR TO" - show SNSIMILAR = "NOT SIMILAR TO" + show = \case + SEQ -> "=" + SGT -> ">" + SLT -> "<" + SIN -> "IN" + SNE -> "<>" + SGTE -> ">=" + SLTE -> "<=" + SNIN -> "NOT IN" + SLIKE -> "LIKE" + SNLIKE -> "NOT LIKE" + SILIKE -> "ILIKE" + SNILIKE -> "NOT ILIKE" + SSIMILAR -> "SIMILAR TO" + SNSIMILAR -> "NOT SIMILAR TO" + SContains -> "@>" + SContainedIn -> "<@" + SHasKey -> "?" + SHasKeysAny -> "?|" + SHasKeysAll -> "?&" instance ToSQL CompareOp where toSQL = BB.string7 . show @@ -635,4 +647,3 @@ instance ToSQL SelectWith where "WITH " <> (", " <+> map f ctes) <-> toSQL sel where f (Alias al, q) = toSQL al <-> "AS" <-> paren (toSQL q) -