title: Aggregation Query Optimization Mongodb
body: <h2>User Schema</h2>
  <p>I have been building a social media application and I have to write a query that returns the user of user. The schema of user is shown below.</p>
  <pre><code>const userSchema = Schema(
    {
      email: {
        type: String,
        unique: true,
        required: [true, &quot;Email is required&quot;],
        index: true,
      },
      active: {
        type: Boolean,
        default: true,
      },
      phone: {
        type: String,
        unique: true,
        required: [true, &quot;Phone is required&quot;],
        index: true,
      },
      name: {
        required: true,
        type: String,
        required: [true, &quot;Name is required&quot;],
      },
      bio: {
        type: String,
      },
      is_admin: {
        type: Boolean,
        index: true,
        default: false,
      },
      is_merchant: {
        type: Boolean,
        index: true,
        default: false,
      },
      password: {
        type: String,
        required: [true, &quot;Password is required&quot;],
      },
      profile_picture: {
        type: String,
      },
      followers: [
        // meaning who has followed me
        {
          type: Types.ObjectId,
          ref: &quot;user&quot;,
          required: false,
        },
      ],
      followings: [
        // meaning all of them who I followed
        {
          type: Types.ObjectId,
          ref: &quot;user&quot;,
          required: false,
        },
      ],
    },
    {
      timestamps: { createdAt: &quot;created_at&quot;, updatedAt: &quot;updated_at&quot; },
      toObject: {
        transform: function (doc, user) {
          delete user.password;
        },
      },
      toJSON: {
        transform: function (doc, user) {
          delete user.password;
        },
      },
    }
  );
  
  </code></pre>
  <h2>Follow/following implementation</h2>
  <p>I have implemented follow/following using the logic shown as below. Each time user follows another user. It would perform 2 queries. One would update the follower followers part using <code>findOneAndUpdate({push:followee._id})</code> and a second query to update the part of followee user.</p>
  <p><a href="https://i.stack.imgur.com/CXbuc.png" rel="nofollow noreferrer"><img src="https://i.stack.imgur.com/CXbuc.png" alt="User Model after follow/following update" /></a></p>
  <h2>Query Response Pattern</h2>
  <p>I have written a query that should return the response with followings response appended to each user</p>
  <pre class="lang-js prettyprint-override"><code>{
    doesViewerFollowsUser: boolean // implying if person we are viewing profile of follows us 
    doesUserFollowsViewer: boolean // implying if person we are viewing profile of follows us
  }
  </code></pre>
  <h3>The actual query</h3>
  <p>The query must looks like this</p>
  <pre><code>
  userModel
      .aggregate([
        {
          $match: {
            _id: {
              $in: [new Types.ObjectId(userId), new Types.ObjectId(viewerId)],
            },
          },
        },
  
        {
          $addFields: {
            order: {
              $cond: [
                {
                  $eq: [&quot;$_id&quot;, new Types.ObjectId(viewerId)], // testing for viewer
                },
                2,
                1,
              ],
            },
          },
        },
        {
          $group: {
            _id: 0,
            subjectFollowings: {
              $first: &quot;$followings&quot;,
            },
            viewerFollowings: {
              $last: &quot;$followings&quot;,
            },
            viewerFollowers: {
              $last: &quot;$followers&quot;,
            },
          },
        },
        {
          $lookup: {
            from: &quot;users&quot;,
            localField: &quot;subjectFollowings&quot;,
            foreignField: &quot;_id&quot;,
            as: &quot;subjectFollowings&quot;,
          },
        },
        {
          $project: {
            subjectFollowings: {
              $map: {
                input: &quot;$subjectFollowings&quot;,
                as: &quot;user&quot;,
                in: {
                  $mergeObjects: [
                    &quot;$$user&quot;,
                    {
                      doesViewerFollowsUser: {
                        $cond: [
                          {
                            $in: [&quot;$$user._id&quot;, &quot;$viewerFollowers&quot;],
                          },
                          true,
                          false,
                        ],
                      },
                    },
                    {
                      doesUserFollowsViewer: {
                        $cond: [
                          {
                            $in: [&quot;$$user._id&quot;, &quot;$viewerFollowings&quot;],
                          },
                          true,
                          false,
                        ],
                      },
                    },
                  ],
                },
              },
            },
          },
        },
        {
          $project: {
            &quot;subjectFollowings.followings&quot;: 0,
            &quot;subjectFollowings.followers&quot;: 0,
            &quot;subjectFollowings.bio&quot;: 0,
            &quot;subjectFollowings.password&quot;: 0,
            &quot;subjectFollowings.is_admin&quot;: 0,
            &quot;subjectFollowings.is_merchant&quot;: 0,
            &quot;subjectFollowings.email&quot;: 0,
            &quot;subjectFollowings.phone&quot;: 0,
            &quot;subjectFollowings.created_at&quot;: 0,
            &quot;subjectFollowings.updated_at&quot;: 0,
            &quot;subjectFollowings.__v&quot;: 0,
          },
        },
      ])
  
  </code></pre>
  <h2>The problem</h2>
  <p>I don't think the current query scales that much. The worst case complexity for this query reaches <strong>0(n^2) (approximately)</strong>. So, please help me optimize this query.</p>