Closed cayuu closed 9 years ago
The current 0.4.0
match spec and match object spec only allows for a list of conditions, with no relation between them. Currently:
{match: [
{field:'age', op:'gte', value:'18'},
{field:'criminal', op:'eq', value:true}
]}
The spec does not explain whether the list is to be AND
or OR
. Either convention excludes the other (and every alternative boolean operator). Further, no nesting is currently possible.
MongoDB specifies nesting as follows:
{
$and : [
{ $or : [ { price : 0.99 }, { price : 1.99 } ] },
{ $or : [ { sale : true }, { qty : { $lt : 20 } } ] }
]
}
Translates to: ( price = 0.99 || price = 1.99 ) && ( sale = true || qty < 20 )
The nested challenge opening this question would represent in Mongo-style as:
// (name = 'x' && age >= 21) || ( state = 'CA' || (state = 'WA' && age >= 18) )
{
$or: [
{ $and: [{name:'x'}, {age: {$gte:21}} ] },
{ $or: [
{state:'CA'},
{ $or: [ {state:'WA'}, {age: {$gte:18}} ] }
]}
]
}
This is ugly to parse.
One alternative is a naive [mo, op, mo], op, mo
style approach, where arrays are blocks:
[
[ {name:x}, 'and', {age: {$gte:21}}],
'or',
[
{state:'CA'},
'or',
[ {state:'WA'}, 'and', {age:{$gte:18}} ]
]
]
Perhaps all nesting is simply ugly.
Trying to consider generating nested matches using fluent interfaces:
.anded( [{name:'x'}, {age:$gte:21}] )
.some_link_to_say_OR_joins_these_two_blocks().
.ored( [ {state:'CA', HOW_TO_FLIENT_EMBED_ANOTHER_AND_BLOCK??!!?! ] )
But to actually NEST you basically end up with a code version of the mongo block (assuming 'and()' and 'or()' are accessible functions that generate Qo compatible match objects):
or(
and([ {name:'x'}, {age: {$gte:21}} ]),
or([
{state:'CA'},
or([ {state:'WA'}, {age: {$gte:18}} ])
])
}
This all sucks. Nesting sucks.
"Deep matching" on nested conditions may be a real pita.
Worth noting that current 0.4.0
match object prevents concise descriptions of some types of matches on a single field. Take the following:
Age between 18 and 21, but not 20
Breaks down to:
gte:18
AND lte:21
AND neq:20
// Concise:
{age: {gte:18, lte:21, neq:20} } // Neat! However implies/assumes an "AND" association
// 0.4.0 spec:
{field:'age', op:'gte', value:18}, // AND operator somewhere
{field:'age', op:'lte', value:21}, // AND operator somewhere
{field:'age', op:'neq', value:20}
Conciseness breakdown somewhat If our conditions deviate from the convention boolean operator. eg:
Age more than 18 or less than 12
gt:18
OR lt:12
// Concise:
{age: {gt:18}},
{age: {lt: 12}} // Requires splitting the match operators to allow an alternative bool operator
// 0.4.0 spec
{field:'age', op:'gt', value:18}, // OR operator somewhere
{field:'age', op:'lt', value:12},
This looks less terrible when broadening use cases outside of a single assumed operator (AND). It's still fairly verbose, but it's trivial to parse by code and by human. +1 for holding up.
Lets assume: mo
is a match object {field:'f', op:'op', value:v}
.
Then, let a match container (mc
) be defined as:
{ '$boolOp': [ mo|mc... ] }
Where:
$boolOp
is a boolean operator eg. and
, or
, etcmo
is a match objectmc
is a match containerIn this way match container mc
may nest. An mc
MUST contain only one $boolOp
.
Qo match
field can now simply be a match container mc
.
match: {
'and': [ {field:'age', op:'gte', value:18}, {field:'age', op:'lte', value:21} ]
}
This is exactly how MongoDB does it. This is the sound of one hand clapping. Good job.
Landed as of commit a87e7e542bf230
Handle: