diff --git a/README.md b/README.md index 18ed7d2..df41542 100644 --- a/README.md +++ b/README.md @@ -276,6 +276,61 @@ query GetAddresses2 { ❓ [see example unit tests.](example/GetAddresses.example.test.ts) +#### using JSONPath on array fields + +By default, mapql will return head (first element) of result from JSONPath query. This can be disabled with argument `head: false`, useful when expecting list of values. Default value is `head: true`. + +example: + +```javascript +const data = { + exampleObj: { + exampleStr: "hello world", + }, + destinations: [ + { + array_string: "foo", + }, + { + array_string: "bar", + }, + { + array_string: "baz", + }, + ], +} +``` + +```graphql + query PathHead { + exampleStr + exampleStrHead(head: true) + exampleStrNoHead(head: false) + destinationsHead(from: "destinations[*].array_string", head: true) + destinationsNoHead(from: "destinations[*].array_string", head: false) + destinations(from: "destinations[*].array_string") + } +``` + +```json +{ + "destinations": "foo", + "destinationsHead": "foo", + "destinationsNoHead": [ + "foo", + "bar", + "baz" + ], + "exampleStr": "hello world", + "exampleStrHead": "hello world", + "exampleStrNoHead": [ + "hello world" + ] +} +``` + +❓ [see example unit tests.](example/PathHead.example.test.ts) + ### filtering fields ⭕ query: diff --git a/example/PathHead.example.test.ts b/example/PathHead.example.test.ts new file mode 100644 index 0000000..575f2e0 --- /dev/null +++ b/example/PathHead.example.test.ts @@ -0,0 +1,43 @@ +import gql from "graphql-tag"; +import map from "../src"; + +test("PathHead", () => { + const data = { + exampleObj: { + exampleStr: "hello world", + }, + destinations: [ + { + array_string: "foo", + }, + { + array_string: "bar", + }, + { + array_string: "baz", + }, + ], + }; + const query = gql` + query PathHead { + exampleStr(from: "exampleObj.exampleStr") + exampleStrHead(from: "exampleObj.exampleStr", head: true) + exampleStrNoHead(from: "exampleObj.exampleStr", head: false) + destinationsHead(from: "destinations[*].array_string", head: true) + destinationsNoHead(from: "destinations[*].array_string", head: false) + destinations(from: "destinations[*].array_string") + } + `; + const result = map(query, data); + expect(result).toEqual( + // + { + destinations: "foo", + destinationsHead: "foo", + destinationsNoHead: ["foo", "bar", "baz"], + exampleStr: "hello world", + exampleStrHead: "hello world", + exampleStrNoHead: ["hello world"], + } + ); +}); diff --git a/src/contract.ts b/src/contract.ts index 5598adb..5e8664d 100644 --- a/src/contract.ts +++ b/src/contract.ts @@ -25,6 +25,7 @@ export type ExecSource = JsonRecord; export interface ExecArgs { from?: PathSelector; filter?: Filter; + head?: boolean; } export type Exec = { diff --git a/src/exec.ts b/src/exec.ts index 4852025..79a3b6f 100644 --- a/src/exec.ts +++ b/src/exec.ts @@ -69,11 +69,11 @@ function execPath(ex: Exec) { const { fieldName, root, - args: { from }, + args: { from, head = true }, } = ex; if (shouldExPath(ex)) { const pathName = isset(from) ? from : fieldName; - return path(pathName, data, root); + return path(pathName, data, root, head); } return root; }; diff --git a/src/path.ts b/src/path.ts index cff1bce..e466eeb 100644 --- a/src/path.ts +++ b/src/path.ts @@ -7,25 +7,31 @@ import type { } from "./contract"; import jp from "jsonpath"; -import { isset } from "./util"; -function jsonpath(selector: string, data: JsonParent): Maybe { - const [child] = jp.query(data, selector); - if (isset(child)) { - return child; +function jsonpath( + selector: string, + data: JsonParent, + head = true +): Maybe { + const result = jp.query(data, selector); + const [first] = result; + if (head) { + return first; } + return result; } export function path( selector: PathSelector, source: JsonRecord, - parent: JsonParent = source + parent: JsonParent = source, + head = true ): Maybe { if (selector === "@") { return jsonpath("$", parent); } if (selector[0] === "$") { - return jsonpath(selector, source); + return jsonpath(selector, source, head); } - return jsonpath(selector, parent); + return jsonpath(selector, parent, head); }