AG_HIHI
AG_HIHI

Reputation: 1995

Mongodb aggregate: How to slice a result of type array of object?

I have an aggregate query that returns an array of objects. I am trying to slice it by adding this stage at the end of the pipeline:

  let limit = 5;
  let skip = limit * 1;
  const page_limit_stage = {
    $project: {
      profiles: {
        $slice: ["$$CURRENT", skip, limit],
      },
    },
  };

But, I keep getting this error:

🚀 ~ file: profile.js ~ line 331 ~ err MongoError: First argument to $slice must be an array, but is of type: object

Any idea how to solve this?


EDIT 2: Here's the query I am using:

const filterProfiles = async function filterProfiles() {
  const { users_filter_option, profiles_filter_option } =
    prepareProfilesFilterSearchOptions();

  const profile_stage = {
    $lookup: {
      from: "profiles",
      let: { profile_id: "$profile" },
      pipeline: [profiles_filter_option],
      as: "profile",
    },
  };

  const profile_populate_options = [
    {
      $lookup: {
        from: "schema_name_1",
        localField: "profile.schema_name_1",
        foreignField: "_id",
        as: "schema_name_1",
      },
    },
    {
      $lookup: {
        from: "schema_name_2",
        localField: "profile.schema_name_2",
        foreignField: "_id",
        as: "schema_name_2",
      },
    },
  ];
  const project_stage = {
    $project: {
      user_id: "$_id",
      name: "$name",
      surname: "$surname",
      country: "$country",
      schema_name_2: { $arrayElemAt: ["$schema_name_2", 0] },
      schema_name_1: "$schema_name_1",
    },
  };
  let limit = 5;
  let skip = limit * 1;
  const page_limit_stage = {
    $project: {
      profiles: {
        $slice: ["$CURRENT", skip, limit],
      },
    },
  };
  const pipeline = [
    users_filter_option,
    profile_stage,
    ...profile_populate_options,
    { $unwind: "$profile" },
    project_stage,
    page_limit_stage,
  ];
  const users = await User.aggregate(pipeline);

  return users;
};

And this is the result without the page_limit_stage:

[
  {
    _id: "aegaeg",
    user_id: "aegaef",
    name: "user_name",
    surname: "user_surname",
    country: "country_name",
    schema_name_1: "value",
    schema_name_2: "value2",
  },
  // multiple documents like this
];

Upvotes: 1

Views: 1322

Answers (2)

turivishal
turivishal

Reputation: 36104

The slice will work on array of object property if you have in your document, but in your case you need to do pagination, There are 2 stages for pagination $skip and $limit,

  let limit = 5;
  let skip = limit * 1;
  const skip_stage = { $skip: skip };
  const limit_stage = { $limit: limit };
    
  const pipeline = [
    users_filter_option,
    profile_stage,
    ...profile_populate_options,
    { $unwind: "$profile" },
    project_stage,
    skip_stage, 
    limit_stage
  ];

Upvotes: 1

Takis
Takis

Reputation: 8695

"$$CURRENT" is a system variable, and it has here the same value as $$ROOT system variable see here

They both have as value the document that enters the pipeline. If collection is like

[{_id : 1 name : "john" addresses [..]} ...]

The $$CURRENT will be = {_id : 1 name : "john" addresses [..]} its a document not array, cannot be sliced.(it has the value of the document that come from the pipeline,the above is the first value it will get)

I think you just want the bellow (instead of $addresses put the name of your array)

{
    $project: {
      profiles: {
        $slice: ["$addresses", skip, limit],
      },
    },
  };

In case you want to slice the $$ROOT document, you should first do $ObjectToArray and after $slice but i dont think you want this.

Upvotes: 1

Related Questions