CH2: Lab2, using cursor like stages. Can't get $addFields to work (spoilers)

WARNING
This post contains part of a solution to the Lab.
Though, it’s not working, the contents might spoil you own suffering.
WARNING
WARNING

I’m having trouble with the $addFields stage, so I broke it down, but still can’t get it to work.

MongoDB Enterprise Cluster0-shard-0:PRIMARY> db.movies.aggregate([ { 
    $addFields : { 
        favoritecast: { 
            $map: { 
                input: "$cast", 
                as: "thecast", 
                in: { 
                    $arrayElemAt: [ { 
                        $match: { 
                            $or: [ 
                                { "$$thecast" : { $eq : "Sandra Bullock" }}, 
                                { "$$thecast" : { $eq : "Tom Hanks" }}, 
                                { "$$thecast" : { $eq : "Julia Roberts" }}, 
                                { "$$thecast" : { $eq : "Kevin Spacey" }}, 
                                { "$$thecast" : { $eq : "George Clooney" }} 
                            ]
                         } 
                    }, 0 ] 
                } 
            }
         } 
} } ])
 
Error: command failed: {
	"errmsg" : "Unrecognized expression '$match'",

I tried $addFields without $arrayElemAt

MongoDB Enterprise Cluster0-shard-0:PRIMARY> db.movies.aggregate([ { 
    $addFields : { 
        favoritecast: { 
            $map: { 
                input: "$cast", 
                as: "thecast", 
                in: { 
                    $match: { 
                        $or: [ 
                            { "$$thecast" : { $eq : "Sandra Bullock" }}, 
                            { "$$thecast" : { $eq : "Tom Hanks" }}, 
                            { "$$thecast" : { $eq : "Julia Roberts" }}, 
                            { "$$thecast" : { $eq : "Kevin Spacey" }}, 
                            { "$$thecast" : { $eq : "George Clooney" }} 
                        ]
                     }
                 } 
            } 
        }
 } } ])

Error: command failed: {
	"errmsg" : "Unrecognized expression '$match'",

$addFields without $map or $arrayElemAt

MongoDB Enterprise Cluster0-shard-0:PRIMARY> db.movies.aggregate([ {
    $addFields : { 
        favoritecast: { 
            $match: { 
                $or: [ 
                    { "$$thecast" : { $eq : "Sandra Bullock" }}, 
                    { "$$thecast" : { $eq : "Tom Hanks" }}, 
                    { "$$thecast" : { $eq : "Julia Roberts" }}, 
                    { "$$thecast" : { $eq : "Kevin Spacey" }}, 
                    { "$$thecast" : { $eq : "George Clooney" }} 
                ] 
            } 
        } 
} } ])

Error: command failed: {
	"errmsg" : "Unrecognized expression '$match'",

but, the $match stage does resolve as the first stage when using the field name

db.movies.aggregate([ { 
    $match: { 
        $or: [ 
            { "cast" : { $eq : "Sandra Bullock" }}, 
            { "cast" : { $eq : "Tom Hanks" }}, 
            { "cast" : { $eq : "Julia Roberts" }}, 
            { "cast" : { $eq : "Kevin Spacey" }}, 
            { "cast" : { $eq : "George Clooney" }} 
        ] 
    } 
} ])

What’s going on with my syntax that I can’t get $addFields to work?

$match is an aggregation pipeline stage. The only place you can use it is at the top level, just like $project, $addFields, $set, $group.

When you want to $addFields using an expression, you simply write the expression such as:

// given the documents
{ _id: 0, a: true, b: false }
{ _id: 1, a: true, b: true }

// the aggregation pipeline
[
  { $addFields : {
    a_or_b : { "$or" : [ "$a" , "$b"] } ,
    a_and_b : { "$and" : [ "$a" , "$b"] } ,
    id_0 : { "$eq" : [ "$_id" , 0 ] }
  } }
]

//  will produce
{ _id: 0,
  a: true,
  b: false,
  a_or_b: true,
  a_and_b: false,
  id_0: true }
{ _id: 1,
  a: true,
  b: true,
  a_or_b: true,
  a_and_b: true,
  id_0: false }

Here are some links to documentation:

To use $map you have to be inside

Example:

// given documents
{ _id: 0, a: [ 1, 2, 3 ] }
{ _id: 1, a: [ 1, 2, 3 ], b: [ 2, 4, 6 ] }

c.aggregate( [ { "$match" :
  { "$expr" :
    { "$eq" :
      [ "$b" ,
        { "$map" :
          { input : "$a" ,
            in : { "$multiply" : [ "$$this" , 2 ] }
          }
        }
      ]
    }
  }
} ]  )

// produces
{ _id: 1, a: [ 1, 2, 3 ], b: [ 2, 4, 6 ] }

For this use lab the following is helpful

“…$match is an aggregation pipeline stage. The only place you can use it is at the top level…”

Ah! That’s super helpful to know. I guessed $match was an expression you could use elsehwere. My mistake.

As a side note, I’ve been wondering about quoting of stages and expressions. I noticed in the supplied examples show both quotes and not quotes.

No quotes:

[  { $addFields : {

With quotes:

c.aggregate( [ { "$match" :

MongoU course explains quotes are required for field paths and I expect anything with a space needs quotes. I’ve tested and it seems in the other cases, both quoted and not-quoted work same. Is there a shortcut explanation for how to use quotes?

For field names, you may always use them; I always use them; I recommend to always use them. In Java, (and Python as far as I know) you have no choice but to use them. (see note below)

The name of a stages ($match, $addFields, …) and the names of an operators ($eq, $map, …) are all field names in the context of a query or a pipeline.

NOTE

In Python and Java you would not use quote if your field name is a variable.

# python syntax error when unquoted
>>> stage = { $match : { "field" : "value" } }
  File "<stdin>", line 1
    stage = { $match : { "field" : "value" } }
              ^
SyntaxError: invalid syntax

# but works unquoted when using a variable
>>> match = "$match"
>>> stage = { match : { "field" : "value" } }
>>> stage
{'$match': {'field': 'value'}}


// in JS unquoted works in normal case
stage = { $match : { "field" : "value" } }
{ '$match': { field: 'value' } }

// but fails when using a variable 
match = "$match"
stage = { match : { "field" : "value" } }

// stage will be equals to 
{ match: { field: 'value' } }

// you would need to use [] if your field (stage) name is the variable match
stage = { [match] : { "field" : "value" } }

// to get
{ '$match': { field: 'value' } }

Thank you for the recommendation. So sounds like the best bet is to quote everything that has the form of a string.

(Also, thank you for going the extra step to define terms–filed names. That would have tripped me up for a spell.)

1 Like

Hi @xtian_simon,

“Always quote” is definitely the easiest rule to remember and should work everywhere.

For more context on why quotes sometimes aren’t used, please see: Is using double quotes compulsory to enclose property names? - #9 by Stennie

My first paragraph has the high level explanation:

Regards,
Stennie

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.