/** * Copyright 2011 (c) 10gen Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License, version 3, * as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "mongo/pch.h" #include "mongo/db/pipeline/document_source.h" #include #include "mongo/db/jsobj.h" #include "mongo/db/pipeline/document.h" #include "mongo/db/pipeline/expression.h" #include "mongo/db/pipeline/value.h" namespace mongo { const char DocumentSourceRedact::redactName[] = "$redact"; DocumentSourceRedact::DocumentSourceRedact(const intrusive_ptr& expCtx, const intrusive_ptr& expression) : DocumentSource(expCtx) , _expression(expression) { } const char *DocumentSourceRedact::getSourceName() const { return redactName; } static const Value descendVal = Value("descend"); static const Value pruneVal = Value("prune"); static const Value keepVal = Value("keep"); boost::optional DocumentSourceRedact::getNext() { while (boost::optional in = pSource->getNext()) { Variables vars = Variables(*in, Value(*in), DOC("DESCEND" << descendVal << "PRUNE" << pruneVal << "KEEP" << keepVal)); if (boost::optional result = redactObject(vars)) { return result; } } return boost::none; } Value DocumentSourceRedact::redactValue(const Variables& vars, const Value& in) { const BSONType valueType = in.getType(); if (valueType == Object) { Variables recurse = vars; recurse.current = in; const boost::optional result = redactObject(recurse); if (result) { return Value(*result); } else { return Value(); } } else if (valueType == Array) { // TODO dont copy if possible vector newArr; const vector& arr = in.getArray(); for (size_t i = 0; i < arr.size(); i++) { if (arr[i].getType() == Object || arr[i].getType() == Array) { const Value toAdd = redactValue(vars, arr[i]) ; if (!toAdd.missing()) { newArr.push_back(toAdd); } } else { newArr.push_back(arr[i]); } } return Value::consume(newArr); } else { return in; } } boost::optional DocumentSourceRedact::redactObject(const Variables& in) { const Value expressionResult = _expression->evaluate(in); if (expressionResult == keepVal) { return in.current.getDocument(); } else if (expressionResult == pruneVal) { return boost::optional(); } else if (expressionResult == descendVal) { MutableDocument out; FieldIterator fields(in.current.getDocument()); while (fields.more()) { const Document::FieldPair field(fields.next()); const Value val = redactValue(in, field.second); if (!val.missing()) { out.addField(field.first, val); } } return out.freeze(); } else { uasserted(17053, str::stream() << "$redact's expression should not return anything " << "aside from the variables $$KEEP, $$DESCEND, and " << "$$PRUNE, but returned " << expressionResult.toString()); } } void DocumentSourceRedact::optimize() { _expression = _expression->optimize(); } Value DocumentSourceRedact::serialize(bool explain) const { return Value(DOC(getSourceName() << _expression.get()->serialize())); } intrusive_ptr DocumentSourceRedact::createFromBson( BSONElement* bsonElement, const intrusive_ptr& expCtx) { uassert(17054, str::stream() << redactName << " specification must be an object", bsonElement->type() == Object); Expression::ObjectCtx oCtx(0); intrusive_ptr expression = Expression::parseObject(bsonElement, &oCtx); return new DocumentSourceRedact(expCtx, expression); } }