@@ -16,21 +16,30 @@ open class ProjectionBase {
16
16
}
17
17
18
18
fun orderBy (variable : String , args : MutableList <Argument >): String {
19
+ val values = getOrderByArgs(args)
20
+ if (values.isEmpty()) {
21
+ return " "
22
+ }
23
+ return " ORDER BY " + values.joinToString(" , " , transform = { (property, direction) -> " $variable .$property $direction " })
24
+ }
25
+
26
+ private fun getOrderByArgs (args : MutableList <Argument >): List <Pair <String , Sort >> {
19
27
val arg = args.find { it.name == ORDER_BY }
20
- val values = arg?.value?.let { it ->
21
- when (it) {
22
- is ArrayValue -> it.values.map { it.toJavaValue().toString() }
23
- is EnumValue -> listOf (it.name)
24
- is StringValue -> listOf (it.value)
25
- else -> null
28
+ return arg?.value
29
+ ?.let { it ->
30
+ when (it) {
31
+ is ArrayValue -> it.values.map { it.toJavaValue().toString() }
32
+ is EnumValue -> listOf (it.name)
33
+ is StringValue -> listOf (it.value)
34
+ else -> null
35
+ }
26
36
}
27
- }
28
- @Suppress(" SimplifiableCallChain" )
29
- return if (values == null ) " "
30
- else " ORDER BY " + values
31
- .map { it.split(" _" ) }
32
- .map { " $variable .${it[0 ]} ${it[1 ].toUpperCase()} " }
33
- .joinToString(" , " )
37
+ ?.map {
38
+ val index = it.lastIndexOf(' _' )
39
+ val property = it.substring(0 , index)
40
+ val direction = Sort .valueOf(it.substring(index + 1 ).toUpperCase())
41
+ property to direction
42
+ } ? : emptyList()
34
43
}
35
44
36
45
fun where (variable : String , fieldDefinition : GraphQLFieldDefinition , type : GraphQLFieldsContainer , arguments : List <Argument >, field : Field ): Cypher {
@@ -139,9 +148,8 @@ open class ProjectionBase {
139
148
return predicates.values + defaults
140
149
}
141
150
142
- fun projectFields (variable : String , field : Field , nodeType : GraphQLFieldsContainer , env : DataFetchingEnvironment , variableSuffix : String? ): Cypher {
143
- val queries = projectSelectionSet(variable, field.selectionSet, nodeType, env, variableSuffix)
144
-
151
+ fun projectFields (variable : String , field : Field , nodeType : GraphQLFieldsContainer , env : DataFetchingEnvironment , variableSuffix : String? , neo4jFieldsToPass : Set <String > = emptySet()): Cypher {
152
+ val queries = projectSelection(variable, field.selectionSet.selections, nodeType, env, variableSuffix, neo4jFieldsToPass)
145
153
@Suppress(" SimplifiableCallChain" )
146
154
val projection = queries
147
155
.map { it.query }
@@ -152,18 +160,18 @@ open class ProjectionBase {
152
160
return Cypher (" $variable $projection " , params)
153
161
}
154
162
155
- private fun projectSelectionSet (variable : String , selectionSet : SelectionSet , nodeType : GraphQLFieldsContainer , env : DataFetchingEnvironment , variableSuffix : String? ): List <Cypher > {
163
+ private fun projectSelection (variable : String , selection : List < Selection < * >> , nodeType : GraphQLFieldsContainer , env : DataFetchingEnvironment , variableSuffix : String? , neo4jFieldsToPass : Set < String > = emptySet() ): List <Cypher > {
156
164
// TODO just render fragments on valid types (Labels) by using cypher like this:
157
165
// apoc.map.mergeList([
158
166
// a{.name},
159
167
// CASE WHEN a:Location THEN a { .foo } ELSE {} END
160
168
// ])
161
169
var hasTypeName = false
162
- val projections = selectionSet.selections .flatMapTo(mutableListOf<Cypher >()) {
170
+ val projections = selection .flatMapTo(mutableListOf<Cypher >()) {
163
171
when (it) {
164
172
is Field -> {
165
173
hasTypeName = hasTypeName || (it.name == TYPE_NAME )
166
- listOf (projectField(variable, it, nodeType, env, variableSuffix))
174
+ listOf (projectField(variable, it, nodeType, env, variableSuffix, neo4jFieldsToPass ))
167
175
}
168
176
is InlineFragment -> projectInlineFragment(variable, it, env, variableSuffix)
169
177
is FragmentSpread -> projectNamedFragments(variable, it, env, variableSuffix)
@@ -180,7 +188,7 @@ open class ProjectionBase {
180
188
return projections
181
189
}
182
190
183
- private fun projectField (variable : String , field : Field , type : GraphQLFieldsContainer , env : DataFetchingEnvironment , variableSuffix : String? ): Cypher {
191
+ private fun projectField (variable : String , field : Field , type : GraphQLFieldsContainer , env : DataFetchingEnvironment , variableSuffix : String? , neo4jFieldsToPass : Set < String > = emptySet() ): Cypher {
184
192
if (field.name == TYPE_NAME ) {
185
193
return if (type.isRelationType()) {
186
194
Cypher (" ${field.aliasOrName()} : '${type.name} '" )
@@ -206,7 +214,11 @@ open class ProjectionBase {
206
214
} ? : when {
207
215
isObjectField -> {
208
216
val patternComprehensions = if (fieldDefinition.isNeo4jType()) {
209
- projectNeo4jObjectType(variable, field)
217
+ if (neo4jFieldsToPass.contains(fieldDefinition.innerName())) {
218
+ Cypher (variable + " ." + fieldDefinition.propertyName().quote())
219
+ } else {
220
+ projectNeo4jObjectType(variable, field)
221
+ }
210
222
} else {
211
223
projectRelationship(variable, field, fieldDefinition, type, env, variableSuffix)
212
224
}
@@ -230,7 +242,7 @@ open class ProjectionBase {
230
242
.filterIsInstance<Field >()
231
243
.map {
232
244
val value = when (it.name) {
233
- NEO4j_FORMATTED_PROPERTY_KEY -> " $variable .${field.name} "
245
+ NEO4j_FORMATTED_PROPERTY_KEY -> " toString( $variable .${field.name} ) "
234
246
else -> " $variable .${field.name} .${it.name} "
235
247
}
236
248
" ${it.name} : $value "
@@ -266,7 +278,7 @@ open class ProjectionBase {
266
278
val fragmentType = env.graphQLSchema.getType(fragmentTypeName) as ? GraphQLFieldsContainer ? : return emptyList()
267
279
// these are the nested fields of the fragment
268
280
// it could be that we have to adapt the variable name too, and perhaps add some kind of rename
269
- return projectSelectionSet (variable, selectionSet, fragmentType, env, variableSuffix)
281
+ return projectSelection (variable, selectionSet.selections , fragmentType, env, variableSuffix)
270
282
}
271
283
272
284
@@ -336,9 +348,27 @@ open class ProjectionBase {
336
348
val relPattern = if (isRelFromType) " $childVariable :${relInfo.relType} " else " :${relInfo.relType} "
337
349
338
350
val where = where(childVariable, fieldDefinition, nodeType, propertyArguments(field), field)
339
- val fieldProjection = projectFields(childVariable, field, nodeType, env, variableSuffix)
340
351
341
- val comprehension = " [($variable )$inArrow -[$relPattern ]-$outArrow ($endNodePattern )${where.query} | ${fieldProjection.query} ]"
352
+ val orderBy = getOrderByArgs(field.arguments)
353
+ val sortByNeo4jTypeFields = orderBy
354
+ .filter { (property, _) -> nodeType.getFieldDefinition(property)?.isNeo4jType() == true }
355
+ .map { (property, _) -> property }
356
+ .toSet()
357
+
358
+ val fieldProjection = projectFields(childVariable, field, nodeType, env, variableSuffix, sortByNeo4jTypeFields)
359
+ var comprehension = " [($variable )$inArrow -[$relPattern ]-$outArrow ($endNodePattern )${where.query} | ${fieldProjection.query} ]"
360
+ if (orderBy.isNotEmpty()) {
361
+ val sortArgs = orderBy.joinToString(" , " , transform = { (property, direction) -> if (direction == Sort .ASC ) " '^$property '" else " '$property '" })
362
+ comprehension = " apoc.coll.sortMulti($comprehension , [$sortArgs ])"
363
+ if (sortByNeo4jTypeFields.isNotEmpty()) {
364
+ val neo4jFiledSelection = field.selectionSet.selections
365
+ .filter { selection -> sortByNeo4jTypeFields.contains((selection as ? Field )?.name) }
366
+ val deferredProjection = projectSelection(" sortedElement" , neo4jFiledSelection, nodeType, env, variableSuffix)
367
+ .map { cypher -> cypher.query }
368
+ .joinNonEmpty(" , " )
369
+ comprehension = " [sortedElement IN $comprehension | sortedElement { .*, $deferredProjection }]"
370
+ }
371
+ }
342
372
val skipLimit = SkipLimit (childVariable, field.arguments)
343
373
val slice = skipLimit.slice(fieldType.isList())
344
374
return Cypher (comprehension + slice.query, (where.params + fieldProjection.params + slice.params))
@@ -392,4 +422,9 @@ open class ProjectionBase {
392
422
}
393
423
}
394
424
}
425
+
426
+ enum class Sort {
427
+ ASC ,
428
+ DESC
429
+ }
395
430
}
0 commit comments