OPTIONS

$redact (aggregation)

Definition

$redact

New in version 2.6.

Restricts the contents of the documents based on information stored in the documents themselves.

Diagram of security architecture with middleware and redaction.

Diagram of security architecture with middleware and redaction.

The $redact stage has the following prototype form:

{ $redact: <expression> }

The argument can be any valid expression as long as it resolves to $$DESCEND, $$PRUNE, or $$KEEP system variables. For more information on expressions, see Expressions.

System Variable Description
$$DESCEND $redact returns the non-subdocument fields at the current document/subdocument level. For subdocuments or subdocuments in arrays, apply the $cond expression to the subdocuments to determine access for these subdocuments.
$$PRUNE $redact excludes all fields at this current document/subdocument level, without further inspection of any of the excluded fields. This applies even if the excluded field contains subdocuments that may have different access levels.
$$KEEP $redact returns or keeps all fields at this current document/subdocument level, without further inspection of the fields at this level. This applies even if the included field contains subdocuments that may have different access levels.

Examples

The examples in this section use the db.collection.aggregate() helper provided in the 2.6 version of the mongo shell.

Evaluate Access at Every Document/Sub-document Level

A forecasts collection contains documents of the following form where the tags field lists the different access values for that document/subdocument level; i.e. a value of [ "G", "STLW" ] specifies either "G" or "STLW" can access the data:

{
   _id: 1,
   title: "123 Department Report",
   tags: [ "G", "STLW" ],
   year: 2014,
   subsections: [
       {
           subtitle: "Section 1: Overview",
           tags: [ "SI", "G" ],
           content:  "Section 1: This is the content of section 1."
       },
       {
           subtitle: "Section 2: Analysis",
           tags: [ "STLW" ],
           content: "Section 2: This is the content of section 2."
       },
       {
           subtitle: "Section 3: Budgeting",
           tags: [ "TK" ],
           content: {
               text: "Section 3: This is the content of section3.",
               tags: [ "HCS" ]
           }
       }
   ]
}

A user has access to view information with either the tag "STLW" or "G". To run a query on all documents with year 2014 for this user, include a $redact stage as in the following:

var userAccess = [ "STLW", "G" ];
db.forecasts.aggregate(
   [
     { $match: { year: 2014 } },
     { $redact:
         {
            $cond:
               {
                 if: { $gt: [ { $size: { $setIntersection: [ "$tags", userAccess ] } }, 0 ] },
                 then: "$$DESCEND",
                 else: "$$PRUNE"
               }
         }
     }
   ]
)

The aggregation operation returns the following “redacted” document:

{
   "_id" : 1,
   "title" : "123 Department Report",
   "tags" : [ "G", "STLW" ],
   "year" : 2014,
   "subsections" : [
      {
         "subtitle" : "Section 1: Overview",
         "tags" : [ "SI", "G" ],
         "content" : "Section 1: This is the content of section 1."
      },
      {
         "subtitle" : "Section 2: Analysis",
         "tags" : [ "STLW" ],
         "content" : "Section 2: This is the content of section 2."
      }
   ]
}

Exclude All Fields at a Given Level

A collection accounts contains the following document:

{
  _id: 1,
  level: 1,
  acct_id: "xyz123",
  cc: {
        level: 5,
        type: "yy",
        num: 000000000000,
        exp_date: ISODate("2015-11-01T00:00:00.000Z"),
        billing_addr: {
                        level: 5,
                        addr1: "123 ABC Street",
                        city: "Some City"
                      },
        shipping_addr: [
                          {
                            level: 3,
                            addr1: "987 XYZ Ave",
                            city: "Some City"
                          },
                          {
                            level: 3,
                            addr1: "PO Box 0123",
                            city: "Some City"
                          }
                       ]
      },
  status: "A"
}

In this example document, the level field determines the access level required to view the data.

To run a query on all documents with status A and exclude all fields contained in a document/subdocument at level 5, include a $redact stage that specifies the system variable "$$PRUNE" in the then field:

db.accounts.aggregate(
   [
     { $match: { status: "A" } },
     { $redact:
        {
            $cond: {
                     if: { $eq: [ "$level", 5 ] },
                     then: "$$PRUNE",
                     else: "$$DESCEND"
                   }
        }
     }
   ]
)

The $redact stage evaluates the level field to determine access. If the level field equals 5, then exclude all fields at that level, even if the excluded field contains subdocuments that may have different level values, such as the shipping_addr field.

The aggregation operation returns the following “redacted” document:

{
  "_id" : 1,
  "level" : 1,
  "acct_id" : "xyz123",
  "status" : "A"
}

The result set shows that the $redact stage excluded the field cc as a whole, including the shipping_addr field which contained subdocuments that had level field values equal to 3 and not 5.

See also

Implement Field Level Redaction for steps to set up multiple combinations of access for the same data.