BigQuery operators: like/nlike, geography

PR-URL: https://github.com/hasura/graphql-engine-mono/pull/2924
GitOrigin-RevId: 3748411cb4419a21a58283836adb5cb39d351d19
This commit is contained in:
Kirill Zaborsky 2022-01-18 16:33:27 +03:00 committed by hasura-bot
parent 69ea0e7026
commit 47b9321ba4
23 changed files with 504 additions and 19 deletions

View File

@ -67,6 +67,7 @@
- cli: fix cli-console failing to add migrations if there are tabs in SQL body (#7362) - cli: fix cli-console failing to add migrations if there are tabs in SQL body (#7362)
- cli: sign windows binary of Hasura CLI (#7147) - cli: sign windows binary of Hasura CLI (#7147)
- cli: core CLI features are not blocked in environments without internet (#7695) - cli: core CLI features are not blocked in environments without internet (#7695)
- server: add `_like`/`_nlike` and spatial operators for BigQuery
## v2.1.0-beta.2 ## v2.1.0-beta.2

View File

@ -1544,8 +1544,9 @@ fromOpExpG expression op =
Ir.AGTE val -> pure (OpExpression MoreOrEqualOp expression val) Ir.AGTE val -> pure (OpExpression MoreOrEqualOp expression val)
Ir.ALTE val -> pure (OpExpression LessOrEqualOp expression val) Ir.ALTE val -> pure (OpExpression LessOrEqualOp expression val)
Ir.ACast _casts -> refute (pure (UnsupportedOpExpG op)) -- mkCastsExp casts Ir.ACast _casts -> refute (pure (UnsupportedOpExpG op)) -- mkCastsExp casts
Ir.ALIKE _val -> refute (pure (UnsupportedOpExpG op)) -- S.BECompare S.SLIKE lhs val Ir.ALIKE val -> pure (OpExpression LikeOp expression val)
Ir.ANLIKE _val -> refute (pure (UnsupportedOpExpG op)) -- S.BECompare S.SNLIKE lhs val Ir.ANLIKE val -> pure (OpExpression NotLikeOp expression val)
Ir.ABackendSpecific op' -> pure (fromBackendSpecificOpExpG expression op')
Ir.CEQ _rhsCol -> refute (pure (UnsupportedOpExpG op)) -- S.BECompare S.SEQ lhs $ mkQCol rhsCol Ir.CEQ _rhsCol -> refute (pure (UnsupportedOpExpG op)) -- S.BECompare S.SEQ lhs $ mkQCol rhsCol
Ir.CNE _rhsCol -> refute (pure (UnsupportedOpExpG op)) -- S.BECompare S.SNE lhs $ mkQCol rhsCol Ir.CNE _rhsCol -> refute (pure (UnsupportedOpExpG op)) -- S.BECompare S.SNE lhs $ mkQCol rhsCol
Ir.CGT _rhsCol -> refute (pure (UnsupportedOpExpG op)) -- S.BECompare S.SGT lhs $ mkQCol rhsCol Ir.CGT _rhsCol -> refute (pure (UnsupportedOpExpG op)) -- S.BECompare S.SGT lhs $ mkQCol rhsCol
@ -1554,6 +1555,18 @@ fromOpExpG expression op =
Ir.CLTE _rhsCol -> refute (pure (UnsupportedOpExpG op)) -- S.BECompare S.SLTE lhs $ mkQCol rhsCol Ir.CLTE _rhsCol -> refute (pure (UnsupportedOpExpG op)) -- S.BECompare S.SLTE lhs $ mkQCol rhsCol
-- These are new as of 2021-02-18 to this API. Not sure what to do with them at present, marking as unsupported. -- These are new as of 2021-02-18 to this API. Not sure what to do with them at present, marking as unsupported.
fromBackendSpecificOpExpG :: Expression -> BigQuery.BooleanOperators Expression -> Expression
fromBackendSpecificOpExpG expression op =
let func name val = FunctionExpression name [expression, val]
in case op of
BigQuery.ASTContains v -> func "ST_CONTAINS" v
BigQuery.ASTEquals v -> func "ST_EQUALS" v
BigQuery.ASTTouches v -> func "ST_TOUCHES" v
BigQuery.ASTWithin v -> func "ST_WITHIN" v
BigQuery.ASTIntersects v -> func "ST_INTERSECTS" v
BigQuery.ASTDWithin (Ir.DWithinGeogOp r v sph) ->
FunctionExpression "ST_DWITHIN" [expression, v, r, sph]
nullableBoolEquality :: Expression -> Expression -> Expression nullableBoolEquality :: Expression -> Expression -> Expression
nullableBoolEquality x y = nullableBoolEquality x y =
OrExpression OrExpression

View File

@ -157,7 +157,7 @@ bqColumnParser columnType (G.Nullability isNullable) =
ColumnScalar scalarType -> case scalarType of ColumnScalar scalarType -> case scalarType of
-- bytestrings -- bytestrings
-- we only accept string literals -- we only accept string literals
BigQuery.BytesScalarType -> pure $ possiblyNullable scalarType $ BigQuery.StringValue <$> P.string BigQuery.BytesScalarType -> pure $ possiblyNullable scalarType $ BigQuery.StringValue <$> stringBased $$(G.litName "Bytes")
-- text -- text
BigQuery.StringScalarType -> pure $ possiblyNullable scalarType $ BigQuery.StringValue <$> P.string BigQuery.StringScalarType -> pure $ possiblyNullable scalarType $ BigQuery.StringValue <$> P.string
-- floating point values -- floating point values
@ -171,12 +171,13 @@ bqColumnParser columnType (G.Nullability isNullable) =
BigQuery.BigDecimalScalarType -> pure $ possiblyNullable scalarType $ BigQuery.BigDecimalValue . BigQuery.doubleToBigDecimal <$> P.float BigQuery.BigDecimalScalarType -> pure $ possiblyNullable scalarType $ BigQuery.BigDecimalValue . BigQuery.doubleToBigDecimal <$> P.float
-- boolean type -- boolean type
BigQuery.BoolScalarType -> pure $ possiblyNullable scalarType $ BigQuery.BoolValue <$> P.boolean BigQuery.BoolScalarType -> pure $ possiblyNullable scalarType $ BigQuery.BoolValue <$> P.boolean
BigQuery.DateScalarType -> pure $ possiblyNullable scalarType $ BigQuery.DateValue . BigQuery.Date <$> P.string BigQuery.DateScalarType -> pure $ possiblyNullable scalarType $ BigQuery.DateValue . BigQuery.Date <$> stringBased $$(G.litName "Date")
BigQuery.TimeScalarType -> pure $ possiblyNullable scalarType $ BigQuery.TimeValue . BigQuery.Time <$> P.string BigQuery.TimeScalarType -> pure $ possiblyNullable scalarType $ BigQuery.TimeValue . BigQuery.Time <$> stringBased $$(G.litName "Time")
BigQuery.DatetimeScalarType -> pure $ possiblyNullable scalarType $ BigQuery.DatetimeValue . BigQuery.Datetime <$> P.string BigQuery.DatetimeScalarType -> pure $ possiblyNullable scalarType $ BigQuery.DatetimeValue . BigQuery.Datetime <$> stringBased $$(G.litName "Datetime")
BigQuery.GeographyScalarType -> pure $ possiblyNullable scalarType $ BigQuery.GeographyValue . BigQuery.Geography <$> P.string BigQuery.GeographyScalarType ->
pure $ possiblyNullable scalarType $ BigQuery.GeographyValue . BigQuery.Geography <$> throughJSON $$(G.litName "Geography")
BigQuery.TimestampScalarType -> do BigQuery.TimestampScalarType -> do
let schemaType = P.TNamed P.Nullable $ P.Definition stringScalar Nothing P.TIScalar let schemaType = P.TNamed P.Nullable $ P.Definition $$(G.litName "Timestamp") Nothing P.TIScalar
pure $ pure $
possiblyNullable scalarType $ possiblyNullable scalarType $
Parser Parser
@ -212,6 +213,9 @@ bqColumnParser columnType (G.Nullability isNullable) =
valueToJSON (P.toGraphQLType schemaType) valueToJSON (P.toGraphQLType schemaType)
>=> either (parseErrorWith ParseFailed . qeError) pure . runAesonParser J.parseJSON >=> either (parseErrorWith ParseFailed . qeError) pure . runAesonParser J.parseJSON
} }
stringBased :: MonadParse m => G.Name -> Parser 'Both m Text
stringBased scalarName =
P.string {pType = P.TNamed P.NonNullable $ P.Definition scalarName Nothing P.TIScalar}
bqJsonPathArg :: bqJsonPathArg ::
MonadParse n => MonadParse n =>
@ -255,6 +259,7 @@ bqComparisonExps ::
m (Parser 'Input n [ComparisonExp 'BigQuery]) m (Parser 'Input n [ComparisonExp 'BigQuery])
bqComparisonExps = P.memoize 'comparisonExps $ \columnType -> do bqComparisonExps = P.memoize 'comparisonExps $ \columnType -> do
collapseIfNull <- asks $ qcDangerousBooleanCollapse . getter collapseIfNull <- asks $ qcDangerousBooleanCollapse . getter
dWithinGeogOpParser <- geographyWithinDistanceInput
-- see Note [Columns in comparison expression are never nullable] -- see Note [Columns in comparison expression are never nullable]
typedParser <- columnParser columnType (G.Nullability False) typedParser <- columnParser columnType (G.Nullability False)
nullableTextParser <- columnParser (ColumnScalar @'BigQuery BigQuery.StringScalarType) (G.Nullability True) nullableTextParser <- columnParser (ColumnScalar @'BigQuery BigQuery.StringScalarType) (G.Nullability True)
@ -275,15 +280,93 @@ bqComparisonExps = P.memoize 'comparisonExps $ \columnType -> do
fmap catMaybes $ fmap catMaybes $
sequenceA $ sequenceA $
concat concat
[ equalityOperators [ -- from https://cloud.google.com/bigquery/docs/reference/standard-sql/data-types:
collapseIfNull -- GEOGRAPHY comparisons are not supported. To compare GEOGRAPHY values, use ST_Equals.
(mkParameter <$> typedParser) guard (isScalarColumnWhere (/= BigQuery.GeographyScalarType) columnType)
(mkListLiteral <$> columnListParser), *> equalityOperators
comparisonOperators collapseIfNull
collapseIfNull (mkParameter <$> typedParser)
(mkParameter <$> typedParser) (mkListLiteral <$> columnListParser),
guard (isScalarColumnWhere (/= BigQuery.GeographyScalarType) columnType)
*> comparisonOperators
collapseIfNull
(mkParameter <$> typedParser),
-- Ops for String type
guard (isScalarColumnWhere (== BigQuery.StringScalarType) columnType)
*> [ mkBoolOperator
collapseIfNull
$$(G.litName "_like")
(Just "does the column match the given pattern")
(ALIKE . mkParameter <$> typedParser),
mkBoolOperator
collapseIfNull
$$(G.litName "_nlike")
(Just "does the column NOT match the given pattern")
(ANLIKE . mkParameter <$> typedParser)
],
-- Ops for Bytes type
guard (isScalarColumnWhere (== BigQuery.BytesScalarType) columnType)
*> [ mkBoolOperator
collapseIfNull
$$(G.litName "_like")
(Just "does the column match the given pattern")
(ALIKE . mkParameter <$> typedParser),
mkBoolOperator
collapseIfNull
$$(G.litName "_nlike")
(Just "does the column NOT match the given pattern")
(ANLIKE . mkParameter <$> typedParser)
],
-- Ops for Geography type
guard (isScalarColumnWhere (== BigQuery.GeographyScalarType) columnType)
*> [ mkBoolOperator
collapseIfNull
$$(G.litName "_st_contains")
(Just "does the column contain the given geography value")
(ABackendSpecific . BigQuery.ASTContains . mkParameter <$> typedParser),
mkBoolOperator
collapseIfNull
$$(G.litName "_st_equals")
(Just "is the column equal to given geography value (directionality is ignored)")
(ABackendSpecific . BigQuery.ASTEquals . mkParameter <$> typedParser),
mkBoolOperator
collapseIfNull
$$(G.litName "_st_touches")
(Just "does the column have at least one point in common with the given geography value")
(ABackendSpecific . BigQuery.ASTTouches . mkParameter <$> typedParser),
mkBoolOperator
collapseIfNull
$$(G.litName "_st_within")
(Just "is the column contained in the given geography value")
(ABackendSpecific . BigQuery.ASTWithin . mkParameter <$> typedParser),
mkBoolOperator
collapseIfNull
$$(G.litName "_st_intersects")
(Just "does the column spatially intersect the given geography value")
(ABackendSpecific . BigQuery.ASTIntersects . mkParameter <$> typedParser),
mkBoolOperator
collapseIfNull
$$(G.litName "_st_d_within")
(Just "is the column within a given distance from the given geometry value")
(ABackendSpecific . BigQuery.ASTDWithin <$> dWithinGeogOpParser)
]
] ]
geographyWithinDistanceInput ::
forall m n r.
(MonadSchema n m, MonadError QErr m, MonadReader r m, Has MkTypename r) =>
m (Parser 'Input n (DWithinGeogOp (UnpreparedValue 'BigQuery)))
geographyWithinDistanceInput = do
geographyParser <- columnParser (ColumnScalar BigQuery.GeographyScalarType) (G.Nullability False)
-- practically BigQuery (as of 2021-11-19) doesn't support TRUE as use_spheroid parameter for ST_DWITHIN
booleanParser <- columnParser (ColumnScalar BigQuery.BoolScalarType) (G.Nullability True)
floatParser <- columnParser (ColumnScalar BigQuery.FloatScalarType) (G.Nullability False)
pure $
P.object $$(G.litName "st_dwithin_input") Nothing $
DWithinGeogOp <$> (mkParameter <$> P.field $$(G.litName "distance") Nothing floatParser)
<*> (mkParameter <$> P.field $$(G.litName "from") Nothing geographyParser)
<*> (mkParameter <$> P.fieldWithDefault $$(G.litName "use_spheroid") Nothing (G.VBoolean False) booleanParser)
bqMkCountType :: bqMkCountType ::
-- | distinct values -- | distinct values
Maybe Bool -> Maybe Bool ->

View File

@ -28,7 +28,7 @@ instance Backend 'BigQuery where
type ScalarType 'BigQuery = BigQuery.ScalarType type ScalarType 'BigQuery = BigQuery.ScalarType
type SQLExpression 'BigQuery = BigQuery.Expression type SQLExpression 'BigQuery = BigQuery.Expression
type SQLOperator 'BigQuery = BigQuery.Op type SQLOperator 'BigQuery = BigQuery.Op
type BooleanOperators 'BigQuery = Const Void type BooleanOperators 'BigQuery = BigQuery.BooleanOperators
type XComputedField 'BigQuery = XDisable type XComputedField 'BigQuery = XDisable
type XRelay 'BigQuery = XDisable type XRelay 'BigQuery = XDisable

View File

@ -105,13 +105,15 @@ fromExpression =
"(" <+> fromExpression x <+> ") != (" <+> fromExpression y <+> ")" "(" <+> fromExpression x <+> ") != (" <+> fromExpression y <+> ")"
ToStringExpression e -> "CONCAT(" <+> fromExpression e <+> ", '')" ToStringExpression e -> "CONCAT(" <+> fromExpression e <+> ", '')"
SelectExpression s -> "(" <+> IndentPrinter 1 (fromSelect s) <+> ")" SelectExpression s -> "(" <+> IndentPrinter 1 (fromSelect s) <+> ")"
ListExpression xs -> " UNNEST ([" <+> (SepByPrinter ", " $ fromExpression <$> xs) <+> "])" ListExpression xs -> " UNNEST ([" <+> SepByPrinter ", " (fromExpression <$> xs) <+> "])"
OpExpression op x y -> OpExpression op x y ->
"(" "("
<+> fromExpression x <+> fromExpression x
<+> ") " <+> ") "
<+> fromOp op <+> fromOp op
<+> fromExpression y <+> fromExpression y
FunctionExpression name args ->
UnsafeTextPrinter name <+> "(" <+> SepByPrinter ", " (fromExpression <$> args) <+> ")"
ConditionalProjection expression fieldName -> ConditionalProjection expression fieldName ->
"(CASE WHEN(" <+> fromExpression expression "(CASE WHEN(" <+> fromExpression expression
<+> ") THEN " <+> ") THEN "
@ -144,6 +146,8 @@ fromOp =
LessOrEqualOp -> "<=" LessOrEqualOp -> "<="
InOp -> "IN" InOp -> "IN"
NotInOp -> "NOT IN" NotInOp -> "NOT IN"
LikeOp -> "LIKE"
NotLikeOp -> "NOT LIKE"
fromPath :: JsonPath -> Printer fromPath :: JsonPath -> Printer
fromPath path = fromPath path =

View File

@ -7,6 +7,7 @@ module Hasura.Backends.BigQuery.Types
ArrayAgg (..), ArrayAgg (..),
Base64, Base64,
BigDecimal, BigDecimal,
BooleanOperators (..),
Cardinality (..), Cardinality (..),
ColumnName (ColumnName), ColumnName (ColumnName),
Countable (..), Countable (..),
@ -65,6 +66,7 @@ where
import Data.Aeson (FromJSON, FromJSONKey, ToJSON, ToJSONKey) import Data.Aeson (FromJSON, FromJSONKey, ToJSON, ToJSONKey)
import Data.Aeson qualified as J import Data.Aeson qualified as J
import Data.Aeson.Extended qualified as J
import Data.Aeson.Types qualified as J import Data.Aeson.Types qualified as J
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
import Data.ByteString.Base64 qualified as Base64 import Data.ByteString.Base64 qualified as Base64
@ -83,6 +85,7 @@ import Data.Vector.Instances ()
import Hasura.Base.Error import Hasura.Base.Error
import Hasura.Incremental.Internal.Dependency import Hasura.Incremental.Internal.Dependency
import Hasura.Prelude import Hasura.Prelude
import Hasura.RQL.IR.BoolExp
import Hasura.RQL.Types.Common qualified as RQL import Hasura.RQL.Types.Common qualified as RQL
import Language.GraphQL.Draft.Syntax qualified as G import Language.GraphQL.Draft.Syntax qualified as G
import Language.Haskell.TH.Syntax import Language.Haskell.TH.Syntax
@ -402,6 +405,7 @@ data Expression
| OpExpression Op Expression Expression | OpExpression Op Expression Expression
| ListExpression [Expression] | ListExpression [Expression]
| CastExpression Expression ScalarType | CastExpression Expression ScalarType
| FunctionExpression !Text [Expression]
| ConditionalProjection Expression FieldName | ConditionalProjection Expression FieldName
deriving (Eq, Ord, Show, Generic, Data, Lift) deriving (Eq, Ord, Show, Generic, Data, Lift)
@ -611,9 +615,9 @@ data Op
| MoreOrEqualOp | MoreOrEqualOp
| InOp | InOp
| NotInOp | NotInOp
| LikeOp
| NotLikeOp
-- | SNE -- | SNE
-- | SLIKE
-- | SNLIKE
-- | SILIKE -- | SILIKE
-- | SNILIKE -- | SNILIKE
-- | SSIMILAR -- | SSIMILAR
@ -858,6 +862,30 @@ data UnifiedOn = UnifiedOn
newtype FunctionName = FunctionName Text -- TODO: Improve this type when SQL function support added newtype FunctionName = FunctionName Text -- TODO: Improve this type when SQL function support added
deriving (FromJSON, ToJSON, ToJSONKey, ToTxt, Show, Eq, Ord, Hashable, Cacheable, NFData) deriving (FromJSON, ToJSON, ToJSONKey, ToTxt, Show, Eq, Ord, Hashable, Cacheable, NFData)
data BooleanOperators a
= ASTContains !a
| ASTEquals !a
| ASTTouches !a
| ASTWithin !a
| ASTIntersects !a
| ASTDWithin !(DWithinGeogOp a)
deriving stock (Eq, Generic, Foldable, Functor, Traversable, Show)
instance NFData a => NFData (BooleanOperators a)
instance Hashable a => Hashable (BooleanOperators a)
instance Cacheable a => Cacheable (BooleanOperators a)
instance ToJSON a => J.ToJSONKeyValue (BooleanOperators a) where
toJSONKeyValue = \case
ASTContains a -> ("_st_contains", J.toJSON a)
ASTEquals a -> ("_st_equals", J.toJSON a)
ASTIntersects a -> ("_st_intersects", J.toJSON a)
ASTTouches a -> ("_st_touches", J.toJSON a)
ASTWithin a -> ("_st_within", J.toJSON a)
ASTDWithin a -> ("_st_dwithin", J.toJSON a)
-------------------------------------------------------------------------------- --------------------------------------------------------------------------------
-- Backend-related stuff -- Backend-related stuff
-- --

View File

@ -0,0 +1,18 @@
type: bulk
args:
- type: bigquery_run_sql
args:
source: bigquery
sql: |
DROP TABLE IF EXISTS `hasura_test.city`;
CREATE TABLE `hasura_test.city` (
`name` STRING,
`country` STRING
);
INSERT INTO `hasura_test.city` (`name`, `country`)
VALUES
('Durham', 'USA'),
('New York', 'USA'),
('Framlingham', 'UK'),
('New Orleans', 'USA');

View File

@ -0,0 +1,21 @@
description: Select cities ending with ham
url: /v1/graphql
status: 200
response:
data:
hasura_test_city:
- name: Durham
country: USA
- name: Framlingham
country: UK
query:
query: |
query {
hasura_test_city (
where: {name: {_like: "%ham" }},
order_by: {name: asc}
) {
name
country
}
}

View File

@ -0,0 +1,21 @@
description: Select cities not ending with ham
url: /v1/graphql
status: 200
response:
data:
hasura_test_city:
- name: New Orleans
country: USA
- name: New York
country: USA
query:
query: |
query {
hasura_test_city (
where: {name: {_nlike: "%ham" }},
order_by: {name: asc}
) {
name
country
}
}

View File

@ -0,0 +1,10 @@
type: bulk
args:
#City table
- type: bigquery_track_table
args:
source: bigquery
table:
dataset: hasura_test
name: city

View File

@ -0,0 +1,10 @@
type: bulk
args:
- type: bigquery_untrack_table
args:
source: bigquery
table:
dataset: hasura_test
name: city
cascade: true

View File

@ -0,0 +1,45 @@
type: bulk
args:
- type: bigquery_run_sql
args:
source: bigquery
sql: |
DROP TABLE IF EXISTS `hasura_test.spatial_types_geog`;
CREATE TABLE `hasura_test.spatial_types_geog` (
`point` GEOGRAPHY,
`linestring` GEOGRAPHY,
`polygon` GEOGRAPHY,
`multipoint` GEOGRAPHY,
`multilinestring` GEOGRAPHY,
`multipolygon` GEOGRAPHY,
`geometrycollection` GEOGRAPHY
);
INSERT INTO `hasura_test.spatial_types_geog` (
`point`,
`linestring`,
`polygon`,
`multipoint`,
`multilinestring`,
`multipolygon`,
`geometrycollection`
)
VALUES (
st_geogfromtext('POINT(3 4)'),
st_geogfromtext('LINESTRING(1 1,2 3,4 8, -6 3)'),
st_geogfromtext('POLYGON((1 1, 1 2, 2 1, 1 1))'),
st_geogfromtext('MULTIPOINT((2 3), (7 8))'),
st_geogfromtext(
'MULTILINESTRING((1 1, 3 3, 5 5),(3 3, 5 5, 7 7))'
),
st_geogfromtext(
-- for some odd reason this multipolygon from 3 polygons goes through a very odd conversion in BigQuery
-- st_geogfromtext('MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)),((2 2, 2 -2, -2 -2, -2 2, 2 2)),((1 1, 3 1, 3 3, 1 3, 1 1)))')
-- ->
-- MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)), ((-2 2, -2 -2, 2 -2, 2 1.00015229710421, 3 1, 3 3, 1 3, 1 2.00091355021717, -2 2)))
'MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)),((2 2, 2 -2, -2 -2, -2 2, 2 2)))'
),
st_geogfromtext(
'GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5),POLYGON((-1 -1, -1 -5, -5 -5, -5 -1, -1 -1)))'
)
);

View File

@ -0,0 +1,30 @@
description: GraphQL query to test different data types of SQL Server
url: /v1/graphql
status: 200
response:
data:
hasura_test_spatial_types_geog:
- point: POINT(3 4)
linestring: LINESTRING(1 1, 2 3, 4 8, -6 3)
polygon: POLYGON((2 1, 1 2, 1 1, 2 1))
multipoint: MULTIPOINT(2 3, 7 8)
multilinestring:
LINESTRING(1 1, 3 3, 5 5, 7 7)
multipolygon:
MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)), ((-2 2, -2 -2, 2 -2, 2 2, -2 2)))
geometrycollection:
GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5), POLYGON((-5 -1, -5 -5, -1 -5, -1 -1, -5 -1)))
query:
query: |
query {
hasura_test_spatial_types_geog {
point
linestring
polygon
multipoint
multilinestring
multipolygon
geometrycollection
}
}

View File

@ -0,0 +1,21 @@
description: Query data from spatial_types_geo using _st_contains
url: /v1/graphql
status: 200
response:
data:
hasura_test_spatial_types_geog:
- multipolygon:
MULTIPOLYGON(((-120.533 46.566, -118.283 46.1, -122.3 47.45, -120.533 46.566)), ((-2 2, -2 -2, 2 -2, 2 2, -2 2)))
query:
query: |
query {
hasura_test_spatial_types_geog(
where: {
multipolygon: {
_st_contains: "POINT(0.5 0)"
}
}
) {
multipolygon
}
}

View File

@ -0,0 +1,20 @@
description: Query data from spatial_types_geog using _st_d_within
url: /v1/graphql
status: 200
response:
data:
hasura_test_spatial_types_geog:
- multipoint: MULTIPOINT(2 3, 7 8)
query:
query: |
query {
hasura_test_spatial_types_geog(
where: {
multipoint: {
_st_d_within: { distance: 1, from: "POLYGON ((0 0, 10 10, 10 10, 10 0, 0 0))"}
}
}
) {
multipoint
}
}

View File

@ -0,0 +1,20 @@
description: Query data from spatial_types_geog using _st_d_within
url: /v1/graphql
status: 200
response:
data:
hasura_test_spatial_types_geog:
- multipoint: MULTIPOINT(2 3, 7 8)
query:
query: |
query {
hasura_test_spatial_types_geog(
where: {
multipoint: {
_st_d_within: { distance: 1, from: "POLYGON ((0 0, 10 10, 10 10, 10 0, 0 0))"}
}
}
) {
multipoint
}
}

View File

@ -0,0 +1,24 @@
description: Query data from spatial_types_geog using _st_equals
url: /v1/graphql
status: 200
response:
data:
hasura_test_spatial_types_geog:
- geometrycollection:
GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5), POLYGON((-5 -1, -5 -5, -1 -5, -1 -1, -5 -1)))
query:
# note: using GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5),POLYGON((-1 -1, -1 -5, -5 -5, -5 -1, -1 -1)))
# doesn't work with st_equals in BigQuery even when the docs say that the function should work with
# any point order (probably the problem is in handling GEOGRAPHY params)
query: |
query {
hasura_test_spatial_types_geog(
where: {
geometrycollection: {
_st_equals: "GEOMETRYCOLLECTION(LINESTRING(1 1, 3 5), POLYGON((-5 -1, -5 -5, -1 -5, -1 -1, -5 -1)))"
}
}
) {
geometrycollection
}
}

View File

@ -0,0 +1,14 @@
description: Query data from spatial_types_geog using _st_intersects
url: /v1/graphql
status: 200
response:
data:
hasura_test_spatial_types_geog:
- polygon: POLYGON((2 1, 1 2, 1 1, 2 1))
query:
query: |
query {
hasura_test_spatial_types_geog(where: { polygon: { _st_intersects: "LINESTRING(0 0, 2 2)" } }) {
polygon
}
}

View File

@ -0,0 +1,20 @@
description: Query data from spatial_types_geog using _st_touches
url: /v1/graphql
status: 200
response:
data:
hasura_test_spatial_types_geog:
- point: POINT(3 4)
query:
query: |
query {
hasura_test_spatial_types_geog(
where: {
point: {
_st_touches: "POLYGON ((3 4, 2 5, 5 5, 5 2, 3 4))"
}
}
) {
point
}
}

View File

@ -0,0 +1,20 @@
description: Query data from spatial_types_geog using _st_within
url: /v1/graphql
status: 200
response:
data:
hasura_test_spatial_types_geog:
- multipoint: MULTIPOINT(2 3, 7 8)
query:
query: |
query {
hasura_test_spatial_types_geog(
where: {
multipoint: {
_st_within: "POLYGON ((0 0, 10 10, 10 10, 10 0, 0 0))"
}
}
) {
multipoint
}
}

View File

@ -0,0 +1,9 @@
type: bulk
args:
- type: bigquery_track_table
args:
source: bigquery
table:
dataset: hasura_test
name: spatial_types_geog

View File

@ -0,0 +1,10 @@
type: bulk
args:
- type: bigquery_untrack_table
args:
source: bigquery
table:
dataset: hasura_test
name: spatial_types_geog
cascade: true

View File

@ -223,6 +223,20 @@ class TestGraphQLQueryBasicBigquery:
def dir(cls): def dir(cls):
return 'queries/graphql_query/bigquery' return 'queries/graphql_query/bigquery'
@pytest.mark.parametrize("transport", ['http', 'websocket'])
@pytest.mark.parametrize("backend", ['bigquery'])
@usefixtures('per_class_tests_db_state')
class TestGraphQLQueryBoolExpSearchBigquery:
def test_city_where_like(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_city_where_like_bigquery.yaml', transport)
def test_city_where_not_like(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_city_where_nlike_bigquery.yaml', transport)
@classmethod
def dir(cls):
return 'queries/graphql_query/boolexp/search'
@pytest.mark.parametrize("transport", ['http', 'websocket']) @pytest.mark.parametrize("transport", ['http', 'websocket'])
@pytest.mark.parametrize("backend", ['citus', 'mssql', 'postgres']) @pytest.mark.parametrize("backend", ['citus', 'mssql', 'postgres'])
@ -1537,3 +1551,32 @@ class TestGraphQLQueryBoolExpSpatialMSSQL:
@classmethod @classmethod
def dir(cls): def dir(cls):
return 'queries/graphql_query/boolexp/spatial' return 'queries/graphql_query/boolexp/spatial'
@pytest.mark.parametrize("transport", ['http', 'websocket'])
@pytest.mark.parametrize("backend", ['bigquery'])
@usefixtures('per_class_tests_db_state')
class TestGraphQLQueryBoolExpSpatialBigquery:
def test_select_spatial_bigquery_types(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_bigquery.yaml', transport)
def test_select_spatial_bigquery_types_where_st_equals(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_where_st_equals_bigquery.yaml', transport)
def test_select_spatial_bigquery_types_where_st_contains(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_where_st_contains_bigquery.yaml', transport)
def test_select_spatial_bigquery_types_where_st_intersects(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_where_st_intersects_bigquery.yaml', transport)
def test_select_spatial_bigquery_types_where_st_within(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_where_st_within_bigquery.yaml', transport)
def test_select_spatial_bigquery_types_where_st_d_within(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_where_st_d_within_bigquery.yaml', transport)
def test_select_spatial_bigquery_types_where_st_touches(self, hge_ctx, transport):
check_query_f(hge_ctx, self.dir() + '/select_query_spatial_types_where_st_touches_bigquery.yaml', transport)
@classmethod
def dir(cls):
return 'queries/graphql_query/boolexp/spatial'