Skip to content

Commit 89f621e

Browse files
authored
Select star (#332)
* Implement select star
1 parent 14e8155 commit 89f621e

File tree

8 files changed

+107
-26
lines changed

8 files changed

+107
-26
lines changed

docs/select.rst

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
.. _edgedb-js-select:
22

33
Select
4-
------
4+
======
55

66
The full power of the EdgeQL ``select`` statement is available as a top-level
77
``e.select`` function. All queries on this page assume the Netflix schema
88
described on the :ref:`Objects page <edgedb-js-objects>`.
99

1010
Selecting scalars
11-
^^^^^^^^^^^^^^^^^
11+
-----------------
1212

1313
Any scalar expression be passed into ``e.select``, though it's often
1414
unnecessary, since expressions are ``run``\ able without being wrapped by
@@ -23,7 +23,7 @@ unnecessary, since expressions are ``run``\ able without being wrapped by
2323
// select 2 + 2;
2424
2525
Selecting free objects
26-
^^^^^^^^^^^^^^^^^^^^^^
26+
----------------------
2727

2828
Select a free object by passing an object into ``e.select``
2929

@@ -41,7 +41,7 @@ Select a free object by passing an object into ``e.select``
4141
} */
4242
4343
Selecting objects
44-
^^^^^^^^^^^^^^^^^
44+
-----------------
4545

4646
As in EdgeQL, selecting an set of objects without a shape will return their
4747
``id`` property only. This is reflected in the TypeScript type of the result.
@@ -91,7 +91,6 @@ This is true for all queries on this page.
9191
As you can see, the type of ``runtime`` is ``Duration | undefined`` since it's
9292
an optional property, whereas ``id`` and ``title`` are required.
9393

94-
9594
Passing a ``boolean`` value (as opposed to a ``true`` literal), which will
9695
make the property optional. Passing ``false`` will exclude that property.
9796

@@ -106,7 +105,31 @@ make the property optional. Passing ``false`` will exclude that property.
106105
const result = await query.run(client);
107106
// {id: string; title: string | undefined; runtime: never}[]
108107
108+
Selecting all properties
109+
^^^^^^^^^^^^^^^^^^^^^^^^
110+
111+
For convenience, the query builder provides a shorthand for selecting all
112+
properties of a given object.
113+
114+
.. code-block:: typescript
115+
116+
e.select(e.Movie, movie => ({
117+
...e.Movie['*']
118+
}));
119+
120+
const result = await query.run(client);
121+
// {id: string; title: string; runtime: Date}[]
122+
123+
This ``*`` property is just a strongly-typed, plain object:
124+
125+
.. code-block::
126+
127+
e.Movie['*'];
128+
// => {id: true, title: true, runtime: true}
129+
130+
109131
Nesting shapes
132+
^^^^^^^^^^^^^^
110133

111134
As in EdgeQL, shapes can be nested to fetch deeply related objects.
112135

@@ -129,7 +152,7 @@ As in EdgeQL, shapes can be nested to fetch deeply related objects.
129152
130153
131154
Why closures?
132-
^^^^^^^^^^^^^
155+
-------------
133156

134157
In EdgeQL, a ``select`` statement introduces a new *scope*; within the clauses
135158
of a select statement, you can refer to fields of the *elements being
@@ -151,7 +174,7 @@ computed fields, and other expressions. Let's see it in action.
151174

152175

153176
Filtering
154-
^^^^^^^^^
177+
---------
155178

156179
To add a filtering clause, just include a ``filter`` key in the returned
157180
params object. This should correspond to a boolean expression.
@@ -176,7 +199,8 @@ params object. This should correspond to a boolean expression.
176199
EdgeDB, there is minimal danger of conflicting with a property or link named
177200
``filter``. All shapes can contain filter clauses, even nested ones.
178201

179-
### Nested filtering
202+
Filters on links
203+
----------------
180204

181205
.. code-block:: typescript
182206
@@ -191,7 +215,7 @@ params object. This should correspond to a boolean expression.
191215
192216
193217
Ordering
194-
^^^^^^^^
218+
--------
195219

196220
As with ``filter``, you can pass a value with the special ``order_by`` key. To
197221
simply order by a property:
@@ -202,8 +226,6 @@ simply order by a property:
202226
order_by: movie.title,
203227
}));
204228
205-
206-
207229
.. note::
208230

209231
Unlike ``filter``, ``order_by`` is *not* a reserved word in EdgeDB. Using
@@ -277,7 +299,7 @@ Pass an array of objects to do multiple ordering.
277299
278300
279301
Pagination
280-
^^^^^^^^^^
302+
----------
281303

282304
Use ``offset`` and ``limit`` to paginate queries. You can pass an expression
283305
with an integer type or a plain JS number.
@@ -295,7 +317,7 @@ with an integer type or a plain JS number.
295317
*/
296318
297319
Computeds
298-
^^^^^^^^^
320+
---------
299321

300322
To add a computed field, just add it to the returned shape alongside the other
301323
elements. All reflected functions are typesafe, so the output type
@@ -339,7 +361,7 @@ signatures agree.
339361
.. _ref_qb_polymorphism:
340362

341363
Polymorphism
342-
^^^^^^^^^^^^
364+
------------
343365

344366
EdgeQL supports polymorphic queries using the ``[is type]`` prefix.
345367

@@ -373,7 +395,7 @@ fact that they will only occur in certain objects.
373395

374396

375397
Detached
376-
^^^^^^^^
398+
--------
377399

378400
Sometimes you need to "detach" a set reference from the current scope. (Read the `reference docs <https://www.edgedb.com/docs/reference/edgeql/with#detached>`_ for details.) You can achieve this in the query builder with the top-level ``e.detached`` function.
379401

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "edgedb",
3-
"version": "0.20.7",
3+
"version": "0.20.8",
44
"description": "The official Node.js client library for EdgeDB",
55
"homepage": "https://edgedb.com/docs",
66
"author": "EdgeDB <info@edgedb.com>",

qb/playground.ts

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,9 @@ import e, * as types from "./dbschema/edgeql-js/index";
77

88
async function run() {
99
const {client, data} = await setupTests();
10-
const query = e.update(e.Movie, movie => ({
11-
filter: e.op(movie.id, "=", e.uuid(data.the_avengers.id)),
12-
set: {
13-
title: "The Avngrrs",
14-
},
10+
console.log(e.Movie["*"]);
11+
const query = e.select(e.Movie, movie => ({
12+
...e.Movie["*"],
1513
}));
1614

1715
console.log(query.toEdgeQL());

qb/test/objectTypes.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,3 +117,27 @@ test("backlinks", () => {
117117
"default::Movie"
118118
);
119119
});
120+
121+
test("select *", () => {
122+
const movieStar = e.Movie["*"];
123+
124+
expect(movieStar).toEqual({
125+
id: true,
126+
title: true,
127+
genre: true,
128+
rating: true,
129+
release_year: true,
130+
});
131+
tc.assert<
132+
tc.IsExact<
133+
typeof movieStar,
134+
{
135+
id: true;
136+
title: true;
137+
genre: true;
138+
rating: true;
139+
release_year: true;
140+
}
141+
>
142+
>(true);
143+
});

qb/test/select.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1120,6 +1120,20 @@ test("filter by sequence", async () => {
11201120
await e.op(e.Bag.seqField, "=", 1).run(client);
11211121
});
11221122

1123+
test("select *", async () => {
1124+
const allFields = await e
1125+
.select(e.Movie, movie => ({
1126+
...e.Movie["*"],
1127+
filter: e.op(movie.title, "=", data.the_avengers.title),
1128+
}))
1129+
.run(client);
1130+
1131+
expect(allFields).toEqual({
1132+
...data.the_avengers,
1133+
characters: undefined,
1134+
});
1135+
});
1136+
11231137
// Modifier methods removed for now, until we can fix typescript inference
11241138
// problems / excessively deep errors
11251139

qb/test/setupTeardown.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ interface Movie {
2626
id: string;
2727
title: string;
2828
genre: string;
29+
rating: number | null;
30+
release_year: number;
2931
characters: {id: string}[];
3032
}
3133

@@ -73,7 +75,7 @@ SELECT (INSERT Movie {
7375
rating := 10,
7476
genre := Genre.Action,
7577
characters := (SELECT Person FILTER .id IN char_ids)
76-
}) {id, title, rating, genre, characters: {id}};`,
78+
}) {id, title, rating, genre, release_year, characters: {id}};`,
7779
{character_ids: [iron_man.id, cap.id]}
7880
);
7981
const civil_war: Movie = await client.queryRequiredSingle(
@@ -82,7 +84,7 @@ SELECT (INSERT Movie {
8284
rating := 10,
8385
genre := Genre.Action,
8486
characters := (SELECT Hero)
85-
}) {id, title, rating, genre, characters: {id}};`
87+
}) {id, title, rating, genre, release_year, characters: {id}};`
8688
);
8789

8890
return {

src/reflection/path.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import {typeutil} from ".";
21
import {Cardinality, ExpressionKind} from "./enums";
32
import type {
43
BaseType,
@@ -11,6 +10,7 @@ import type {
1110
PropertyShape,
1211
TypeSet,
1312
} from "./typesystem";
13+
import {typeutil} from ".";
1414
import {cardinalityUtil} from "./util/cardinalityUtil";
1515

1616
// get the set representing the result of a path traversal
@@ -142,6 +142,14 @@ type pathifyLinkProps<
142142
: unknown;
143143
};
144144

145+
export type getPropsShape<T extends ObjectType> = typeutil.flatten<
146+
typeutil.stripNever<{
147+
[k in keyof T["__pointers__"]]: T["__pointers__"][k]["__kind__"] extends "property"
148+
? true
149+
: never;
150+
}>
151+
>;
152+
145153
export type $expr_PathNode<
146154
Root extends ObjectTypeSet = ObjectTypeSet,
147155
Parent extends PathParent | null = PathParent | null,
@@ -152,6 +160,7 @@ export type $expr_PathNode<
152160
__parent__: Parent;
153161
__kind__: ExpressionKind.PathNode;
154162
__exclusive__: Exclusive;
163+
"*": getPropsShape<Root["__element__"]>;
155164
}>;
156165

157166
export type $expr_TypeIntersection<

src/syntax/path.ts

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,14 +51,26 @@ function PathNode<
5151
exclusive: Exclusive,
5252
scopeRoot: TypeSet | null = null
5353
): $expr_PathNode<Root, Parent, Exclusive> {
54-
return $expressionify({
54+
const obj = {
5555
__kind__: ExpressionKind.PathNode,
5656
__element__: root.__element__,
5757
__cardinality__: root.__cardinality__,
5858
__parent__: parent,
5959
__exclusive__: exclusive,
6060
__scopeRoot__: scopeRoot,
61-
}) as any;
61+
};
62+
63+
const shape: any = {};
64+
Object.entries(obj.__element__.__pointers__).map(([key, ptr]) => {
65+
if (ptr.__kind__ === "property") {
66+
shape[key] = true;
67+
}
68+
});
69+
Object.defineProperty(obj, "*", {
70+
writable: false,
71+
value: shape,
72+
});
73+
return $expressionify(obj) as any;
6274
}
6375

6476
const _pathCache = Symbol();

0 commit comments

Comments
 (0)