Search code examples
mongodbmongooseaggregatelimit

How can i limit each ID i pass to $in operator inside the $match stage to only 4 elements


I have an aggregate like this :

            const files = await File.aggregate([
              {
                $match: { facilityId: { $in: facilities } }
              },
              {
                $sort: { createdAt: 1 }
              },
              {
                $project: {
                  file: 0,
                }
              }
            ])

And i would like to have each "facility" return only 4 files, i used to do something like facilities.map(id => query(id)) but id like to speed things up in production env.

Using $limit will limit the whole query, that's not what i want, i tried using $slice in the projection stage but got en error :

MongoError: Bad projection specification, cannot include fields or add computed fields during an exclusion projection

how can i achieve that in a single query ?

Schema of the collections is :

const FileStorageSchema = new Schema({
  name: { type: String, required: true },
  userId: { type: String },
  facilityId: { type: String },
  patientId: { type: String },
  type: { type: String },
  accessed: { type: Boolean, default: false, required: true },
  file: {
    type: String, //
    required: true,
    set: AES.encrypt,
    get: AES.decrypt
  },
  sent: { type: Boolean, default: false, required: true },
},
{
  timestamps: true,
  toObject: { getters: true },
  toJSON: { getters: true }
})

And i would like to returns all fields except for the file fields that contains the encrypted blob encoded as base64.

Also: i have the feeling that my approach is not correct, what i really would like to get is being able to query all facilityId at once but limited to the 4 latest file created for each facility, i though using an aggregate would help me achieve this but im starting to think it's not how its done.


Solution

  • From the question the schema is not clear. So I have two answers based on two Schemas. Please use what works for you

    #1

    db.collection.aggregate([
      {
        $match: {
          facilityId: {
            $in: [
              1,
              2
            ]
          }
        }
      },
      {
        $group: {
          _id: "$facilityId",
          files: {
            $push: "$file"
          }
        }
      },
      {
        $project: {
          files: {
            $slice: [
              "$files",
              0,
              4
            ],
            
          }
        }
      }
    ])
    

    Test Here

    #2

    db.collection.aggregate([
      {
        $match: {
          facilityId: {
            $in: [
              1,
              2
            ]
          }
        }
      },
      {
        $project: {
          facilityId: 1,
          file: {
            $slice: [
              "$file",
              4
            ]
          }
        }
      }
    ])
    

    Test Here