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

Commit c4f6a21

Browse files
authored
Merge pull request #341 from michaeldgraham/master
Spatial filters
2 parents d71d73b + 3a5ebfb commit c4f6a21

File tree

9 files changed

+623
-127
lines changed

9 files changed

+623
-127
lines changed

src/augment/input-values.js

+70-37
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ import {
77
buildEnumType,
88
buildEnumValue
99
} from './ast';
10-
import { isNeo4jPropertyType } from './types/types';
10+
import {
11+
isNeo4jTemporalType,
12+
isNeo4jPointType,
13+
Neo4jTypeName
14+
} from './types/types';
1115
import { isCypherField } from './directives';
1216
import {
1317
TypeWrappers,
@@ -18,9 +22,10 @@ import {
1822
isStringField,
1923
isBooleanField,
2024
isTemporalField,
21-
getFieldDefinition
25+
getFieldDefinition,
26+
isSpatialField
2227
} from './fields';
23-
28+
import { SpatialType, Neo4jPointDistanceFilter } from './types/spatial';
2429
/**
2530
* An enum describing the names of the input value definitions
2631
* used for the field argument AST for data result pagination
@@ -326,10 +331,24 @@ const buildPropertyFilters = ({
326331
}) => {
327332
let filters = [];
328333
if (
334+
isSpatialField({ type: outputType }) ||
335+
isNeo4jPointType({ type: outputType })
336+
) {
337+
filters = buildFilters({
338+
fieldName,
339+
fieldConfig: {
340+
name: fieldName,
341+
type: {
342+
name: outputType
343+
}
344+
},
345+
filterTypes: ['not', ...Object.values(Neo4jPointDistanceFilter)]
346+
});
347+
} else if (
329348
isIntegerField({ type: outputType }) ||
330349
isFloatField({ type: outputType }) ||
331350
isTemporalField({ type: outputType }) ||
332-
isNeo4jPropertyType({ type: outputType })
351+
isNeo4jTemporalType({ type: outputType })
333352
) {
334353
filters = buildFilters({
335354
fieldName,
@@ -339,7 +358,7 @@ const buildPropertyFilters = ({
339358
name: outputType
340359
}
341360
},
342-
filterTypes: ['_not', '_in', '_not_in', '_lt', '_lte', '_gt', '_gte']
361+
filterTypes: ['not', 'in', 'not_in', 'lt', 'lte', 'gt', 'gte']
343362
});
344363
} else if (isBooleanField({ type: outputType })) {
345364
filters = buildFilters({
@@ -350,7 +369,7 @@ const buildPropertyFilters = ({
350369
name: outputType
351370
}
352371
},
353-
filterTypes: ['_not']
372+
filterTypes: ['not']
354373
});
355374
} else if (isStringField({ kind: outputKind, type: outputType })) {
356375
if (outputKind === Kind.ENUM_TYPE_DEFINITION) {
@@ -362,7 +381,7 @@ const buildPropertyFilters = ({
362381
name: outputType
363382
}
364383
},
365-
filterTypes: ['_not', '_in', '_not_in']
384+
filterTypes: ['not', 'in', 'not_in']
366385
});
367386
} else {
368387
filters = buildFilters({
@@ -374,15 +393,15 @@ const buildPropertyFilters = ({
374393
}
375394
},
376395
filterTypes: [
377-
'_not',
378-
'_in',
379-
'_not_in',
380-
'_contains',
381-
'_not_contains',
382-
'_starts_with',
383-
'_not_starts_with',
384-
'_ends_with',
385-
'_not_ends_with'
396+
'not',
397+
'in',
398+
'not_in',
399+
'contains',
400+
'not_contains',
401+
'starts_with',
402+
'not_starts_with',
403+
'ends_with',
404+
'not_ends_with'
386405
]
387406
});
388407
}
@@ -394,25 +413,39 @@ const buildPropertyFilters = ({
394413
* Builds the input value definitions that compose input object types
395414
* used by filtering arguments
396415
*/
397-
export const buildFilters = ({ fieldName, fieldConfig, filterTypes = [] }) => [
398-
buildInputValue({
399-
name: buildName({ name: fieldConfig.name }),
400-
type: buildNamedType(fieldConfig.type)
401-
}),
402-
...filterTypes.map(filter => {
403-
let wrappers = {};
404-
if (filter === '_in' || filter === '_not_in') {
405-
wrappers = {
406-
[TypeWrappers.NON_NULL_NAMED_TYPE]: true,
407-
[TypeWrappers.LIST_TYPE]: true
408-
};
409-
}
410-
return buildInputValue({
411-
name: buildName({ name: `${fieldName}${filter}` }),
412-
type: buildNamedType({
413-
name: fieldConfig.type.name,
414-
wrappers
416+
export const buildFilters = ({ fieldName, fieldConfig, filterTypes = [] }) => {
417+
return filterTypes.reduce(
418+
(inputValues, name) => {
419+
const filterName = `${fieldName}_${name}`;
420+
const isPointDistanceFilter = Object.values(
421+
Neo4jPointDistanceFilter
422+
).some(distanceFilter => distanceFilter === name);
423+
const isListFilter = name === 'in' || name === 'not_in';
424+
let wrappers = {};
425+
if (isListFilter) {
426+
wrappers = {
427+
[TypeWrappers.NON_NULL_NAMED_TYPE]: true,
428+
[TypeWrappers.LIST_TYPE]: true
429+
};
430+
} else if (isPointDistanceFilter) {
431+
fieldConfig.type.name = `${Neo4jTypeName}${SpatialType.POINT}DistanceFilter`;
432+
}
433+
inputValues.push(
434+
buildInputValue({
435+
name: buildName({ name: filterName }),
436+
type: buildNamedType({
437+
name: fieldConfig.type.name,
438+
wrappers
439+
})
440+
})
441+
);
442+
return inputValues;
443+
},
444+
[
445+
buildInputValue({
446+
name: buildName({ name: fieldConfig.name }),
447+
type: buildNamedType(fieldConfig.type)
415448
})
416-
});
417-
})
418-
];
449+
]
450+
);
451+
};

src/augment/types/relationship/query.js

+2-10
Original file line numberDiff line numberDiff line change
@@ -285,15 +285,7 @@ export const buildRelationshipFilters = ({
285285
name: outputType
286286
}
287287
},
288-
filterTypes: [
289-
'_not',
290-
'_in',
291-
'_not_in',
292-
'_some',
293-
'_none',
294-
'_single',
295-
'_every'
296-
]
288+
filterTypes: ['not', 'in', 'not_in', 'some', 'none', 'single', 'every']
297289
});
298290
} else {
299291
filters = buildFilters({
@@ -304,7 +296,7 @@ export const buildRelationshipFilters = ({
304296
name: outputType
305297
}
306298
},
307-
filterTypes: ['_not', '_in', '_not_in']
299+
filterTypes: ['not', 'in', 'not_in']
308300
});
309301
}
310302
}

src/augment/types/spatial.js

+62-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
1-
import { GraphQLInt, GraphQLString } from 'graphql';
2-
import { buildNeo4jTypes } from '../types/types';
1+
import { GraphQLInt, GraphQLString, GraphQLFloat } from 'graphql';
2+
import { buildNeo4jTypes, Neo4jTypeName } from '../types/types';
3+
import {
4+
buildName,
5+
buildNamedType,
6+
buildInputValue,
7+
buildInputObjectType
8+
} from '../ast';
9+
import { TypeWrappers } from '../fields';
310

411
/**
512
* An enum describing the name of the Neo4j Point type
@@ -38,12 +45,29 @@ export const Neo4jPoint = {
3845
[Neo4jPointField.SRID]: GraphQLInt.name
3946
};
4047

48+
export const Neo4jPointDistanceFilter = {
49+
DISTANCE: 'distance',
50+
DISTANCE_LESS_THAN: 'distance_lt',
51+
DISTANCE_LESS_THAN_OR_EQUAL: 'distance_lte',
52+
DISTANCE_GREATER_THAN: 'distance_gt',
53+
DISTANCE_GREATER_THAN_OR_EQUAL: 'distance_gte'
54+
};
55+
56+
export const Neo4jPointDistanceArgument = {
57+
POINT: 'point',
58+
DISTANCE: 'distance'
59+
};
60+
4161
/**
4262
* The main export for building the GraphQL input and output type definitions
4363
* for Neo4j Temporal property types
4464
*/
4565
export const augmentSpatialTypes = ({ typeMap, config = {} }) => {
4666
config.spatial = decideSpatialConfig({ config });
67+
typeMap = buildSpatialDistanceFilterInputType({
68+
typeMap,
69+
config
70+
});
4771
return buildNeo4jTypes({
4872
typeMap,
4973
neo4jTypes: SpatialType,
@@ -69,3 +93,39 @@ const decideSpatialConfig = ({ config }) => {
6993
}
7094
return defaultConfig;
7195
};
96+
97+
/**
98+
* Builds the AST for the input object definition used for
99+
* providing arguments to the spatial filters that use the
100+
* distance Cypher function
101+
*/
102+
const buildSpatialDistanceFilterInputType = ({ typeMap = {}, config }) => {
103+
if (config.spatial.point) {
104+
const typeName = `${Neo4jTypeName}${SpatialType.POINT}DistanceFilter`;
105+
// Overwrite
106+
typeMap[typeName] = buildInputObjectType({
107+
name: buildName({ name: typeName }),
108+
fields: [
109+
buildInputValue({
110+
name: buildName({ name: Neo4jPointDistanceArgument.POINT }),
111+
type: buildNamedType({
112+
name: `${Neo4jTypeName}${SpatialType.POINT}Input`,
113+
wrappers: {
114+
[TypeWrappers.NON_NULL_NAMED_TYPE]: true
115+
}
116+
})
117+
}),
118+
buildInputValue({
119+
name: buildName({ name: Neo4jPointDistanceArgument.DISTANCE }),
120+
type: buildNamedType({
121+
name: GraphQLFloat.name,
122+
wrappers: {
123+
[TypeWrappers.NON_NULL_NAMED_TYPE]: true
124+
}
125+
})
126+
})
127+
]
128+
});
129+
}
130+
return typeMap;
131+
};

0 commit comments

Comments
 (0)