/** * Copyright (C) 2018-present MongoDB, Inc. * * This program is free software: you can redistribute it and/or modify * it under the terms of the Server Side Public License, version 1, * as published by MongoDB, Inc. * * 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 * Server Side Public License for more details. * * You should have received a copy of the Server Side Public License * along with this program. If not, see * . * * As a special exception, the copyright holders give permission to link the * code of portions of this program with the OpenSSL library under certain * conditions as described in each individual source file and distribute * linked combinations including the program with the OpenSSL library. You * must comply with the Server Side Public License in all respects for * all of the code used other than as permitted herein. If you modify file(s) * with this exception, you may extend this exception to your version of the * file(s), but you are not obligated to do so. If you do not wish to do so, * delete this exception statement from your version. If you delete this * exception statement from all source files in the program, then also delete * it in the license file. */ #pragma once #include #include #include "mongo/client/connpool.h" #include "mongo/db/matcher/matcher.h" #include "mongo/db/pipeline/document_source.h" namespace mongo { class DocumentSourceMatch : public DocumentSource { public: static constexpr StringData kStageName = "$match"_sd; /** * Convenience method for creating a $match stage. */ static boost::intrusive_ptr create( BSONObj filter, const boost::intrusive_ptr& expCtx); /** * Parses a $match stage from 'elem'. */ static boost::intrusive_ptr createFromBson( BSONElement elem, const boost::intrusive_ptr& pCtx); /** * Returns a new DocumentSourceMatch with a MatchExpression that, if executed on the * sub-document at 'path', is equivalent to 'expression'. * * For example, if the original expression is {$and: [{'a.b': {$gt: 0}}, {'a.d': {$eq: 3}}]}, * the new $match will have the expression {$and: [{b: {$gt: 0}}, {d: {$eq: 3}}]} after * descending on the path 'a'. * * Should be called _only_ on a MatchExpression that is a predicate on 'path', or subfields of * 'path'. It is also invalid to call this method on an expression including a $elemMatch on * 'path', for example: {'path': {$elemMatch: {'subfield': 3}}} */ static boost::intrusive_ptr descendMatchOnPath( MatchExpression* matchExpr, const std::string& path, const boost::intrusive_ptr& expCtx); virtual ~DocumentSourceMatch() = default; /** * Parses 'filter' and resets the member of this source to be consistent with the new * MatchExpression. Takes ownership of 'filter'. */ void rebuild(BSONObj filter); boost::intrusive_ptr optimize() final; const char* getSourceName() const override; StageConstraints constraints(Pipeline::SplitState pipeState) const override { return {StreamType::kStreaming, PositionRequirement::kNone, HostTypeRequirement::kNone, DiskUseRequirement::kNoDiskUse, FacetRequirement::kAllowed, TransactionRequirement::kAllowed, LookupRequirement::kAllowed, ChangeStreamRequirement::kWhitelist}; } Value serialize( boost::optional explain = boost::none) const override; /** * Attempts to combine with any subsequent $match stages, joining the query objects with a * $and. */ Pipeline::SourceContainer::iterator doOptimizeAt(Pipeline::SourceContainer::iterator itr, Pipeline::SourceContainer* container) final; DepsTracker::State getDependencies(DepsTracker* deps) const final; GetModPathsReturn getModifiedPaths() const final { // This stage does not modify or rename any paths. return {GetModPathsReturn::Type::kFiniteSet, std::set{}, {}}; } /** * Access the MatchExpression stored inside the DocumentSourceMatch. Does not release ownership. */ MatchExpression* getMatchExpression() const { return _expression.get(); } /** * Combines the filter in this $match with the filter of 'other' using a $and, updating this * match in place. */ void joinMatchWith(boost::intrusive_ptr other); /** * Returns the query in MatchExpression syntax. */ BSONObj getQuery() const; /** Returns the portion of the match that can safely be promoted to before a $redact. * If this returns an empty BSONObj, no part of this match may safely be promoted. * * To be safe to promote, removing a field from a document to be matched must not cause * that document to be accepted when it would otherwise be rejected. As an example, * {name: {$ne: "bob smith"}} accepts documents without a name field, which means that * running this filter before a redact that would remove the name field would leak * information. On the other hand, {age: {$gt:5}} is ok because it doesn't accept documents * that have had their age field removed. */ BSONObj redactSafePortion() const; static bool isTextQuery(const BSONObj& query); bool isTextQuery() const { return _isTextQuery; } /** * Attempt to split this $match into two stages, where the first is not dependent upon any path * from 'fields', and where applying them in sequence is equivalent to applying this stage once. * * Will return two intrusive_ptrs to new $match stages, where the first pointer is independent * of 'fields', and the second is dependent. Either pointer may be null, so be sure to check the * return value. * * For example, {$match: {a: "foo", "b.c": 4}} split by "b" will return pointers to two stages: * {$match: {a: "foo"}}, and {$match: {"b.c": 4}}. * * The 'renames' structure maps from a field to an alias that should be used in the independent * portion of the match. For example, suppose that we split by fields "a" with the rename "b" => * "c". The match {$match: {a: "foo", b: "bar", z: "baz"}} will split into {$match: {c: "bar", * z: "baz"}} and {$match: {a: "foo"}}. */ std::pair, boost::intrusive_ptr> splitSourceBy(const std::set& fields, const StringMap& renames); boost::optional distributedPlanLogic() final { return boost::none; } protected: GetNextResult doGetNext() override; DocumentSourceMatch(const BSONObj& query, const boost::intrusive_ptr& expCtx); BSONObj _predicate; private: std::unique_ptr _expression; bool _isTextQuery; // Cache the dependencies so that we know what fields we need to serialize to BSON for matching. DepsTracker _dependencies; }; } // namespace mongo