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

Commit

Permalink
Merge pull request #334 from michaeldgraham/master
Browse files Browse the repository at this point in the history
Initial spatial support using Point type
  • Loading branch information
johnymontana committed Oct 31, 2019
2 parents ed82d31 + c5e9515 commit 42f6ca3
Show file tree
Hide file tree
Showing 15 changed files with 1,253 additions and 297 deletions.
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

0 comments on commit 42f6ca3

Please sign in to comment.