typescriptdatabasemongodbnosqltypegoose

how to use nested lookup query?


I have two collections "new_users" and "workspaces". I am trying to do a lookup query but it gives only empty array in the expected field.

My 'new_users' collection:

export class UserEntity {
    @Prop({ required: false, type: Types.ObjectId, default: () => new ObjectId() })
    _id?: Types.ObjectId;

    @Expose()
    @Prop({ default: '', trim: true, text: true })
    firstName: string;

    @Expose()
    @Prop({ default: '', trim: true, text: true })
    lastName: string;
}

My "workspaces" collection:


export class WorkspaceUsers {
    @Expose()
    @Prop({ type: () => [WorkspaceUserGroup] })
    groups: WorkspaceUserGroup[];

    @Expose()
    @Prop({ required: true, type: String, enum: WorkspaceRoleEnum })
    workSpaceRole: WorkspaceRoleEnum;

    @Expose()
    @Prop({ required: true, type: String, ref: () => UserEntity })
    userID: string;
}

export class WorkspaceEntity {

    @Prop({ required: false, type: Types.ObjectId, default: () => new ObjectId() })
    _id?: Types.ObjectId;
    
    @Expose()
    @IsString()
    @IsDefined()
    @Prop({ required: true, trim: true })
    name: string;
    

    @Expose()
    @IsOptional()
    @IsArray()
    @ValidateNested({ each: true })
    @Type(() => WorkspaceUsers)
    @Prop({ required: false, type: () => [WorkspaceUsers], default: [] })
    users: WorkspaceUsers[];
}

Now I want to run a lookup query in my workspaces model like this:

    const aggregationQuery: PipelineStage[] = [];

    aggregationQuery.push({
        $unwind: '$users',
    });

    aggregationQuery.push({
        $lookup: {
            from: 'new_users',
            localField: 'users.userID',
            foreignField: '_id',
            as: 'userData',
        },
    });

It is returning an empty array in userData field. What am I doing wrong here?


Solution

  • The problem is you are trying to match a string and an object id in the lookup stage so it returns an empty array as a result

    you can either change your model so that userID is Types.ObjectId or you can manage that in the aggregation stage as well. An example is to convert the string field to object id field in a pipeline stage

    for example you can use $addFields to convert the string field to object id field before doing the lookup

    db.workspaces.aggregate([
      {
        $unwind: "$users"
      },
      {
        $addFields: {
          "users.userID": {
            $toObjectId: "$users.userID"
          }
        }
      },
      {
        $lookup: {
          from: "new_users",
          localField: "users.userID",
          foreignField: "_id",
          as: "userData"
        }
      }
    ])
    

    sample playground