FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix: mergeQuery flattenAndBranches by fratzinger · Pull Request #40 · feathersjs/feathers-utils · GitHub

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension .ts  (3) All 1 file type selected
Viewed files
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Unified
Split
Hide whitespace
Diff view
Unified
Split
Hide whitespace
56 changes: 56 additions & 0 deletions src/utils/merge-query/flatten-and-branches.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
type QueryRecord = Record<string, any>

/**
* Recursively hoists nested `$and` conjuncts so that an `$and` never contains
* another `$and` branch. `$and` is associative, so a branch like
* `{ $or: [...], $and: [n] }` is split into the two conjuncts `{ $or: [...] }`
* and `n`. A pure `{ $and: [...] }` branch contributes only its inner branches.
* Internal helper for {@link mergeQuery} (intersect).
*/
export function flattenAndBranches(branches: QueryRecord[]): QueryRecord[] {
const result: QueryRecord[] = []
for (const branch of branches) {
if (Array.isArray(branch.$and)) {
const { $and, ...rest } = branch
if (Object.keys(rest).length > 0) {
result.push(rest)
}
result.push(...flattenAndBranches($and))
} else {
result.push(branch)
}
}
return result
}

if (import.meta.vitest) {
const { describe, it, expect } = import.meta.vitest

describe('flattenAndBranches', () => {
it('leaves plain branches untouched', () => {
expect(flattenAndBranches([{ id: 1 }, { id: 2 }])).toEqual([
{ id: 1 },
{ id: 2 },
])
})

it('hoists a nested $and and keeps the remaining keys as a branch', () => {
expect(
flattenAndBranches([{ $or: ['c'], $and: [{ $nor: ['n'] }] }]),
).toEqual([{ $or: ['c'] }, { $nor: ['n'] }])
})

it('drops the remainder when a branch is a pure $and', () => {
expect(flattenAndBranches([{ $and: [{ id: 1 }, { id: 2 }] }])).toEqual([
{ id: 1 },
{ id: 2 },
])
})

it('flattens recursively', () => {
expect(
flattenAndBranches([{ $and: [{ $and: [{ id: 1 }] }, { id: 2 }] }]),
).toEqual([{ id: 1 }, { id: 2 }])
})
})
}
20 changes: 18 additions & 2 deletions src/utils/merge-query/merge-query-bodies.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type { MergeQueryMode } from './merge-query.util.js'
import { isEmptyObject } from '../../common/is-empty-object.js'
import { logicalBranches } from './logical-branches.js'
import { dedupeBranches } from './dedupe-branches.js'
import { flattenAndBranches } from './flatten-and-branches.js'
import { hasConflict } from './has-conflict.js'

type QueryRecord = Record<string, any>
Expand Down Expand Up @@ -54,10 +55,15 @@ export function mergeQueryBodies(
return { ...target, ...source }
}

const branches = dedupeBranches([
const collected = [
...(targetBranches ?? [target]),
...(sourceBranches ?? [source]),
])
]

// under `$and`, hoist any nested `$and` so the result never nests `$and` in `$and`
const branches = dedupeBranches(
op === '$and' ? flattenAndBranches(collected) : collected,
)

if (branches.length === 0) {
return {}
Expand Down Expand Up @@ -132,5 +138,15 @@ if (import.meta.vitest) {
),
).toEqual({ $and: [{ id: 1 }, { id: 2 }, { id: 3 }] })
})

it('intersect hoists a nested $and instead of nesting it', () => {
expect(
mergeQueryBodies(
{ $or: ['u'] },
{ $or: ['c'], $and: [{ $nor: ['n'] }] },
'intersect',
),
).toEqual({ $and: [{ $or: ['u'] }, { $or: ['c'] }, { $nor: ['n'] }] })
})
})
}
13 changes: 13 additions & 0 deletions src/utils/merge-query/merge-query.util.test.ts
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode characters
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,19 @@ describe('mergeQuery', () => {
options: { mode: 'intersect' },
expected: { $and: [{ price: { $gt: 5 } }, { price: { $gt: 8 } }] },
},
// e.g. a user $or merged with a CASL rule that carries its own $and ($nor)
'hoists the source $and instead of nesting it': {
target: { $or: [{ a: 1 }] },
source: { $or: [{ b: 2 }], $and: [{ $nor: [{ c: 3 }] }] },
options: { mode: 'intersect' },
expected: {
$and: [
{ $or: [{ a: 1 }] },
{ $or: [{ b: 2 }] },
{ $nor: [{ c: 3 }] },
],
},
},
})
})

Expand Down
Loading

Back | FazBrowse Home | New Git URL