Skip to content

Commit

Permalink
Simplified spatial property projection
Browse files Browse the repository at this point in the history
  • Loading branch information
darrellwarde committed Sep 2, 2024
1 parent bef58b5 commit 2e6de36
Show file tree
Hide file tree
Showing 13 changed files with 80 additions and 181 deletions.
5 changes: 5 additions & 0 deletions .changeset/nervous-masks-beam.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@neo4j/graphql": patch
---

Simplify the projection of spatial properties
7 changes: 3 additions & 4 deletions packages/graphql/src/graphql/objects/CartesianPoint.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import { GraphQLFloat, GraphQLInt, GraphQLNonNull, GraphQLObjectType, GraphQLString } from "graphql";
import { numericalResolver } from "../../schema/resolvers/field/numerical";
import { sridToCrs } from "./utils/srid-to-crs";

export const CartesianPoint = new GraphQLObjectType({
name: "CartesianPoint",
Expand All @@ -27,22 +28,20 @@ export const CartesianPoint = new GraphQLObjectType({
fields: {
x: {
type: new GraphQLNonNull(GraphQLFloat),
resolve: (source) => source.point.x,
},
y: {
type: new GraphQLNonNull(GraphQLFloat),
resolve: (source) => source.point.y,
},
z: {
type: GraphQLFloat,
resolve: (source) => source.point.z,
},
crs: {
type: new GraphQLNonNull(GraphQLString),
resolve: (source) => sridToCrs(source.srid),
},
srid: {
type: new GraphQLNonNull(GraphQLInt),
resolve: (source, args, context, info) => numericalResolver(source.point, args, context, info),
resolve: (source, args, context, info) => numericalResolver(source, args, context, info),
},
},
});
10 changes: 6 additions & 4 deletions packages/graphql/src/graphql/objects/Point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@

import { GraphQLFloat, GraphQLInt, GraphQLNonNull, GraphQLObjectType, GraphQLString } from "graphql";
import { numericalResolver } from "../../schema/resolvers/field/numerical";
import { sridToCrs } from "./utils/srid-to-crs";

export const Point = new GraphQLObjectType({
name: "Point",
Expand All @@ -27,22 +28,23 @@ export const Point = new GraphQLObjectType({
fields: {
longitude: {
type: new GraphQLNonNull(GraphQLFloat),
resolve: (source) => source.point.x,
resolve: (source) => source.longitude || source.x,
},
latitude: {
type: new GraphQLNonNull(GraphQLFloat),
resolve: (source) => source.point.y,
resolve: (source) => source.latitude || source.y,
},
height: {
type: GraphQLFloat,
resolve: (source) => source.point.z,
resolve: (source) => source.height || source.z,
},
crs: {
type: new GraphQLNonNull(GraphQLString),
resolve: (source) => sridToCrs(source.srid),
},
srid: {
type: new GraphQLNonNull(GraphQLInt),
resolve: (source, args, context, info) => numericalResolver(source.point, args, context, info),
resolve: (source, args, context, info) => numericalResolver(source, args, context, info),
},
},
});
42 changes: 42 additions & 0 deletions packages/graphql/src/graphql/objects/utils/srid-to-crs.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
/*
* Copyright (c) "Neo4j"
* Neo4j Sweden AB [http://neo4j.com]
*
* This file is part of Neo4j.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { integer } from "neo4j-driver";
import { isIntegerable } from "../../../schema/resolvers/field/numerical";

export function sridToCrs(srid: unknown): string {
if (!isIntegerable(srid)) {
return "";
}

const integerSrid = integer.toNumber(srid);

switch (integerSrid) {
case 4326:
return "wgs-84";
case 4979:
return "wgs-84-3d";
case 7203:
return "cartesian";
case 9157:
return "cartesian-3d";
default:
return "";
}
}
4 changes: 3 additions & 1 deletion packages/graphql/src/schema/resolvers/field/numerical.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ import type { Integer } from "neo4j-driver";
import { integer, isInt } from "neo4j-driver";
import { defaultFieldResolver } from "./defaultField";

function isIntegerable(value: unknown): value is number | string | Integer | { low: number; high: number } | bigint {
export function isIntegerable(
value: unknown
): value is number | string | Integer | { low: number; high: number } | bigint {
if (!value) {
return false;
}
Expand Down

This file was deleted.

13 changes: 0 additions & 13 deletions packages/graphql/src/translate/queryAST/factory/FieldFactory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@ import type { AggregationField } from "../ast/fields/aggregation-fields/Aggregat
import { CountField } from "../ast/fields/aggregation-fields/CountField";
import { AttributeField } from "../ast/fields/attribute-fields/AttributeField";
import { DateTimeField } from "../ast/fields/attribute-fields/DateTimeField";
import { PointAttributeField } from "../ast/fields/attribute-fields/PointAttributeField";
import type { ConnectionReadOperation } from "../ast/operations/ConnectionReadOperation";
import type { CompositeConnectionReadOperation } from "../ast/operations/composite/CompositeConnectionReadOperation";
import { isConcreteEntity } from "../utils/is-concrete-entity";
Expand Down Expand Up @@ -213,18 +212,6 @@ export class FieldFactory {
});
}

if (attribute.typeHelper.isPoint() || attribute.typeHelper.isCartesianPoint()) {
const typeName = attribute.typeHelper.isList()
? (attribute.type as ListType).ofType.name
: attribute.type.name;
const { crs } = field.fieldsByTypeName[typeName] as any;
return new PointAttributeField({
attribute,
alias: field.alias,
crs: Boolean(crs),
});
}

if (attribute.typeHelper.isDateTime()) {
return new DateTimeField({
attribute,
Expand Down
5 changes: 1 addition & 4 deletions packages/graphql/tests/tck/array-methods.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -156,10 +156,7 @@ describe("Arrays Methods", () => {
WITH this
WHERE apoc.util.validatePredicate(this.filmingLocations IS NULL, \\"Property %s cannot be NULL\\", ['filmingLocations'])
SET this.filmingLocations = this.filmingLocations + [p in $this_update_filmingLocations_PUSH | point(p)]
RETURN collect(DISTINCT this { .title, filmingLocations: CASE
WHEN this.filmingLocations IS NOT NULL THEN [update_var0 IN this.filmingLocations | { point: update_var0 }]
ELSE NULL
END }) AS data"
RETURN collect(DISTINCT this { .title, .filmingLocations }) AS data"
`);

expect(formatParams(result.params)).toMatchInlineSnapshot(`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -97,10 +97,7 @@ describe("Cypher -> Connections -> Filtering -> Node -> Points", () => {
WITH edges
UNWIND edges AS edge
WITH edge.node AS this1, edge.relationship AS this0
RETURN collect({ properties: { screenTime: this0.screenTime, __resolveType: \\"ActedIn\\" }, node: { name: this1.name, currentLocation: CASE
WHEN this1.currentLocation IS NOT NULL THEN { point: this1.currentLocation }
ELSE NULL
END, __resolveType: \\"Actor\\" } }) AS var2
RETURN collect({ properties: { screenTime: this0.screenTime, __resolveType: \\"ActedIn\\" }, node: { name: this1.name, currentLocation: this1.currentLocation, __resolveType: \\"Actor\\" } }) AS var2
}
RETURN { edges: var2, totalCount: totalCount } AS var3
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Points", () => {
WITH edges
UNWIND edges AS edge
WITH edge.node AS this1, edge.relationship AS this0
RETURN collect({ properties: { screenTime: this0.screenTime, location: CASE
WHEN this0.location IS NOT NULL THEN { point: this0.location }
ELSE NULL
END, __resolveType: \\"ActedIn\\" }, node: { name: this1.name, __resolveType: \\"Actor\\" } }) AS var2
RETURN collect({ properties: { screenTime: this0.screenTime, location: this0.location, __resolveType: \\"ActedIn\\" }, node: { name: this1.name, __resolveType: \\"Actor\\" } }) AS var2
}
RETURN { edges: var2, totalCount: totalCount } AS var3
}
Expand Down
5 changes: 1 addition & 4 deletions packages/graphql/tests/tck/projection.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,7 @@ describe("Cypher Projection", () => {
WITH create_this1
MATCH (create_this1)-[create_this2:HAS_PHOTO]->(create_this3:Photo)
WHERE create_this3.url = $create_param1
WITH create_this3 { .url, location: CASE
WHEN create_this3.location IS NOT NULL THEN { point: create_this3.location }
ELSE NULL
END } AS create_this3
WITH create_this3 { .url, .location } AS create_this3
RETURN collect(create_this3) AS create_var4
}
CALL {
Expand Down
Loading

0 comments on commit 2e6de36

Please sign in to comment.