Search code examples
arraysnode.jsmongodbmongoosepug

Looping through similar keys in node/jade


I have an array of mongoose model objects being returned from a mongo database (using Model.find()):

[
    {_id: '...', type: 'colour', value: 'red'},
    {_id: '...', type: 'colour', value: 'blue'},
    {_id: '...', type: 'material', value: 'steel'},
    {_id: '...', type: 'colour', value: 'green'},
    {_id: '...', type: 'material', value: 'wood'},
    {_id: '...', type: 'material', value: 'plastic'}
]

There is an unknown number of types with an unknown number of values.

I was to create drop downs for each type, populated with values of that type:

<select id="colour" name="colour">
    <option value="abc123">red</option>
    <option value="abc124">blue</option>
    <option value="abc125">green</option>
</select>
<select id="material" name="material">
    <option value="abc123">steel</option>
    <option value="abc124">wood</option>
    <option value="abc125">plastic</option>
</select>

Currently I am converting the array of objects to a multi dimensional array using the type as the array key:

let facets = [];
for (var f in dbfacets) {
    if (f != '0'){
        if (dbfacets[f].type in facets) {
            facets[dbfacets[f].type].push(dbfacets[f].name);
        } else {
            facets[dbfacets[f].type] = [];
            facets[dbfacets[f].type].push(dbfacets[f].name);
        }
    }
}

This seems to work but when being used in Jade it skips the facets object as if it has no members (despite the facets object being populated):

each fac, index in facets
select(name='#{index}', id='#{index}')
    each f in fac
        option(value='#{f._id}') #{f.name}

Is there something I'm doing wrong iterating over the array?

Alternatively is there a way to convert the array directly from the DB to an object like:

{
    type: 'colour',
    vals: {
        'red',
        'green',
        'blue'
    }
},
{
    type: 'material',
    vals: {
        'steel',
        'wood',
        'plastic'
    }

}

Calling aggregate

Facet.collection.aggregate([
    {
        '$group': {
            '_id': '$type',
            'vals': {
                '$push': '$value'
            }
        }
    },
    {
        '$project': {
            'vals': 1,
            'type': '$_id',
            '_id': false
        }
    }
], (err, result) => {
    if (err){return next(err);}
    console.log(result);
});

This gives me an output of:

result
AggregationCursor {pool: null, server: null, disconnectHandler: Store, bson: BSON, ns: "toylib.facets", …}
_events:Object {}
_eventsCount:0
_maxListeners:undefined
_readableState:ReadableState {objectMode: true, highWaterMark: 16, buffer: BufferList, …}
bson:BSON {}
cmd:Object {aggregate: "facets", pipeline: Array(2), cursor: Object}
cursorState:Object {cursorId: null, cmd: Object, documents: Array(0), …}
destroyed:false
disconnectHandler:Store {s: Object, length: <accessor>}
domain:null
logger:Logger {className: "Cursor"}
ns:"toylib.facets"
options:Object {readPreference: null, cursor: Object, promiseLibrary: , …}
pool:null
readable:true
readableHighWaterMark:16
s:Object {maxTimeMS: null, state: 0, streamOptions: Object, …}
server:null
sortValue:undefined
topology:Server {domain: null, _events: Object, _eventsCount: 26, …}
__proto__:Readable {_next: , setCursorBatchSize: , cursorBatchSize: , …}

Solution

  • You can try below $group with $push aggregation

    Firstly you need to $group your type field and then $push which returns an array of values that result from applying an expression to each document in a $group

    Facet.aggregate([
      {
        "$group": {
          "_id": "$type",
          "vals": {
            "$push": "$value"
          }
        }
      },
      {
        "$project": {
          "vals": 1,
          "type": "$_id",
          "_id": false
        }
      }
    ]).then((result) => {
       console.log(result)
    })
    

    Output

    [
      {
        "color": "material",
        "vals": [
          "steel",
          "wood",
          "plastic"
        ]
      },
      {
        "color": "colour",
        "vals": [
          "red",
          "blue",
          "green"
        ]
      }
    ]