Skip to content

Commit

Permalink
fix(groq): array traversals with projection shouldn't return null
Browse files Browse the repository at this point in the history
  • Loading branch information
saiichihashimoto committed Dec 24, 2024
1 parent ad1da43 commit eebdd5f
Show file tree
Hide file tree
Showing 2 changed files with 225 additions and 4 deletions.
13 changes: 9 additions & 4 deletions packages/groq/src/internal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1416,10 +1416,15 @@ type Dereference<
| (Exclude<ParseInner<`${_Prefix}${TBase}`>, Level10> extends never
? never
: TIdentifier extends ""
? {
base: Exclude<ParseInner<`${_Prefix}${TBase}`>, Level10>;
type: "Deref";
}
? MaybeMap<
Exclude<ParseInner<`${_Prefix}${TBase}`>, Level10>,
{
base: MaybeMapBase<
Exclude<ParseInner<`${_Prefix}${TBase}`>, Level10>
>;
type: "Deref";
}
>
: Identifier<TIdentifier> extends never
? never
: MaybeMap<
Expand Down
216 changes: 216 additions & 0 deletions packages/groq/src/traversal-operators.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1347,6 +1347,55 @@ describe("traversal operators", () => {
>().toStrictEqual<WritableDeep<typeof expectedResult>>();
});

it("$param[]->", async () => {
const query = "$param[]->";

const tree = parse(query);

const expectedTree = {
base: { base: { name: "param", type: "Parameter" }, type: "ArrayCoerce" },
expr: { base: { type: "This" }, type: "Deref" },
type: "Map",
} as const;

expect(tree).toStrictEqual(expectedTree);
expectType<Parse<typeof query>>().toStrictEqual<
WritableDeep<typeof expectedTree>
>();

const params = {
param: [
{ _type: "reference", _ref: "foo" },
{ _type: "reference", _ref: "bar" },
{ _type: "reference", _ref: "bar2" },
],
} as const;

const dataset = [
{ _id: "foo", _type: "foo", value: "foo" },
{ _id: "bar", _type: "bar", value: "bar" },
] as const;

const result = await (await evaluate(tree, { dataset, params })).get();

const expectedResult = [
{ _id: "foo", _type: "foo", value: "foo" },
{ _id: "bar", _type: "bar", value: "bar" },
null,
] as const;

expect(result).toStrictEqual(expectedResult);
expectType<
ExecuteQuery<
typeof query,
ScopeFromPartialContext<{
dataset: WritableDeep<typeof dataset>;
parameters: WritableDeep<typeof params>;
}>
>
>().toStrictEqual<WritableDeep<typeof expectedResult>>();
});

it("$param[]->value", async () => {
const query = "$param[]->value";

Expand Down Expand Up @@ -1405,6 +1454,65 @@ describe("traversal operators", () => {
>().toStrictEqual<WritableDeep<typeof expectedResult>>();
});

it("$param[]->{value}", async () => {
const query = "$param[]->{value}";

const tree = parse(query);

const expectedTree = {
base: { base: { name: "param", type: "Parameter" }, type: "ArrayCoerce" },
expr: {
base: { base: { type: "This" }, type: "Deref" },
expr: {
attributes: [
{
name: "value",
type: "ObjectAttributeValue",
value: { name: "value", type: "AccessAttribute" },
},
],
type: "Object",
},
type: "Projection",
},
type: "Map",
} as const;

expect(tree).toStrictEqual(expectedTree);
// TODO The evaluations are equivalent, but the parse tree is not.
expectType<Parse<typeof query>>().not.toStrictEqual<
WritableDeep<typeof expectedTree>
>();

const params = {
param: [
{ _type: "reference", _ref: "foo" },
{ _type: "reference", _ref: "bar" },
{ _type: "reference", _ref: "bar2" },
],
} as const;

const dataset = [
{ _id: "foo", _type: "foo", value: "foo" },
{ _id: "bar", _type: "bar", value: "bar" },
] as const;

const result = await (await evaluate(tree, { dataset, params })).get();

const expectedResult = [{ value: "foo" }, { value: "bar" }, null] as const;

expect(result).toStrictEqual(expectedResult);
expectType<
ExecuteQuery<
typeof query,
ScopeFromPartialContext<{
dataset: WritableDeep<typeof dataset>;
parameters: WritableDeep<typeof params>;
}>
>
>().toStrictEqual<WritableDeep<typeof expectedResult>>();
});

it("$param-> (weak)", async () => {
const query = "$param->";

Expand Down Expand Up @@ -1489,6 +1597,55 @@ describe("traversal operators", () => {
>().toStrictEqual<WritableDeep<typeof expectedResult>>();
});

it("$param[]-> (weak)", async () => {
const query = "$param[]->";

const tree = parse(query);

const expectedTree = {
base: { base: { name: "param", type: "Parameter" }, type: "ArrayCoerce" },
expr: { base: { type: "This" }, type: "Deref" },
type: "Map",
} as const;

expect(tree).toStrictEqual(expectedTree);
expectType<Parse<typeof query>>().toStrictEqual<
WritableDeep<typeof expectedTree>
>();

const params = {
param: [
{ _type: "reference", _ref: "foo", weak: true },
{ _type: "reference", _ref: "bar", weak: true },
{ _type: "reference", _ref: "foo2", weak: true },
],
} as const;

const dataset = [
{ _id: "foo", _type: "foo", value: "foo" },
{ _id: "bar", _type: "bar", value: "bar" },
] as const;

const result = await (await evaluate(tree, { dataset, params })).get();

const expectedResult = [
{ _id: "foo", _type: "foo", value: "foo" },
{ _id: "bar", _type: "bar", value: "bar" },
null,
] as const;

expect(result).toStrictEqual(expectedResult);
expectType<
ExecuteQuery<
typeof query,
ScopeFromPartialContext<{
dataset: WritableDeep<typeof dataset>;
parameters: WritableDeep<typeof params>;
}>
>
>().toStrictEqual<WritableDeep<typeof expectedResult>>();
});

it("$param[]->value (weak)", async () => {
const query = "$param[]->value";

Expand Down Expand Up @@ -1546,4 +1703,63 @@ describe("traversal operators", () => {
>
>().toStrictEqual<WritableDeep<typeof expectedResult>>();
});

it("$param[]->{value} (weak)", async () => {
const query = "$param[]->{value}";

const tree = parse(query);

const expectedTree = {
base: { base: { name: "param", type: "Parameter" }, type: "ArrayCoerce" },
expr: {
base: { base: { type: "This" }, type: "Deref" },
expr: {
attributes: [
{
name: "value",
type: "ObjectAttributeValue",
value: { name: "value", type: "AccessAttribute" },
},
],
type: "Object",
},
type: "Projection",
},
type: "Map",
} as const;

expect(tree).toStrictEqual(expectedTree);
// TODO The evaluations are equivalent, but the parse tree is not.
expectType<Parse<typeof query>>().not.toStrictEqual<
WritableDeep<typeof expectedTree>
>();

const params = {
param: [
{ _type: "reference", _ref: "foo", weak: true },
{ _type: "reference", _ref: "bar", weak: true },
{ _type: "reference", _ref: "foo2", weak: true },
],
} as const;

const dataset = [
{ _id: "foo", _type: "foo", value: "foo" },
{ _id: "bar", _type: "bar", value: "bar" },
] as const;

const result = await (await evaluate(tree, { dataset, params })).get();

const expectedResult = [{ value: "foo" }, { value: "bar" }, null] as const;

expect(result).toStrictEqual(expectedResult);
expectType<
ExecuteQuery<
typeof query,
ScopeFromPartialContext<{
dataset: WritableDeep<typeof dataset>;
parameters: WritableDeep<typeof params>;
}>
>
>().toStrictEqual<WritableDeep<typeof expectedResult>>();
});
});

0 comments on commit eebdd5f

Please sign in to comment.