I’m giving another shot at Attribute Pattern - How to sort by some attribute value
I was wondering if we could build something with aggregation.
Using the example from the previous post
{
"productId": 1,
"customerId": 1,
"attr": [
{ "k": "price", "v": 42 },
{ "k": "color", "v": "red" }
]
}
I was thinking maybe we can
[{$match: { <filter> }},
{$unwind: { path: "$attr" }},
{$match: { "attr.k": "price"}},
{$sort: { "attr.v": 1 }},
{$lookup: {
from: 'product',
localField: '_id',
foreignField: '_id',
as: 'fullProduct'
}},
{$replaceRoot: {
newRoot: { $arrayElemAt: [ "$fullProduct", 0] }
}}]
First problem I can see: we lose the documents that do not have a value.
I’m currently testing if the wildcard index would be a better solution for my use case.
steevej
(Steeve Juneau)
3
This is a very interesting problem.
Here is an alternative solution that would also involve a memory sort but might be more efficient than the solution with $unwind and $lookup.
Basically, it uses $reduce on $attr to $set a new field to the sort value desired.
Warning: Quickly tested code, use at your own risk.
SortKey = "price"
function pipeline() {
return [ _match_stage() , _set_stage() , _sort_stage() ]
}
function _match_stage() {
return { "$match" : { "customerId" : 1 } }
}
function _set_stage() {
return { "$set" : { [SortKey] : _reduce() } }
}
function _sort_stage() {
return { "$sort" : { [SortKey] : 1 } }
}
function _reduce() {
return { "$reduce" : {
"input" : "$attr" ,
"initialValue" : null ,
"in" : { '$cond': [ { "$eq : [ "$$this.k" , SortKey ] } , '$$this.v', '$$value' ] } }
} }
}
c.aggregate( pipeline() )