Home > OS >  $filter inside $reduce or inside $map from array without unwind
$filter inside $reduce or inside $map from array without unwind

Time:02-05

I need some help: I want to optimize this query to be faster , it need to filter by events.eventType:"log" all docs with server:"strong" , but without separate unwind & filter stages , maybe somehow inside the $reduce stage to add $filter.

example single document:

 {
 server: "strong",
 events: [
  {
    eventType: "log",
    createdAt: "2022-01-23T10:26:11.214Z",
    visitorInfo: {
      visitorId: "JohnID"
    }
  }

current aggregation query:

   db.collection.aggregate([
   {
    $match: {
    server: "strong"
    }
   },
   {
    $project: {
     total: {
      $reduce: {
      input: "$events",
      initialValue: {
        visitor: [],
        uniquevisitor: []
      },
      in: {
        visitor: {
          $concatArrays: [
            "$$value.visitor",
            [
              "$$this.visitorInfo.visitorId"
            ]
          ]
        },
        uniquevisitor: {
          $cond: [
            {
              $in: [
                "$$this.visitorInfo.visitorId",
                "$$value.uniquevisitor"
              ]
            },
            "$$value.uniquevisitor",
            {
              $concatArrays: [
                "$$value.uniquevisitor",
                [
                  "$$this.visitorInfo.visitorId"
                ]
              ]
            }
          ]
          }
        }
       }
      }
    }
    }
    ])

expected output , two lists with unique visitorId & list of all visitorId:

 [
{
"total": {
  "uniquevisitor": [
    "JohnID"
  ],
  "visitor": [
    "JohnID",
    "JohnID"
  ]
}

} ]

playground

In the example query no filter is added for events.eventType:"log" , how can this be implemented without $unwind?

CodePudding user response:

I am not sure this approach is more optimized than yours but might be this will help,

  • $filter to iterate loop of events and filter by eventType
  • $let to declare a variable events and store the above filters result
  • return array of visitor by using dot notation $$events.visitorInfo.visitorId
  • return array of unique visitor uniquevisitor by using dot notation $$events.visitorInfo.visitorId and $setUnion operator
db.collection.aggregate([
  { $match: { server: "strong" } },
  {
    $project: {
      total: {
        $let: {
          vars: {
            events: {
              $filter: {
                input: "$events",
                cond: { $eq: ["$$this.eventType", "log"] }
              }
            }
          },
          in: {
            visitor: "$$events.visitorInfo.visitorId",
            uniquevisitor: {
              $setUnion: "$$events.visitorInfo.visitorId"
            }
          }
        }
      }
    }
  }
])

Playground


Or similar approach without $let and two $project stages,

db.collection.aggregate([
  { $match: { server: "strong" } },
  {
    $project: {
      events: {
        $filter: {
          input: "$events",
          cond: { $eq: ["$$this.eventType", "log"] }
        }
      }
    }
  },
  {
    $project: {
      total: {
        visitor: "$events.visitorInfo.visitorId",
        uniquevisitor: {
          $setUnion: "$events.visitorInfo.visitorId"
        }
      }
    }
  }
])

Playground

  •  Tags:  
  • Related