# rn-docyrus-query-builder URL: /docs/native/docyrus/docyrus-query-builder Author Docyrus data-source queries on a phone — columns tree, nested filters, sort, pagination, calculations, formulas, child queries and pivots, with a live Run preview and the JSON payload. ## Installation ```bash pnpm dlx @docyrus/cli add @docyrus/rn-docyrus-query-builder ``` **Dependencies:** - [@react-querybuilder/core](https://www.npmjs.com/package/@react-querybuilder/core) - [react-native-reanimated](https://www.npmjs.com/package/react-native-reanimated) - [react-native-gesture-handler](https://www.npmjs.com/package/react-native-gesture-handler) - [@shopify/flash-list](https://www.npmjs.com/package/@shopify/flash-list) - [tailwind-variants](https://www.npmjs.com/package/tailwind-variants) `DocyrusDataSourceQueryBuilder` is the React Native port of the web component with the same name. It uses the same query model (`ISelectQueryParams`) and the same hook (`useDocyrusQueryBuilder`). Its section editors produce the same payload for the Docyrus items endpoint (`/v1/apps/{app}/data-sources/{ds}/items`), so a query written on a phone can be saved and run on the web without changes, and the other way round. The layout is built for mobile: - **Primary tabs**: **Configure**, **Preview** and **JSON**. - **Configure** shows one row per section with a live summary, such as "Filters · 3 rules" or "Columns · 12 selected". Tapping a row opens that section in a **full-height bottom sheet** (`presentation="sheet"`, the default) or **inline with a back row** (`"stack"`). You can also push your own screen from `onSectionPress`. - **Preview** calls `runQuery()` and shows the returned rows in a horizontally scrolling read-only table, with the row count, the total and any error. - **JSON** shows the cleaned payload read-only in a monospace font, in pretty or compact form. It has a **Copy** button when you pass `onCopy`. The component adds no clipboard dependency, so wire `expo-clipboard` or a similar library yourself. Every section except **Select Data Source** stays disabled until a data source is selected. ### Works without a client When you pass `dataSources`, and optionally `fields`, but no `client`, every configure section still works offline. Only **Run** needs a client: without one, it reports that authentication is required. With a `client`, the builder: - discovers data sources (`/v1/apps/data-sources`) - loads the selected source's schema including enums (`/v1/apps/{app}/data-sources/{ds}?expand=enums`) - loads user, team, role and org-unit filter options (`/v1/users`, `/v1/teams`, `/v1/users/acl/roles`, `/v1/users/acl/hierarchy-units`) - loads relation filter pickers (`…/items?columns=id,name`) - loads parent and child field groups (`/v1/dev/data-sources/:id/fields?expand=parent,child`) - runs the query (`/items`) ## Usage ```tsx import { useState } from 'react'; import * as Clipboard from 'expo-clipboard'; import { useDocyrusClient } from '@docyrus/signin/react-native'; import { DocyrusDataSourceQueryBuilder, type ISelectQueryParams } from '@/components/docyrus-native/docyrus-query-builder'; export function QueryScreen() { const client = useDocyrusClient(); const [value, setValue] = useState ``` ## Sections | Section | Web counterpart | Native editor | |---------|-----------------|---------------| | Select Data Source | Two panes: grouped list and field table | A searchable list grouped by app, with collapsible groups (search opens every group), plus a card for the selected source whose field list you can expand. Hidden by `lockDataSource`. Picking a source closes the sheet. | | Columns | `TreeView` multi-select | The native `TreeView` with checkboxes. Leaf ids are paths (`status\|id` → `status(id)`, `company\|owner\|email` → `company(owner(email))`). User and enum fields expand into fixed sub-field folders. The **Parent Data Sources** switch nests a parent's fields under the relation column that points to it. Shows the generated columns string. | | Filters | react-querybuilder `QueryBuilderDocyrus` | The native `QueryBuilderDocyrus`, driven by `getOperatorsForFieldType`, user / team / role / unit option sources (`createDocyrusOperatorValueConfig`) and relation record pickers, plus the full-text `filterKeyword`. The id-bearing rule tree is kept locally and rebuilt only when `value.filters` changes from outside (web #238). | | Sort | Field selector, ASC/DESC, drag handle | Field selector sheet, ASC/DESC toggle, **Move up / Move down** and remove | | Pagination | Limit (with presets), offset, full count | Same controls: number inputs, preset chips and a `fullCount` switch | | Calculations (`enableCalculations`) | Aggregate rows with an Advanced collapsible | Aggregate `Select`, field, alias, and an Advanced section (distinct, min/max, number type), with move up/down | | Formulas (`enableFormulas`) | Nested block cards with drag | Nested block cards with a colour chip per kind and a coloured left border. Each card has kind, cast and tz controls. **Add / insert first / remove / move up / move down** replace drag. Inline or subquery formulas (`from` / `with`). Formula keys are renamed when editing ends. | | Child Queries (`enableChildQueries`) | Child data-source picker and per-entry cards | Same flow. Opening the section turns on `relatedExpand.child`. `value.childQueries` is an **array**, and each entry's `alias` is injected into the parent `columns` (the items endpoint drops the child block without it) and removed from them again when the entry is deleted. Each card shows `from` / `using` read-only, a columns tree, order by, direction and limit. | | Pivot (`enablePivot`) | Matrix entries | Matrix entry cards (using, columns, spread, and an optional date range typed as ISO text), move up/down, plus `hideEmptyRows` / `limit` | ## API Reference ### DocyrusDataSourceQueryBuilderProps | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `Partial` | - | **Required.** The current query payload (controlled). | | `onChange` | `(value: Partial) => void` | - | **Required.** Receives the next payload. `dataSourceId`, `dataSourceFullSlug` and `showGroupSummaries` are removed from it. | | `client` | `DocyrusDataSourceQueryBuilderClient \| null` | `null` | An authenticated client, which only needs `get`. It enables discovery, filter option sources, related fields and Run. | | `fields` | `IField[]` | `[]` | Fallback field list, used when the selected source has no fields. | | `dataSources` | `IDataSourceReference[]` | `[]` | Static data sources. Used when there is no client, and as a fallback when discovery fails. | | `locale` | `UiI18nLocale` | `'en'` | Locale exposed on the context. | | `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | Text size of section titles and summaries. | | `variant` | `'default' \| 'bordered' \| 'compact'` | `'default'` | Visual style. | | `className` | `string` | - | Root container className. The root is `flex-1`, so give it a height. | | `defaultSection` | `DSQBSection` | - | Section to open on mount (as a sheet or inline). Leave it out to start on the section list. | | `lockDataSource` | `boolean` | `false` | Fixes the data source to the one in `value`: hides the data-source section and the clear button. | | `enableCalculations` | `boolean` | `false` | Shows the Calculations section. | | `enableFormulas` | `boolean` | `false` | Shows the Formulas section. | | `enableChildQueries` | `boolean` | `false` | Shows the Child Queries section. | | `enablePivot` | `boolean` | `false` | Shows the Pivot section. | | `presentation` | `'stack' \| 'sheet'` | `'sheet'` | `'sheet'` opens a tapped section in a full-height bottom sheet. `'stack'` shows it inline, in place of the list, with a back row. | | `onSectionPress` | `(section: DSQBSection) => boolean \| void` | - | Called when a section row is tapped. Return `true` to handle navigation yourself. | | `onCopy` | `(json: string) => void` | - | Copy handler for the JSON tab. The Copy button is hidden when this is omitted. | | `filterControlElements` | `Partial` | - | Control-element overrides for the Filters `QueryBuilderDocyrus`. They are merged over the defaults. Pass a stable reference. | | `renderFilterValueEditor` | `(props: ValueEditorProps, context: unknown) => ReactNode \| undefined` | - | Replaces the value editor of a rule. Return `undefined` to keep the default editor. Ignored when `filterControlElements.valueEditor` is set. | | `filterContext` | `unknown` | - | Passed to `renderFilterValueEditor` as its second argument. | ### DSQBFieldSelector | Prop | Type | Default | Description | |------|------|---------|-------------| | `fields` | `IField[]` | - | Fields to pick from. They are grouped by category (text, numeric, date, select, relation, user, boolean, other). | | `value` | `string` | - | Slug of the selected field. | | `onSelect` | `(slug: string) => void` | - | Called when a field is picked. | | `placeholder` | `string` | `'Select field...'` | Text on the trigger when nothing is selected. | | `title` | `string` | placeholder | Title of the sheet. | | `size` | `'sm' \| 'default' \| 'lg'` | `'default'` | Size of the trigger. | | `showTypeIcon` | `boolean` | `true` | Shows the columns icon on the trigger. | | `disabled` | `boolean` | `false` | Disables the trigger. | | `className` | `string` | - | className for the trigger. | ### DSQBJsonPreview | Prop | Type | Default | Description | |------|------|---------|-------------| | `value` | `Partial` | - | Query payload. It is cleaned with `sanitizeSelectQueryParams` and `cleanPayload` before display. | | `onCopy` | `(json: string) => void` | - | Shows the Copy button. | | `className` | `string` | `'flex-1 gap-2'` | Container className. | ### DSQBFormulaBlock | Prop | Type | Default | Description | |------|------|---------|-------------| | `block` | `IQueryFormulaBlock` | - | The block node. | | `onChange` | `(block: IQueryFormulaBlock) => void` | - | Replaces the node. | | `onRemove` | `() => void` | - | Shows the remove button. | | `onMoveUp` / `onMoveDown` | `() => void` | - | Native only. Shows the move buttons, which replace drag. | | `fields` | `Array<{ slug: string; name: string }>` | - | Fields offered by column blocks. | | `depth` | `number` | `0` | Nesting depth, used for indentation. | The other section editors take no props. They read everything through `useDSQB()`. ### useDocyrusQueryBuilder(options) It takes `UseDocyrusQueryBuilderOptions`: `value`, `onChange`, `client`, `fields`, `dataSources`, `locale` and `size`. It returns `UseDocyrusQueryBuilderResult`. The hook uses no React Query. | Field | Type | Description | |-------|------|-------------| | `value` / `updateValue(patch)` | `Partial` / `(patch) => void` | Current payload, and a patch writer that sanitizes the result. | | `fields` | `IField[]` | The selected source's fields, or the `fields` option as a fallback. | | `filterOptionSources` | `DocyrusQueryBuilderOptionSources` | Users, teams, roles and units for filter pickers. | | `relationValuesByField` | `Record` | Related records (id and name) for each relation field slug. | | `dataSources`, `dataSourcesStatus`, `dataSourcesError`, `reloadDataSources()` | — | Discovery state. | | `selectedDataSource`, `selectDataSource(ds)`, `unselectDataSource()` | — | Selection. Selecting a source replaces the payload with `{ dataSourceId, dataSourceFullSlug, limit: 25 }`. Unselecting clears it. | | `dataSourceDetailStatus`, `dataSourceDetailError`, `reloadSelectedDataSource()` | — | Detail fetch with enums. | | `ensureDataSourceFields(id)` | `(id: string) => Promise` | Lazy-loads the schema of a related source. | | `fetchRelatedFields(expand)` | `(expand: Array<'parent' \| 'child'>) => Promise` | Parent and child field groups. | | `relatedExpand` / `setRelatedExpand` | `DSQBRelatedExpand` / `Dispatch>` | Expansion state kept in the hook. The fetch is de-duplicated on a `::` key, and a failed attempt stays pinned to that key. | | `relatedFields`, `relatedFieldsLoading` | — | The resolved groups. | | `preview`, `runQuery()` | `DSQBPreviewState` / `() => Promise` | Run state. | | `locale`, `size` | — | Echoed from the options. | ### Context `DSQBContext` and `useDSQB()` hold a `DSQBContextValue`: the hook result plus `filterControlElements`, `renderFilterValueEditor` and `filterContext`. `useDSQB()` throws when it is called outside the builder. ## Components | Component | Description | |-----------|-------------| | `DocyrusDataSourceQueryBuilder` | Root: the Configure / Preview / JSON tabs, the section list and the sheets | | `DSQBDataSourceSelector` | Data-source section | | `DSQBColumnsEditor` | Columns tree section | | `DSQBFiltersEditor` | Filters section (keyword and `QueryBuilderDocyrus`) | | `DSQBOrderByEditor` | Sort section | | `DSQBPaginationEditor` | Pagination section | | `DSQBCalculationsEditor` | Calculations section | | `DSQBFormulaEditor` / `DSQBFormulaBlock` | Formulas section and a single block node | | `DSQBChildQueriesEditor` | Child Queries section | | `DSQBPivotEditor` | Pivot section | | `DSQBRunPreview` | Run and result table | | `DSQBJsonPreview` | JSON payload view | | `DSQBFieldSelector` | Searchable field picker in a sheet | ## Hooks, constants & utilities | Export | Description | |--------|-------------| | `useDocyrusQueryBuilder`, `useDSQB`, `DSQBContext` | Hook and context | | `createDocyrusOperatorValueConfig(fieldType, sources)` | Per-operator value options and editor types for user and follower fields | | `loadDocyrusQueryBuilderOptionSources(client)` | Fetches users, teams, roles and units | | `docyrusQueryBuilderVariants` | `tv()` slot styles (`variant` / `size`) | | `FILTER_OPERATOR_CATEGORIES`, `ALL_FILTER_OPERATORS` | Filter operator catalog | | `FUNCTION_CATEGORIES`, `ALL_ALLOWED_FUNCTIONS`, `ALLOWED_AGGREGATES`, `ALLOWED_CAST_TYPES`, `DATE_FORMULAS` | Allowed functions, aggregates, casts and date formulas | | `BUILTIN_NAMES`, `EXTRACT_PARTS`, `MATH_OPS`, `COMPARE_OPS`, `BOOLEAN_OPS` | Formula operators | | `BLOCK_KINDS`, `BLOCK_KIND_LABELS`, `BLOCK_KIND_COLORS` | Formula block kinds | | `QUERY_MODES`, `QUERY_MODE_DESCRIPTIONS`, `FILTER_TYPES`, `NUMBER_TYPES`, `DATE_RANGE_INTERVALS` | Enumerations | | `parseColumnString`, `serializeColumns` | Column DSL parser and serializer (`alias:func[args]@field(sub)`, `...spread`) | | `normalizeOrderBy`, `serializeOrderBy` | Order-by normalization | | `buildDefaultColumns`, `cleanPayload` | Payload helpers | | `getFieldTypeCategory`, `getOperatorsForFieldType` | Field-type helpers | | `validateQuery(params, fields)` | Validation errors and warnings. On native, a filter rule on a field that is not in a non-empty `fields` list is reported as a warning. | | `countFilterRules` | Counts leaf rules | | `createBlock`, `updateBlockAt`, `removeBlockAt`, `insertBlockAt`, `getBlockLabel` | Formula-block tree helpers | | `prepareRuleGroup`, `toRuleGroup`, `fromRuleGroup` | Native only. Local id-stamping and the `IQueryFilterGroup` ⇄ rule-tree bridge, so the component does not import `react-querybuilder` | ## Type Exports | Type | Description | |------|-------------| | `DocyrusDataSourceQueryBuilderProps` | Root props | | `DocyrusDataSourceQueryBuilderClient` | `{ get(path, params?) }` | | `UseDocyrusQueryBuilderOptions` / `UseDocyrusQueryBuilderResult` / `DSQBContextValue` | Hook and context | | `DSQBPrimaryTab` / `DSQBSection` / `DSQBPresentation` | `'configure' \| 'preview' \| 'json'`, the section ids, `'stack' \| 'sheet'` | | `DSQBFilterValueEditorRenderer` | Type of `renderFilterValueEditor` | | `DSQBPreviewState` | `{ status, rows, columns, total?, error?, raw? }` | | `DocyrusQueryBuilderOption` / `DocyrusQueryBuilderOptionSources` | Filter picker options | | `ISelectQueryParams` | Full query payload | | `IQueryFilterGroup` / `IQueryFilterRule` / `QueryFilterType` | Filters | | `ISelectQueryOrderBy` / `ISelectQueryCalculationRule` / `AggregateFunction` / `NumberType` | Sort and calculations | | `IQueryFormula` / `IQueryBlockInlineFormula` / `IQueryBlockSubqueryFormula` / `IQueryFormulaBlock` / `BlockKind` | Formulas | | `IBlockLiteral` … `IBlockBoolean`, `BuiltinName`, `ExtractPart`, `MathOp`, `CompareOp`, `BooleanOp` | Block AST | | `IQueryChildQueryParams` | A child query entry (`alias`, `from`, `using`, `columns?`, `filters?`, `calculations?`, `orderBy?`, `limit?`) | | `ISelectPivot` / `ISelectPivotMatrixQuery` / `DateRangeInterval` | Pivot | | `QueryMode` / `ExpandType` | Enumerations | | `IDataSourceReference` | A data source with `fields` | | `DSQBParentFieldGroup` / `DSQBChildFieldGroup` / `DSQBRelatedFields` / `DSQBRelatedExpand` | Related field groups | | `IQueryValidationError` / `ParsedColumn` | Validation and column DSL | | `IField` / `IFieldType` / `UiI18nLocale` | Field model | | `ValueEditorType` / `DSQBRuleType` / `DSQBRuleGroupType` | Native only. Local structural react-querybuilder types | ## Differences from web - **Removed:** `aiAssistantOpen`, `onAiAssistantOpenChange`, `renderAiAssistant`, `useApplyQuery`, `IQueryBuilderAiAssistantRenderContext` and the Cody agent toggle. - **Added:** `presentation`, `onSectionPress`, `onCopy`, `renderFilterValueEditor`. - **Changed:** `filterControlElements` uses the native `QBControlElements` slot map. The web version uses react-querybuilder's `ControlElementsProp`. `filterContext` goes to `renderFilterValueEditor`, because the native builder has no `context` prop. - **Changed:** `defaultSection` defaults to no open section. The web version always shows one section. - **Replaced interactions:** Drag handles in Sort and Formulas become move up / move down buttons. Pivot date-range bounds are ISO text inputs instead of `datetime-local`. ## Translations All copy goes through `useUiTranslation()` under `ui.docyrusQueryBuilder.*`, with English fallbacks. The web component hard-codes English, so these keys are new: `selectDataSource`, `columns`, `filters`, `sort`, `pagination`, `calculations`, `formulas`, `childQueries`, `pivot` and their `*Description` keys, `configure`, `preview`, `run`, `runQuery`, `running`, `searchFields`, `selectField`, `parentDataSources`, `allColumnsReturned`, `selected`, `keyword`, `filterRules`, `addSortRule`, `limit`, `offset`, `fullCount`, `addCalculation`, `advanced`, `distinct`, `addFormula`, `addInput`, `addWhen`, `addChildDataSource`, `addMatrixEntry`, `moveUp`, `moveDown`, `remove`, `copy`, `copied`, `pretty`, `compact`, `jsonOutput`, `noRows`, `runError`, `selectDataSourceFirst`, the `category*` field-group labels and the empty or loading state messages.