Reading
matter.table(name) gives a table of the app’s space, by name. Each read goes through one of its indexes.
const deals = matter.table('Deals')
const page = await deals.query({ index: 'by_stage', eq: { Stage: 'Lead' }, limit: 50 })// { rows: [{ id, title, values, createdBy, createdAt, updatedAt, cursor }], nextCursor, prevCursor }
await deals.count({ index: 'by_owner_stage', eq: { Owner: '$viewer', Stage: ['Lead', 'Won'] } })await deals.exists({ index: 'by_email', eq: { Email: 'sam@acme.com' } })await deals.groupCount({ index: 'by_stage', by: 'Stage' }) // { groups: [{ values: { Stage: 'Lead' }, count }], more }await deals.ids({ index: 'by_stage', eq: { Stage: 'Won' } }) // { ids, nextCursor }await deals.get(id)await deals.getMany(ids)The reads
Section titled “The reads”| Read | What it gives |
|---|---|
query(read) |
A page of rows, with cursors for the next and previous pages. |
count(read) |
How many rows match. Without an index, every row of the table. |
exists(read) |
Whether any row matches. |
groupCount({ index, eq?, by }) |
Rows per value of the index’s match fields named in by. eq fixes the match fields before them. more says there were more groups than the limit. |
ids(read) |
Row ids in index order, with a cursor. Cheaper than query when you only need ids. |
get(id), getMany(ids) |
Rows by id. get gives null for a row that doesn’t exist. |
exists, groupCount and ids need an index.
Read options
Section titled “Read options”index: the index to read through, by name.eq: a value for every match field of the index. A list means “any of these”, andnullmatches an empty value.range: { field, gt, gte, lt, lte }: bounds on the index’s first order field.filter: [{ field, operator, value }]: checks on the rows the index finds.order:'asc', the index’s own order (the default), or'desc', its reverse.limit: up to 1,000. The default is 50 forqueryand 100 foridsandgroupCount.afterandbefore: a row’scursor, to read the rows after or before it.until: a row’scursor, to read up to and including it. Use it to re-read a page exactly as it grows.
Filter operators are is, is_not, contains, not_contains, is_empty, is_not_empty, gt, gte, lt, lte, before, after, on_or_before and on_or_after.
Anything a read doesn’t recognise is an error, not ignored. A misspelt option would otherwise read more than you meant.
Without an index
Section titled “Without an index”A read without an index scans the table in sort order:
await deals.query({ sort: [{ field: 'Amount', direction: 'desc' }], filter: [{ field: 'Stage', operator: 'is', value: 'Lead' }],})That only works for a small table, one under 10,000 rows. Once a table grows past that, it’s read through the server: a sort, or a filter without an index, is refused, and a read without an index pages through the table in its own order. See Small and large tables.
Values
Section titled “Values”Rows give values by field name, the same as in Matter’s tables:
- options by name;
- people as
{ id, name }, and'$viewer'means the member reading; - linked rows as
{ id, title }; - dates as
YYYY-MM-DD; - attachments as
{ id, name, size, mime_type }.
A person or relation field that holds one value gives one object; one that allows several gives a list. Empty values are left out of values.
In eq, range and filter, people can be given by id, by name or as '$viewer', and linked rows by id or title. An option must exist: a read that names one the field doesn’t have fails, and lists the options.
Paging
Section titled “Paging”Page with cursors:
let afterdo { const page = await deals.query({ index: 'by_stage', eq: { Stage: 'Lead' }, limit: 200, after }) handle(page.rows) after = page.nextCursor} while (after)To keep a page live as rows arrive, use live reads.