Attribute Pattern - sorting by attribute value

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.

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() )