Skip to content

Commit

Permalink
Merge pull request #361 from danstarns/fix/empty-and-or
Browse files Browse the repository at this point in the history
Fix/empty and or
  • Loading branch information
darrellwarde authored Jul 28, 2021
2 parents 94b725f + aec4fe8 commit 091d1ed
Show file tree
Hide file tree
Showing 3 changed files with 369 additions and 4 deletions.
11 changes: 7 additions & 4 deletions packages/graphql/src/translate/create-where-and-params.ts
Original file line number Diff line number Diff line change
Expand Up @@ -514,12 +514,15 @@ function createWhereAndParams({
context,
recursing: true,
});

innerClauses.push(`${recurse[0]}`);
res.params = { ...res.params, ...recurse[1] };
if (recurse[0]) {
innerClauses.push(`${recurse[0]}`);
res.params = { ...res.params, ...recurse[1] };
}
});

res.clauses.push(`(${innerClauses.join(` ${key} `)})`);
if (innerClauses.length) {
res.clauses.push(`(${innerClauses.join(` ${key} `)})`);
}

return res;
}
Expand Down
205 changes: 205 additions & 0 deletions packages/graphql/tests/integration/issues/360.int.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,205 @@
/*
* 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 { Driver } from "neo4j-driver";
import { graphql } from "graphql";
import { generate } from "randomstring";
import camelcase from "camelcase";
import pluralize from "pluralize";
import { Neo4jGraphQL } from "../../../src/classes";
import neo4j from "../neo4j";

describe("360", () => {
let driver: Driver;

beforeAll(async () => {
driver = await neo4j();
});

afterAll(async () => {
await driver.close();
});

test("should return all nodes when AND is used and members are optional", async () => {
const session = driver.session();

const type = `${generate({
charset: "alphabetic",
readable: true,
})}Event`;

const pluralType = pluralize(camelcase(type));

const typeDefs = `
type ${type} {
id: ID!
name: String
start: DateTime
end: DateTime
activity: String
}
`;

const neoSchema = new Neo4jGraphQL({
typeDefs,
});

const query = `
query ($rangeStart: DateTime, $rangeEnd: DateTime, $activity: String) {
${pluralType}(where: { AND: [{ start_GTE: $rangeStart }, { start_LTE: $rangeEnd }, { activity: $activity }] }) {
id
}
}
`;

try {
await session.run(
`
CREATE (:${type} {id: randomUUID(), name: randomUUID(), start: datetime(), end: datetime()})
CREATE (:${type} {id: randomUUID(), name: randomUUID(), start: datetime(), end: datetime()})
CREATE (:${type} {id: randomUUID(), name: randomUUID(), start: datetime(), end: datetime()})
`
);

const gqlResult = await graphql({
schema: neoSchema.schema,
source: query,
contextValue: { driver },
});

expect(gqlResult.errors).toBe(undefined);
expect((gqlResult.data as any)[pluralType]).toHaveLength(3);
} finally {
await session.close();
}
});

test("should return all nodes when OR is used and members are optional", async () => {
const session = driver.session();

const type = `${generate({
charset: "alphabetic",
readable: true,
})}Event`;

const pluralType = pluralize(camelcase(type));

const typeDefs = `
type ${type} {
id: ID!
name: String
start: DateTime
end: DateTime
activity: String
}
`;

const neoSchema = new Neo4jGraphQL({
typeDefs,
});

const query = `
query ($rangeStart: DateTime, $rangeEnd: DateTime, $activity: String) {
${pluralType}(where: { OR: [{ start_GTE: $rangeStart }, { start_LTE: $rangeEnd }, { activity: $activity }] }) {
id
}
}
`;

try {
await session.run(
`
CREATE (:${type} {id: randomUUID(), name: randomUUID(), start: datetime(), end: datetime()})
CREATE (:${type} {id: randomUUID(), name: randomUUID(), start: datetime(), end: datetime()})
CREATE (:${type} {id: randomUUID(), name: randomUUID(), start: datetime(), end: datetime()})
`
);

const gqlResult = await graphql({
schema: neoSchema.schema,
source: query,
contextValue: { driver },
});

expect(gqlResult.errors).toBe(undefined);
expect((gqlResult.data as any)[pluralType]).toHaveLength(3);
} finally {
await session.close();
}
});

test("should recreate given test in issue and return correct results", async () => {
const session = driver.session();

const type = `${generate({
charset: "alphabetic",
readable: true,
})}Event`;

const pluralType = pluralize(camelcase(type));

const typeDefs = `
type ${type} {
id: ID!
name: String
start: DateTime
end: DateTime
activity: String
}
`;

const neoSchema = new Neo4jGraphQL({
typeDefs,
});

const rangeStart = new Date().toISOString();
const rangeEnd = new Date().toISOString();

const query = `
query ($rangeStart: DateTime, $rangeEnd: DateTime, $activity: String) {
${pluralType}(where: { OR: [{ start_GTE: $rangeStart }, { start_LTE: $rangeEnd }, { activity: $activity }] }) {
id
}
}
`;

try {
await session.run(
`
CREATE (:${type} {id: randomUUID(), name: randomUUID(), start: datetime($rangeStart), end: datetime($rangeEnd)})
CREATE (:${type} {id: randomUUID(), name: randomUUID(), start: datetime($rangeStart), end: datetime($rangeEnd)})
CREATE (:${type} {id: randomUUID(), name: randomUUID(), start: datetime(), end: datetime()})
`,
{ rangeStart, rangeEnd }
);

const gqlResult = await graphql({
schema: neoSchema.schema,
source: query,
contextValue: { driver },
variableValues: { rangeStart, rangeEnd },
});

expect(gqlResult.errors).toBe(undefined);
expect((gqlResult.data as any)[pluralType]).toHaveLength(3);
} finally {
await session.close();
}
});
});
157 changes: 157 additions & 0 deletions packages/graphql/tests/tck/tck-test-files/cypher/issues/360.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
## #360

<https://github.com/neo4j/graphql/issues/360>

Type definitions:

```schema
type Event {
id: ID!
name: String
start: DateTime
end: DateTime
activity: String
}
```

---

### Should exclude undefined members in AND

**GraphQL input**

```graphql
query($rangeStart: DateTime, $rangeEnd: DateTime, $activity: String) {
events(
where: {
AND: [
{ start_GTE: $rangeStart }
{ start_LTE: $rangeEnd }
{ activity: $activity }
]
}
) {
start
activity
}
}
```

```graphql-params
{
"rangeStart": "2021-07-18T00:00:00+0100",
"rangeEnd": "2021-07-18T23:59:59+0100"
}
```

**Expected Cypher output**

```cypher
MATCH (this:Event)
WHERE (this.start >= $this_AND_start_GTE AND this.start <= $this_AND1_start_LTE)
RETURN this {
start: apoc.date.convertFormat(toString(this.start), "iso_zoned_date_time", "iso_offset_date_time"),
.activity
} as this
```

**Expected Cypher params**

```cypher-params
{
"this_AND1_start_LTE": {
"day": 18,
"hour": 22,
"minute": 59,
"month": 7,
"nanosecond": 0,
"second": 59,
"timeZoneId": null,
"timeZoneOffsetSeconds": 0,
"year": 2021
},
"this_AND_start_GTE": {
"day": 17,
"hour": 23,
"minute": 0,
"month": 7,
"nanosecond": 0,
"second": 0,
"timeZoneId": null,
"timeZoneOffsetSeconds": 0,
"year": 2021
}
}
```

---

### Should exclude undefined members in OR

**GraphQL input**

```graphql
query($rangeStart: DateTime, $rangeEnd: DateTime, $activity: String) {
events(
where: {
OR: [
{ start_GTE: $rangeStart }
{ start_LTE: $rangeEnd }
{ activity: $activity }
]
}
) {
start
activity
}
}
```

```graphql-params
{
"rangeStart": "2021-07-18T00:00:00+0100",
"rangeEnd": "2021-07-18T23:59:59+0100"
}
```

**Expected Cypher output**

```cypher
MATCH (this:Event)
WHERE (this.start >= $this_OR_start_GTE OR this.start <= $this_OR1_start_LTE)
RETURN this {
start: apoc.date.convertFormat(toString(this.start), "iso_zoned_date_time", "iso_offset_date_time"),
.activity
} as this
```

**Expected Cypher params**

```cypher-params
{
"this_OR1_start_LTE": {
"day": 18,
"hour": 22,
"minute": 59,
"month": 7,
"nanosecond": 0,
"second": 59,
"timeZoneId": null,
"timeZoneOffsetSeconds": 0,
"year": 2021
},
"this_OR_start_GTE": {
"day": 17,
"hour": 23,
"minute": 0,
"month": 7,
"nanosecond": 0,
"second": 0,
"timeZoneId": null,
"timeZoneOffsetSeconds": 0,
"year": 2021
}
}
```

---

0 comments on commit 091d1ed

Please sign in to comment.