Querying
By the end of this page the app will list its records sorted, search them, and find the people who work at an organization.
Before you start
Section titled “Before you start”A query has two parts. A filter says which objects match: by type, by field values, by text. A query wraps the filter and adds what to do with the matches: their order, a limit, or a step to the objects they reference or that reference them.
A query is a value. Building one runs nothing; db.query(query) runs it against the database on the device, so a query never waits on the network. run returns the matches once. subscribe returns them, then runs again whenever an object that could match changes, whichever peer changed it.
The client returns typed objects only for the types it knows. The app passes its types to the client when it starts, in Installation.
The Filter reference lists every comparison and combinator.
-
List each type, sorted.
Filter.typeselects the objects of a type, andorderBysorts them by a field.export constorganizations =const organizations: Query.Query<Organization & OfKind<EntityKind.Object>>Query.import Queryselect(const select: <Filter.Filter<Organization & OfKind<EntityKind.Object>>>(filter: Filter.Filter<Organization & OfKind<EntityKind.Object>>) => Query.Query<Organization & OfKind<EntityKind.Object>>Select objects based on a filter.
Filter.import Filtertype(const type: <typeof Organization>(type: typeof Organization, props?: Filter.Props<Organization & OfKind<EntityKind.Object>> | undefined) => Filter.Filter<Organization & OfKind<EntityKind.Object>> (+4 overloads)Organization)).class OrganizationorderBy(Query<Organization & OfKind<EntityKind.Object>>.orderBy(order_0: Order.Order<Organization & OfKind<EntityKind.Object>>, ...order: Order.Order<Organization & OfKind<EntityKind.Object>>[]): Query.Query<Organization & OfKind<EntityKind.Object>>Order the query results. Orders are specified in priority order. The first order will be applied first, etc.
Order.propertyorders by the current result shape's fields, so it works both before and after anaggregate
: before, by member (row) properties; after, by the flat record's fields (any group or aggregate field — e.g.
Order.property('lastMessageAt')reorders the groups by that aggregate).Order.import Orderproperty<const property: <Organization>(property: "website" | "name" | "~@dxos/echo/Kind" | "id", direction: OrderDirection) => Order.Order<Organization>Order by a property of the current result shape.
Organization>('name', 'asc'),class Organization);export constpeople =const people: Query.Query<Person & OfKind<EntityKind.Object>>Query.import Queryselect(const select: <Filter.Filter<Person & OfKind<EntityKind.Object>>>(filter: Filter.Filter<Person & OfKind<EntityKind.Object>>) => Query.Query<Person & OfKind<EntityKind.Object>>Select objects based on a filter.
Filter.import Filtertype(const type: <typeof Person>(type: typeof Person, props?: Filter.Props<Person & OfKind<EntityKind.Object>> | undefined) => Filter.Filter<Person & OfKind<EntityKind.Object>> (+4 overloads)Person)).class PersonorderBy(Query<Person & OfKind<EntityKind.Object>>.orderBy(order_0: Order.Order<Person & OfKind<EntityKind.Object>>, ...order: Order.Order<Person & OfKind<EntityKind.Object>>[]): Query.Query<Person & OfKind<EntityKind.Object>>Order the query results. Orders are specified in priority order. The first order will be applied first, etc.
Order.propertyorders by the current result shape's fields, so it works both before and after anaggregate
: before, by member (row) properties; after, by the flat record's fields (any group or aggregate field — e.g.
Order.property('lastMessageAt')reorders the groups by that aggregate).Order.import Orderproperty<const property: <Person>(property: "name" | "~@dxos/echo/Kind" | "id" | "email" | "employer", direction: OrderDirection) => Order.Order<Person>Order by a property of the current result shape.
Person>('name', 'asc'));class Personexport constevents =const events: Query.Query<Event & OfKind<EntityKind.Object>>Query.import Queryselect(const select: <Filter.Filter<Event & OfKind<EntityKind.Object>>>(filter: Filter.Filter<Event & OfKind<EntityKind.Object>>) => Query.Query<Event & OfKind<EntityKind.Object>>Select objects based on a filter.
Filter.import Filtertype(const type: <typeof Event>(type: typeof Event, props?: Filter.Props<Event & OfKind<EntityKind.Object>> | undefined) => Filter.Filter<Event & OfKind<EntityKind.Object>> (+4 overloads)Event)).class EventorderBy(Query<Event & OfKind<EntityKind.Object>>.orderBy(order_0: Order.Order<Event & OfKind<EntityKind.Object>>, ...order: Order.Order<Event & OfKind<EntityKind.Object>>[]): Query.Query<Event & OfKind<EntityKind.Object>>Order the query results. Orders are specified in priority order. The first order will be applied first, etc.
Order.propertyorders by the current result shape's fields, so it works both before and after anaggregate
: before, by member (row) properties; after, by the flat record's fields (any group or aggregate field — e.g.
Order.property('lastMessageAt')reorders the groups by that aggregate).Order.import Orderproperty<const property: <Event>(property: "~@dxos/echo/Kind" | "id" | "description" | "attendees" | "title", direction: OrderDirection) => Order.Order<Event>Order by a property of the current result shape.
Event>('title', 'asc'));class Event -
Run a query. The results are typed, so
person.nameis astring.consteveryone = awaitconst everyone: (Person & OfKind<EntityKind.Object>)[]db.const db: Database.Databasequery(Database.query: Database.QueryFn<Query<Person & OfKind<EntityKind.Object>>>(query: Query<Person & OfKind<EntityKind.Object>>) => QueryResult<Person & OfKind<EntityKind.Object>> (+1 overload)people).const people: Query<Person & OfKind<EntityKind.Object>>run();QueryResult<Person & OfKind<EntityKind.Object>>.run(opts?: RunOptions): Promise<(Person & OfKind<EntityKind.Object>)[]>Returns all known results.
console.var console: ConsoleThe
consolemodule provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.The module exports two specific components:
- A
Consoleclass with methods such asconsole.log(),console.error()andconsole.warn()that can be used to write to any Node.js stream. - A global
consoleinstance configured to write toprocess.stdoutandprocess.stderr. The globalconsolecan be used without importing thenode:consolemodule.
Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the
note on process I/Ofor more information.Example using the global
console:console.log('hello world');// Prints: hello world, to stdoutconsole.log('hello %s', 'world');// Prints: hello world, to stdoutconsole.error(new Error('Whoops, something bad happened'));// Prints error message and stack trace to stderr:// Error: Whoops, something bad happened// at [eval]:5:15// at Script.runInThisContext (node:vm:132:18)// at Object.runInThisContext (node:vm:309:38)// at node:internal/process/execution:77:19// at [eval]-wrapper:6:22// at evalScript (node:internal/process/execution:76:60)// at node:internal/main/eval_string:23:3const name = 'Will Robinson';console.warn(`Danger ${name}! Danger!`);// Prints: Danger Will Robinson! Danger!, to stderrExample using the
Consoleclass:const out = getStreamSomehow();const err = getStreamSomehow();const myConsole = new console.Console(out, err);myConsole.log('hello world');// Prints: hello world, to outmyConsole.log('hello %s', 'world');// Prints: hello world, to outmyConsole.error(new Error('Whoops, something bad happened'));// Prints: [Error: Whoops, something bad happened], to errconst name = 'Will Robinson';myConsole.warn(`Danger ${name}! Danger!`);// Prints: Danger Will Robinson! Danger!, to errlog(Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)Prints to
stdoutwith newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar toprintf(3)(the arguments are all passed toutil.format()).const count = 5;console.log('count: %d', count);// Prints: count: 5, to stdoutconsole.log('count:', count);// Prints: count: 5, to stdoutSee
util.format()for more information.everyone.const everyone: (Person & OfKind<EntityKind.Object>)[]map((Array<Person & OfKind<EntityKind.Object>>.map<string>(callbackfn: (value: Person & OfKind<EntityKind.Object>, index: number, array: (Person & OfKind<EntityKind.Object>)[]) => string, thisArg?: any): string[]Calls a defined callback function on each element of an array, and returns an array that contains the results.
person) =>person: Person & OfKind<EntityKind.Object>person.person: Person & OfKind<EntityKind.Object>name));name: string - A
-
Search text.
Filter.textmatches objects whose text contains the words, whatever their type. The space indexes text as objects are written./** Every record whose text contains `text`, whatever its type. */export constsearch = (const search: (text: string) => Query.Query<any>Every record whose text contains
text, whatever its type.text: string) =>text: stringQuery.import Queryselect(const select: <Filter.Any>(filter: Filter.Any) => Query.Query<any>Select objects based on a filter.
Filter.import Filtertext(const text: (text: string, options?: Filter.TextSearchOptions) => Filter.AnyFull-text or vector search.
text, {text: stringtype: 'full-text' }));type?: "full-text" | "vector" | undefinedconstmatches = awaitconst matches: any[]db.const db: Database.Databasequery(Database.query: Database.QueryFn<Query<any>>(query: Query<any>) => QueryResult<any> (+1 overload)search('Northwind')).function search(text: string): Query<any>Every record whose text contains
text, whatever its type.run();QueryResult<any>.run(opts?: RunOptions): Promise<any[]>Returns all known results.
-
Follow references backwards.
referencedBysteps from the selected organization to the people whoseemployerpoints at it./** The people whose `employer` points at `organization`. */export constemployeesOf = (const employeesOf: (organization: Organization) => Query.Query<Person & OfKind<EntityKind.Object>>The people whose
employerpoints atorganization.organization:organization: OrganizationOrganization) =>class OrganizationQuery.import Queryselect(const select: <Filter.Any>(filter: Filter.Any) => Query.Query<any>Select objects based on a filter.
Filter.import Filterid(const id: (...ids: EntityId[]) => Filter.AnyFilter by EntityId.
organization.organization: Organizationid)).OfKind<EntityKind.Object>.id: stringreferencedBy(Query<any>.referencedBy<typeof Person>(target: URI | typeof Person, key: RefPropKey<Person & OfKind<EntityKind.Object>>): Query.Query<Person & OfKind<EntityKind.Object>> (+2 overloads)Find objects referencing this object.
Person, 'employer');class Personconststaff = awaitconst staff: (Person & OfKind<EntityKind.Object>)[]db.const db: Database.Databasequery(Database.query: Database.QueryFn<Query<Person & OfKind<EntityKind.Object>>>(query: Query<Person & OfKind<EntityKind.Object>>) => QueryResult<Person & OfKind<EntityKind.Object>> (+1 overload)employeesOf(function employeesOf(organization: Organization): Query<Person & OfKind<EntityKind.Object>>The people whose
employerpoints atorganization.northwind)).const northwind: BaseObj & Organization & OfKind<EntityKind.Object>run();QueryResult<Person & OfKind<EntityKind.Object>>.run(opts?: RunOptions): Promise<(Person & OfKind<EntityKind.Object>)[]>Returns all known results.
-
Keep a query live.
subscribecalls back with the current results each time they change, and returns a function that stops it.constunsubscribe =const unsubscribe: CleanupFndb.const db: Database.Databasequery(Database.query: Database.QueryFn<Query<Person & OfKind<EntityKind.Object>>>(query: Query<Person & OfKind<EntityKind.Object>>) => QueryResult<Person & OfKind<EntityKind.Object>> (+1 overload)people).const people: Query<Person & OfKind<EntityKind.Object>>subscribe(({QueryResult<Person & OfKind<EntityKind.Object>>.subscribe(callback?: ((query: QueryResult<Person & OfKind<EntityKind.Object>>) => void) | undefined, opts?: SubscriptionOptions): CleanupFnSubscribes to changes in query results.
results }) => {results: (Person & OfKind<EntityKind.Object>)[]Currently available results.
console.var console: ConsoleThe
consolemodule provides a simple debugging console that is similar to the JavaScript console mechanism provided by web browsers.The module exports two specific components:
- A
Consoleclass with methods such asconsole.log(),console.error()andconsole.warn()that can be used to write to any Node.js stream. - A global
consoleinstance configured to write toprocess.stdoutandprocess.stderr. The globalconsolecan be used without importing thenode:consolemodule.
Warning: The global console object's methods are neither consistently synchronous like the browser APIs they resemble, nor are they consistently asynchronous like all other Node.js streams. See the
note on process I/Ofor more information.Example using the global
console:console.log('hello world');// Prints: hello world, to stdoutconsole.log('hello %s', 'world');// Prints: hello world, to stdoutconsole.error(new Error('Whoops, something bad happened'));// Prints error message and stack trace to stderr:// Error: Whoops, something bad happened// at [eval]:5:15// at Script.runInThisContext (node:vm:132:18)// at Object.runInThisContext (node:vm:309:38)// at node:internal/process/execution:77:19// at [eval]-wrapper:6:22// at evalScript (node:internal/process/execution:76:60)// at node:internal/main/eval_string:23:3const name = 'Will Robinson';console.warn(`Danger ${name}! Danger!`);// Prints: Danger Will Robinson! Danger!, to stderrExample using the
Consoleclass:const out = getStreamSomehow();const err = getStreamSomehow();const myConsole = new console.Console(out, err);myConsole.log('hello world');// Prints: hello world, to outmyConsole.log('hello %s', 'world');// Prints: hello world, to outmyConsole.error(new Error('Whoops, something bad happened'));// Prints: [Error: Whoops, something bad happened], to errconst name = 'Will Robinson';myConsole.warn(`Danger ${name}! Danger!`);// Prints: Danger Will Robinson! Danger!, to errlog(`${Console.log(message?: any, ...optionalParams: any[]): void (+1 overload)Prints to
stdoutwith newline. Multiple arguments can be passed, with the first used as the primary message and all additional used as substitution values similar toprintf(3)(the arguments are all passed toutil.format()).const count = 5;console.log('count: %d', count);// Prints: count: 5, to stdoutconsole.log('count:', count);// Prints: count: 5, to stdoutSee
util.format()for more information.results.results: (Person & OfKind<EntityKind.Object>)[]Currently available results.
length} people`);Array<Person & OfKind<EntityKind.Object>>.length: numberGets or sets the length of the array. This is a number one higher than the highest index in the array.
});addPerson(function addPerson(db: Database.Database, { employer, ...props }: {name: string;email?: string;employer?: Organization;}): BaseObj & Person & OfKind<EntityKind.Object>db, {const db: Database.Databasename: 'Grace Hopper' });name: stringunsubscribe();const unsubscribe: () => voidThe callback runs again for the person added after the subscription, before
addPersonreturns. - A
Try it
Section titled “Try it”Type in the search box to search every record. The table button shows the records of the chosen type with one column per field. Each list is a live query, so an edit in the form shows up in the list as you save it.
What you can do now
Section titled “What you can do now”You can select records by type, sort them, search their text, follow references backwards, and keep a query running as the results change.
Sharing a space: replicate the app’s space to a second peer.