0
votes

I have an aggregate query that extracts sub-documents from a database in mongodb:

    [
      {
        $match: {
          username: query.username,
        },
      },
      {
        $project: {
          connections: {
            $filter: {
              input: '$connections',
              as: 'connections',
              cond: {
                $eq: ['$$connections.state', query.state],
              },
            },
          },
        },
      }
    ]

The document schema looks as the following (from typegoose, but fairly self explanatory):

export class Connection {
  @prop({ default: () => new Date() })
  public updated?: Date;

  @prop({ required: true, enum: ConnectionStateEnum })
  public state: ConnectionStateEnum;

  @prop({ required: true })
  public username: string;

  @prop()
  public requestIP?: string;

  @prop()
  public requestBrowserString?: string;
}

@modelOptions({
  schemaOptions: {
    collection: 'connections',
    timestamps: true,
  },
})
export class UserConnections {
  @prop({ default: v1 })
    /* tslint:disable */
  _id: string;
  /* tslint: enable */

  @prop({ unique: true })
  username: string;

  @prop({ required: true })
  lastConnectionActivityWithUsername: string;

  @arrayProp({ items: Connection })
  connections: Connection[];
}

The idea is a user has 1 doc containing their connections, thus the connections attribute is an array of connection objects.

If i add a $skip or $limit to the aggregate query this will not change the result of the query as this limits the qty of docs returned, but as there is a macth on a unique field the doc count will always be 1.

Is it possible to limit/skip the number of connection objects to return from a single doc within the query, or can this only be done post query?

(eg a user has 20 connections, but i only want to return 10 connections from the doc)


Based on the answer below I arrived at mixin slice and filter into a $let aggregation pipeline operator as using slice next $filter failed with an error of:

MongoError: Invalid $project :: caused by :: an expression specification must contain exactly one field, the name of the expression. Found 2 fields in { $filter: { input: "$connections", as: "connections", cond: { $eq: [ "$$connections.state", "requestSentAccepted" ] } }, $slice: [ "$$connections", 0, 20 ] }, while parsing object { connections: { $filter: { input: "$connections", as: "connections", cond: { $eq: [ "$$connections.state", "requestSentAccepted" ] } }, $slice: [ "$$connections", 0, 20 ] }

This worked though:

{
        $project: {
          connections: {
            $let: {
              vars: {
                connections: {
                  $filter: {
                    input: '$connections',
                    as: 'connections',
                    cond: {
                      $eq: ['$$connections.state', query.state],
                    },
                  },
                },
              },
              in: {
                $slice: ['$$connections', offset, limit],
              },
            },
          },
        },
      },
1

1 Answers

1
votes

You could use $slice for that:

...
{
  $project: {
    connections: {
      $slice: [ "$connections", skip, limit ]
    }
  }
}