Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/query-builder.md
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,15 @@ The `label`, which gets specified after **AS**, denotes the name of the column t
| `node` | Use the label to edit the column header of the first column | [Link](examples.md#first-column-header) |
| `add({label1}, {label2})` | Add the values of two columns. Supports adding values to dates. | [Link](examples.md#add-or-subtract) |
| `subtract({label1}, {label2})` | Subtract the values betweenn two columns. Supports adding values to dates. | [Link](examples.md#add-or-subtract) |
| `count({node})` | Count all matches for an intermediary `{node}` variable (for example, references per block). | N/A |
| `count-distinct({node})` | Count unique matches for an intermediary `{node}` variable. | N/A |

Aggregate selections are grouped by the returned `node` by default. A common pattern is:

- Condition: `node` `references` `tag`
- Selection: `count(tag)`
- Sort by that count column descending
- Optional exclusion: add a `not` condition using the same intermediary variable (for example, `tag` `has title` `Some Page`)

## Manipulating Results

Expand Down
42 changes: 42 additions & 0 deletions src/utils/predefinedSelections.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ const ADD_TEST = /^add\(([^,)]+),([^,)]+)\)$/i;
const NODE_TEST = /^node:(\s*[^:]+\s*)(:.*)?$/i;
const ACTION_TEST = /^action:\s*([^:]+)\s*(?::(.*))?$/i;
const DATE_FORMAT_TEST = /^date-format\(([^,)]+),([^,)]+)\)$/i;
const COUNT_TEST = /^count\(\s*([^)]+?)\s*\)$/i;
const COUNT_DISTINCT_TEST = /^count-distinct\(\s*([^)]+?)\s*\)$/i;
const MILLISECONDS_IN_DAY = 1000 * 60 * 60 * 24;

const getArgValue = (key: string, result: QueryResult) => {
Expand All @@ -42,6 +44,9 @@ const getArgValue = (key: string, result: QueryResult) => {
return val;
};

const toDatalogVariable = (value = "") =>
value.replace(/^\?/, "").replace(/[\s"()[\]{}/\\^@,~`]/g, "");

const getUserDisplayNameById = (id?: number) => {
if (!id) {
return "Anonymous User";
Expand Down Expand Up @@ -168,6 +173,25 @@ const isVariableExposed = (
}
});

const getAggregateSelection = ({
match,
where,
fn,
}: {
match: RegExpExecArray | null;
where: DatalogClause[];
fn: "count" | "count-distinct";
}) => {
const variable = (match?.[1] || "")
.trim()
.replace(/^\?/, "")
.replace(/^\{+/, "")
.replace(/\}+$/, "");
if (!variable || !isVariableExposed(where, variable)) return "";
const datalogVariable = toDatalogVariable(variable);
return datalogVariable ? `(${fn} ?${datalogVariable})` : "";
};

export type SelectionSuggestion = {
text: string;
children?: SelectionSuggestion[];
Expand Down Expand Up @@ -216,6 +240,10 @@ const EDIT_DATE_SUGGESTIONS: SelectionSuggestion[] = [
];
const CREATE_BY_SUGGESTIONS: SelectionSuggestion[] = [{ text: "created by" }];
const EDIT_BY_SUGGESTIONS: SelectionSuggestion[] = [{ text: "edited by" }];
const COUNT_SUGGESTIONS: SelectionSuggestion[] = [{ text: "count({{node}})" }];
const COUNT_DISTINCT_SUGGESTIONS: SelectionSuggestion[] = [
{ text: "count-distinct({{node}})" },
];
// TOO SLOW
// const ATTR_SUGGESTIONS: SelectionSuggestion[] = (
// window.roamAlphaAPI.data.fast.q(
Expand Down Expand Up @@ -479,6 +507,20 @@ const predefinedSelections: PredefinedSelection[] = [
},
suggestions: [{ text: "date-format({{node}},[MMM do, yyyy])" }],
},
{
test: COUNT_TEST,
pull: ({ match, where }) =>
getAggregateSelection({ match, where, fn: "count" }),
mapper: () => "",
suggestions: COUNT_SUGGESTIONS,
},
{
test: COUNT_DISTINCT_TEST,
pull: ({ match, where }) =>
getAggregateSelection({ match, where, fn: "count-distinct" }),
mapper: () => "",
suggestions: COUNT_DISTINCT_SUGGESTIONS,
},
{
test: ACTION_TEST,
pull: ({ returnNode }) => `(pull ?${returnNode} [:block/uid])`,
Expand Down