dotifyQuery
Converts the nested properties of a Feathers query into dot notation — { user: { name: 'x' } } becomes { 'user.name': 'x' }. This is the form every Feathers adapter understands, so it is the direction you normally want.
The conversion is query-aware rather than a generic object flatten:
- operators (
$ne,$in, ...) never become path segments $or/$and/$nor/$notbranches are converted individually$sortkeys are flattened, its directions are kept$select,$limit,$skipand custom operators pass through untouched
A value is only treated as a path when it is a non-empty plain object with at least one non-$ key. Date, RegExp, bson ObjectId, class instances, arrays, primitives and {} are always values. An object that mixes operators and plain keys keeps its operators on the current path: { user: { $ne: null, name: 'x' } } becomes { user: { $ne: null }, 'user.name': 'x' }.
Use descend, exclude or include for properties that legitimately hold an object value. The query is not mutated and is returned unchanged (same reference) when there was nothing to convert.
Nothing is ever dropped when two paths collide. Deep-equal values collapse into one, operator objects with disjoint keys merge, and a genuine contradiction is wrapped in $and — the colliding keys were an implicit AND to begin with. This matches addToQuery.
import { dotifyQuery } from 'feathers-utils/utils';Examples
import { dotifyQuery } from 'feathers-utils/utils'
dotifyQuery({ user: { name: { $ne: 'x' } } })
// => { 'user.name': { $ne: 'x' } }
dotifyQuery({ $or: [{ user: { name: 'a' } }] })
// => { $or: [{ 'user.name': 'a' }] }
dotifyQuery({ $sort: { user: { name: 1 } } })
// => { $sort: { 'user.name': 1 } }// contradicting conditions for the same path are kept as an `$and`
dotifyQuery({ 'user.name': 'a', user: { name: 'b' } })
// => { 'user.name': 'a', $and: [{ 'user.name': 'b' }] }
// disjoint operators merge, deep-equal values collapse
dotifyQuery({ 'user.age': { $gt: 18 }, user: { age: { $lt: 30 } } })
// => { 'user.age': { $gt: 18, $lt: 30 } }// `meta` holds an object that should be matched by equality
dotifyQuery({ meta: { a: 1 } }, { exclude: ['meta'] })
// => { meta: { a: 1 } }
// depth-agnostic: never descend into a key named `meta`
dotifyQuery(query, {
descend: ({ key }) => (key === 'meta' ? false : undefined),
})// normalize incoming queries for the whole service
import { transformQuery } from 'feathers-utils/hooks'
app.service('users').hooks({ before: { find: [transformQuery(dotifyQuery)] } })Type declaration
Show Type Declarations
export type DotifyQueryPredicateOptions = {
/** the current key, e.g. `'owner'` */
key: string
/** the full dotted path including the key, e.g. `'company.owner'` */
path: string
/** the value at that key */
value: any
}
export type DotifyQueryOptions = {
/**
* Per-key override. Return `true` to descend into the value, `false` to treat
* it as a leaf value, or `undefined` to fall through to `exclude`/`include`
* and then the default heuristic.
*
* Only called for values that could be descended at all (non-empty plain
* objects), and it takes precedence over `exclude`/`include`.
*/
descend?: (options: DotifyQueryPredicateOptions) => boolean | undefined | void
/** Dotted paths that are never descended into. Matches the full `path`. */
exclude?: string[]
/**
* If given, only these dotted paths are descended into. Matches the full
* `path`.
*/
include?: string[]
}
/**
* Converts the nested properties of a Feathers query into dot notation —
* `{ user: { name: 'x' } }` becomes `{ 'user.name': 'x' }`. This is the form
* every Feathers adapter understands, so it is the direction you normally want.
*
* The conversion is query-aware rather than a generic object flatten:
* - operators (`$ne`, `$in`, ...) never become path segments
* - `$or`/`$and`/`$nor`/`$not` branches are converted individually
* - `$sort` keys are flattened, its directions are kept
* - `$select`, `$limit`, `$skip` and custom operators pass through untouched
*
* A value is only treated as a path when it is a non-empty plain object with at
* least one non-`$` key. `Date`, `RegExp`, bson `ObjectId`, class instances,
* arrays, primitives and `{}` are always values. An object that mixes operators
* and plain keys keeps its operators on the current path:
* `{ user: { $ne: null, name: 'x' } }` becomes
* `{ user: { $ne: null }, 'user.name': 'x' }`.
*
* Use `descend`, `exclude` or `include` for properties that legitimately hold an
* object value. The query is not mutated and is returned unchanged (same
* reference) when there was nothing to convert.
*
* Nothing is ever dropped when two paths collide. Deep-equal values collapse
* into one, operator objects with disjoint keys merge, and a genuine
* contradiction is wrapped in `$and` — the colliding keys were an implicit AND
* to begin with. This matches {@link addToQuery}.
*
* @example
* ```ts
*
*
* dotifyQuery({ user: { name: { $ne: 'x' } } })
* // => { 'user.name': { $ne: 'x' } }
*
* dotifyQuery({ $or: [{ user: { name: 'a' } }] })
* // => { $or: [{ 'user.name': 'a' }] }
*
* dotifyQuery({ $sort: { user: { name: 1 } } })
* // => { $sort: { 'user.name': 1 } }
* ```
*
* @example
* ```ts
* // contradicting conditions for the same path are kept as an `$and`
* dotifyQuery({ 'user.name': 'a', user: { name: 'b' } })
* // => { 'user.name': 'a', $and: [{ 'user.name': 'b' }] }
*
* // disjoint operators merge, deep-equal values collapse
* dotifyQuery({ 'user.age': { $gt: 18 }, user: { age: { $lt: 30 } } })
* // => { 'user.age': { $gt: 18, $lt: 30 } }
* ```
*
* @example
* ```ts
* // `meta` holds an object that should be matched by equality
* dotifyQuery({ meta: { a: 1 } }, { exclude: ['meta'] })
* // => { meta: { a: 1 } }
*
* // depth-agnostic: never descend into a key named `meta`
* dotifyQuery(query, {
* descend: ({ key }) => (key === 'meta' ? false : undefined),
* })
* ```
*
* @example
* ```ts
* // normalize incoming queries for the whole service
*
*
* app.service('users').hooks({ before: { find: [transformQuery(dotifyQuery)] } })
* ```
*
* @see https://utils.feathersjs.com/utils/dotify-query.html
*/
export declare const dotifyQuery: <Q extends Query>(
query: Q,
options?: DotifyQueryOptions,
) => Q| Argument | Type | Description |
|---|---|---|
| query | Q | |
| options | DotifyQueryOptions |
