Skip to content

nestifyQuery

Category
Tags
Export size
min 2.26 kB · gzip 1.11 kB
See also

Converts the dot-notation properties of a Feathers query into nested objects — { 'user.name': 'x' } becomes { user: { name: 'x' } }. This is the inverse of dotifyQuery.

The conversion is query-aware rather than a generic object unflatten:

  • $or/$and/$nor/$not branches are converted individually
  • $sort keys are kept in dot notation (nested ones are flattened), because that is the only form the Feathers adapters understand
  • $select, $limit, $skip and custom operators pass through untouched — $select holds paths as values, not as keys
  • a path containing a $-prefixed segment is never split

Use split, exclude or include for keys whose dots are meaningful data. The query is not mutated and is returned unchanged (same reference) when there was nothing to convert.

Nothing is ever dropped when two keys collide. Deep-equal values collapse into one, objects with disjoint keys merge, and a genuine contradiction is wrapped in $and, since the colliding keys were an implicit AND to begin with — this matches addToQuery. A key whose path is blocked by a non-object value needs no $and at all: it simply stays in dot notation, which is already a valid condition.

Caveat: this direction is best effort and not semantics-preserving on MongoDB, where { user: { name: 'x' } } means document equality while { 'user.name': 'x' } means a subfield match. dotifyQuery is the reliable direction; reach for nestifyQuery when a consumer genuinely needs the nested shape.

ts
  import { 
nestifyQuery
} from 'feathers-utils/utils';

Examples

ts
import { nestifyQuery } from 'feathers-utils/utils'

nestifyQuery({ 'user.name': { $ne: 'x' } })
// => { user: { name: { $ne: 'x' } } }

nestifyQuery({ 'user.name': 'a', 'user.age': { $gt: 18 } })
// => { user: { name: 'a', age: { $gt: 18 } } }

nestifyQuery({ $sort: { 'user.name': 1 } })
// => { $sort: { 'user.name': 1 } } (unchanged on purpose)
ts
// the dots in this key are data, not a path
nestifyQuery({ 'x.y': 1, 'a.b': 2 }, { exclude: ['x.y'] })
// => { 'x.y': 1, a: { b: 2 } }
ts
// sibling paths merge into one object
nestifyQuery({ 'user.name': 'a', user: { age: 1 } })
// => { user: { name: 'a', age: 1 } }

// `user` is not an object here, so the dotted key stays as it is
nestifyQuery({ user: 5, 'user.name': 'a' })
// => { user: 5, 'user.name': 'a' } (unchanged)

// a real contradiction still needs an `$and`
nestifyQuery({ 'user.name': 'a', user: { name: 'b' } })
// => { user: { name: 'a' }, $and: [{ user: { name: 'b' } }] }

Type declaration

Show Type Declarations
ts
export type NestifyQueryPredicateOptions = {
  /** the current — possibly dotted — key, e.g. `'owner.name'` */
  key: string
  /** the full dotted path including the key, e.g. `'company.owner.name'` */
  path: string
  /** the value at that key */
  value: any
}
export type NestifyQueryOptions = {
  /**
   * Per-key override. Return `true` to split the key into nested objects,
   * `false` to keep it as-is, or `undefined` to fall through to
   * `exclude`/`include` and then the default heuristic.
   *
   * Only called for keys that actually contain a `.`, and it takes precedence
   * over `exclude`/`include`.
   */
  split?: (options: NestifyQueryPredicateOptions) => boolean | undefined | void
  /** Dotted paths that are never split. Matches the full `path`. */
  exclude?: string[]
  /** If given, only these dotted paths are split. Matches the full `path`. */
  include?: string[]
}
/**
 * Converts the dot-notation properties of a Feathers query into nested objects —
 * `{ 'user.name': 'x' }` becomes `{ user: { name: 'x' } }`. This is the inverse
 * of {@link dotifyQuery}.
 *
 * The conversion is query-aware rather than a generic object unflatten:
 * - `$or`/`$and`/`$nor`/`$not` branches are converted individually
 * - `$sort` keys are kept in dot notation (nested ones are flattened), because
 *   that is the only form the Feathers adapters understand
 * - `$select`, `$limit`, `$skip` and custom operators pass through untouched —
 *   `$select` holds paths as *values*, not as keys
 * - a path containing a `$`-prefixed segment is never split
 *
 * Use `split`, `exclude` or `include` for keys whose dots are meaningful data.
 * The query is not mutated and is returned unchanged (same reference) when there
 * was nothing to convert.
 *
 * Nothing is ever dropped when two keys collide. Deep-equal values collapse into
 * one, objects with disjoint keys merge, and a genuine contradiction is wrapped
 * in `$and`, since the colliding keys were an implicit AND to begin with — this
 * matches {@link addToQuery}. A key whose path is blocked by a non-object value
 * needs no `$and` at all: it simply stays in dot notation, which is already a
 * valid condition.
 *
 * **Caveat:** this direction is best effort and not semantics-preserving on
 * MongoDB, where `{ user: { name: 'x' } }` means *document equality* while
 * `{ 'user.name': 'x' }` means a *subfield match*. `dotifyQuery` is the reliable
 * direction; reach for `nestifyQuery` when a consumer genuinely needs the nested
 * shape.
 *
 * @example
 * ```ts
 *
 *
 * nestifyQuery({ 'user.name': { $ne: 'x' } })
 * // => { user: { name: { $ne: 'x' } } }
 *
 * nestifyQuery({ 'user.name': 'a', 'user.age': { $gt: 18 } })
 * // => { user: { name: 'a', age: { $gt: 18 } } }
 *
 * nestifyQuery({ $sort: { 'user.name': 1 } })
 * // => { $sort: { 'user.name': 1 } } (unchanged on purpose)
 * ```
 *
 * @example
 * ```ts
 * // the dots in this key are data, not a path
 * nestifyQuery({ 'x.y': 1, 'a.b': 2 }, { exclude: ['x.y'] })
 * // => { 'x.y': 1, a: { b: 2 } }
 * ```
 *
 * @example
 * ```ts
 * // sibling paths merge into one object
 * nestifyQuery({ 'user.name': 'a', user: { age: 1 } })
 * // => { user: { name: 'a', age: 1 } }
 *
 * // `user` is not an object here, so the dotted key stays as it is
 * nestifyQuery({ user: 5, 'user.name': 'a' })
 * // => { user: 5, 'user.name': 'a' } (unchanged)
 *
 * // a real contradiction still needs an `$and`
 * nestifyQuery({ 'user.name': 'a', user: { name: 'b' } })
 * // => { user: { name: 'a' }, $and: [{ user: { name: 'b' } }] }
 * ```
 *
 * @see https://utils.feathersjs.com/utils/nestify-query.html
 */
export declare const nestifyQuery: <Q extends Query>(
  query: Q,
  options?: NestifyQueryOptions,
) => Q
ArgumentTypeDescription
queryQ
optionsNestifyQueryOptions

Released under the MIT License.