StatusWithMatchExpression JSONSchemaParser::_parse(StringData path, BSONObj schema) { // Map from JSON Schema keyword to the corresponding element from 'schema', or to an empty // BSONElement if the JSON Schema keyword is not specified. StringMap<BSONElement> keywordMap{ {kSchemaTypeKeyword, {}}, {kSchemaPropertiesKeyword, {}}, {kSchemaMaximumKeyword, {}}}; for (auto&& elt : schema) { auto it = keywordMap.find(elt.fieldNameStringData()); if (it == keywordMap.end()) { return Status(ErrorCodes::FailedToParse, str::stream() << "Unknown $jsonSchema keyword: " << elt.fieldNameStringData()); } if (it->second) { return Status(ErrorCodes::FailedToParse, str::stream() << "Duplicate $jsonSchema keyword: " << elt.fieldNameStringData()); } keywordMap[elt.fieldNameStringData()] = elt; } auto typeExpr = parseType(path, keywordMap[kSchemaTypeKeyword]); if (!typeExpr.isOK()) { return typeExpr.getStatus(); } auto andExpr = stdx::make_unique<AndMatchExpression>(); if (auto propertiesElt = keywordMap[kSchemaPropertiesKeyword]) { auto propertiesExpr = _parseProperties(path, propertiesElt, typeExpr.getValue().get()); if (!propertiesExpr.isOK()) { return propertiesExpr; } andExpr->add(propertiesExpr.getValue().release()); } if (auto maximumElt = keywordMap[kSchemaMaximumKeyword]) { auto maxExpr = parseMaximum(path, maximumElt, typeExpr.getValue().get()); if (!maxExpr.isOK()) { return maxExpr; } andExpr->add(maxExpr.getValue().release()); } if (path.empty() && typeExpr.getValue() && typeExpr.getValue()->getBSONType() != BSONType::Object) { // This is a top-level schema which requires that the type is something other than // "object". Since we only know how to store objects, this schema matches nothing. return {stdx::make_unique<FalseMatchExpression>(StringData{})}; } if (!path.empty() && typeExpr.getValue()) { andExpr->add(typeExpr.getValue().release()); } return {std::move(andExpr)}; }
StatusWithMatchExpression JSONSchemaParser::_parse(StringData path, BSONObj schema) { // Map from JSON Schema keyword to the corresponding element from 'schema', or to an empty // BSONElement if the JSON Schema keyword is not specified. StringMap<BSONElement> keywordMap{{kSchemaTypeKeyword, {}}, {kSchemaPropertiesKeyword, {}}, {kSchemaMaximumKeyword, {}}, {kSchemaMinimumKeyword, {}}, {kSchemaExclusiveMaximumKeyword, {}}, {kSchemaExclusiveMinimumKeyword, {}}, {kSchemaMaxLengthKeyword, {}}, {kSchemaMinLengthKeyword, {}}, {kSchemaPatternKeyword, {}}, {kSchemaMultipleOfKeyword, {}}}; for (auto&& elt : schema) { auto it = keywordMap.find(elt.fieldNameStringData()); if (it == keywordMap.end()) { return Status(ErrorCodes::FailedToParse, str::stream() << "Unknown $jsonSchema keyword: " << elt.fieldNameStringData()); } if (it->second) { return Status(ErrorCodes::FailedToParse, str::stream() << "Duplicate $jsonSchema keyword: " << elt.fieldNameStringData()); } keywordMap[elt.fieldNameStringData()] = elt; } auto typeExpr = parseType(path, keywordMap[kSchemaTypeKeyword]); if (!typeExpr.isOK()) { return typeExpr.getStatus(); } auto andExpr = stdx::make_unique<AndMatchExpression>(); if (auto propertiesElt = keywordMap[kSchemaPropertiesKeyword]) { auto propertiesExpr = _parseProperties(path, propertiesElt, typeExpr.getValue().get()); if (!propertiesExpr.isOK()) { return propertiesExpr; } andExpr->add(propertiesExpr.getValue().release()); } if (auto maximumElt = keywordMap[kSchemaMaximumKeyword]) { bool isExclusiveMaximum = false; if (auto exclusiveMaximumElt = keywordMap[kSchemaExclusiveMaximumKeyword]) { if (!exclusiveMaximumElt.isBoolean()) { return {Status(ErrorCodes::TypeMismatch, str::stream() << "$jsonSchema keyword '" << kSchemaExclusiveMaximumKeyword << "' must be a boolean")}; } else { isExclusiveMaximum = exclusiveMaximumElt.boolean(); } } auto maxExpr = parseMaximum(path, maximumElt, typeExpr.getValue().get(), isExclusiveMaximum); if (!maxExpr.isOK()) { return maxExpr; } andExpr->add(maxExpr.getValue().release()); } else if (keywordMap[kSchemaExclusiveMaximumKeyword]) { // If "exclusiveMaximum" is present, "maximum" must also be present. return {Status(ErrorCodes::FailedToParse, str::stream() << "$jsonSchema keyword '" << kSchemaMaximumKeyword << "' must be a present if " << kSchemaExclusiveMaximumKeyword << " is present")}; } if (auto minimumElt = keywordMap[kSchemaMinimumKeyword]) { bool isExclusiveMinimum = false; if (auto exclusiveMinimumElt = keywordMap[kSchemaExclusiveMinimumKeyword]) { if (!exclusiveMinimumElt.isBoolean()) { return {Status(ErrorCodes::TypeMismatch, str::stream() << "$jsonSchema keyword '" << kSchemaExclusiveMinimumKeyword << "' must be a boolean")}; } else { isExclusiveMinimum = exclusiveMinimumElt.boolean(); } } auto minExpr = parseMinimum(path, minimumElt, typeExpr.getValue().get(), isExclusiveMinimum); if (!minExpr.isOK()) { return minExpr; } andExpr->add(minExpr.getValue().release()); } else if (keywordMap[kSchemaExclusiveMinimumKeyword]) { // If "exclusiveMinimum" is present, "minimum" must also be present. return {Status(ErrorCodes::FailedToParse, str::stream() << "$jsonSchema keyword '" << kSchemaMinimumKeyword << "' must be a present if " << kSchemaExclusiveMinimumKeyword << " is present")}; } if (auto maxLengthElt = keywordMap[kSchemaMaxLengthKeyword]) { auto maxLengthExpr = parseStrLength<InternalSchemaMaxLengthMatchExpression>( path, maxLengthElt, typeExpr.getValue().get(), kSchemaMaxLengthKeyword); if (!maxLengthExpr.isOK()) { return maxLengthExpr; } andExpr->add(maxLengthExpr.getValue().release()); } if (auto minLengthElt = keywordMap[kSchemaMinLengthKeyword]) { auto minLengthExpr = parseStrLength<InternalSchemaMinLengthMatchExpression>( path, minLengthElt, typeExpr.getValue().get(), kSchemaMinLengthKeyword); if (!minLengthExpr.isOK()) { return minLengthExpr; } andExpr->add(minLengthExpr.getValue().release()); } if (auto patternElt = keywordMap[kSchemaPatternKeyword]) { auto patternExpr = parsePattern(path, patternElt, typeExpr.getValue().get()); if (!patternExpr.isOK()) { return patternExpr; } andExpr->add(patternExpr.getValue().release()); } if (auto multipleOfElt = keywordMap[kSchemaMultipleOfKeyword]) { auto multipleOfExpr = parseMultipleOf(path, multipleOfElt, typeExpr.getValue().get()); if (!multipleOfExpr.isOK()) { return multipleOfExpr; } andExpr->add(multipleOfExpr.getValue().release()); } if (path.empty() && typeExpr.getValue() && typeExpr.getValue()->getBSONType() != BSONType::Object) { // This is a top-level schema which requires that the type is something other than // "object". Since we only know how to store objects, this schema matches nothing. return {stdx::make_unique<AlwaysFalseMatchExpression>()}; } if (!path.empty() && typeExpr.getValue()) { andExpr->add(makeTypeRestriction(std::move(typeExpr.getValue())).release()); } return {std::move(andExpr)}; }