Projection Operation¶
ProjectionOperation đại diện cho stage $project của MongoDB. Nó cho phép chọn/bỏ cột, đặt bí danh, và xây dựng các biểu thức số học, chuỗi, mảng, so sánh, điều kiện, cũng như lồng biến let trong phần chiếu.
import { ProjectionOperation } from 'red-aggregation/operations/projectionOperation';
import { Fields } from 'red-aggregation/aggregate/field';
import { ArithmeticOperators } from 'red-aggregation/operator/arithmeticOperators/arithmeticOperators';
import { Cond, IfNull } from 'red-aggregation/operator/conditionalOperators';
import { ExpressionVariable } from 'red-aggregation/operator/variableOperators/let';
import { ArrayOperator } from 'red-aggregation/operator/arrayOperators/arrayOperator';
import { ToUpper } from 'red-aggregation/operator/stringOperators/toUppercase';
Phương thức¶
- new ProjectionOperation()
- andInclude(...fields)
- andExclude(...fields)
- and(name|expression|document).as(alias)
- and(name).plus/minus/multiply/divide/mod/eq/gt/gte/lt/lte/ne
- Array helpers: arrayElementAt/slice/concatArrays/isArray/asArray/andArrayOf
- Điều kiện: applyCondition(Cond|IfNull)
- nested(fields)
- previousOperation()
- lets(...) với $let
new ProjectionOperation¶
new ProjectionOperation().toDocument(context);
// => { $project: {} }
andInclude(...fields)¶
new ProjectionOperation()
.andInclude('firstName')
.andInclude('lastName')
.toDocument(context);
// => { $project: { firstName: 1, lastName: 1 } }
andExclude(...fields)¶
new ProjectionOperation()
.andExclude('password', 'secret')
.toDocument(context);
// => { $project: { password: 0, secret: 0 } }
and(...).as(alias)¶
Alias field hoặc gán biểu thức/literal cho trường mới.
// Alias trường
new ProjectionOperation().and('fullName').as('displayName').toDocument(context);
// => { $project: { displayName: '$fullName' } }
// Gán biểu thức số học
new ProjectionOperation()
.and(ArithmeticOperators.add('$foo').add(41))
.as('bar')
.toDocument(context);
// => { $project: { bar: { $add: ['$foo', 41] } } }
// Gán biểu thức chuỗi (AggregationExpression)
new ProjectionOperation()
.and(ToUpper.valueOf('item'))
.as('upperItem')
.toDocument(context);
// => { $project: { upperItem: { $toUpper: '$item' } } }
// Kết hợp include + biểu thức
new ProjectionOperation()
.andInclude('firstName')
.and('firstName').toUpper().as('firstNameUpper')
.toDocument(context);
// => { $project: { firstName: 1, firstNameUpper: { $toUpper: '$firstName' } } }
Helpers số học/so sánh (qua and('field').…).as('alias')¶
new ProjectionOperation()
.and('price').minus(5).as('discounted') // { $subtract: ['$price', 5] }
.and('price').multiply(2).as('double') // { $multiply: ['$price', 2] }
.and('price').divide(2).as('half') // { $divide: ['$price', 2] }
.and('price').mod(3).as('modulus') // { $mod: ['$price', 3] }
.and('price').plus(10).as('total') // { $add: ['$price', 10] }
.and('score').eq(10).as('eq') // { $eq: ['$score', 10] }
.and('score').gt(10).as('gt') // { $gt: ['$score', 10] }
.and('score').gte(10).as('gte') // { $gte: ['$score', 10] }
.and('score').lt(10).as('lt') // { $lt: ['$score', 10] }
.and('score').lte(10).as('lte') // { $lte: ['$score', 10] }
.and('score').ne(10).as('ne') // { $ne: ['$score', 10] }
.toDocument(context);
Helpers mảng¶
new ProjectionOperation()
.and('favorites').arrayElementAt(0).as('first') // { $arrayElemAt: ['$favorites', 0] }
.and('favorites').slice(3).as('firstThree') // { $slice: ['$favorites', 3] }
.and('favorites').slice(3, 1).as('offsetSlice') // { $slice: ['$favorites', 1, 3] }
.and('favorites').concatArrays('others').as('all') // { $concatArrays: ['$favorites', '$others'] }
.and('favorites').isArray().as('isArray') // { $isArray: '$favorites' }
.toDocument(context);
// Biến các projection hiện tại thành một mảng
new ProjectionOperation()
.andInclude('firstName').andInclude('lastName')
.asArray('fullNameParts')
.toDocument(context);
// => { $project: { fullNameParts: ['$firstName', '$lastName'] } }
// Tạo mảng từ danh sách phần tử (field/expression/literal)
new ProjectionOperation()
.andArrayOf([Fields.field('city'), { $literal: 'VN' }, 42])
.as('details')
.toDocument(context);
// => { $project: { details: ['$city', { $literal: 'VN' }, 42] } }
Điều kiện¶
const condExpr = Cond.newBuilder().when({ $gt: ['$score', 90] }).then('honors').otherwise('standard');
const ifNullExpr = IfNull.ifNull('nickname').orIfNull('alias').then('unknown');
new ProjectionOperation()
.and('grade').applyCondition(condExpr)
.and('displayName').applyCondition(ifNullExpr)
.toDocument(context);
// => {
// $project: {
// grade: { $cond: { if: { $gt: ['$score', 90] }, then: 'honors', else: 'standard' } },
// displayName: { $ifNull: ['$nickname', '$alias', 'unknown'] }
// }
// }
nested(fields)¶
new ProjectionOperation()
.and('profile').nested(Fields.fields('name', 'email'))
.toDocument(context);
// => { $project: { profile: { name: '$name', email: '$email' } } }
previousOperation()¶
Tham chiếu kết quả của stage trước đó qua $_id và ẩn _id hiện tại.
new ProjectionOperation().and('prop').previousOperation().toDocument(context);
// => { $project: { prop: '$_id', _id: 0 } }
lets(...) (nhúng $let trong projection)¶
// Dạng (valueExpression, variableName, inExpression)
const total = ArithmeticOperators.add('price').add('tax');
const inExpr = ArithmeticOperators.multiply('$$total').multiplyBy(2);
new ProjectionOperation()
.and('finalPrice').lets(total, 'total', inExpr).as('finalPrice')
.toDocument(context);
// => {
// $project: {
// finalPrice: {
// $let: { vars: { total: { $add: ['$price', '$tax'] } }, in: { $multiply: ['$$total', 2] } }
// }
// }
// }
// Dạng (variables[], inExpression)
const vars = [
ExpressionVariable
.newVariable('discount')
.forExpression(ArithmeticOperators.multiply('price').multiplyBy(0.1))
];
const apply = ArithmeticOperators.subtract('price').subtract('discount');
new ProjectionOperation()
.and('finalPrice').lets(vars, apply).as('finalPrice')
.toDocument(context);
// => {
// $project: {
// finalPrice: {
// $let: { vars: { discount: { $multiply: ['$price', 0.1] } }, in: { $subtract: ['$price', '$$discount'] } }
// }
// }
// }
Lưu ý sử dụng¶
- Không trộn include và exclude trong cùng một
$project("Mixing inclusion and exclusion in projection is not allowed."). and(name)trả về builder; cần.as(alias)để chốt projection khi dùng các helper.- Có thể khởi tạo từ
Fields.fields(...)để include/alias nhiều field một lượt. - Một số helper nhận
field/expression/document/literal theo đúng overload (xem ví dụ).