Mathieu
Mathieu

Reputation: 759

MongoDB aggregation pipeline : using $cond to return a $match stage?

I'm trying to build queries to have actually interesting ways to interact with a database. One of them is to search for documents in a certain range of years. My thinkingnwas to build an aggregation pipeline where I check if the year range have been selected in the search form, then return the matching documents. If no range is selected, then return all the documents and go to the next stage of the aggregation pipeline.

Here is what I have tried (there's only one stage in the aggregate because i haven't managed to make this first one work, yet) :

    db.collection('archives').aggregate([
      { $cond: { if: yearStart.length === 4 && yearEnd === 4 },
        then: { $match:
          { $and:
            [
              { year: {$gte: yearStart} },
              { year: {$lte: yearEnd} }
            ]
          }
        },
        else: { $match: {} }
      }
    ]).toArray( (err, docs) => {
      if (err) throw (err)
      res.json(docs)
    })

So, this doesn't work. I get the error MongoError: A pipeline stage specification object must contain exactly one field.. So I thought it was just a matter of enclosing the $cond statement in curlies. But nope. I get a full on unexpected token { crash at this. So I'm thinkning that maybe $cond aren't meant to be used like this (returning $match stages). I'm inclined to think so, as the documentation only shows an example returning a simple value... Is it right?

Thanks in advance!

Upvotes: 0

Views: 1544

Answers (1)

chridam
chridam

Reputation: 103375

Create the query object to be used with $match outside the pipeline first with native JavaScript conditionals as using the $cond operator is only applicable within specified pipeline stages. Here you are using it as a pipeline step hence the error thrown.

Consider the following:

const query = { "year": { } };
if (yearStart.length === 4 && yearEnd === 4) {
    query["year"]["$gte"] = yearStart;
    query["year"]["$lte"] = yearEnd;    
}

db.collection('archives').aggregate([ { "$match": query } ]).toArray((err, docs) => {
    if (err) throw (err);
    res.json(docs);
});

Or using the find() method as

db.collection('archives').find(query).toArray((err, docs) => {
    if (err) throw (err);
    res.json(docs);
});

Upvotes: 2

Related Questions