ngmir
ngmir

Reputation: 488

node.js/mongoDB: Return custom value if field is null

What I want to do is to query my database for documents and, if a field is not set, return a custom value instead of it.

Imagine I'm having a collection of songs and some of these have the field pathToCover set and some don't. For these, I want to return the URL to a placeholder image that I store in a config variable.

I am running node.js and mongoDB with express and mongoose.

I am not sure what the best approach for this is. An obvious solution would be to query for the documents and then in the callback iterate over them to check if the field is set. But this feels quite superfluous.

Currently, my code looks like this:

exports.getByAlbum = function listByAlbum(query, callback) {
    Song.aggregate({ 
            $group: { 
                _id: '$album',
                artists: { $addToSet: '$artist' },
                songs: { $push: '$title' },
                covers: { $addToSet: '$pathToCover'},
                genres: { $addToSet: '$genre'},
                pathToCover: { $first: '$pathToCover'}
            }
        },
        function (err, result) {
            if (err) return handleError(err);

            result.forEach(function(album) {
                if ( album.pathToCover == null) {
                    album.pathToCover = config.library.placeholders.get('album');
                }
            })

            callback(result);
    });
}

What is the best approach for this?

Thanks a lot in advance!

Upvotes: 1

Views: 2503

Answers (1)

Neil Lunn
Neil Lunn

Reputation: 151112

Where the value of the field is either null or is unset and possibly missing from the document, then you use $ifNull in your aggregation to set to the alternate value:

exports.getByAlbum = function listByAlbum(query, callback) {
  var defaultCover = config.library.placeholders.get('album');
  Song.aggregate(
    [
      { "$group": { 
        "_id": "$album",
        "artists": { "$addToSet": "$artist" },
        "songs": { "$push": "$title" }, 
        "covers": { "$addToSet": { "$ifNull": [ "$pathToCover", defaultCover ] } },
        "genres": { "$addToSet": "$genre" }, 
        "pathToCover": { "$first": { "$ifNull": [ "$pathToCover", defaultCover ] } }
      }}
    ],
    function (err, result) {
        if (err) return handleError(err);
        callback(result);
    }
  );
}

If it is an empty string then use the $cond statement with an $eq test in place of $ifNull:

{ "$cond": [ { "$eq": [ "$pathToCover", "" ] }, defaultCover, "$pathToCover" ] }

Either statement can be used inside of a grouping operator to replace the value that is considered.

If you are just worried that perhaps not "all" of the values are set on the song, then use something like $min or $max as a appropriate to your data to just pick one of the values instead of using $first.

Upvotes: 2

Related Questions