diff --git a/graphql/schema/dgraph_schemagen_test.yml b/graphql/schema/dgraph_schemagen_test.yml index 2eb40116b60..ca9f8b685c7 100644 --- a/graphql/schema/dgraph_schemagen_test.yml +++ b/graphql/schema/dgraph_schemagen_test.yml @@ -606,4 +606,19 @@ schemas: url: "http://mock:8888/users", method: "POST" }) - } \ No newline at end of file + } + + - name: "geo type." + input: | + type Hotel { + id: ID! + name: String! + location: Point @search + } + output: | + type Hotel { + Hotel.name + Hotel.location + } + Hotel.name: string . + Hotel.location: geo @index(geo) . diff --git a/graphql/schema/gqlschema.go b/graphql/schema/gqlschema.go index 2e262cf2b47..02c95b9fe5c 100644 --- a/graphql/schema/gqlschema.go +++ b/graphql/schema/gqlschema.go @@ -89,6 +89,7 @@ enum DgraphIndex { month day hour + geo } input AuthRule { @@ -123,6 +124,25 @@ input CustomHTTP { skipIntrospection: Boolean } +type Point { + Latitude: Float! + Longitude: Float! +} + +input PointInput { + Latitude: Float! + Longitude: Float! +} + +input NearFilter { + Distance: Float! + Coordinate: PointInput! +} + +input PointGeoFilter { + near: NearFilter +} + directive @hasInverse(field: String!) on FIELD_DEFINITION directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION @@ -244,6 +264,7 @@ var supportedSearches = map[string]searchTypeIndex{ "month": {"DateTime", "month"}, "day": {"DateTime", "day"}, "hour": {"DateTime", "hour"}, + "geo": {"Point", "geo"}, } // GraphQL scalar type -> default search arg @@ -255,6 +276,7 @@ var defaultSearches = map[string]string{ "Float": "float", "String": "term", "DateTime": "year", + "Point": "geo", } // graphqlSpecScalars holds all the scalar types supported by the graphql spec. @@ -305,6 +327,7 @@ var builtInFilters = map[string]string{ "fulltext": "StringFullTextFilter", "exact": "StringExactFilter", "hash": "StringHashFilter", + "geo": "PointGeoFilter", } // GraphQL scalar -> Dgraph scalar diff --git a/graphql/schema/rules.go b/graphql/schema/rules.go index 598dd47af64..77fed63bd51 100644 --- a/graphql/schema/rules.go +++ b/graphql/schema/rules.go @@ -948,6 +948,13 @@ func validateSearchArg(searchArg string, return nil } +func isGeoType(typ *ast.Type) bool { + if typ.Name() == "Point" { + return true + } + return false +} + func searchValidation( sch *ast.Schema, typ *ast.Definition, @@ -961,7 +968,7 @@ func searchValidation( // If there's no arg, then it can be an enum or has to be a scalar that's // not ID. The schema generation will add the default search // for that type. - if sch.Types[field.Type.Name()].Kind == ast.Enum || + if sch.Types[field.Type.Name()].Kind == ast.Enum || isGeoType(field.Type) || (sch.Types[field.Type.Name()].Kind == ast.Scalar && !isIDField(typ, field)) { return nil } diff --git a/graphql/schema/schemagen.go b/graphql/schema/schemagen.go index 5213a88ab2e..c0774f5b21f 100644 --- a/graphql/schema/schemagen.go +++ b/graphql/schema/schemagen.go @@ -438,7 +438,12 @@ func genDgSchema(gqlSch *ast.Schema, definitions []string) string { var typStr string switch gqlSch.Types[f.Type.Name()].Kind { case ast.Object, ast.Interface: - typStr = fmt.Sprintf("%suid%s", prefix, suffix) + if isGeoType(f.Type) { + typStr = "geo" + dgPreds[fname] = getUpdatedPred(fname, typStr, "", []string{"geo"}) + } else { + typStr = fmt.Sprintf("%suid%s", prefix, suffix) + } if parentInt == nil { if strings.HasPrefix(fname, "~") { diff --git a/graphql/schema/schemagen_test.go b/graphql/schema/schemagen_test.go index 914eae7a69e..bc3693a40e0 100644 --- a/graphql/schema/schemagen_test.go +++ b/graphql/schema/schemagen_test.go @@ -77,6 +77,9 @@ func TestSchemaString(t *testing.T) { for _, testFile := range files { t.Run(testFile.Name(), func(t *testing.T) { inputFileName := inputDir + testFile.Name() + if testFile.Name() != "geo-type.graphql" { + return + } str1, err := ioutil.ReadFile(inputFileName) require.NoError(t, err) diff --git a/graphql/schema/testdata/schemagen/input/geo-type.graphql b/graphql/schema/testdata/schemagen/input/geo-type.graphql new file mode 100644 index 00000000000..43000613526 --- /dev/null +++ b/graphql/schema/testdata/schemagen/input/geo-type.graphql @@ -0,0 +1,5 @@ +type Hotel { + id: ID! + name: String! + location: Point @search +} \ No newline at end of file diff --git a/graphql/schema/testdata/schemagen/output/geo-type.graphql b/graphql/schema/testdata/schemagen/output/geo-type.graphql new file mode 100644 index 00000000000..61716fbe121 --- /dev/null +++ b/graphql/schema/testdata/schemagen/output/geo-type.graphql @@ -0,0 +1,258 @@ +####################### +# Input Schema +####################### + +type Hotel { + id: ID! + name: String! + location: Point @search +} + +####################### +# Extended Definitions +####################### + +""" +The Int64 scalar type represents a signed 64‐bit numeric non‐fractional value. +Int64 can represent values in range [-(2^63),(2^63 - 1)]. +""" +scalar Int64 + +""" +The DateTime scalar type represents date and time as a string in RFC3339 format. +For example: "1985-04-12T23:20:50.52Z" represents 20 minutes and 50.52 seconds after the 23rd hour of April 12th, 1985 in UTC. +""" +scalar DateTime + +enum DgraphIndex { + int + int64 + float + bool + hash + exact + term + fulltext + trigram + regexp + year + month + day + hour + geo +} + +input AuthRule { + and: [AuthRule] + or: [AuthRule] + not: AuthRule + rule: String +} + +enum HTTPMethod { + GET + POST + PUT + PATCH + DELETE +} + +enum Mode { + BATCH + SINGLE +} + +input CustomHTTP { + url: String! + method: HTTPMethod! + body: String + graphql: String + mode: Mode + forwardHeaders: [String!] + secretHeaders: [String!] + introspectionHeaders: [String!] + skipIntrospection: Boolean +} + +type Point { + Latitude: Float! + Longitude: Float! +} + +input PointInput { + Latitude: Float! + Longitude: Float! +} + +input NearFilter { + Distance: Float! + Coordinate: PointInput! +} + +input PointGeoFilter { + near: NearFilter +} + +directive @hasInverse(field: String!) on FIELD_DEFINITION +directive @search(by: [DgraphIndex!]) on FIELD_DEFINITION +directive @dgraph(type: String, pred: String) on OBJECT | INTERFACE | FIELD_DEFINITION +directive @id on FIELD_DEFINITION +directive @withSubscription on OBJECT | INTERFACE +directive @secret(field: String!, pred: String) on OBJECT | INTERFACE +directive @auth( + query: AuthRule, + add: AuthRule, + update: AuthRule, + delete:AuthRule) on OBJECT +directive @custom(http: CustomHTTP, dql: String) on FIELD_DEFINITION +directive @remote on OBJECT | INTERFACE +directive @cascade(fields: [String]) on FIELD + +input IntFilter { + eq: Int + le: Int + lt: Int + ge: Int + gt: Int +} + +input Int64Filter { + eq: Int64 + le: Int64 + lt: Int64 + ge: Int64 + gt: Int64 +} + +input FloatFilter { + eq: Float + le: Float + lt: Float + ge: Float + gt: Float +} + +input DateTimeFilter { + eq: DateTime + le: DateTime + lt: DateTime + ge: DateTime + gt: DateTime +} + +input StringTermFilter { + allofterms: String + anyofterms: String +} + +input StringRegExpFilter { + regexp: String +} + +input StringFullTextFilter { + alloftext: String + anyoftext: String +} + +input StringExactFilter { + eq: String + le: String + lt: String + ge: String + gt: String +} + +input StringHashFilter { + eq: String +} + +####################### +# Generated Types +####################### + +type AddHotelPayload { + hotel(filter: HotelFilter, order: HotelOrder, first: Int, offset: Int): [Hotel] + numUids: Int +} + +type DeleteHotelPayload { + hotel(filter: HotelFilter, order: HotelOrder, first: Int, offset: Int): [Hotel] + msg: String + numUids: Int +} + +type UpdateHotelPayload { + hotel(filter: HotelFilter, order: HotelOrder, first: Int, offset: Int): [Hotel] + numUids: Int +} + +####################### +# Generated Enums +####################### + +enum HotelHasFilter { + name + location +} + +enum HotelOrderable { + name +} + +####################### +# Generated Inputs +####################### + +input AddHotelInput { + name: String! +} + +input HotelFilter { + id: [ID!] + location: PointGeoFilter + has: HotelHasFilter + and: HotelFilter + or: HotelFilter + not: HotelFilter +} + +input HotelOrder { + asc: HotelOrderable + desc: HotelOrderable + then: HotelOrder +} + +input HotelPatch { + name: String +} + +input HotelRef { + id: ID + name: String +} + +input UpdateHotelInput { + filter: HotelFilter! + set: HotelPatch + remove: HotelPatch +} + +####################### +# Generated Query +####################### + +type Query { + getHotel(id: ID!): Hotel + queryHotel(filter: HotelFilter, order: HotelOrder, first: Int, offset: Int): [Hotel] +} + +####################### +# Generated Mutations +####################### + +type Mutation { + addHotel(input: [AddHotelInput!]!): AddHotelPayload + updateHotel(input: UpdateHotelInput!): UpdateHotelPayload + deleteHotel(filter: HotelFilter!): DeleteHotelPayload +} +