@dxos/echo 0.8.3-main.672df60 → 0.8.3-staging.0fa589b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -23
- package/dist/lib/browser/chunk-UYPR62ZB.mjs +624 -0
- package/dist/lib/browser/chunk-UYPR62ZB.mjs.map +7 -0
- package/dist/lib/browser/index.mjs +11 -280
- package/dist/lib/browser/index.mjs.map +4 -4
- package/dist/lib/browser/meta.json +1 -1
- package/dist/lib/browser/testing/index.mjs +70 -0
- package/dist/lib/browser/testing/index.mjs.map +7 -0
- package/dist/lib/node/chunk-4HQE2F3L.cjs +644 -0
- package/dist/lib/node/chunk-4HQE2F3L.cjs.map +7 -0
- package/dist/lib/node/index.cjs +11 -282
- package/dist/lib/node/index.cjs.map +4 -4
- package/dist/lib/node/meta.json +1 -1
- package/dist/lib/node/testing/index.cjs +89 -0
- package/dist/lib/node/testing/index.cjs.map +7 -0
- package/dist/lib/node-esm/chunk-BYBICDIO.mjs +624 -0
- package/dist/lib/node-esm/chunk-BYBICDIO.mjs.map +7 -0
- package/dist/lib/node-esm/index.mjs +11 -280
- package/dist/lib/node-esm/index.mjs.map +4 -4
- package/dist/lib/node-esm/meta.json +1 -1
- package/dist/lib/node-esm/testing/index.mjs +70 -0
- package/dist/lib/node-esm/testing/index.mjs.map +7 -0
- package/dist/types/src/Key.d.ts +2 -0
- package/dist/types/src/Key.d.ts.map +1 -0
- package/dist/types/src/Obj.d.ts +37 -18
- package/dist/types/src/Obj.d.ts.map +1 -1
- package/dist/types/src/Ref.d.ts +9 -3
- package/dist/types/src/Ref.d.ts.map +1 -1
- package/dist/types/src/Relation.d.ts +36 -10
- package/dist/types/src/Relation.d.ts.map +1 -1
- package/dist/types/src/Type.d.ts +82 -17
- package/dist/types/src/Type.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +5 -3
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/query/dsl.d.ts +218 -0
- package/dist/types/src/query/dsl.d.ts.map +1 -0
- package/dist/types/src/query/dsl.test.d.ts +2 -0
- package/dist/types/src/query/dsl.test.d.ts.map +1 -0
- package/dist/types/src/query/index.d.ts +2 -0
- package/dist/types/src/query/index.d.ts.map +1 -0
- package/dist/types/src/testing/index.d.ts +2 -0
- package/dist/types/src/testing/index.d.ts.map +1 -0
- package/dist/types/src/testing/types.d.ts +113 -0
- package/dist/types/src/testing/types.d.ts.map +1 -0
- package/package.json +56 -12
- package/src/Key.ts +5 -0
- package/src/Obj.ts +66 -25
- package/src/Ref.ts +9 -6
- package/src/Relation.ts +75 -13
- package/src/Type.ts +122 -18
- package/src/index.ts +5 -3
- package/src/query/dsl.test.ts +323 -0
- package/src/query/dsl.ts +646 -0
- package/src/query/index.ts +5 -0
- package/src/test/api.test.ts +54 -9
- package/src/testing/index.ts +5 -0
- package/src/testing/types.ts +91 -0
package/src/Type.ts
CHANGED
|
@@ -3,32 +3,75 @@
|
|
|
3
3
|
//
|
|
4
4
|
|
|
5
5
|
import { type Schema } from 'effect';
|
|
6
|
+
import type { Simplify } from 'effect/Schema';
|
|
6
7
|
|
|
7
8
|
import type { EncodedReference } from '@dxos/echo-protocol';
|
|
8
9
|
import * as EchoSchema from '@dxos/echo-schema';
|
|
10
|
+
import type { ToMutable } from '@dxos/echo-schema';
|
|
9
11
|
import { invariant } from '@dxos/invariant';
|
|
10
12
|
import type * as Keys from '@dxos/keys';
|
|
11
13
|
|
|
14
|
+
import type * as RelationModule from './Relation';
|
|
15
|
+
|
|
16
|
+
export const KindId: unique symbol = EchoSchema.EntityKindId as any;
|
|
17
|
+
export type KindId = typeof KindId;
|
|
18
|
+
|
|
19
|
+
export { EntityKind as Kind } from '@dxos/echo-schema';
|
|
20
|
+
|
|
12
21
|
/**
|
|
13
|
-
*
|
|
22
|
+
* Assigns a kind to an Object or Relation instance.
|
|
14
23
|
*/
|
|
15
|
-
|
|
24
|
+
// NOTE: Needed to make `isRelation` and `isObject` checks work.
|
|
25
|
+
export interface OfKind<Kind extends EchoSchema.EntityKind> {
|
|
26
|
+
readonly id: Keys.ObjectId;
|
|
27
|
+
readonly [KindId]: Kind;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
interface ObjJsonProps {
|
|
31
|
+
id: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface RelationJsonProps {
|
|
35
|
+
id: string;
|
|
36
|
+
[EchoSchema.ATTR_RELATION_SOURCE]: string;
|
|
37
|
+
[EchoSchema.ATTR_RELATION_TARGET]: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Returns all properties of an object or relation except for the id and kind.
|
|
42
|
+
*/
|
|
43
|
+
export type Properties<T> = Omit<T, 'id' | KindId | RelationModule.Source | RelationModule.Target>;
|
|
16
44
|
|
|
17
45
|
/**
|
|
18
|
-
*
|
|
46
|
+
* Base ECHO schema type.
|
|
19
47
|
*/
|
|
20
|
-
export
|
|
48
|
+
export type Schema = EchoSchema.EchoSchema;
|
|
21
49
|
|
|
22
50
|
/**
|
|
23
|
-
*
|
|
51
|
+
* Return type of the `Obj` schema constructor.
|
|
52
|
+
*
|
|
53
|
+
* This typedef avoids `TS4023` error (name from external module cannot be used named).
|
|
54
|
+
* See Effect's note on interface types.
|
|
24
55
|
*/
|
|
25
|
-
export
|
|
56
|
+
export interface obj<Self extends Schema.Schema.Any>
|
|
57
|
+
extends Schema.AnnotableClass<
|
|
58
|
+
obj<Self>,
|
|
59
|
+
OfKind<EchoSchema.EntityKind.Object> & ToMutable<Schema.Schema.Type<Self>>,
|
|
60
|
+
Simplify<ObjJsonProps & ToMutable<Schema.Schema.Encoded<Self>>>,
|
|
61
|
+
Schema.Schema.Context<Self>
|
|
62
|
+
>,
|
|
63
|
+
EchoSchema.TypeMeta {}
|
|
26
64
|
|
|
27
65
|
/**
|
|
28
|
-
*
|
|
66
|
+
* Object schema.
|
|
29
67
|
*/
|
|
30
|
-
export const
|
|
68
|
+
export const Obj: {
|
|
69
|
+
(opts: EchoSchema.TypeMeta): <Self extends Schema.Schema.Any>(self: Self) => obj<Self>;
|
|
70
|
+
} = EchoSchema.EchoObject as any;
|
|
31
71
|
|
|
72
|
+
/**
|
|
73
|
+
* Object schema type definitions.
|
|
74
|
+
*/
|
|
32
75
|
export namespace Obj {
|
|
33
76
|
/**
|
|
34
77
|
* Type that represents an arbitrary schema type of an object.
|
|
@@ -38,6 +81,39 @@ export namespace Obj {
|
|
|
38
81
|
export type Any = Schema.Schema.AnyNoContext;
|
|
39
82
|
}
|
|
40
83
|
|
|
84
|
+
/**
|
|
85
|
+
* Return type of the `Relation` schema constructor.
|
|
86
|
+
*
|
|
87
|
+
* This typedef avoids `TS4023` error (name from external module cannot be used named).
|
|
88
|
+
* See Effect's note on interface types.
|
|
89
|
+
*/
|
|
90
|
+
export interface relation<
|
|
91
|
+
Self extends Schema.Schema.Any,
|
|
92
|
+
SourceSchema extends Schema.Schema.Any,
|
|
93
|
+
TargetSchema extends Schema.Schema.Any,
|
|
94
|
+
> extends Schema.AnnotableClass<
|
|
95
|
+
relation<Self, SourceSchema, TargetSchema>,
|
|
96
|
+
OfKind<EchoSchema.EntityKind.Relation> &
|
|
97
|
+
Relation.Endpoints<Schema.Schema.Type<SourceSchema>, Schema.Schema.Type<TargetSchema>> &
|
|
98
|
+
ToMutable<Schema.Schema.Type<Self>>,
|
|
99
|
+
Simplify<RelationJsonProps & ToMutable<Schema.Schema.Encoded<Self>>>,
|
|
100
|
+
Schema.Schema.Context<Self>
|
|
101
|
+
>,
|
|
102
|
+
EchoSchema.TypeMeta {}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Relation schema.
|
|
106
|
+
*/
|
|
107
|
+
// TODO(dmaretskyi): I have to redefine the type here so that the definition uses symbols from @dxos/echo/Relation.
|
|
108
|
+
export const Relation: {
|
|
109
|
+
<Source extends Schema.Schema.AnyNoContext, Target extends Schema.Schema.AnyNoContext>(
|
|
110
|
+
opts: EchoSchema.EchoRelationOptions<Source, Target>,
|
|
111
|
+
): <Self extends Schema.Schema.Any>(self: Self) => relation<Self, Source, Target>;
|
|
112
|
+
} = EchoSchema.EchoRelation as any;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Relation schema type definitions.
|
|
116
|
+
*/
|
|
41
117
|
export namespace Relation {
|
|
42
118
|
/**
|
|
43
119
|
* Type that represents an arbitrary schema type of a relation.
|
|
@@ -49,14 +125,36 @@ export namespace Relation {
|
|
|
49
125
|
/**
|
|
50
126
|
* Get relation target type.
|
|
51
127
|
*/
|
|
52
|
-
export type Target<A> = A extends
|
|
128
|
+
export type Target<A> = A extends Relation.Endpoints<infer _S, infer T> ? T : never;
|
|
53
129
|
|
|
54
130
|
/**
|
|
55
131
|
* Get relation source type.
|
|
56
132
|
*/
|
|
57
|
-
export type Source<A> = A extends
|
|
133
|
+
export type Source<A> = A extends Relation.Endpoints<infer S, infer _T> ? S : never;
|
|
134
|
+
|
|
135
|
+
export type Endpoints<Source, Target> = {
|
|
136
|
+
[RelationModule.Source]: Source;
|
|
137
|
+
[RelationModule.Target]: Target;
|
|
138
|
+
};
|
|
58
139
|
}
|
|
59
140
|
|
|
141
|
+
/**
|
|
142
|
+
* Return type of the `Ref` schema constructor.
|
|
143
|
+
*
|
|
144
|
+
* This typedef avoids `TS4023` error (name from external module cannot be used named).
|
|
145
|
+
* See Effect's note on interface types.
|
|
146
|
+
*/
|
|
147
|
+
export interface ref<TargetSchema extends Schema.Schema.Any>
|
|
148
|
+
extends EchoSchema.Ref$<Schema.Schema.Type<TargetSchema>> {}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Ref schema.
|
|
152
|
+
*/
|
|
153
|
+
export const Ref: <S extends Obj.Any>(schema: S) => ref<S> = EchoSchema.Ref;
|
|
154
|
+
|
|
155
|
+
export interface Ref<T> extends Schema.SchemaClass<EchoSchema.Ref<T>, EncodedReference> {}
|
|
156
|
+
|
|
157
|
+
// TODO(buurdon): Move to Ref?
|
|
60
158
|
export namespace Ref {
|
|
61
159
|
/**
|
|
62
160
|
* Type that represents an arbitrary schema type of a reference.
|
|
@@ -107,21 +205,27 @@ export const getMeta = (schema: Obj.Any | Relation.Any): Meta | undefined => {
|
|
|
107
205
|
return EchoSchema.getTypeAnnotation(schema);
|
|
108
206
|
};
|
|
109
207
|
|
|
110
|
-
export { EntityKind as Kind } from '@dxos/echo-schema';
|
|
111
|
-
|
|
112
208
|
/**
|
|
113
209
|
* @returns True if the schema is mutable.
|
|
114
210
|
*/
|
|
115
|
-
export const isMutable =
|
|
116
|
-
return EchoSchema.isMutable(schema);
|
|
117
|
-
};
|
|
211
|
+
export const isMutable = EchoSchema.isMutable;
|
|
118
212
|
|
|
119
213
|
export { SpaceId, ObjectId, DXN } from '@dxos/keys';
|
|
120
214
|
|
|
121
|
-
export {
|
|
122
|
-
|
|
215
|
+
export interface Expando extends OfKind<EchoSchema.EntityKind.Object> {
|
|
216
|
+
[key: string]: any;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
export const Expando: Schema.Schema<
|
|
123
220
|
Expando,
|
|
221
|
+
Simplify<ObjJsonProps & { [key: string]: any }>,
|
|
222
|
+
never
|
|
223
|
+
> = EchoSchema.Expando as any;
|
|
224
|
+
|
|
225
|
+
export {
|
|
226
|
+
// TODO(burdon): Standardize.
|
|
227
|
+
Format,
|
|
124
228
|
JsonSchemaType as JsonSchema,
|
|
229
|
+
toEffectSchema,
|
|
125
230
|
toJsonSchema,
|
|
126
|
-
Format,
|
|
127
231
|
} from '@dxos/echo-schema';
|
package/src/index.ts
CHANGED
|
@@ -2,10 +2,12 @@
|
|
|
2
2
|
// Copyright 2025 DXOS.org
|
|
3
3
|
//
|
|
4
4
|
|
|
5
|
-
export * as
|
|
5
|
+
export * as Key from './Key';
|
|
6
6
|
export * as Obj from './Obj';
|
|
7
|
-
export * as Relation from './Relation';
|
|
8
7
|
export * as Ref from './Ref';
|
|
8
|
+
export * as Relation from './Relation';
|
|
9
|
+
export * as Type from './Type';
|
|
9
10
|
|
|
11
|
+
export { DXN } from '@dxos/keys';
|
|
12
|
+
export { Filter, Query } from './query';
|
|
10
13
|
export { type Live } from '@dxos/live-object';
|
|
11
|
-
export { Filter, Query } from '@dxos/echo-schema';
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
//
|
|
2
|
+
// Copyright 2025 DXOS.org
|
|
3
|
+
//
|
|
4
|
+
|
|
5
|
+
import { Schema } from 'effect';
|
|
6
|
+
import { describe, expect, test } from 'vitest';
|
|
7
|
+
|
|
8
|
+
import { QueryAST } from '@dxos/echo-protocol';
|
|
9
|
+
import { DXN } from '@dxos/keys';
|
|
10
|
+
import { log } from '@dxos/log';
|
|
11
|
+
|
|
12
|
+
import { Filter, Query } from './dsl';
|
|
13
|
+
import * as Obj from '../Obj';
|
|
14
|
+
import * as Ref from '../Ref';
|
|
15
|
+
import * as Type from '../Type';
|
|
16
|
+
|
|
17
|
+
//
|
|
18
|
+
// Example schema
|
|
19
|
+
//
|
|
20
|
+
|
|
21
|
+
// TODO(dmaretskyi): Need common set of test types.
|
|
22
|
+
const Person = Schema.Struct({
|
|
23
|
+
name: Schema.String,
|
|
24
|
+
email: Schema.optional(Schema.String),
|
|
25
|
+
age: Schema.optional(Schema.Number),
|
|
26
|
+
}).pipe(
|
|
27
|
+
Type.Obj({
|
|
28
|
+
typename: 'dxos.org/type/Person',
|
|
29
|
+
version: '0.1.0',
|
|
30
|
+
}),
|
|
31
|
+
);
|
|
32
|
+
interface Person extends Schema.Schema.Type<typeof Person> {}
|
|
33
|
+
|
|
34
|
+
const Organization = Schema.Struct({
|
|
35
|
+
name: Schema.String,
|
|
36
|
+
}).pipe(
|
|
37
|
+
Type.Obj({
|
|
38
|
+
typename: 'dxos.org/type/Organization',
|
|
39
|
+
version: '0.1.0',
|
|
40
|
+
}),
|
|
41
|
+
);
|
|
42
|
+
interface Organization extends Schema.Schema.Type<typeof Organization> {}
|
|
43
|
+
|
|
44
|
+
const WorksFor = Schema.Struct({
|
|
45
|
+
since: Schema.String,
|
|
46
|
+
}).pipe(
|
|
47
|
+
Type.Relation({
|
|
48
|
+
typename: 'dxos.org/type/WorksFor',
|
|
49
|
+
version: '0.1.0',
|
|
50
|
+
source: Person,
|
|
51
|
+
target: Organization,
|
|
52
|
+
}),
|
|
53
|
+
);
|
|
54
|
+
interface WorksFor extends Schema.Schema.Type<typeof WorksFor> {}
|
|
55
|
+
|
|
56
|
+
const Task = Schema.Struct({
|
|
57
|
+
title: Schema.String,
|
|
58
|
+
createdAt: Schema.String,
|
|
59
|
+
assignee: Schema.optional(Type.Ref(Person)),
|
|
60
|
+
}).pipe(Type.Obj({ typename: 'dxos.org/type/Task', version: '0.1.0' }));
|
|
61
|
+
interface Task extends Schema.Schema.Type<typeof Task> {}
|
|
62
|
+
|
|
63
|
+
//
|
|
64
|
+
// Example queries
|
|
65
|
+
//
|
|
66
|
+
|
|
67
|
+
describe('query api', () => {
|
|
68
|
+
test('get all people', () => {
|
|
69
|
+
const getAllPeople = Query.type(Person);
|
|
70
|
+
|
|
71
|
+
log('query', { ast: getAllPeople.ast });
|
|
72
|
+
Schema.validateSync(QueryAST.Query)(getAllPeople.ast);
|
|
73
|
+
console.log('getAllPeople', JSON.stringify(getAllPeople.ast, null, 2));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('get all people named Fred', () => {
|
|
77
|
+
const PeopleNamedFred = Query.select(Filter.type(Person, { name: 'Fred' }));
|
|
78
|
+
|
|
79
|
+
log('query', { ast: PeopleNamedFred.ast });
|
|
80
|
+
Schema.validateSync(QueryAST.Query)(PeopleNamedFred.ast);
|
|
81
|
+
console.log('PeopleNamedFred', JSON.stringify(PeopleNamedFred.ast, null, 2));
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('get all orgs Fred worked for since 2020', () => {
|
|
85
|
+
const fred = Obj.make(Person, { name: 'Fred' });
|
|
86
|
+
const OrganizationsFredWorkedForSince2020 = Query.select(Filter.type(Person, { id: fred.id }))
|
|
87
|
+
.sourceOf(WorksFor, { since: Filter.gt('2020') })
|
|
88
|
+
.target();
|
|
89
|
+
|
|
90
|
+
log('query', { ast: OrganizationsFredWorkedForSince2020.ast });
|
|
91
|
+
Schema.validateSync(QueryAST.Query)(OrganizationsFredWorkedForSince2020.ast);
|
|
92
|
+
console.log(
|
|
93
|
+
'OrganizationsFredWorkedForSince2020',
|
|
94
|
+
JSON.stringify(OrganizationsFredWorkedForSince2020.ast, null, 2),
|
|
95
|
+
);
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
test('get all tasks for Fred', () => {
|
|
99
|
+
const fred = Obj.make(Person, { name: 'Fred' });
|
|
100
|
+
const TasksForFred = Query.select(Filter.type(Person, { id: fred.id })).referencedBy(Task, 'assignee');
|
|
101
|
+
|
|
102
|
+
log('query', { ast: TasksForFred.ast });
|
|
103
|
+
Schema.validateSync(QueryAST.Query)(TasksForFred.ast);
|
|
104
|
+
console.log('TasksForFred', JSON.stringify(TasksForFred.ast, null, 2));
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test('get all tasks for employees of Cyberdyne', () => {
|
|
108
|
+
const TasksForEmployeesOfCyberdyne = Query.select(Filter.type(Organization, { name: 'Cyberdyne' }))
|
|
109
|
+
.targetOf(WorksFor)
|
|
110
|
+
.source()
|
|
111
|
+
.referencedBy(Task, 'assignee');
|
|
112
|
+
|
|
113
|
+
log('query', { ast: TasksForEmployeesOfCyberdyne.ast });
|
|
114
|
+
Schema.validateSync(QueryAST.Query)(TasksForEmployeesOfCyberdyne.ast);
|
|
115
|
+
console.log('TasksForEmployeesOfCyberdyne', JSON.stringify(TasksForEmployeesOfCyberdyne.ast, null, 2));
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('get all people or orgs', () => {
|
|
119
|
+
const PeopleOrOrganizations = Query.all(Query.select(Filter.type(Person)), Query.select(Filter.type(Organization)));
|
|
120
|
+
|
|
121
|
+
log('query', { ast: PeopleOrOrganizations.ast });
|
|
122
|
+
Schema.validateSync(QueryAST.Query)(PeopleOrOrganizations.ast);
|
|
123
|
+
console.log('PeopleOrOrganizations', JSON.stringify(PeopleOrOrganizations.ast, null, 2));
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('get all people not in orgs', () => {
|
|
127
|
+
const PeopleNotInOrganizations = Query.without(
|
|
128
|
+
Query.select(Filter.type(Person)),
|
|
129
|
+
Query.select(Filter.type(Person)).sourceOf(WorksFor).source(),
|
|
130
|
+
);
|
|
131
|
+
|
|
132
|
+
log('query', { ast: PeopleNotInOrganizations.ast });
|
|
133
|
+
Schema.validateSync(QueryAST.Query)(PeopleNotInOrganizations.ast);
|
|
134
|
+
console.log('PeopleNotInOrganizations', JSON.stringify(PeopleNotInOrganizations.ast, null, 2));
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('get assignees of all tasks created after 2020', () => {
|
|
138
|
+
const AssigneesOfAllTasksCreatedAfter2020 = Query.select(
|
|
139
|
+
Filter.type(Task, { createdAt: Filter.gt('2020') }),
|
|
140
|
+
).reference('assignee');
|
|
141
|
+
|
|
142
|
+
log('query', { ast: AssigneesOfAllTasksCreatedAfter2020.ast });
|
|
143
|
+
Schema.validateSync(QueryAST.Query)(AssigneesOfAllTasksCreatedAfter2020.ast);
|
|
144
|
+
console.log(
|
|
145
|
+
'AssigneesOfAllTasksCreatedAfter2020',
|
|
146
|
+
JSON.stringify(AssigneesOfAllTasksCreatedAfter2020.ast, null, 2),
|
|
147
|
+
);
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test('untyped full-text search', () => {
|
|
151
|
+
const contactFullTextSearch = Query.select(Filter.text('Bill'));
|
|
152
|
+
|
|
153
|
+
log('query', { ast: contactFullTextSearch.ast });
|
|
154
|
+
Schema.validateSync(QueryAST.Query)(contactFullTextSearch.ast);
|
|
155
|
+
expect(contactFullTextSearch.ast).toMatchInlineSnapshot(`
|
|
156
|
+
{
|
|
157
|
+
"filter": {
|
|
158
|
+
"searchKind": undefined,
|
|
159
|
+
"text": "Bill",
|
|
160
|
+
"type": "text-search",
|
|
161
|
+
},
|
|
162
|
+
"type": "select",
|
|
163
|
+
}
|
|
164
|
+
`);
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
test('typed full-text search', () => {
|
|
168
|
+
const contactFullTextSearch = Query.select(Filter.type(Person)).select(Filter.text('Bill'));
|
|
169
|
+
|
|
170
|
+
log('query', { ast: contactFullTextSearch.ast });
|
|
171
|
+
Schema.validateSync(QueryAST.Query)(contactFullTextSearch.ast);
|
|
172
|
+
expect(contactFullTextSearch.ast).toMatchInlineSnapshot(`
|
|
173
|
+
{
|
|
174
|
+
"filter": {
|
|
175
|
+
"searchKind": undefined,
|
|
176
|
+
"text": "Bill",
|
|
177
|
+
"type": "text-search",
|
|
178
|
+
},
|
|
179
|
+
"selection": {
|
|
180
|
+
"filter": {
|
|
181
|
+
"id": undefined,
|
|
182
|
+
"props": {},
|
|
183
|
+
"type": "object",
|
|
184
|
+
"typename": "dxn:type:dxos.org/type/Person:0.1.0",
|
|
185
|
+
},
|
|
186
|
+
"type": "select",
|
|
187
|
+
},
|
|
188
|
+
"type": "filter",
|
|
189
|
+
}
|
|
190
|
+
`);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test('filter by ref', () => {
|
|
194
|
+
const fred = Obj.make(Person, { name: 'Fred' });
|
|
195
|
+
const tasksByFred = Filter.type(Task, { assignee: Ref.make(fred) });
|
|
196
|
+
expect(tasksByFred.ast).toEqual({
|
|
197
|
+
props: {
|
|
198
|
+
assignee: {
|
|
199
|
+
operator: 'eq',
|
|
200
|
+
type: 'compare',
|
|
201
|
+
value: {
|
|
202
|
+
'/': DXN.fromLocalObjectId(fred.id).toString(),
|
|
203
|
+
},
|
|
204
|
+
},
|
|
205
|
+
},
|
|
206
|
+
type: 'object',
|
|
207
|
+
typename: 'dxn:type:dxos.org/type/Task:0.1.0',
|
|
208
|
+
});
|
|
209
|
+
console.log('tasksByFred', JSON.stringify(tasksByFred.ast, null, 2));
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
test('select orgs and people', () => {
|
|
213
|
+
const orgsAndPeople = Query.select(Filter.or(Filter.type(Organization), Filter.type(Person)));
|
|
214
|
+
|
|
215
|
+
Schema.validateSync(QueryAST.Query)(orgsAndPeople.ast);
|
|
216
|
+
expect(orgsAndPeople.ast).toMatchInlineSnapshot(`
|
|
217
|
+
{
|
|
218
|
+
"filter": {
|
|
219
|
+
"filters": [
|
|
220
|
+
{
|
|
221
|
+
"id": undefined,
|
|
222
|
+
"props": {},
|
|
223
|
+
"type": "object",
|
|
224
|
+
"typename": "dxn:type:dxos.org/type/Organization:0.1.0",
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
"id": undefined,
|
|
228
|
+
"props": {},
|
|
229
|
+
"type": "object",
|
|
230
|
+
"typename": "dxn:type:dxos.org/type/Person:0.1.0",
|
|
231
|
+
},
|
|
232
|
+
],
|
|
233
|
+
"type": "or",
|
|
234
|
+
},
|
|
235
|
+
"type": "select",
|
|
236
|
+
}
|
|
237
|
+
`);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
test('select everything but orgs and people', () => {
|
|
241
|
+
const everythingButOrgsAndPeople = Query.select(
|
|
242
|
+
Filter.not(Filter.or(Filter.type(Organization), Filter.type(Person))),
|
|
243
|
+
);
|
|
244
|
+
|
|
245
|
+
Schema.validateSync(QueryAST.Query)(everythingButOrgsAndPeople.ast);
|
|
246
|
+
expect(everythingButOrgsAndPeople.ast).toMatchInlineSnapshot(`
|
|
247
|
+
{
|
|
248
|
+
"filter": {
|
|
249
|
+
"filter": {
|
|
250
|
+
"filters": [
|
|
251
|
+
{
|
|
252
|
+
"id": undefined,
|
|
253
|
+
"props": {},
|
|
254
|
+
"type": "object",
|
|
255
|
+
"typename": "dxn:type:dxos.org/type/Organization:0.1.0",
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"id": undefined,
|
|
259
|
+
"props": {},
|
|
260
|
+
"type": "object",
|
|
261
|
+
"typename": "dxn:type:dxos.org/type/Person:0.1.0",
|
|
262
|
+
},
|
|
263
|
+
],
|
|
264
|
+
"type": "or",
|
|
265
|
+
},
|
|
266
|
+
"type": "not",
|
|
267
|
+
},
|
|
268
|
+
"type": "select",
|
|
269
|
+
}
|
|
270
|
+
`);
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
test('select deleted tasks', () => {
|
|
274
|
+
const deletedTasks = Query.select(Filter.type(Task)).options({
|
|
275
|
+
deleted: 'only',
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
Schema.validateSync(QueryAST.Query)(deletedTasks.ast);
|
|
279
|
+
expect(deletedTasks.ast).toMatchInlineSnapshot(`
|
|
280
|
+
{
|
|
281
|
+
"options": {
|
|
282
|
+
"deleted": "only",
|
|
283
|
+
},
|
|
284
|
+
"query": {
|
|
285
|
+
"filter": {
|
|
286
|
+
"id": undefined,
|
|
287
|
+
"props": {},
|
|
288
|
+
"type": "object",
|
|
289
|
+
"typename": "dxn:type:dxos.org/type/Task:0.1.0",
|
|
290
|
+
},
|
|
291
|
+
"type": "select",
|
|
292
|
+
},
|
|
293
|
+
"type": "options",
|
|
294
|
+
}
|
|
295
|
+
`);
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
test.skip('chain', () => {
|
|
299
|
+
// NOTE: Can't support props without type since they can't be inferred.
|
|
300
|
+
// const f1: Filter<Person> = Filter.props({ name: 'Fred' });
|
|
301
|
+
|
|
302
|
+
// const x = Query.select(Filter.props({ id: '123' }));
|
|
303
|
+
const y = Query.select(Filter.type(Person));
|
|
304
|
+
|
|
305
|
+
const or = Filter.or(Filter.type(Person, { id: Filter.in('1', '2', '3') }), Filter.type(Organization));
|
|
306
|
+
|
|
307
|
+
const and = Filter.and(
|
|
308
|
+
Filter.type(Person, { id: Filter.in('1', '2', '3') }),
|
|
309
|
+
Filter.type(Person, { name: 'Fred' }),
|
|
310
|
+
);
|
|
311
|
+
|
|
312
|
+
const q = Query
|
|
313
|
+
//
|
|
314
|
+
// NOTE: Can't support functions since they can't be serialized (to server).
|
|
315
|
+
// .filter((object) => Math.random() > 0.5)
|
|
316
|
+
.select(Filter.type(Person))
|
|
317
|
+
.select(Filter.type(Person, { name: 'Fred' }))
|
|
318
|
+
.select({ age: Filter.between(20, 40) })
|
|
319
|
+
.select(Filter.and(Filter.type(Person), Filter.type(Person, { name: Filter.in('bob', 'bill') })));
|
|
320
|
+
|
|
321
|
+
log('stuff', { fOr: or, fAnd: and, q, y });
|
|
322
|
+
});
|
|
323
|
+
});
|