Parse a Projection tree from a BSON projection specification
| 832 | |
| 833 | // Parse a Projection tree from a BSON projection specification |
| 834 | Reference<Projection> parseProjection(bson::BSONObj const& fieldSelector) { |
| 835 | std::set<std::string> parsedFields; |
| 836 | bool includeID = true; |
| 837 | bool hasIncludeValue = false; |
| 838 | |
| 839 | Reference<Projection> root(new Projection()); |
| 840 | |
| 841 | for (auto itr = fieldSelector.begin(); itr.more(); ++itr) { |
| 842 | bson::BSONElement el = *itr; |
| 843 | std::string fieldName = el.fieldName(); |
| 844 | if (fieldName.length() >= 2 && fieldName.compare(fieldName.length() - 2, 2, ".$") == 0) { |
| 845 | // TODO: $ operator |
| 846 | throw invalid_projection(); |
| 847 | } else if (el.type() == bson::Object) { |
| 848 | // TODO: $slice operator |
| 849 | // TODO: $elemMatch operator |
| 850 | // TODO: $meta operator |
| 851 | throw invalid_projection(); |
| 852 | } else { |
| 853 | bool elIncluded = el.trueValue(); |
| 854 | if (fieldName == DocLayerConstants::ID_FIELD) { |
| 855 | // Specifying _id:1 makes the projection an inclusive projection |
| 856 | if (elIncluded) { |
| 857 | if (hasIncludeValue && root->included) { |
| 858 | throw invalid_projection(); |
| 859 | } |
| 860 | |
| 861 | root->included = false; // Our specification is inclusive, so unspecified fields should be excluded |
| 862 | hasIncludeValue = true; |
| 863 | } |
| 864 | |
| 865 | includeID = elIncluded; |
| 866 | } else { |
| 867 | if (!hasIncludeValue) { |
| 868 | root->included = |
| 869 | !elIncluded; // If el was inclusive, then we should exclude unspecified fields (and vice versa) |
| 870 | hasIncludeValue = true; |
| 871 | } |
| 872 | |
| 873 | // inclusive and exclusive modes cannot be mixed |
| 874 | else if (root->included == elIncluded) { |
| 875 | throw invalid_projection(); |
| 876 | } |
| 877 | |
| 878 | // The whole _id field is either included or excluded as a unit regardless of whether its sub-fields are |
| 879 | // included in the projection |
| 880 | if (fieldName.size() >= 4 && fieldName.compare(0, 4, "_id.") == 0) { |
| 881 | continue; |
| 882 | } |
| 883 | |
| 884 | // Split the field into its consituent parts and insert the parts into the Projection tree |
| 885 | if (parsedFields.insert(fieldName).second) { |
| 886 | size_t pos = -1; |
| 887 | size_t prev; |
| 888 | Reference<Projection>* current = &root; |
| 889 | do { |
| 890 | prev = pos + 1; |
| 891 | pos = fieldName.find('.', prev); |
no test coverage detected