@@ -35,6 +35,7 @@ import cats.instances.list._
35
35
import cats .syntax .all ._
36
36
import org .opencypher .okapi .api .value .CypherValue .{CypherEntity , CypherMap , CypherNode , CypherRelationship }
37
37
import org .opencypher .okapi .impl .exception .{IllegalArgumentException , UnsupportedOperationException }
38
+ import org .opencypher .okapi .impl .temporal .TemporalTypesHelper .parseDate
38
39
import org .opencypher .okapi .impl .temporal .{Duration , TemporalTypesHelper }
39
40
import org .opencypher .v9_0 .ast ._
40
41
import org .opencypher .v9_0 .ast .semantics .{SemanticErrorDef , SemanticState }
@@ -111,7 +112,10 @@ object CreateGraphFactory extends InMemoryGraphFactory {
111
112
case head :: tail =>
112
113
head match {
113
114
case Create (pattern) =>
114
- processPattern(pattern) >> processClauses(tail)
115
+ processPattern(pattern, merge = false ) >> processClauses(tail)
116
+
117
+ case Merge (pattern, _, _) =>
118
+ processPattern(pattern, merge = true ) >> processClauses(tail)
115
119
116
120
case Unwind (expr, variable) =>
117
121
for {
@@ -133,16 +137,16 @@ object CreateGraphFactory extends InMemoryGraphFactory {
133
137
}
134
138
}
135
139
136
- def processPattern (pattern : Pattern ): Result [Unit ] = {
140
+ def processPattern (pattern : Pattern , merge : Boolean ): Result [Unit ] = {
137
141
val parts = pattern.patternParts.map {
138
142
case EveryPath (element) => element
139
143
case other => throw UnsupportedOperationException (s " Processing pattern: ${other.getClass.getSimpleName}" )
140
144
}
141
145
142
- Foldable [List ].sequence_[Result , CypherEntity [Long ]](parts.toList.map(pe => processPatternElement(pe)))
146
+ Foldable [List ].sequence_[Result , CypherEntity [Long ]](parts.toList.map(pe => processPatternElement(pe, merge )))
143
147
}
144
148
145
- def processPatternElement (patternElement : ASTNode ): Result [CypherEntity [Long ]] = {
149
+ def processPatternElement (patternElement : ASTNode , merge : Boolean ): Result [CypherEntity [Long ]] = {
146
150
patternElement match {
147
151
case NodePattern (Some (variable), labels, props, _) =>
148
152
for {
@@ -160,7 +164,7 @@ object CreateGraphFactory extends InMemoryGraphFactory {
160
164
}
161
165
_ <- modify[ParsingContext ] { context =>
162
166
if (context.variableMapping.get(variable.name).isEmpty) {
163
- context.updated(variable.name, node)
167
+ context.updated(variable.name, node, merge )
164
168
} else {
165
169
context
166
170
}
@@ -169,12 +173,12 @@ object CreateGraphFactory extends InMemoryGraphFactory {
169
173
170
174
case RelationshipChain (first, RelationshipPattern (Some (variable), relType, None , props, direction, _, _), third) =>
171
175
for {
172
- source <- processPatternElement(first)
176
+ source <- processPatternElement(first, merge )
173
177
sourceId <- pure[ParsingContext , Long ](source match {
174
178
case n : CypherNode [Long ] => n.id
175
179
case r : CypherRelationship [Long ] => r.endId
176
180
})
177
- target <- processPatternElement(third)
181
+ target <- processPatternElement(third, merge )
178
182
properties <- props match {
179
183
case Some (expr : MapExpression ) => extractProperties(expr)
180
184
case Some (other) => throw IllegalArgumentException (" a RelationshipChain with MapExpression" , other)
@@ -214,15 +218,41 @@ object CreateGraphFactory extends InMemoryGraphFactory {
214
218
215
219
case ListLiteral (expressions) => expressions.toList.traverse[Result , Any ](processExpr)
216
220
221
+ case Modulo (lhs, rhs) =>
222
+ for {
223
+ leftVal <- processExpr(lhs)
224
+ rightVal <- processExpr(rhs)
225
+ res <- pure[ParsingContext , Any ](leftVal.asInstanceOf [Long ] % rightVal.asInstanceOf [Long ])
226
+ } yield res
227
+
228
+ case MapExpression (items) =>
229
+ for {
230
+ keys <- pure(items.map { case (k, _) => k.name })
231
+ valueTypes <- items.toList.traverse[Result , Any ] { case (_, v) => processExpr(v) }
232
+ res <- pure[ParsingContext , Any ](keys.zip(valueTypes).toMap)
233
+ } yield res
234
+
217
235
case FunctionInvocation (_, FunctionName (" date" ), _, Seq (dateString : StringLiteral )) =>
218
- pure[ParsingContext , Any ](TemporalTypesHelper .parseDate(Right (dateString.value)))
236
+ pure[ParsingContext , Any ](parseDate(Right (dateString.value)))
237
+
238
+ case FunctionInvocation (_, FunctionName (" date" ), _, Seq (map : MapExpression )) =>
239
+ for {
240
+ dateMap <- processExpr(map)
241
+ res <- pure[ParsingContext , Any ](parseDate(Left (dateMap.asInstanceOf [Map [String , Long ]].mapValues(_.toInt))))
242
+ } yield res
219
243
220
244
case FunctionInvocation (_, FunctionName (" localdatetime" ), _, Seq (dateString : StringLiteral )) =>
221
245
pure[ParsingContext , Any ](TemporalTypesHelper .parseLocalDateTime(Right (dateString.value)))
222
246
223
247
case FunctionInvocation (_, FunctionName (" duration" ), _, Seq (dateString : StringLiteral )) =>
224
248
pure[ParsingContext , Any ](Duration .parse(dateString.value))
225
249
250
+ case FunctionInvocation (_, FunctionName (" duration" ), _, Seq (map : MapExpression )) =>
251
+ for {
252
+ durationMap <- processExpr(map)
253
+ res <- pure[ParsingContext , Any ](Duration (durationMap.asInstanceOf [Map [String , Long ]]))
254
+ } yield res
255
+
226
256
case Property (variable : Variable , propertyKey) =>
227
257
inspect[ParsingContext , Any ]({ context =>
228
258
context.variableMapping(variable.name) match {
@@ -280,16 +310,22 @@ final case class ParsingContext(
280
310
281
311
def popProtectedScope : ParsingContext = copy(protectedScopes = protectedScopes.tail)
282
312
283
- def updated (k : String , v : Any ): ParsingContext = v match {
284
- case n : InMemoryTestNode =>
313
+ def updated (k : String , v : Any , merge : Boolean = false ): ParsingContext = v match {
314
+ case n : InMemoryTestNode if ! merge || ! containsNode(n) =>
285
315
copy(graph = graph.updated(n), variableMapping = variableMapping.updated(k, n))
286
316
287
- case r : InMemoryTestRelationship =>
317
+ case r : InMemoryTestRelationship if ! merge || ! containsRel(r) =>
288
318
copy(graph = graph.updated(r), variableMapping = variableMapping.updated(k, r))
289
319
290
320
case _ =>
291
321
copy(variableMapping = variableMapping.updated(k, v))
292
322
}
323
+
324
+ private def containsNode (n : InMemoryTestNode ): Boolean =
325
+ graph.nodes.exists(n.equalsSemantically)
326
+
327
+ private def containsRel (r : InMemoryTestRelationship ): Boolean =
328
+ graph.relationships.exists(r.equalsSemantically)
293
329
}
294
330
295
331
object ParsingContext {
0 commit comments