Skip to content
This repository has been archived by the owner on Sep 3, 2021. It is now read-only.

Initial spatial support using Point type #334

Merged
merged 6 commits into from
Oct 31, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions example/apollo-server/movies-schema.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ type Movie {
actors(first: Int = 3, offset: Int = 0): [Actor] @relation(name: "ACTED_IN", direction:"IN")
avgStars: Float
filmedIn: State @relation(name: "FILMED_IN", direction: "OUT")
location: Point
locations: [Point]
scaleRating(scale: Int = 3): Float @cypher(statement: "WITH $this AS this RETURN $scale * this.imdbRating")
scaleRatingFloat(scale: Float = 1.5): Float @cypher(statement: "WITH $this AS this RETURN $scale * this.imdbRating")
}
Expand Down Expand Up @@ -68,6 +70,13 @@ type OnlyDate {
date: Date
}
type SpatialNode {
pointKey: Point
point: Point
spatialNodes(pointKey: Point): [SpatialNode]
@relation(name: "SPATIAL", direction: OUT)
}
type Book {
genre: BookGenre
}
Expand Down
7 changes: 7 additions & 0 deletions src/augment/fields.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const isPropertyTypeField = ({ kind, type }) =>
isBooleanField({ type }) ||
isCustomScalarField({ kind }) ||
isTemporalField({ type }) ||
isSpatialField({ type }) ||
isNeo4jPropertyType({ type });

export const isIntegerField = ({ type }) =>
Expand All @@ -34,9 +35,15 @@ export const isStringField = ({ kind, type }) =>
export const isBooleanField = ({ type }) =>
Neo4jDataType.PROPERTY[type] === 'Boolean';

export const isNeo4jTypeField = ({ type }) =>
isTemporalField({ type }) || isSpatialField({ type });

export const isTemporalField = ({ type }) =>
Neo4jDataType.PROPERTY[type] === 'Temporal';

export const isSpatialField = ({ type }) =>
Neo4jDataType.PROPERTY[type] === 'Spatial';

export const isNeo4jIDField = ({ name }) => name === Neo4jSystemIDField;

export const isCustomScalarField = ({ kind }) =>
Expand Down
71 changes: 71 additions & 0 deletions src/augment/types/spatial.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
import { GraphQLInt, GraphQLString } from 'graphql';
import { buildNeo4jTypes } from '../types/types';

/**
* An enum describing the name of the Neo4j Point type
*/
export const SpatialType = {
POINT: 'Point'
};

/**
* An enum describing the property names of the Neo4j Point type
* See: https://neo4j.com/docs/cypher-manual/current/syntax/spatial/#cypher-spatial-instants
*/
const Neo4jPointField = {
X: 'x',
Y: 'y',
Z: 'z',
LONGITUDE: 'longitude',
LATITUDE: 'latitude',
HEIGHT: 'height',
CRS: 'crs',
SRID: 'srid'
};

/**
* A map of the Neo4j Temporal Time type fields to their respective
* GraphQL types
*/
export const Neo4jPoint = {
[Neo4jPointField.X]: GraphQLInt.name,
[Neo4jPointField.Y]: GraphQLInt.name,
[Neo4jPointField.Z]: GraphQLInt.name,
[Neo4jPointField.LONGITUDE]: GraphQLInt.name,
[Neo4jPointField.LATITUDE]: GraphQLInt.name,
[Neo4jPointField.HEIGHT]: GraphQLInt.name,
[Neo4jPointField.CRS]: GraphQLString.name,
[Neo4jPointField.SRID]: GraphQLInt.name
};

/**
* The main export for building the GraphQL input and output type definitions
* for Neo4j Temporal property types
*/
export const augmentSpatialTypes = ({ typeMap, config = {} }) => {
config.spatial = decideSpatialConfig({ config });
return buildNeo4jTypes({
typeMap,
neo4jTypes: SpatialType,
config: config.spatial
});
};

/**
* A helper function for ensuring a fine-grained spatial
* configmration
*/
const decideSpatialConfig = ({ config }) => {
let defaultConfig = {
point: true
};
const providedConfig = config ? config.spatial : defaultConfig;
if (typeof providedConfig === 'boolean') {
if (providedConfig === false) {
defaultConfig.point = false;
}
} else if (typeof providedConfig === 'object') {
defaultConfig = providedConfig;
}
return defaultConfig;
};
93 changes: 15 additions & 78 deletions src/augment/types/temporal.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { GraphQLInt, GraphQLString } from 'graphql';
import { Neo4jTypeName, buildNeo4jType } from '../types/types';
import { buildName, buildField, buildNamedType, buildInputValue } from '../ast';
import { buildNeo4jTypes } from '../types/types';

/**
* An enum describing the names of Neo4j Temporal types
* See: https://neo4j.com/docs/cypher-manual/current/syntax/temporal/#cypher-temporal-instants
*/
export const TemporalType = {
TIME: 'Time',
Expand All @@ -16,7 +16,7 @@ export const TemporalType = {
/**
* An enum describing the property names of the Neo4j Time type
*/
const Neo4jTimeField = {
export const Neo4jTimeField = {
HOUR: 'hour',
MINUTE: 'minute',
SECOND: 'second',
Expand All @@ -29,26 +29,17 @@ const Neo4jTimeField = {
/**
* An enum describing the property names of the Neo4j Date type
*/
const Neo4jDateField = {
export const Neo4jDateField = {
YEAR: 'year',
MONTH: 'month',
DAY: 'day'
};

/**
* An enum describing the names of fields computed and added to the input
* and output type definitions representing non-scalar Neo4j property types
* TODO support for the Neo4j Point data type should also use this
*/
export const Neo4jTypeFormatted = {
FORMATTED: 'formatted'
};

/**
* A map of the Neo4j Temporal Time type fields to their respective
* GraphQL types
*/
const Neo4jTime = {
export const Neo4jTime = {
[Neo4jTimeField.HOUR]: GraphQLInt.name,
[Neo4jTimeField.MINUTE]: GraphQLInt.name,
[Neo4jTimeField.SECOND]: GraphQLInt.name,
Expand All @@ -62,86 +53,32 @@ const Neo4jTime = {
* A map of the Neo4j Temporal Date type fields to their respective
* GraphQL types
*/
const Neo4jDate = {
export const Neo4jDate = {
[Neo4jDateField.YEAR]: GraphQLInt.name,
[Neo4jDateField.MONTH]: GraphQLInt.name,
[Neo4jDateField.DAY]: GraphQLInt.name
};

/**
* The main export for building the GraphQL input and output type definitions
* for the Neo4j Temporal property types. Each TemporalType can be constructed
* using either or both of the Time and Date type fields.
* for Neo4j Temporal property types. Each TemporalType can be constructed
* using either or both of the Time and Date type fields
*/
export const buildTemporalTypes = ({ typeMap, config = {} }) => {
config.temporal = decideTemporalConfig(config);
const temporalConfig = config.temporal;
Object.values(TemporalType).forEach(typeName => {
const typeNameLower = typeName.toLowerCase();
if (temporalConfig[typeNameLower] === true) {
const objectTypeName = `${Neo4jTypeName}${typeName}`;
const inputTypeName = `${objectTypeName}Input`;
let fields = [];
if (typeName === TemporalType.DATE) {
fields = Object.entries(Neo4jDate);
} else if (typeName === TemporalType.TIME) {
fields = Object.entries(Neo4jTime);
} else if (typeName === TemporalType.LOCALTIME) {
fields = Object.entries({
...Neo4jTime
}).filter(([name]) => name !== Neo4jTimeField.TIMEZONE);
} else if (typeName === TemporalType.DATETIME) {
fields = Object.entries({
...Neo4jDate,
...Neo4jTime
});
} else if (typeName === TemporalType.LOCALDATETIME) {
fields = Object.entries({
...Neo4jDate,
...Neo4jTime
}).filter(([name]) => name !== Neo4jTimeField.TIMEZONE);
}
let inputFields = [];
let outputFields = [];
fields.forEach(([fieldName, fieldType]) => {
const fieldNameLower = fieldName.toLowerCase();
const fieldConfig = {
name: buildName({ name: fieldNameLower }),
type: buildNamedType({
name: fieldType
})
};
inputFields.push(buildInputValue(fieldConfig));
outputFields.push(buildField(fieldConfig));
});
const formattedFieldConfig = {
name: buildName({
name: Neo4jTypeFormatted.FORMATTED
}),
type: buildNamedType({
name: GraphQLString.name
})
};
inputFields.push(buildInputValue(formattedFieldConfig));
outputFields.push(buildField(formattedFieldConfig));
typeMap = buildNeo4jType({
inputTypeName,
inputFields,
objectTypeName,
outputFields,
typeMap
});
}
export const augmentTemporalTypes = ({ typeMap, config = {} }) => {
config.temporal = decideTemporalConfig({ config });
return buildNeo4jTypes({
typeMap,
neo4jTypes: TemporalType,
config: config.temporal
});
return typeMap;
};

/**
* A helper function for ensuring a fine-grained temporal
* configmration, used to simplify checking it
* throughout the augmnetation process
*/
const decideTemporalConfig = config => {
const decideTemporalConfig = ({ config }) => {
let defaultConfig = {
time: true,
date: true,
Expand Down
Loading