Evaluate Access at Every Document Level

Hello,

I´m using db version 4.2.12.

I’m trying to use the $size and $setIntersection just like in the $redact example on mongo documentation:

$redact: {
        $cond: {
           if: { $gt: [ { $size: { $setIntersection: [ "$tags", userAccess ] } }, 0 ] },
           then: "$$DESCEND",
           else: "$$PRUNE"
         }
       }

but it gives an error "The argument to $size must be an array but was of type null.

I have a Security Object at root level with a string array field inside (just like the example). The Security object is also inside an Embedded array of objects of which I would like to return only the objects that intersect with the Security Object string array. Can you help me please?

My Code:

$redact: {
  $cond: {
    'if': {
      $gt: [
        {
          $size: {
            $setIntersection: [
              '$Programmes.Security.AccessTeams',
              [
                'Lorem1'
              ]
            ]
          }
        },
        0
      ]
    },
    then: '$$DESCEND',
    'else': '$$PRUNE'
  }
}

Document Example:

{
  "_id": {
    "$oid": ""
  },
  "PXXXXXID": "Lorem",
  "PXXXXXDescription": "Lorem",
  "PXXXXXXXs": [
    {
      "PXXXXXXID": "Lorem",
      "Security": {
        "DXXXXXXX": "Lorem",
        "AccessTeams": [
          "Lorem1"
        ]
      }
    },
    {
      "PXXXXXXID": "Lorem",
      "Security": {
        "DXXXXXXX": "Lorem",
        "AccessTeams": [
          "Lorem2"
        ]
      }
    }
  ],
  "UXXXXXXXPXXXX": {
    "UXXXID": "Lorem",
    "Security": {
      "DXXXXXXX": "Lorem",
      "AccessTeams": [
        "Lorem1"
      ]
    }
  },
  "Security": {
    "DXXXXXXX": "Lorem",
    "OwningTeam": "Lorem",
    "AccessTeams": [
      "Lorem1"
    ]
  }
}

Thank you

Hi @Vasco_Pedro,

Looking at the document example the AccessTeams are on level $PXXXXXXXs.Security.AccessTeams , is tgat the same as $Programmes.Security.AccessTeams.

I would recommend to project just Programmes.Security.AccessTeams : 1 and see if some documents produce a null value. If this happens use $ifNull and switch to empty arrays.

Thanks,
Pavel

After $project test there are no null values on $Programmes.Security.AccessTeams.

But if I try $ifNull like you said:

{
“Programmes.Security.AccessTeams”: { $ifNull:
["$Programmes.Security.AccessTeams" , ] }
}

Then AccessTeams shows as an array, of string arrays.

Thank you

@Vasco_Pedro, thanks for the update.

Hope it helped.

Thanks,
Pavel

Still can’t replicate $redact “$tags” example on mongo documentation.

Neither debug or similar approaches worked.

@Pavel_Duchovny thank you for your reply.
I have one question related to that. If we want to always show one sub-document that doesn’t need the security. How we can do that?

Thank you

1 Like

@Alexandre_Barreto,

You mean that you don’t want it to be prune?

I guess you can do a nested $cond in the else section to check if its the needed document and prune in case it’s not…

Thanks,
Pavel

Let try to explain.

I have this document with this structure:

{
	“Field1”: “P001”,
	“Field2”: “Lorem”,
	“Field3”: “Lorem”,
	“Field4”: [
	{
		“SubField1”: “Lorem”,
		“SubField2”: “Lorem”,
		“SubField3”: “Lorem”,
		“Security”:{
			“Access”:“Role3”
			“AccessArray”:[“Role1”,“Role2”]
		}
	},
	{
		“SubField1”: “Lorem”,
		“SubField2”: “Lorem”,
		“SubField3”: “Lorem”,
		“Security”:{
			“Access”:“Role3”
			“AccessArray”:[“Role1”,“Role2”]
		}
	}],
	“FieldWithoutSecurity”:{
		“Sub_Field1”: “Lorem”,
		“Sub_Field2”: “Lorem”
	}
	“Security”:{
		“Access”:“Role3”
		“AccessArray”:[“Role1”,“Role2”]
	}
}

Then I have this redact:

db.Collection.aggregate([
{
$redact:{
	$cond: {
		if: {
			$or: [
			{
				$gt: [
				{
					$size:
					{
						$setIntersection:
						[
						{
							“$cond”: {
								“if”: {
									“$ifNull”: ["$Security.AccessArray",false]
								},
								“then”: “$Security.AccessArray”,
								“else”: [""]
							}
						},
						[“Role1”]
						]
					}
				}, 0
				]
			},
			{$in: ["$Security.Access", [“Role1”]]}
			]
		},
		then: “$$DESCEND”,
		else: “$$PRUNE”
	}
}}])

I want to always show the subDocument “FieldWithoutSecurity”, but with this redact that field it’s never returned.

@Pavel_Duchovny
Any suggestion?

Hi @Alexandre_Barreto,

So I understand that fields without “Security” field will endup in “else” condition of:

“if”: {
									“$ifNull”: ["$Security.AccessArray",false]
								},
								“then”: “$Security.AccessArray”,
								“else”: [""]

But in else you return an empty array which will get Prune. You should return the “filtered” value to allow them to be shown:

[{$redact: {
  $cond: {
    'if': {
      $or: [
        {
          $gt: [
            {
              $size: {
                $setIntersection: [
                  {
                    $cond: {
                      'if': {
                        $ifNull: [
                          '$Security.AccessArray',
                          false
                        ]
                      },
                      then: '$Security.AccessArray',
                      'else': ['Role1']

                                    }
                  },
                  [
                    'Role1'
                  ]
                ]
              }
            },
            0
          ]
        },
        {
          $in: [
            '$Security.Access',
            [
              'Role1'
            ]
          ]
        }
      ]
    },
    then: '$$DESCEND',
    'else': '$$PRUNE'
  }
}}]

This will result in the desired document I think:

{ _id: 
     { _bsontype: 'ObjectID',
       id: <Buffer 60 2e 41 d4 2e ac 2d bb 54 e2 c4 ab> },
    Field1: 'P001',
    Field2: 'Lorem',
    Field3: 'Lorem',
    Field4: 
     [ { SubField1: 'Lorem',
         SubField2: 'Lorem',
         SubField3: 'Lorem',
         Security: { Access: 'Role3', AccessArray: [ 'Role1', 'Role2' ] } },
       { SubField1: 'Lorem',
         SubField2: 'Lorem',
         SubField3: 'Lorem',
         Security: { Access: 'Role3', AccessArray: [ 'Role1', 'Role2' ] } } ],
    FieldWithoutSecurity: { Sub_Field1: 'Lorem', Sub_Field2: 'Lorem' },
    Security: { Access: 'Role3', AccessArray: [ 'Role1', 'Role2' ] } }

Thanks,
Pavel