Skip to content

Commit

Permalink
add jsonb boolean operators (close #369) (#376)
Browse files Browse the repository at this point in the history
  • Loading branch information
0x777 authored and shahidhk committed Sep 7, 2018
1 parent e905535 commit dd8e09d
Show file tree
Hide file tree
Showing 5 changed files with 141 additions and 68 deletions.
40 changes: 24 additions & 16 deletions server/src-lib/Hasura/GraphQL/Resolve/BoolExp.hs
Expand Up @@ -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 "
Expand Down
10 changes: 10 additions & 0 deletions server/src-lib/Hasura/GraphQL/Resolve/InputValue.hs
Expand Up @@ -16,6 +16,7 @@ module Hasura.GraphQL.Resolve.InputValue
, withArray
, withArrayM
, parseMany
, asPGColText
) where

import Hasura.Prelude
Expand Down Expand Up @@ -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"
30 changes: 30 additions & 0 deletions server/src-lib/Hasura/GraphQL/Schema.hs
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
58 changes: 36 additions & 22 deletions 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

Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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

Expand Down
71 changes: 41 additions & 30 deletions server/src-lib/Hasura/SQL/DML.hs
Expand Up @@ -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
Expand Down Expand Up @@ -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)

0 comments on commit dd8e09d

Please sign in to comment.