diff --git a/.changeset/good-sheep-refuse.md b/.changeset/good-sheep-refuse.md new file mode 100644 index 0000000000..a2359ba828 --- /dev/null +++ b/.changeset/good-sheep-refuse.md @@ -0,0 +1,5 @@ +--- +"@neo4j/graphql": major +--- + +`DateTime` and `Time` values are now converted from strings into temporal types in the generated Cypher instead of in server code using the driver. This could result in different values when the database is in a different timezone to the GraphQL server. diff --git a/packages/graphql/src/graphql/scalars/DateTime.ts b/packages/graphql/src/graphql/scalars/DateTime.ts index 30b3311235..e726d4c71c 100644 --- a/packages/graphql/src/graphql/scalars/DateTime.ts +++ b/packages/graphql/src/graphql/scalars/DateTime.ts @@ -19,7 +19,8 @@ import type { ValueNode } from "graphql"; import { GraphQLError, GraphQLScalarType, Kind } from "graphql"; -import neo4j, { isDateTime } from "neo4j-driver"; +import type neo4j from "neo4j-driver"; +import { isDateTime } from "neo4j-driver"; export const GraphQLDateTime = new GraphQLScalarType({ name: "DateTime", @@ -43,10 +44,6 @@ export const GraphQLDateTime = new GraphQLScalarType({ throw new GraphQLError(`DateTime cannot represent non temporal value: ${inputValue}`); } - return neo4j.types.DateTime.fromStandardDate(date); - } - - if (isDateTime(inputValue)) { return inputValue; } @@ -57,6 +54,6 @@ export const GraphQLDateTime = new GraphQLScalarType({ throw new GraphQLError("DateTime cannot represent non string value."); } - return neo4j.types.DateTime.fromStandardDate(new Date(ast.value)); + return ast.value; }, }); diff --git a/packages/graphql/src/graphql/scalars/Time.test.ts b/packages/graphql/src/graphql/scalars/Time.test.ts deleted file mode 100644 index ea94f95e40..0000000000 --- a/packages/graphql/src/graphql/scalars/Time.test.ts +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) "Neo4j" - * Neo4j Sweden AB [http://neo4j.com] - * - * This file is part of Neo4j. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import { parseTime } from "./Time"; - -describe("Time Scalar", () => { - describe("parseTime", () => { - test.each(["22:20:00", "22:20"])("should properly parse %s", (input: string) => { - const parsedTime = parseTime(input); - expect(parsedTime).toEqual({ - hour: 22, - minute: 20, - second: 0, - nanosecond: 0, - timeZoneOffsetSeconds: 0, - }); - }); - - test("should properly parse time in RFC3339 format", () => { - const parsedTime = parseTime("22:10:15.555-01:02"); - - expect(parsedTime).toEqual({ - hour: 22, - minute: 10, - second: 15, - nanosecond: 555000000, - timeZoneOffsetSeconds: -3720, - }); - }); - - test.each(["22", "22:00.555"])("should not parse %s", (input: string) => { - expect(() => parseTime(input)).toThrow(); - }); - }); -}); diff --git a/packages/graphql/src/graphql/scalars/Time.ts b/packages/graphql/src/graphql/scalars/Time.ts index 6f68e6bc49..9d2eeeeb49 100644 --- a/packages/graphql/src/graphql/scalars/Time.ts +++ b/packages/graphql/src/graphql/scalars/Time.ts @@ -19,30 +19,12 @@ import type { ValueNode } from "graphql"; import { GraphQLError, GraphQLScalarType, Kind } from "graphql"; -import neo4j, { isTime } from "neo4j-driver"; +import neo4j from "neo4j-driver"; export const TIME_REGEX = /^(?[01]\d|2[0-3]):(?[0-5]\d)(:(?[0-5]\d)(\.(?\d{1}(?:\d{0,8})))?((?:[Zz])|((?[-|+])(?[01]\d|2[0-3]):(?[0-5]\d)))?)?$/; -type TimeRegexMatchGroups = { - hour: string; - minute: string; - second: string; - fraction: string; - offsetDirection: string; - offsetHour: string; - offsetMinute: string; -}; - -type ParsedTime = { - hour: number; - minute: number; - second: number; - nanosecond: number; - timeZoneOffsetSeconds: number; -}; - -export const parseTime = (value: unknown): ParsedTime => { +export const validateTime = (value: unknown): string => { if (typeof value !== "string") { throw new TypeError(`Value must be of type string: ${value}`); } @@ -53,39 +35,7 @@ export const parseTime = (value: unknown): ParsedTime => { throw new TypeError(`Value must be formatted as Time: ${value}`); } - const { hour, minute, second, fraction, offsetDirection, offsetHour, offsetMinute } = - match.groups as TimeRegexMatchGroups; - // Calculate the number of nanoseconds by padding the fraction of seconds with zeroes to nine digits - let nanosecond = 0; - if (fraction) { - nanosecond = +`${fraction}000000000`.substring(0, 9); - } - - // Calculate the timeZoneOffsetSeconds by calculating the offset in seconds with the appropriate sign - let timeZoneOffsetSeconds = 0; - if (offsetDirection && offsetHour && offsetMinute) { - const offsetInMinutes = +offsetMinute + +offsetHour * 60; - const offsetInSeconds = offsetInMinutes * 60; - timeZoneOffsetSeconds = +`${offsetDirection}${offsetInSeconds}`; - } - - return { - hour: +hour, - minute: +minute, - second: +(second || 0), - nanosecond, - timeZoneOffsetSeconds, - }; -}; - -const parse = (value: unknown) => { - if (isTime(value)) { - return value; - } - - const { hour, minute, second, nanosecond, timeZoneOffsetSeconds } = parseTime(value); - - return new neo4j.types.Time(hour, minute, second, nanosecond, timeZoneOffsetSeconds); + return value; }; export const GraphQLTime = new GraphQLScalarType({ @@ -105,12 +55,12 @@ export const GraphQLTime = new GraphQLScalarType({ return stringifiedValue; }, parseValue: (value: unknown) => { - return parse(value); + return validateTime(value); }, parseLiteral: (ast: ValueNode) => { if (ast.kind !== Kind.STRING) { throw new GraphQLError(`Only strings can be validated as Time, but received: ${ast.kind}`); } - return parse(ast.value); + return validateTime(ast.value); }, }); diff --git a/packages/graphql/src/schema/validation/custom-rules/directives/default.ts b/packages/graphql/src/schema/validation/custom-rules/directives/default.ts index 68781386c9..3b4022dc70 100644 --- a/packages/graphql/src/schema/validation/custom-rules/directives/default.ts +++ b/packages/graphql/src/schema/validation/custom-rules/directives/default.ts @@ -21,7 +21,7 @@ import { Kind } from "graphql"; import { GRAPHQL_BUILTIN_SCALAR_TYPES } from "../../../../constants"; import { GraphQLDate, GraphQLDateTime, GraphQLLocalDateTime } from "../../../../graphql/scalars"; import { GraphQLLocalTime, parseLocalTime } from "../../../../graphql/scalars/LocalTime"; -import { GraphQLTime, parseTime } from "../../../../graphql/scalars/Time"; +import { GraphQLTime, validateTime } from "../../../../graphql/scalars/Time"; import { DocumentValidationError } from "../utils/document-validation-error"; import type { ObjectOrInterfaceWithExtensions } from "../utils/path-parser"; import { assertArgumentHasSameTypeAsField } from "../utils/same-type-argument-as-field"; @@ -60,7 +60,7 @@ export function verifyDefault(enums: EnumTypeDefinitionNode[]) { } } else if (expectedType === GraphQLTime.name) { try { - parseTime((defaultArg?.value as StringValueNode).value); + validateTime((defaultArg?.value as StringValueNode).value); } catch { throw new DocumentValidationError( `@default.${defaultArg.name.value} is not a valid ${expectedType}`, diff --git a/packages/graphql/src/translate/create-create-and-params.ts b/packages/graphql/src/translate/create-create-and-params.ts index a08301bb76..9553c901df 100644 --- a/packages/graphql/src/translate/create-create-and-params.ts +++ b/packages/graphql/src/translate/create-create-and-params.ts @@ -77,6 +77,7 @@ function createCreateAndParams({ const relationField = node.relationFields.find((x) => key === x.fieldName); const primitiveField = node.primitiveFields.find((x) => key === x.fieldName); const pointField = node.pointFields.find((x) => key === x.fieldName); + const temporalField = node.temporalFields.find((x) => key === x.fieldName); const dbFieldName = mapToDbProperty(node, key); if (primitiveField) { @@ -259,6 +260,22 @@ function createCreateAndParams({ return res; } + if (temporalField && ["DateTime", "Time"].includes(temporalField.typeMeta.name)) { + if (temporalField.typeMeta.array) { + res.creates.push( + `SET ${varName}.${dbFieldName} = [t in $${varNameKey} | ${temporalField.typeMeta.name.toLowerCase()}(t)]` + ); + } else { + res.creates.push( + `SET ${varName}.${dbFieldName} = ${temporalField.typeMeta.name.toLowerCase()}($${varNameKey})` + ); + } + + res.params[varNameKey] = value; + + return res; + } + res.creates.push(`SET ${varName}.${dbFieldName} = $${varNameKey}`); res.params[varNameKey] = value; diff --git a/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDateTimePropertyFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDateTimePropertyFilter.ts new file mode 100644 index 0000000000..bd97e336c0 --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDateTimePropertyFilter.ts @@ -0,0 +1,50 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import type { AggregationLogicalOperator } from "../../../factory/parsers/parse-where-field"; +import { AggregationPropertyFilter } from "./AggregationPropertyFilter"; + +export class AggregationDateTimeFilter extends AggregationPropertyFilter { + protected getOperation(expr: Cypher.Expr): Cypher.ComparisonOp { + return this.createDateTimeOperation({ + operator: this.logicalOperator, + property: expr, + param: new Cypher.Param(this.comparisonValue), + }); + } + + private createDateTimeOperation({ + operator, + property, + param, + }: { + operator: AggregationLogicalOperator; + property: Cypher.Expr; + param: Cypher.Expr; + }) { + const variable = Cypher.datetime(param); + + return this.createBaseOperation({ + operator, + property, + param: variable, + }); + } +} diff --git a/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDurationPropertyFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDurationPropertyFilter.ts index 8036adc709..a6d1059ad5 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDurationPropertyFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationDurationPropertyFilter.ts @@ -18,8 +18,8 @@ */ import Cypher from "@neo4j/cypher-builder"; -import { AggregationPropertyFilter } from "./AggregationPropertyFilter"; import type { AggregationLogicalOperator } from "../../../factory/parsers/parse-where-field"; +import { AggregationPropertyFilter } from "./AggregationPropertyFilter"; export class AggregationDurationFilter extends AggregationPropertyFilter { protected getOperation(expr: Cypher.Expr): Cypher.ComparisonOp { diff --git a/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationTimePropertyFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationTimePropertyFilter.ts new file mode 100644 index 0000000000..a37464df0f --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/filters/aggregation/AggregationTimePropertyFilter.ts @@ -0,0 +1,50 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import type { AggregationLogicalOperator } from "../../../factory/parsers/parse-where-field"; +import { AggregationPropertyFilter } from "./AggregationPropertyFilter"; + +export class AggregationTimeFilter extends AggregationPropertyFilter { + protected getOperation(expr: Cypher.Expr): Cypher.ComparisonOp { + return this.createTimeOperation({ + operator: this.logicalOperator, + property: expr, + param: new Cypher.Param(this.comparisonValue), + }); + } + + private createTimeOperation({ + operator, + property, + param, + }: { + operator: AggregationLogicalOperator; + property: Cypher.Expr; + param: Cypher.Expr; + }) { + const variable = Cypher.time(param); + + return this.createBaseOperation({ + operator, + property, + param: variable, + }); + } +} diff --git a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/CypherFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/CypherFilter.ts index 48f976b538..55d740d198 100644 --- a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/CypherFilter.ts +++ b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/CypherFilter.ts @@ -26,8 +26,10 @@ import type { CustomCypherSelection } from "../../selection/CustomCypherSelectio import type { FilterOperator } from "../Filter"; import { Filter } from "../Filter"; import { coalesceValueIfNeeded } from "../utils/coalesce-if-needed"; +import { createDateTimeOperation } from "../utils/create-date-time-operation"; import { createDurationOperation } from "../utils/create-duration-operation"; import { createPointOperation } from "../utils/create-point-operation"; +import { createTimeOperation } from "../utils/create-time-operation"; /** A property which comparison has already been parsed into a Param */ export class CypherFilter extends Filter { @@ -127,6 +129,24 @@ export class CypherFilter extends Filter { }); } + if (this.attribute.typeHelper.isDateTime()) { + return createDateTimeOperation({ + operator, + property: coalesceProperty, + param: this.comparisonValue, + attribute: this.attribute, + }); + } + + if (this.attribute.typeHelper.isTime()) { + return createTimeOperation({ + operator, + property: coalesceProperty, + param: this.comparisonValue, + attribute: this.attribute, + }); + } + return createComparisonOperation({ operator, property: coalesceProperty, param }); } } diff --git a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/DateTimeFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/DateTimeFilter.ts new file mode 100644 index 0000000000..0eb4c1d65c --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/DateTimeFilter.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import { createDateTimeOperation } from "../utils/create-date-time-operation"; +import { PropertyFilter } from "./PropertyFilter"; + +export class DateTimeFilter extends PropertyFilter { + protected getOperation(prop: Cypher.Property): Cypher.ComparisonOp { + return createDateTimeOperation({ + operator: this.operator || "EQ", + property: prop, + param: new Cypher.Param(this.comparisonValue), + attribute: this.attribute, + }); + } +} diff --git a/packages/graphql/src/translate/queryAST/ast/filters/property-filters/TimeFilter.ts b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/TimeFilter.ts new file mode 100644 index 0000000000..dbd48d96f7 --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/filters/property-filters/TimeFilter.ts @@ -0,0 +1,33 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import { createTimeOperation } from "../utils/create-time-operation"; +import { PropertyFilter } from "./PropertyFilter"; + +export class TimeFilter extends PropertyFilter { + protected getOperation(prop: Cypher.Property): Cypher.ComparisonOp { + return createTimeOperation({ + operator: this.operator || "EQ", + property: prop, + param: new Cypher.Param(this.comparisonValue), + attribute: this.attribute, + }); + } +} diff --git a/packages/graphql/src/translate/queryAST/ast/filters/utils/create-date-time-operation.ts b/packages/graphql/src/translate/queryAST/ast/filters/utils/create-date-time-operation.ts new file mode 100644 index 0000000000..338684781f --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/filters/utils/create-date-time-operation.ts @@ -0,0 +1,71 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import type { AttributeAdapter } from "../../../../../schema-model/attribute/model-adapters/AttributeAdapter"; +import type { FilterOperator } from "../Filter"; + +export function createDateTimeOperation({ + operator, + property, + param, + attribute, +}: { + operator: FilterOperator; + property: Cypher.Expr; + param: Cypher.Param | Cypher.Variable | Cypher.Property; + attribute: AttributeAdapter; +}): Cypher.ComparisonOp { + const datetime = Cypher.datetime(param); + + switch (operator) { + case "LT": + return Cypher.lt(property, datetime); + case "LTE": + return Cypher.lte(property, datetime); + case "GT": + return Cypher.gt(property, datetime); + case "GTE": + return Cypher.gte(property, datetime); + case "EQ": { + if (attribute.typeHelper.isList()) { + const dateTimeList = createDateTimeListComprehension(param); + return Cypher.eq(property, dateTimeList); + } + + return Cypher.eq(property, datetime); + } + case "IN": { + const dateTimeList = createDateTimeListComprehension(param); + return Cypher.in(property, dateTimeList); + } + case "INCLUDES": + return Cypher.in(datetime, property); + default: + throw new Error(`Invalid operator ${operator}`); + } +} + +function createDateTimeListComprehension( + param: Cypher.Param | Cypher.Variable | Cypher.Property +): Cypher.ListComprehension { + const comprehensionVar = new Cypher.Variable(); + const mapDateTime = Cypher.datetime(comprehensionVar); + return new Cypher.ListComprehension(comprehensionVar, param).map(mapDateTime); +} diff --git a/packages/graphql/src/translate/queryAST/ast/filters/utils/create-time-operation.ts b/packages/graphql/src/translate/queryAST/ast/filters/utils/create-time-operation.ts new file mode 100644 index 0000000000..ba811a4b85 --- /dev/null +++ b/packages/graphql/src/translate/queryAST/ast/filters/utils/create-time-operation.ts @@ -0,0 +1,71 @@ +/* + * Copyright (c) "Neo4j" + * Neo4j Sweden AB [http://neo4j.com] + * + * This file is part of Neo4j. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import Cypher from "@neo4j/cypher-builder"; +import type { AttributeAdapter } from "../../../../../schema-model/attribute/model-adapters/AttributeAdapter"; +import type { FilterOperator } from "../Filter"; + +export function createTimeOperation({ + operator, + property, + param, + attribute, +}: { + operator: FilterOperator; + property: Cypher.Expr; + param: Cypher.Param | Cypher.Variable | Cypher.Property; + attribute: AttributeAdapter; +}): Cypher.ComparisonOp { + const time = Cypher.time(param); + + switch (operator) { + case "LT": + return Cypher.lt(property, time); + case "LTE": + return Cypher.lte(property, time); + case "GT": + return Cypher.gt(property, time); + case "GTE": + return Cypher.gte(property, time); + case "EQ": { + if (attribute.typeHelper.isList()) { + const timeList = createTimeListComprehension(param); + return Cypher.eq(property, timeList); + } + + return Cypher.eq(property, time); + } + case "IN": { + const timeList = createTimeListComprehension(param); + return Cypher.in(property, timeList); + } + case "INCLUDES": + return Cypher.in(time, property); + default: + throw new Error(`Invalid operator ${operator}`); + } +} + +function createTimeListComprehension( + param: Cypher.Param | Cypher.Variable | Cypher.Property +): Cypher.ListComprehension { + const comprehensionVar = new Cypher.Variable(); + const mapTime = Cypher.time(comprehensionVar); + return new Cypher.ListComprehension(comprehensionVar, param).map(mapTime); +} diff --git a/packages/graphql/src/translate/queryAST/ast/input-fields/PropertyInputField.ts b/packages/graphql/src/translate/queryAST/ast/input-fields/PropertyInputField.ts index a70c9d4a2e..2a40ca2518 100644 --- a/packages/graphql/src/translate/queryAST/ast/input-fields/PropertyInputField.ts +++ b/packages/graphql/src/translate/queryAST/ast/input-fields/PropertyInputField.ts @@ -78,6 +78,25 @@ export class PropertyInputField extends InputField { const mapPoint = Cypher.point(comprehensionVar); return new Cypher.ListComprehension(comprehensionVar, variable).map(mapPoint); } + + if (this.attribute.typeHelper.isDateTime()) { + if (!this.attribute.typeHelper.isList()) { + return Cypher.datetime(variable); + } + const comprehensionVar = new Cypher.Variable(); + const mapDateTime = Cypher.datetime(comprehensionVar); + return new Cypher.ListComprehension(comprehensionVar, variable).map(mapDateTime); + } + + if (this.attribute.typeHelper.isTime()) { + if (!this.attribute.typeHelper.isList()) { + return Cypher.time(variable); + } + const comprehensionVar = new Cypher.Variable(); + const mapTime = Cypher.time(comprehensionVar); + return new Cypher.ListComprehension(comprehensionVar, variable).map(mapTime); + } + return variable; } diff --git a/packages/graphql/src/translate/queryAST/factory/FilterFactory.ts b/packages/graphql/src/translate/queryAST/factory/FilterFactory.ts index bc7194abc0..ecc2b8205b 100644 --- a/packages/graphql/src/translate/queryAST/factory/FilterFactory.ts +++ b/packages/graphql/src/translate/queryAST/factory/FilterFactory.ts @@ -35,14 +35,18 @@ import type { Filter, FilterOperator, RelationshipWhereOperator } from "../ast/f import { isLegacyRelationshipOperator } from "../ast/filters/Filter"; import { LogicalFilter } from "../ast/filters/LogicalFilter"; import { RelationshipFilter } from "../ast/filters/RelationshipFilter"; +import { AggregationDateTimeFilter } from "../ast/filters/aggregation/AggregationDateTimePropertyFilter"; import { AggregationDurationFilter } from "../ast/filters/aggregation/AggregationDurationPropertyFilter"; import { AggregationFilter } from "../ast/filters/aggregation/AggregationFilter"; import { AggregationPropertyFilter } from "../ast/filters/aggregation/AggregationPropertyFilter"; +import { AggregationTimeFilter } from "../ast/filters/aggregation/AggregationTimePropertyFilter"; import { CountFilter } from "../ast/filters/aggregation/CountFilter"; import { CypherFilter } from "../ast/filters/property-filters/CypherFilter"; +import { DateTimeFilter } from "../ast/filters/property-filters/DateTimeFilter"; import { DurationFilter } from "../ast/filters/property-filters/DurationFilter"; import { PointFilter } from "../ast/filters/property-filters/PointFilter"; import { PropertyFilter } from "../ast/filters/property-filters/PropertyFilter"; +import { TimeFilter } from "../ast/filters/property-filters/TimeFilter"; import { TypenameFilter } from "../ast/filters/property-filters/TypenameFilter"; import { CustomCypherSelection } from "../ast/selection/CustomCypherSelection"; import { getConcreteEntities } from "../utils/get-concrete-entities"; @@ -268,6 +272,22 @@ export class FilterFactory { attachedTo, }); } + if (attribute.typeHelper.isDateTime()) { + return new DateTimeFilter({ + attribute, + comparisonValue, + operator, + attachedTo, + }); + } + if (attribute.typeHelper.isTime()) { + return new TimeFilter({ + attribute, + comparisonValue, + operator, + attachedTo, + }); + } return new PropertyFilter({ attribute, @@ -891,6 +911,26 @@ export class FilterFactory { }); } + if (attr.typeHelper.isDateTime()) { + return new AggregationDateTimeFilter({ + attribute: attr, + comparisonValue: value, + logicalOperator: parsedOperator || "EQUAL", + aggregationOperator: parsedAggregationOperation, + attachedTo, + }); + } + + if (attr.typeHelper.isTime()) { + return new AggregationTimeFilter({ + attribute: attr, + comparisonValue: value, + logicalOperator: parsedOperator || "EQUAL", + aggregationOperator: parsedAggregationOperation, + attachedTo, + }); + } + return new AggregationPropertyFilter({ attribute: attr, relationship, @@ -914,6 +954,26 @@ export class FilterFactory { }); } + if (attr.typeHelper.isDateTime()) { + return new AggregationDateTimeFilter({ + attribute: attr, + comparisonValue: value, + logicalOperator: logicalOperator || "EQUAL", + aggregationOperator: aggregationOperator, + attachedTo, + }); + } + + if (attr.typeHelper.isTime()) { + return new AggregationTimeFilter({ + attribute: attr, + comparisonValue: value, + logicalOperator: logicalOperator || "EQUAL", + aggregationOperator: aggregationOperator, + attachedTo, + }); + } + return new AggregationPropertyFilter({ attribute: attr, relationship, diff --git a/packages/graphql/src/translate/utils/get-mutation-field-statements.ts b/packages/graphql/src/translate/utils/get-mutation-field-statements.ts index 4effb42d14..12814ae6e7 100644 --- a/packages/graphql/src/translate/utils/get-mutation-field-statements.ts +++ b/packages/graphql/src/translate/utils/get-mutation-field-statements.ts @@ -69,12 +69,23 @@ export function getMutationFieldStatements({ switch (operator ?? "SET") { case "SET": { const isSpatial = SPATIAL_TYPES.includes(settableField.typeMeta.name); + const isZonedTemporal = ["DateTime", "Time"].includes(settableField.typeMeta.name); if (isSpatial) { if (settableField.typeMeta.array) { strs.push(`SET ${varName}.${dbFieldName} = [p in $${param} | point(p)]`); } else { strs.push(`SET ${varName}.${dbFieldName} = point($${param})`); } + } else if (isZonedTemporal) { + if (settableField.typeMeta.array) { + strs.push( + `SET ${varName}.${dbFieldName} = [t in $${param} | ${settableField.typeMeta.name.toLowerCase()}(t)]` + ); + } else { + strs.push( + `SET ${varName}.${dbFieldName} = ${settableField.typeMeta.name.toLowerCase()}($${param})` + ); + } } else { strs.push(`SET ${varName}.${dbFieldName} = $${param}`); } @@ -95,11 +106,20 @@ export function getMutationFieldStatements({ } case "PUSH": { const pointArrayField = nodeOrRel.pointFields.find((x) => x.fieldName === settableField.fieldName); + const zonedTemporalArrayField = nodeOrRel.temporalFields.find( + (x) => + x.fieldName === settableField.fieldName && + ["DateTime", "Time"].includes(settableField.typeMeta.name) + ); if (pointArrayField) { strs.push( `SET ${varName}.${dbFieldName} = ${varName}.${dbFieldName} + [p in $${param} | point(p)]` ); + } else if (zonedTemporalArrayField) { + strs.push( + `SET ${varName}.${dbFieldName} = ${varName}.${dbFieldName} + [t in $${param} | ${settableField.typeMeta.name.toLowerCase()}(t)]` + ); } else { strs.push(`SET ${varName}.${dbFieldName} = ${varName}.${dbFieldName} + $${param}`); } diff --git a/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-time-filter-deprecated.int.test.ts b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-time-filter-deprecated.int.test.ts index 14680d3b34..6275f1ebf8 100644 --- a/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-time-filter-deprecated.int.test.ts +++ b/packages/graphql/tests/integration/deprecations/generic-filtering/types/types-time-filter-deprecated.int.test.ts @@ -17,9 +17,7 @@ * limitations under the License. */ -import { Time } from "neo4j-driver"; import { generate } from "randomstring"; -import { parseTime } from "../../../../../src/graphql/scalars/Time"; import type { UniqueType } from "../../../../utils/graphql-types"; import { TestHelper } from "../../../../utils/tests-helper"; @@ -47,17 +45,15 @@ describe("Time - deprecated filters", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const id = generate({ readable: false }); - const date = new Date("2024-02-17T11:49:48.322Z"); - const time = date.toISOString().split("T")[1]; - const neo4jTime = Time.fromStandardDate(date); - const parsedTime = parseTime(time); + const time = "11:49:48.322000000Z"; await testHelper.executeCypher( ` CREATE (movie:${Movie}) - SET movie = $movie + SET movie.id = $id + SET movie.time = time($time) `, - { movie: { id, time: neo4jTime } } + { id, time } ); const query = /* GraphQL */ ` @@ -76,7 +72,7 @@ describe("Time - deprecated filters", () => { const graphqlMovie: { id: string; time: string } = (graphqlResult.data as any)[Movie.plural][0]; expect(graphqlMovie).toBeDefined(); expect(graphqlMovie.id).toEqual(id); - expect(parseTime(graphqlMovie.time)).toStrictEqual(parsedTime); + expect(graphqlMovie.time).toStrictEqual(time); }); test.each(["LT", "LTE", "GT", "GTE"])("should filter based on time comparison for filter: %s", async (filter) => { @@ -90,51 +86,33 @@ describe("Time - deprecated filters", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const futureId = generate({ readable: false }); - const future = "13:00:00"; - const parsedFuture = parseTime(future); - const neo4jFuture = new Time( - parsedFuture.hour, - parsedFuture.minute, - parsedFuture.second, - parsedFuture.nanosecond, - parsedFuture.timeZoneOffsetSeconds - ); + const future = "13:00:00Z"; const presentId = generate({ readable: false }); - const present = "12:00:00"; - const parsedPresent = parseTime(present); - const neo4jPresent = new Time( - parsedPresent.hour, - parsedPresent.minute, - parsedPresent.second, - parsedPresent.nanosecond, - parsedPresent.timeZoneOffsetSeconds - ); + const present = "12:00:00Z"; const pastId = generate({ readable: false }); - const past = "11:00:00"; - const parsedPast = parseTime(past); - const neo4jPast = new Time( - parsedPast.hour, - parsedPast.minute, - parsedPast.second, - parsedPast.nanosecond, - parsedPast.timeZoneOffsetSeconds - ); + const past = "11:00:00Z"; await testHelper.executeCypher( ` - CREATE (future:${Movie}) - SET future = $future - CREATE (present:${Movie}) - SET present = $present - CREATE (past:${Movie}) - SET past = $past - `, + CREATE (future:${Movie}) + SET future.id = $futureId + SET future.time = time($future) + CREATE (present:${Movie}) + SET present.id = $presentId + SET present.time = time($present) + CREATE (past:${Movie}) + SET past.id = $pastId + SET past.time = time($past) + `, { - future: { id: futureId, time: neo4jFuture }, - present: { id: presentId, time: neo4jPresent }, - past: { id: pastId, time: neo4jPast }, + futureId, + future, + presentId, + present, + pastId, + past, } ); @@ -165,31 +143,31 @@ describe("Time - deprecated filters", () => { if (filter === "LT") { expect(graphqlMovies).toHaveLength(1); expect(graphqlMovies[0]?.id).toBe(pastId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPast); + expect(graphqlMovies[0]?.time).toStrictEqual(past); } if (filter === "LTE") { expect(graphqlMovies).toHaveLength(2); expect(graphqlMovies[0]?.id).toBe(pastId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPast); + expect(graphqlMovies[0]?.time).toStrictEqual(past); expect(graphqlMovies[1]?.id).toBe(presentId); - expect(parseTime(graphqlMovies[1]?.time)).toStrictEqual(parsedPresent); + expect(graphqlMovies[1]?.time).toStrictEqual(present); } if (filter === "GT") { expect(graphqlMovies).toHaveLength(1); expect(graphqlMovies[0]?.id).toBe(futureId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedFuture); + expect(graphqlMovies[0]?.time).toStrictEqual(future); } if (filter === "GTE") { expect(graphqlMovies).toHaveLength(2); expect(graphqlMovies[0]?.id).toBe(presentId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPresent); + expect(graphqlMovies[0]?.time).toStrictEqual(present); expect(graphqlMovies[1]?.id).toBe(futureId); - expect(parseTime(graphqlMovies[1]?.time)).toStrictEqual(parsedFuture); + expect(graphqlMovies[1]?.time).toStrictEqual(future); } /* eslint-enable jest/no-conditional-expect */ }); diff --git a/packages/graphql/tests/integration/directives/populatedBy/populatedBy-node-properties.int.test.ts b/packages/graphql/tests/integration/directives/populatedBy/populatedBy-node-properties.int.test.ts index 8458e7b536..1df2cd5d7a 100644 --- a/packages/graphql/tests/integration/directives/populatedBy/populatedBy-node-properties.int.test.ts +++ b/packages/graphql/tests/integration/directives/populatedBy/populatedBy-node-properties.int.test.ts @@ -1747,7 +1747,7 @@ describe("@populatedBy directive - Node properties", () => { [testMovie.plural]: [ { id: movieId, - callback: `${date.toISOString().split("T")[1]?.split("Z")[0]}000000Z`, + callback: `${date.toISOString().split("T")[1]?.split("Z")[0]}Z`, }, ], }, @@ -1804,7 +1804,7 @@ describe("@populatedBy directive - Node properties", () => { [testMovie.plural]: [ { id: movieId, - callback: `${date.toISOString().split("T")[1]?.split("Z")[0]}000000Z`, + callback: `${date.toISOString().split("T")[1]?.split("Z")[0]}Z`, }, ], }, diff --git a/packages/graphql/tests/integration/directives/populatedBy/populatedBy-relationship-properties.int.test.ts b/packages/graphql/tests/integration/directives/populatedBy/populatedBy-relationship-properties.int.test.ts index 2080643540..6d0dfc8ae7 100644 --- a/packages/graphql/tests/integration/directives/populatedBy/populatedBy-relationship-properties.int.test.ts +++ b/packages/graphql/tests/integration/directives/populatedBy/populatedBy-relationship-properties.int.test.ts @@ -2439,7 +2439,7 @@ describe("@populatedBy directive - Relationship properties", () => { description: "@populatedBy - Time", type: "Time", callback: () => Promise.resolve(`${date.toISOString().split("T")[1]}`), - expectedValue: `${date.toISOString().split("T")[1]?.split("Z")[0]}000000Z`, + expectedValue: `${date.toISOString().split("T")[1]?.split("Z")[0]}Z`, }, { description: "@populatedBy - LocalDateTime", diff --git a/packages/graphql/tests/integration/types/time.int.test.ts b/packages/graphql/tests/integration/types/time.int.test.ts index 998c29f7ee..5c48d99dfb 100644 --- a/packages/graphql/tests/integration/types/time.int.test.ts +++ b/packages/graphql/tests/integration/types/time.int.test.ts @@ -17,9 +17,8 @@ * limitations under the License. */ -import { Time, isTime } from "neo4j-driver"; +import { isTime } from "neo4j-driver"; import { generate } from "randomstring"; -import { parseTime } from "../../../src/graphql/scalars/Time"; import type { UniqueType } from "../../utils/graphql-types"; import { TestHelper } from "../../utils/tests-helper"; @@ -48,8 +47,7 @@ describe("Time", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const id = generate({ readable: false }); - const time = "2024-01-29T03:57:32.358Z".split("T")[1]; - const parsedTime = parseTime(time); + const time = "2024-01-29T03:57:32.358000000Z".split("T")[1]; const mutation = ` mutation ($id: ID!, $time: Time!) { @@ -71,7 +69,7 @@ describe("Time", () => { ][0]; expect(graphqlMovie).toBeDefined(); expect(graphqlMovie.id).toBe(id); - expect(parseTime(graphqlMovie.time)).toStrictEqual(parsedTime); + expect(graphqlMovie.time).toStrictEqual(time); const neo4jResult = await testHelper.executeCypher( ` @@ -85,7 +83,7 @@ describe("Time", () => { expect(neo4jMovie).toBeDefined(); expect(neo4jMovie.id).toEqual(id); expect(isTime(neo4jMovie.time)).toBe(true); - expect(parseTime(neo4jMovie.time.toString())).toStrictEqual(parsedTime); + expect(neo4jMovie.time.toString()).toStrictEqual(time); }); test("should create a movie (with many Times)", async () => { @@ -99,8 +97,7 @@ describe("Time", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const id = generate({ readable: false }); - const times = [...new Array(4)].map(() => "2023-06-09T11:17:47.789Z".split("T")[1]); - const parsedTimes = times.map((time) => parseTime(time)); + const times = [...new Array(4)].map(() => "2023-06-09T11:17:47.789000000Z".split("T")[1]); const mutation = ` mutation ($id: ID!, $times: [Time!]!) { @@ -123,10 +120,8 @@ describe("Time", () => { expect(graphqlMovie.id).toBe(id); expect(graphqlMovie.times).toHaveLength(times.length); - const parsedGraphQLTimes = graphqlMovie.times.map((time) => parseTime(time)); - - parsedTimes.forEach((parsedTime) => { - expect(parsedGraphQLTimes).toContainEqual(parsedTime); + times.forEach((time) => { + expect(graphqlMovie.times).toContainEqual(time); }); const neo4jResult = await testHelper.executeCypher( @@ -146,10 +141,8 @@ describe("Time", () => { expect(isTime(time)).toBe(true); } - const parsedNeo4jTimes = neo4jMovie.times.map((time) => parseTime(time.toString())); - - parsedTimes.forEach((parsedTime) => { - expect(parsedNeo4jTimes).toContainEqual(parsedTime); + neo4jMovie.times.forEach((time) => { + expect(times).toContainEqual(time.toString()); }); }); }); @@ -166,8 +159,7 @@ describe("Time", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const id = generate({ readable: false }); - const time = "2023-07-12T05:44:06.918Z".split("T")[1]; - const parsedTime = parseTime(time); + const time = "2023-07-12T05:44:06.918000000Z".split("T")[1]; await testHelper.executeCypher( ` @@ -197,7 +189,7 @@ describe("Time", () => { ][0]; expect(graphqlMovie).toBeDefined(); expect(graphqlMovie.id).toEqual(id); - expect(parseTime(graphqlMovie.time)).toStrictEqual(parsedTime); + expect(graphqlMovie.time).toStrictEqual(time); const neo4jResult = await testHelper.executeCypher( ` @@ -211,7 +203,7 @@ describe("Time", () => { expect(neo4jMovie).toBeDefined(); expect(neo4jMovie.id).toEqual(id); expect(isTime(neo4jMovie.time)).toBe(true); - expect(parseTime(neo4jMovie.time.toString())).toStrictEqual(parsedTime); + expect(neo4jMovie.time.toString()).toStrictEqual(time); }); }); @@ -227,17 +219,15 @@ describe("Time", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const id = generate({ readable: false }); - const date = new Date("2024-02-17T11:49:48.322Z"); - const time = date.toISOString().split("T")[1]; - const neo4jTime = Time.fromStandardDate(date); - const parsedTime = parseTime(time); + const time = "11:49:48.322000000Z"; await testHelper.executeCypher( ` CREATE (movie:${Movie}) - SET movie = $movie + SET movie.id = $id + SET movie.time = time($time) `, - { movie: { id, time: neo4jTime } } + { id, time } ); const query = /* GraphQL */ ` @@ -256,7 +246,7 @@ describe("Time", () => { const graphqlMovie: { id: string; time: string } = (graphqlResult.data as any)[Movie.plural][0]; expect(graphqlMovie).toBeDefined(); expect(graphqlMovie.id).toEqual(id); - expect(parseTime(graphqlMovie.time)).toStrictEqual(parsedTime); + expect(graphqlMovie.time).toStrictEqual(time); }); test.each(["lt", "lte", "gt", "gte"])( @@ -272,51 +262,33 @@ describe("Time", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const futureId = generate({ readable: false }); - const future = "13:00:00"; - const parsedFuture = parseTime(future); - const neo4jFuture = new Time( - parsedFuture.hour, - parsedFuture.minute, - parsedFuture.second, - parsedFuture.nanosecond, - parsedFuture.timeZoneOffsetSeconds - ); + const future = "13:00:00Z"; const presentId = generate({ readable: false }); - const present = "12:00:00"; - const parsedPresent = parseTime(present); - const neo4jPresent = new Time( - parsedPresent.hour, - parsedPresent.minute, - parsedPresent.second, - parsedPresent.nanosecond, - parsedPresent.timeZoneOffsetSeconds - ); + const present = "12:00:00Z"; const pastId = generate({ readable: false }); - const past = "11:00:00"; - const parsedPast = parseTime(past); - const neo4jPast = new Time( - parsedPast.hour, - parsedPast.minute, - parsedPast.second, - parsedPast.nanosecond, - parsedPast.timeZoneOffsetSeconds - ); + const past = "11:00:00Z"; await testHelper.executeCypher( ` - CREATE (future:${Movie}) - SET future = $future - CREATE (present:${Movie}) - SET present = $present - CREATE (past:${Movie}) - SET past = $past - `, + CREATE (future:${Movie}) + SET future.id = $futureId + SET future.time = time($future) + CREATE (present:${Movie}) + SET present.id = $presentId + SET present.time = time($present) + CREATE (past:${Movie}) + SET past.id = $pastId + SET past.time = time($past) + `, { - future: { id: futureId, time: neo4jFuture }, - present: { id: presentId, time: neo4jPresent }, - past: { id: pastId, time: neo4jPast }, + futureId, + future, + presentId, + present, + pastId, + past, } ); @@ -347,31 +319,31 @@ describe("Time", () => { if (filter === "lt") { expect(graphqlMovies).toHaveLength(1); expect(graphqlMovies[0]?.id).toBe(pastId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPast); + expect(graphqlMovies[0]?.time).toStrictEqual(past); } if (filter === "lte") { expect(graphqlMovies).toHaveLength(2); expect(graphqlMovies[0]?.id).toBe(pastId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPast); + expect(graphqlMovies[0]?.time).toStrictEqual(past); expect(graphqlMovies[1]?.id).toBe(presentId); - expect(parseTime(graphqlMovies[1]?.time)).toStrictEqual(parsedPresent); + expect(graphqlMovies[1]?.time).toStrictEqual(present); } if (filter === "gt") { expect(graphqlMovies).toHaveLength(1); expect(graphqlMovies[0]?.id).toBe(futureId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedFuture); + expect(graphqlMovies[0]?.time).toStrictEqual(future); } if (filter === "gte") { expect(graphqlMovies).toHaveLength(2); expect(graphqlMovies[0]?.id).toBe(presentId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPresent); + expect(graphqlMovies[0]?.time).toStrictEqual(present); expect(graphqlMovies[1]?.id).toBe(futureId); - expect(parseTime(graphqlMovies[1]?.time)).toStrictEqual(parsedFuture); + expect(graphqlMovies[1]?.time).toStrictEqual(future); } /* eslint-enable jest/no-conditional-expect */ } @@ -389,51 +361,33 @@ describe("Time", () => { await testHelper.initNeo4jGraphQL({ typeDefs }); const futureId = generate({ readable: false }); - const future = "13:00:00"; - const parsedFuture = parseTime(future); - const neo4jFuture = new Time( - parsedFuture.hour, - parsedFuture.minute, - parsedFuture.second, - parsedFuture.nanosecond, - parsedFuture.timeZoneOffsetSeconds - ); + const future = "13:00:00Z"; const presentId = generate({ readable: false }); - const present = "12:00:00"; - const parsedPresent = parseTime(present); - const neo4jPresent = new Time( - parsedPresent.hour, - parsedPresent.minute, - parsedPresent.second, - parsedPresent.nanosecond, - parsedPresent.timeZoneOffsetSeconds - ); + const present = "12:00:00Z"; const pastId = generate({ readable: false }); - const past = "11:00:00"; - const parsedPast = parseTime(past); - const neo4jPast = new Time( - parsedPast.hour, - parsedPast.minute, - parsedPast.second, - parsedPast.nanosecond, - parsedPast.timeZoneOffsetSeconds - ); + const past = "11:00:00Z"; await testHelper.executeCypher( ` CREATE (future:${Movie}) - SET future = $future + SET future.id = $futureId + SET future.time = time($future) CREATE (present:${Movie}) - SET present = $present + SET present.id = $presentId + SET present.time = time($present) CREATE (past:${Movie}) - SET past = $past + SET past.id = $pastId + SET past.time = time($past) `, { - future: { id: futureId, time: neo4jFuture }, - present: { id: presentId, time: neo4jPresent }, - past: { id: pastId, time: neo4jPast }, + futureId, + future, + presentId, + present, + pastId, + past, } ); @@ -464,24 +418,24 @@ describe("Time", () => { /* eslint-disable jest/no-conditional-expect */ if (sort === "ASC") { expect(graphqlMovies[0]?.id).toBe(pastId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedPast); + expect(graphqlMovies[0]?.time).toStrictEqual(past); expect(graphqlMovies[1]?.id).toBe(presentId); - expect(parseTime(graphqlMovies[1]?.time)).toStrictEqual(parsedPresent); + expect(graphqlMovies[1]?.time).toStrictEqual(present); expect(graphqlMovies[2]?.id).toBe(futureId); - expect(parseTime(graphqlMovies[2]?.time)).toStrictEqual(parsedFuture); + expect(graphqlMovies[2]?.time).toStrictEqual(future); } if (sort === "DESC") { expect(graphqlMovies[0]?.id).toBe(futureId); - expect(parseTime(graphqlMovies[0]?.time)).toStrictEqual(parsedFuture); + expect(graphqlMovies[0]?.time).toStrictEqual(future); expect(graphqlMovies[1]?.id).toBe(presentId); - expect(parseTime(graphqlMovies[1]?.time)).toStrictEqual(parsedPresent); + expect(graphqlMovies[1]?.time).toStrictEqual(present); expect(graphqlMovies[2]?.id).toBe(pastId); - expect(parseTime(graphqlMovies[2]?.time)).toStrictEqual(parsedPast); + expect(graphqlMovies[2]?.time).toStrictEqual(past); } /* eslint-enable jest/no-conditional-expect */ }); diff --git a/packages/graphql/tests/tck/aggregations/where/edge/datetime.test.ts b/packages/graphql/tests/tck/aggregations/where/edge/datetime.test.ts index 229cbb7781..e3176fa0d9 100644 --- a/packages/graphql/tests/tck/aggregations/where/edge/datetime.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/edge/datetime.test.ts @@ -64,7 +64,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someDateTime) = $param0 AS var2 + RETURN min(this0.someDateTime) = datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -73,16 +73,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -105,7 +96,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someDateTime) > $param0 AS var2 + RETURN min(this0.someDateTime) > datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -114,16 +105,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -146,7 +128,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someDateTime) >= $param0 AS var2 + RETURN min(this0.someDateTime) >= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -155,16 +137,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -187,7 +160,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someDateTime) < $param0 AS var2 + RETURN min(this0.someDateTime) < datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -196,16 +169,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -228,7 +192,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someDateTime) <= $param0 AS var2 + RETURN min(this0.someDateTime) <= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -237,16 +201,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -269,7 +224,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someDateTime) = $param0 AS var2 + RETURN max(this0.someDateTime) = datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -278,16 +233,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -310,7 +256,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someDateTime) > $param0 AS var2 + RETURN max(this0.someDateTime) > datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -319,16 +265,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -351,7 +288,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someDateTime) >= $param0 AS var2 + RETURN max(this0.someDateTime) >= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -360,16 +297,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -392,7 +320,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someDateTime) < $param0 AS var2 + RETURN max(this0.someDateTime) < datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -401,16 +329,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -433,7 +352,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someDateTime) <= $param0 AS var2 + RETURN max(this0.someDateTime) <= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -442,16 +361,7 @@ describe("Cypher Aggregations where edge with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); diff --git a/packages/graphql/tests/tck/aggregations/where/edge/time.test.ts b/packages/graphql/tests/tck/aggregations/where/edge/time.test.ts index df102a2f96..2a88b1282b 100644 --- a/packages/graphql/tests/tck/aggregations/where/edge/time.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/edge/time.test.ts @@ -62,7 +62,7 @@ describe("Cypher Aggregations where edge with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someTime) = $param0 AS var2 + RETURN min(this0.someTime) = time($param0) AS var2 } WITH * WHERE var2 = true @@ -71,13 +71,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -98,7 +92,7 @@ describe("Cypher Aggregations where edge with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someTime) > $param0 AS var2 + RETURN min(this0.someTime) > time($param0) AS var2 } WITH * WHERE var2 = true @@ -107,13 +101,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -134,7 +122,7 @@ describe("Cypher Aggregations where edge with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someTime) >= $param0 AS var2 + RETURN min(this0.someTime) >= time($param0) AS var2 } WITH * WHERE var2 = true @@ -143,13 +131,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -170,7 +152,7 @@ describe("Cypher Aggregations where edge with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someTime) < $param0 AS var2 + RETURN min(this0.someTime) < time($param0) AS var2 } WITH * WHERE var2 = true @@ -179,13 +161,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -206,7 +182,7 @@ describe("Cypher Aggregations where edge with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this0.someTime) <= $param0 AS var2 + RETURN min(this0.someTime) <= time($param0) AS var2 } WITH * WHERE var2 = true @@ -215,13 +191,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -242,7 +212,7 @@ describe("Cypher Aggregations where edge with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someTime) = $param0 AS var2 + RETURN max(this0.someTime) = time($param0) AS var2 } WITH * WHERE var2 = true @@ -251,13 +221,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -278,7 +242,7 @@ describe("Cypher Aggregations where edge with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someTime) > $param0 AS var2 + RETURN max(this0.someTime) > time($param0) AS var2 } WITH * WHERE var2 = true @@ -287,13 +251,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -314,7 +272,7 @@ describe("Cypher Aggregations where edge with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someTime) >= $param0 AS var2 + RETURN max(this0.someTime) >= time($param0) AS var2 } WITH * WHERE var2 = true @@ -323,13 +281,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -350,7 +302,7 @@ describe("Cypher Aggregations where edge with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someTime) < $param0 AS var2 + RETURN max(this0.someTime) < time($param0) AS var2 } WITH * WHERE var2 = true @@ -359,13 +311,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -386,7 +332,7 @@ describe("Cypher Aggregations where edge with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this0.someTime) <= $param0 AS var2 + RETURN max(this0.someTime) <= time($param0) AS var2 } WITH * WHERE var2 = true @@ -395,13 +341,7 @@ describe("Cypher Aggregations where edge with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); diff --git a/packages/graphql/tests/tck/aggregations/where/node/datetime.test.ts b/packages/graphql/tests/tck/aggregations/where/node/datetime.test.ts index 2dfa600783..953c02b178 100644 --- a/packages/graphql/tests/tck/aggregations/where/node/datetime.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/node/datetime.test.ts @@ -60,7 +60,7 @@ describe("Cypher Aggregations where node with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someDateTime) = $param0 AS var2 + RETURN min(this1.someDateTime) = datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -69,16 +69,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -101,7 +92,7 @@ describe("Cypher Aggregations where node with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someDateTime) > $param0 AS var2 + RETURN min(this1.someDateTime) > datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -110,16 +101,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -142,7 +124,7 @@ describe("Cypher Aggregations where node with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someDateTime) >= $param0 AS var2 + RETURN min(this1.someDateTime) >= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -151,16 +133,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -183,7 +156,7 @@ describe("Cypher Aggregations where node with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someDateTime) < $param0 AS var2 + RETURN min(this1.someDateTime) < datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -192,16 +165,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -224,7 +188,7 @@ describe("Cypher Aggregations where node with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someDateTime) <= $param0 AS var2 + RETURN min(this1.someDateTime) <= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -233,16 +197,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -265,7 +220,7 @@ describe("Cypher Aggregations where node with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someDateTime) = $param0 AS var2 + RETURN max(this1.someDateTime) = datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -274,16 +229,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -306,7 +252,7 @@ describe("Cypher Aggregations where node with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someDateTime) > $param0 AS var2 + RETURN max(this1.someDateTime) > datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -315,16 +261,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -347,7 +284,7 @@ describe("Cypher Aggregations where node with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someDateTime) >= $param0 AS var2 + RETURN max(this1.someDateTime) >= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -356,16 +293,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -388,7 +316,7 @@ describe("Cypher Aggregations where node with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someDateTime) < $param0 AS var2 + RETURN max(this1.someDateTime) < datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -397,16 +325,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); @@ -429,7 +348,7 @@ describe("Cypher Aggregations where node with DateTime", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someDateTime) <= $param0 AS var2 + RETURN max(this1.someDateTime) <= datetime($param0) AS var2 } WITH * WHERE var2 = true @@ -438,16 +357,7 @@ describe("Cypher Aggregations where node with DateTime", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 9, - \\"day\\": 25, - \\"hour\\": 12, - \\"minute\\": 51, - \\"second\\": 24, - \\"nanosecond\\": 37000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-09-25T12:51:24.037Z\\" }" `); }); diff --git a/packages/graphql/tests/tck/aggregations/where/node/time.test.ts b/packages/graphql/tests/tck/aggregations/where/node/time.test.ts index cbd91c97da..8cf26a17c4 100644 --- a/packages/graphql/tests/tck/aggregations/where/node/time.test.ts +++ b/packages/graphql/tests/tck/aggregations/where/node/time.test.ts @@ -58,7 +58,7 @@ describe("Cypher Aggregations where node with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someTime) = $param0 AS var2 + RETURN min(this1.someTime) = time($param0) AS var2 } WITH * WHERE var2 = true @@ -67,13 +67,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -94,7 +88,7 @@ describe("Cypher Aggregations where node with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someTime) > $param0 AS var2 + RETURN min(this1.someTime) > time($param0) AS var2 } WITH * WHERE var2 = true @@ -103,13 +97,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -130,7 +118,7 @@ describe("Cypher Aggregations where node with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someTime) >= $param0 AS var2 + RETURN min(this1.someTime) >= time($param0) AS var2 } WITH * WHERE var2 = true @@ -139,13 +127,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -166,7 +148,7 @@ describe("Cypher Aggregations where node with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someTime) < $param0 AS var2 + RETURN min(this1.someTime) < time($param0) AS var2 } WITH * WHERE var2 = true @@ -175,13 +157,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -202,7 +178,7 @@ describe("Cypher Aggregations where node with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN min(this1.someTime) <= $param0 AS var2 + RETURN min(this1.someTime) <= time($param0) AS var2 } WITH * WHERE var2 = true @@ -211,13 +187,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -238,7 +208,7 @@ describe("Cypher Aggregations where node with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someTime) = $param0 AS var2 + RETURN max(this1.someTime) = time($param0) AS var2 } WITH * WHERE var2 = true @@ -247,13 +217,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -274,7 +238,7 @@ describe("Cypher Aggregations where node with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someTime) > $param0 AS var2 + RETURN max(this1.someTime) > time($param0) AS var2 } WITH * WHERE var2 = true @@ -283,13 +247,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -310,7 +268,7 @@ describe("Cypher Aggregations where node with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someTime) >= $param0 AS var2 + RETURN max(this1.someTime) >= time($param0) AS var2 } WITH * WHERE var2 = true @@ -319,13 +277,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -346,7 +298,7 @@ describe("Cypher Aggregations where node with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someTime) < $param0 AS var2 + RETURN max(this1.someTime) < time($param0) AS var2 } WITH * WHERE var2 = true @@ -355,13 +307,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -382,7 +328,7 @@ describe("Cypher Aggregations where node with Time", () => { CALL { WITH this MATCH (this)<-[this0:LIKES]-(this1:User) - RETURN max(this1.someTime) <= $param0 AS var2 + RETURN max(this1.someTime) <= time($param0) AS var2 } WITH * WHERE var2 = true @@ -391,13 +337,7 @@ describe("Cypher Aggregations where node with Time", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); diff --git a/packages/graphql/tests/tck/connections/filtering/relationship/temporal.test.ts b/packages/graphql/tests/tck/connections/filtering/relationship/temporal.test.ts index c05110aa34..bf1011d6e6 100644 --- a/packages/graphql/tests/tck/connections/filtering/relationship/temporal.test.ts +++ b/packages/graphql/tests/tck/connections/filtering/relationship/temporal.test.ts @@ -78,7 +78,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Temporal", () => CALL { WITH this MATCH (this)<-[this0:ACTED_IN]-(this1:Actor) - WHERE (this0.startDate > $param0 AND this0.endDateTime < $param1) + WHERE (this0.startDate > $param0 AND this0.endDateTime < datetime($param1)) WITH collect({ node: this1, relationship: this0 }) AS edges WITH edges, size(edges) AS totalCount CALL { @@ -99,16 +99,7 @@ describe("Cypher -> Connections -> Filtering -> Relationship -> Temporal", () => \\"month\\": 1, \\"day\\": 1 }, - \\"param1\\": { - \\"year\\": 2010, - \\"month\\": 1, - \\"day\\": 1, - \\"hour\\": 0, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param1\\": \\"2010-01-01T00:00:00.000Z\\" }" `); }); diff --git a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-temporal.test.ts b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-temporal.test.ts index 765c62fc98..7787ea9fdc 100644 --- a/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-temporal.test.ts +++ b/packages/graphql/tests/tck/directives/cypher/filtering/cypher-filtering-temporal.test.ts @@ -63,7 +63,7 @@ describe("cypher directive filtering - Auth", () => { RETURN this0 AS var1 } WITH * - WHERE var1 > $param0 + WHERE var1 > datetime($param0) CALL { WITH this CALL { @@ -79,16 +79,7 @@ describe("cypher directive filtering - Auth", () => { expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2024, - \\"month\\": 9, - \\"day\\": 2, - \\"hour\\": 0, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2024-09-02T00:00:00Z\\" }" `); }); diff --git a/packages/graphql/tests/tck/issues/360.test.ts b/packages/graphql/tests/tck/issues/360.test.ts index a9dc2009af..8666b3fe29 100644 --- a/packages/graphql/tests/tck/issues/360.test.ts +++ b/packages/graphql/tests/tck/issues/360.test.ts @@ -64,32 +64,14 @@ describe("#360", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "MATCH (this:Event) - WHERE (this.start >= $param0 AND this.start <= $param1) + WHERE (this.start >= datetime($param0) AND this.start <= datetime($param1)) RETURN this { .activity, start: apoc.date.convertFormat(toString(this.start), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 7, - \\"day\\": 17, - \\"hour\\": 23, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - }, - \\"param1\\": { - \\"year\\": 2021, - \\"month\\": 7, - \\"day\\": 18, - \\"hour\\": 22, - \\"minute\\": 59, - \\"second\\": 59, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-07-18T00:00:00+0100\\", + \\"param1\\": \\"2021-07-18T23:59:59+0100\\" }" `); }); @@ -118,32 +100,14 @@ describe("#360", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "MATCH (this:Event) - WHERE (this.start >= $param0 OR this.start <= $param1) + WHERE (this.start >= datetime($param0) OR this.start <= datetime($param1)) RETURN this { .activity, start: apoc.date.convertFormat(toString(this.start), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 2021, - \\"month\\": 7, - \\"day\\": 17, - \\"hour\\": 23, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - }, - \\"param1\\": { - \\"year\\": 2021, - \\"month\\": 7, - \\"day\\": 18, - \\"hour\\": 22, - \\"minute\\": 59, - \\"second\\": 59, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"2021-07-18T00:00:00+0100\\", + \\"param1\\": \\"2021-07-18T23:59:59+0100\\" }" `); }); diff --git a/packages/graphql/tests/tck/types/datetime.test.ts b/packages/graphql/tests/tck/types/datetime.test.ts index 9f28f649ac..afb4312e55 100644 --- a/packages/graphql/tests/tck/types/datetime.test.ts +++ b/packages/graphql/tests/tck/types/datetime.test.ts @@ -50,22 +50,13 @@ describe("Cypher DateTime", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "MATCH (this:Movie) - WHERE this.datetime = $param0 + WHERE this.datetime = datetime($param0) RETURN this { datetime: apoc.date.convertFormat(toString(this.datetime), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"year\\": 1970, - \\"month\\": 1, - \\"day\\": 1, - \\"hour\\": 0, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"1970-01-01T00:00:00.000Z\\" }" `); }); @@ -89,7 +80,7 @@ describe("Cypher DateTime", () => { WITH create_var0 CREATE (create_this1:Movie) SET - create_this1.datetime = create_var0.datetime + create_this1.datetime = datetime(create_var0.datetime) RETURN create_this1 } RETURN collect(create_this1 { datetime: apoc.date.convertFormat(toString(create_this1.datetime), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") }) AS data" @@ -99,16 +90,7 @@ describe("Cypher DateTime", () => { "{ \\"create_param0\\": [ { - \\"datetime\\": { - \\"year\\": 1970, - \\"month\\": 1, - \\"day\\": 1, - \\"hour\\": 0, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"datetime\\": \\"1970-01-01T00:00:00.000Z\\" } ] }" @@ -131,22 +113,13 @@ describe("Cypher DateTime", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "MATCH (this:Movie) - SET this.datetime = $this_update_datetime_SET + SET this.datetime = datetime($this_update_datetime_SET) RETURN collect(DISTINCT this { .id, datetime: apoc.date.convertFormat(toString(this.datetime), \\"iso_zoned_date_time\\", \\"iso_offset_date_time\\") }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"this_update_datetime_SET\\": { - \\"year\\": 1970, - \\"month\\": 1, - \\"day\\": 1, - \\"hour\\": 0, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - }, + \\"this_update_datetime_SET\\": \\"1970-01-01T00:00:00.000Z\\", \\"resolvedCallbacks\\": {} }" `); diff --git a/packages/graphql/tests/tck/types/time.test.ts b/packages/graphql/tests/tck/types/time.test.ts index 124a22cb26..85e58c322b 100644 --- a/packages/graphql/tests/tck/types/time.test.ts +++ b/packages/graphql/tests/tck/types/time.test.ts @@ -50,19 +50,13 @@ describe("Cypher Time", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "MATCH (this:Movie) - WHERE this.time = $param0 + WHERE this.time = time($param0) RETURN this { .time } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 12, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"12:00:00\\" }" `); }); @@ -80,19 +74,13 @@ describe("Cypher Time", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "MATCH (this:Movie) - WHERE this.time >= $param0 + WHERE this.time >= time($param0) RETURN this { .time } AS this" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"param0\\": { - \\"hour\\": 13, - \\"minute\\": 45, - \\"second\\": 33, - \\"nanosecond\\": 250000000, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"param0\\": \\"13:45:33.250\\" }" `); }); @@ -116,7 +104,7 @@ describe("Cypher Time", () => { WITH create_var0 CREATE (create_this1:Movie) SET - create_this1.time = create_var0.time + create_this1.time = time(create_var0.time) RETURN create_this1 } RETURN collect(create_this1 { .time }) AS data" @@ -126,13 +114,7 @@ describe("Cypher Time", () => { "{ \\"create_param0\\": [ { - \\"time\\": { - \\"hour\\": 22, - \\"minute\\": 0, - \\"second\\": 15, - \\"nanosecond\\": 555000000, - \\"timeZoneOffsetSeconds\\": -3600 - } + \\"time\\": \\"22:00:15.555-01:00\\" } ] }" @@ -155,19 +137,13 @@ describe("Cypher Time", () => { expect(formatCypher(result.cypher)).toMatchInlineSnapshot(` "MATCH (this:Movie) - SET this.time = $this_update_time_SET + SET this.time = time($this_update_time_SET) RETURN collect(DISTINCT this { .id, .time }) AS data" `); expect(formatParams(result.params)).toMatchInlineSnapshot(` "{ - \\"this_update_time_SET\\": { - \\"hour\\": 9, - \\"minute\\": 24, - \\"second\\": 40, - \\"nanosecond\\": 845512000, - \\"timeZoneOffsetSeconds\\": 23400 - }, + \\"this_update_time_SET\\": \\"09:24:40.845512+06:30\\", \\"resolvedCallbacks\\": {} }" `); @@ -192,7 +168,7 @@ describe("Cypher Time", () => { WITH create_var0 CREATE (create_this1:Movie) SET - create_this1.time = create_var0.time + create_this1.time = time(create_var0.time) RETURN create_this1 } RETURN collect(create_this1 { .time }) AS data" @@ -202,13 +178,7 @@ describe("Cypher Time", () => { "{ \\"create_param0\\": [ { - \\"time\\": { - \\"hour\\": 22, - \\"minute\\": 0, - \\"second\\": 0, - \\"nanosecond\\": 0, - \\"timeZoneOffsetSeconds\\": 0 - } + \\"time\\": \\"22:00\\" } ] }"