MongoDB schema validation - sub schemas with oneOf or $or - mongodb

I have the next validation schema
use("bio_db_schema")
For Biology
const biologyChapterSchema = {
bsonType: "object",
additionalProperties: false,
properties: {
"chapter_name": {enum: ["Biologia"]},
"taxonomias": {
bsonType: ["array"],
items: {
bsonType: "object",
// required: ["taxonomia_name"],
additionalProperties: false,
properties: {
"valor": {bsonType: "string"},
"taxonomia_name": {bsonType: "string"},
"taxonomia_id": {bsonType: "objectId"},
"associates": {
bsonType: ["array"],
items: {
bsonType: "object",
additionalProperties: false,
properties: {
"associate_name": {bsonType: "string"},
"associate_role": {bsonType: "string"},
"associate_id": {bsonType: "objectId"},
},
},
},
},
},
},
}
};
For Physics (just for test, i change the "valor" from "string" to "double")
const physicsChapterValidation = {
bsonType: "object",
properties: {
"chapter_name": {enum: ["Fisica"]},
"estaciones": {
bsonType: ["array"],
items: {
bsonType: "object",
// required: ["taxonomia_name"],
additionalProperties: false,
properties: {
"valor": {bsonType: "double"},
"estacion_name": {bsonType: "string"},
"estacion_id": {bsonType: "objectId"},
"associates": {
bsonType: ["array"],
items: {
bsonType: "object",
additionalProperties: false,
properties: {
"associate_name": {bsonType: "string"},
"associate_role": {bsonType: "string"},
"associate_id": {bsonType: "objectId"},
},
},
},
},
},
},
}
};
Schema
db.createCollection("projects", {
validator: {
$jsonSchema:{
bsonType: "object",
// title: "",
// required: ["chapter_name"],
additionalProperties: false,
properties: {
"_id": {bsonType: "objectId"},
"project_name": {bsonType: "string"},
"sub_projects": {
bsonType: ["array"],
items: {
bsonType: "object",
additionalProperties: false,
properties: {
"sub_project_name": {bsonType: "string"},
"IGA": {bsonType: "string"},
"sub_project_stage": {
bsonType: ["array"],
items: {
bsonType: "object",
additionalProperties: false,
properties:{
"sub_project_stage_name": {bsonType: "string"},
"chapters": {
bsonType: ["array"],
items: {
oneOf: [
biologyChapterSchema,
physicsChapterValidation
]
}
}
},
},
},
},
},
},
},
},
},
});
What i expect:
When i insert a document with:
"chapter_name" = "Biologia", it must use the Schema "biologyChapterSchema" (where "valor" is a "string")
"chapter_name" = "Fisica", it must use the Schema "physicsChapterValidation" (where "valor" is a "double")
i use "oneOf" for it,
works for "Biologia", only accept a string, but when i change to "Fisica" it allows "strins", "integers" and allow another properties like "valor2".
I use VSCode extensión and said $or operator doesnt work for $jsonSchema
Those are just a test for my app, I need that they are in variables to make it readable and to be able to keep it over time and add more sub schemas.
Any tips?
For test:
db.projects.insertOne({
"project_name": "Proyecto Fisica",
"sub_projects": [
{
"sub_project_name": "Subproyecto Fisica",
"IGA": "IGA Fisica",
"sub_project_stage": [
{
"sub_project_stage_name": "Etapa Fisica",
"chapters": [
{
"chapter_name": "Fisica",
"taxonomias": [
{
"taxonomia_name": "Taxonomía Fisica",
"taxonomia_id": ObjectId("123456789235"),
"associates": [
{
"associate_name": "Asociado Fisica",
"associate_role": "Rol Biología",
"associate_id": ObjectId("123456789232")
}
],
"valor": "asdasd"
}
]
}
]
}
]
}
]
})

You are missing additionalProperties in physicsChapterValidation.
const physicsChapterValidation = {
bsonType: "object",
additionalProperties: false,
properties: {
"chapter_name": {enum: ["Fisica"]},

Related

How to get only specific nested object from MongoDB collection where uuid is used as key

I only want this matched object in query result, by querying uuid "630c260e333c41549c43cae5f8e626d5":
{
"enableAnnotations": false,
"enableMultipleFilesUpload": true,
"enableWorkflows": true,
"formSectionID": "a9bcde7f8fc14e519965a655d9574fb7",
"isThumbnailField": false,
"label": "WD multi upload Ttl in Dup Section DW",
"publishToFolder": false,
"publishWhenApproved": false,
"type": "upload",
"uuid": "630c260e333c41549c43cae5f8e626d5"
}
from the following Collection document:
[{
"_id": "07672b703cc44ce6887159984911dd6e",
"createTime": "2022-06-21T12:08:00Z",
"creator": "75e5d59fa1be0f932bff8407536730d0",
"defination": {
"fields": {
"a9bcde7f8fc14e519965a655d9574fb7": [
{
"enableAnnotations": false,
"enableMultipleFilesUpload": true,
"enableWorkflows": true,
"formSectionID": "a9bcde7f8fc14e519965a655d9574fb7",
"isThumbnailField": false,
"label": "WD multi upload Ttl in Dup Section DW",
"publishToFolder": false,
"publishWhenApproved": false,
"type": "upload",
"uuid": "630c260e333c41549c43cae5f8e626d5"
},
{
"enableAnnotations": false,
"enableMultipleFilesUpload": false,
"formSectionID": "a9bcde7f8fc14e519965a655d9574fb7",
"isThumbnailField": false,
"label": "WD single upload in Dup section DW",
"publishToFolder": false,
"type": "upload",
"uuid": "1aecb33709434d9b9a0cb455c67e2295"
}
],
"eda3ce9883a14b948eae5b053f720227": [
{
"enableAnnotations": false,
"enableMultipleFilesUpload": true,
"enableWorkflows": true,
"formSectionID": "eda3ce9883a14b948eae5b053f720227",
"isThumbnailField": false,
"label": "WD upload multi",
"publishToFolder": false,
"publishWhenApproved": false,
"type": "upload",
"uuid": "e88a097621cc44c4bb440902006f5a12"
},
{
"formSectionID": "eda3ce9883a14b948eae5b053f720227",
"label": "WD Txt Fld Ttl",
"type": "text",
"uuid": "e1bf361509364718b9d52e65cbc292de",
},
{
"enableAnnotations": false,
"enableMultipleFilesUpload": true,
"enableWorkflows": true,
"formSectionID": "eda3ce9883a14b948eae5b053f720227",
"isThumbnailField": false,
"label": "WD 2nd-multi-upload",
"publishToFolder": false,
"publishWhenApproved": false,
"type": "upload",
"uuid": "fbee1d1adf224712a90f3f37b8395aa0"
},
{
"enableAnnotations": true,
"enableMultipleFilesUpload": false,
"formSectionID": "eda3ce9883a14b948eae5b053f720227",
"isThumbnailField": false,
"label": "WD single upload Ttl DW",
"publishToFolder": false,
"type": "upload",
"uuid": "5504ce66d1bc4f659eefed4df4e73b99",
}
]
},
"sections": {
"291b58eddee04a05bd8e7d80d99241b9": [
{
"uuid": "eda3ce9883a14b948eae5b053f720227"
},
{
"uuid": "a9bcde7f8fc14e519965a655d9574fb7"
}
]
},
"tabs": [
{
"uuid": "291b58eddee04a05bd8e7d80d99241b9"
}
]
}
}]
After some research, I used the query: (by referring how to select value if key is uuid in mongodb)
db.collection.aggregate([
{
"$set": {
"fields": {
"$objectToArray": "$defination.fields"
}
}
},
{
"$unwind": "$fields"
},
{
"$match": {
"fields.v.uuid": "630c260e333c41549c43cae5f8e626d5",
"fields.v.enableWorkflows": true,
"fields.v.enableMultipleFilesUpload": true,
"fields.v.type": "upload"
}
},
{
"$group": {
"_id": "$_id",
"fields": {
"$push": "$fields"
}
}
},
{
"$set": {
"fields": {
"$arrayToObject": "$fields"
}
}
}
])
and I got the result:
[
{
"_id": "07672b703cc44ce6887159984911dd6e",
"fields": {
"a9bcde7f8fc14e519965a655d9574fb7": [
{
"enableAnnotations": false,
"enableMultipleFilesUpload": true,
"enableWorkflows": true,
"formSectionID": "a9bcde7f8fc14e519965a655d9574fb7",
"isThumbnailField": false,
"label": "WD multi upload Ttl in Dup Section DW",
"publishToFolder": false,
"publishWhenApproved": false,
"type": "upload",
"uuid": "630c260e333c41549c43cae5f8e626d5"
},
{
"enableAnnotations": false,
"enableMultipleFilesUpload": false,
"formSectionID": "a9bcde7f8fc14e519965a655d9574fb7",
"isThumbnailField": false,
"label": "WD single upload in Dup section DW",
"publishToFolder": false,
"type": "upload",
"uuid": "1aecb33709434d9b9a0cb455c67e2295"
}
]
}
}
]
But it contains extra sibling objects.
I tried the answer from post but it doesn't work: How can I get only specific object from nested array mongodb
One option to drill down is using $reduce with $filter, but first we need known keys, hence the $objectToArray which you already have:
db.collection.aggregate([
{$project: {fields: {$objectToArray: "$defination.fields"}}},
{$project: {res: {
$first: {
$reduce: {
input: "$fields",
initialValue: [],
in: {$concatArrays: [
"$$value",
{$filter: {
input: "$$this.v",
as: "inner",
cond: {
$eq: [
"$$inner.uuid",
"630c260e333c41549c43cae5f8e626d5"
]
}
}
}
]
}
}
}
}
}
},
{$replaceRoot: {newRoot: {$ifNull: ["$res", {}]}}}
])
See how it works on the playground example

mongoose ref only returns one element inside array

I am trying to reference an array of categories but output is only returning one element inside.
here is the post model, I'm I am wondering about post_categories that has a ref to Category
here is the post model
import mongoose from 'mongoose'
mongoose.set('debug', true)
const postSchema = mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
required: false,
ref: 'User'
},
post_title: {
type: String,
trim: true,
min: 3,
max: 160,
required: true
},
post_slug: {
type: {},
unique: true,
index: true
},
post_excerpt: {
type: String,
max: 1000
},
post_content: {
type: {},
required: true
},
post_thumbnail: {
data: Buffer,
contentType: String
},
post_comments: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Comment',
require: false
}],
post_tags: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Tag',
require: false
}],
post_categories: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Category'
}
],
post_meta_title: {
type: String,
required: false
},
post_meta_description: {
type: String,
required: false
},
post_meta_keywords: [{
type: String,
required: false
}],
allow_comments: {
type: Boolean,
required: false,
default: 1,
},
allow_search_engines: {
type: Boolean,
required: false,
default: 1
},
published: {
type: Boolean,
required: false,
default: 0
}
}, {
timestamps: true
})
const Post = mongoose.model('Post', postSchema)
export default Post
this is from the post controller
const getPosts = asyncHandler(async (req, res) => {
const posts = await Post.find().populate('post_categories')
res.json(posts)
})
this is the category model, I'm thinking the problem is within the post controller or the post model though.
import mongoose from 'mongoose'
const categorySchema = mongoose.Schema({
name: {
type: String,
required: true
},
post_count: {
type: Number,
default: 0
}
}, {
timestamps: true
})
const Category = mongoose.model('Category', categorySchema)
export default Category
here is output with the populate
[
{
"post_comments": [
"60257ffd6b08fb4334078b56"
],
"post_tags": [],
"post_categories": [
{
"post_count": 2,
"_id": "602148846168c7408831ac4c",
"name": "Category One",
"__v": 0,
"createdAt": "2021-02-08T14:19:48.647Z",
"updatedAt": "2021-02-19T05:55:45.944Z"
}
],
"post_meta_keywords": [],
"allow_comments": true,
"allow_search_engines": true,
"published": false,
"_id": "602148846168c7408831ac49",
"post_title": "Post One",
"post_excerpt": "Post one excerpt",
"post_content": "This is post one content blah blah blah",
"post_slug": "post-one",
"__v": 0,
"createdAt": "2021-02-08T14:19:48.606Z",
"updatedAt": "2021-02-08T14:19:48.606Z"
},
{
"post_comments": [
"6025e1150d215935688d68ba",
"6025e1150d215935688d68bb"
],
"post_tags": [
"602148846168c7408831ac51",
"602148846168c7408831ac52"
],
"post_categories": [
{
"post_count": 2,
"_id": "602148846168c7408831ac4d",
"name": "Category Two",
"__v": 0,
"createdAt": "2021-02-08T14:19:48.647Z",
"updatedAt": "2021-02-19T05:55:46.053Z"
}
],
"post_meta_keywords": [],
"allow_comments": true,
"allow_search_engines": true,
"published": false,
"_id": "602148846168c7408831ac4a",
"post_title": "Post Two",
"post_excerpt": "Post Two excerpt",
"post_content": "This is post two content blah blah blah",
"post_slug": "post-two",
"__v": 0,
"createdAt": "2021-02-08T14:19:48.606Z",
"updatedAt": "2021-02-08T14:19:48.606Z"
},
{
"post_comments": [],
"post_tags": [],
"post_categories": [
{
"post_count": 2,
"_id": "602148846168c7408831ac4c",
"name": "Category One",
"__v": 0,
"createdAt": "2021-02-08T14:19:48.647Z",
"updatedAt": "2021-02-19T05:55:45.944Z"
}
],
"post_meta_keywords": [],
"allow_comments": true,
"allow_search_engines": true,
"published": false,
"_id": "602148846168c7408831ac4b",
"post_title": "Post Three",
"post_excerpt": "Post three excerpt",
"post_content": "This is post three content blah blah blah",
"post_slug": "post-three",
"__v": 0,
"createdAt": "2021-02-08T14:19:48.606Z",
"updatedAt": "2021-02-08T14:19:48.606Z"
}
]
that output only shows one category however if I show the output without populate it shows all the categories
[
{
"post_comments": [
"60257ffd6b08fb4334078b56"
],
"post_tags": [],
"post_categories": [
"602148846168c7408831ac4c"
],
"post_meta_keywords": [],
"allow_comments": true,
"allow_search_engines": true,
"published": false,
"_id": "602148846168c7408831ac49",
"post_title": "Post One",
"post_excerpt": "Post one excerpt",
"post_content": "This is post one content blah blah blah",
"post_slug": "post-one",
"__v": 0,
"createdAt": "2021-02-08T14:19:48.606Z",
"updatedAt": "2021-02-08T14:19:48.606Z"
},
{
"post_comments": [
"6025e1150d215935688d68ba",
"6025e1150d215935688d68bb"
],
"post_tags": [
"602148846168c7408831ac51",
"602148846168c7408831ac52"
],
"post_categories": [
"60342fbdfb8c6a0db2aecf00",
"602148846168c7408831ac4d"
],
"post_meta_keywords": [],
"allow_comments": true,
"allow_search_engines": true,
"published": false,
"_id": "602148846168c7408831ac4a",
"post_title": "Post Two",
"post_excerpt": "Post Two excerpt",
"post_content": "This is post two content blah blah blah",
"post_slug": "post-two",
"__v": 0,
"createdAt": "2021-02-08T14:19:48.606Z",
"updatedAt": "2021-02-08T14:19:48.606Z"
},
{
"post_comments": [],
"post_tags": [],
"post_categories": [
"6021479b255d7a3360d4bffa",
"602148846168c7408831ac4c"
],
"post_meta_keywords": [],
"allow_comments": true,
"allow_search_engines": true,
"published": false,
"_id": "602148846168c7408831ac4b",
"post_title": "Post Three",
"post_excerpt": "Post three excerpt",
"post_content": "This is post three content blah blah blah",
"post_slug": "post-three",
"__v": 0,
"createdAt": "2021-02-08T14:19:48.606Z",
"updatedAt": "2021-02-08T14:19:48.606Z"
}
]

trying to insert document into my collection, but i m getting "errmsg" : "Document failed validation", code: "121"

i am learning MongoDB, I have created a collection and now when i am trying to insert document its showing "errmsg" : "Document failed validation", code: "121"
Following is my collection code:
db.createCollection( "personprofiles", {
validator: { $jsonSchema: {
bsonType: "object",
required: [ "PersonID", "PersonName", "Gender", "DOB", "Occupation", "Noofchildren", "ProfileType", "Purge" ],
properties: {
PersonID: {
bsonType: "number",
description: "must be an integer and is required"
},
PersonName: {
bsonType: "string",
description: "must be an string and is required"
},
Gender: {
enum: [ "Male", "Female" ],
description: "can only be one of the enum values"
},
MartialStatus: {
enum: [ "Single", "Married" ],
description: "can only be one of the enum values"
},
DOB : {
bsonType: "date",
description: "must be an date and is required"
},
Occupation: {
bsonType: "string",
description: "must be an string and is required"
},
Noofchildren: {
bsonType: "number",
description: "must be an integer and is required"
},
ProfileType: {
enum: [ "Gymnast", "Judge", "CompetitionDirector", "Organizers", "Coach", "Administrator", "Volunteers" ],
description: "can only be one of the enum values"
},
Purge: {
enum: [ "Yes", "No" ],
description: "can only be one of the enum values"
},
Addresses: {
bsonType: "array",
description: "must be an array and is required",
required: [ "AddressID", "AddressType", "AddressLine1", "AddressLine2", "Area", "Locality","City","State","Country","PostalCode"],
properties: {
AddressID: {
bsonType: "number",
description: "must be an integer and is required"
},
AddressType: {
enum: [ "Permanent", "Temporary", "Correspondence", "Office" ],
description: "can only be one of the enum values"
},
AddressLine1: {
bsonType: "string",
description: "must be an string and is required"
},
AddressLine2: {
bsonType: "string",
description: "must be an string and is required"
},
Area: {
bsonType: "string",
description: "must be an string and is required"
},
Locality: {
bsonType: "string",
description: "must be an string and is required"
},
City: {
bsonType: "string",
description: "must be an string and isrequired"
},
State: {
bsonType: "string",
description: "must be an string and is required"
},
Country: {
bsonType: "string",
description: "must be an string and is required"
},
PostalCode: {
bsonType: "string",
description: "must be an string and is required"
}
}
},
ContactNumbers: {
bsonType: "array",
description: "must be an array and is required",
required:[ "ContactID", "ContactType"],
properties: {
ContactID: {
bsonType: "number",
description: "must be an integer and is required"
},
ContactType: {
enum: [ "Home", "Mobile", "Office", "CareOf", "Emergency" ],
description: "can only be one of the enum values and is required"
},
ContactNumber: {
bsonType: "object",
description: "must be an string and is required",
required: ["CountryCode", "RegionCode", "PhoneNumber"],
properties:{
CountryCode: {
bsonType: "string",
pattern: "^[+0-9]{2,4}$",
description: "must be an string and is required"
},
RegionCode: {
bsonType: "string",
pattern: "^[0-9]{3}$",
description: "must be an string and is required"
},
PhoneNumber:
{
bsonType: "string",
pattern: "^[0-9]{10}$",
description: "must be an string and is required"
}
}
}
}
},
Email:{
bsonType: "array",
description:"must be an array and is required",
required:["EmailID", "EmailType"],
properties:{
EmailID: {
bsonType: "number",
description:"must be an integer and is required"
},
EmailType:{
bsonType: "string",
pattern : "^([a-zA-Z0-9_\\-\\.]+)#([a-zA-Z0-9_\\-\\.]+)\\.([a-zA-Z]{2,5})$",
description: "must be a string and match regular expression pattern"
}
}
}
}
} }
} )
This is the document i am trying to insert
db.personprofiles.insert({
"PersonID": 1001,
"PersonName": "john simon",
"Gender": "Male",
"MartialStatus": "Single",
"DOB": "1997-8-1",
"Occupation": "Service",
"Noofchildren": 0,
"ProfileType": "Administrator",
"Purge": "yes",
"Addresses":[{
"AddressID": 1,
"AddressType":"Permanent",
"AddressLine1": "101/4",
"AddressLine2":"sundar nagar",
"Area": "kalina",
"Locality": "Santacruz(east)",
"City": "Mumbai",
"State": "Maharashtra",
"Country": "India",
"PostalCode": "400055"
}],
"ContactNumbers": [{
"ContactID":1,
"ContactType":"Home",
"ContactNumber":{
"CountryCode": "+91",
"RegionCode": "022",
"PhoneNumber": "700383555"
}
}],
Email:[{
EmailID: 1,
EmailType: "johnxyz#gmail.com"
}]
})
collection has been created successfully, the only issue i am having to insert a document. Unable to figure out error.
Someone can help me? Look like i forget something.

mongodb validation jsonschema + expressive query failing to insert document

So this is kind of a two part question. I have the follow validation schema defined:
db.createCollection(
"users", {
validator: {
$or: [{
company_name: {
$exists: true,
$type: "string"
},
type: {
$in: [
"creator"
],
$exists: true,
$type: "string"
}
},
{
firstname: {
$exists: true,
$type: "string",
},
lastname: {
$exists: true,
$type: "string",
},
type: {
$in: [
"user"
],
$exists: true,
$type: "string"
}
}
],
$jsonSchema: {
bsonType: "object",
required: [
"contacts",
"created",
"email",
"password",
],
properties: {
"contacts": {
bsonType: "array",
items: {
required: [
"email",
"name"
],
properties: {
"email": {
bsonType: "string",
description: "email of the other account"
},
"name": {
bsonType: "string",
description: "a way for the owner to easily identify"
},
}
}
},
"created": {
bsonType: "date",
description: "the date this account was created"
},
"email": {
bsonType: "string",
description: "owner's registered email"
},
"password": {
bsonType: "string",
description: "a hashed password"
},
"reset_code": {
bsonType: "string",
description: "part of the getter link to reset password"
},
}
}
}
}
)
and I'm trying to insert:
db.users.insert({
email: "sadf",
password: "asdf",
created: Date.now(),
contacts: [],
type: "user",
firstname: "test",
lastname: "test"
})
but it's giving me "Document failed validation"`
Problem turned out to be created with the date format. Instead of Date.now() I need to use new Date() or new ISODate()

sails js many to many associations not working

User.js
module.exports = {
tableName: 'user',
attributes: {
firstName: {
type: 'string',
},
lastName: {
type: 'string',
},
id: {
type: 'integer',
autoIncrement: true,
primaryKey: true
},
pets: { //integer
collection:'pet',
via:'owners'
}
}
};
================================
Pet.js
------
module.exports = {
tableName: 'pet',
attributes: {
id: {
type: 'integer',
autoIncrement: true,
primaryKey: true
},
breed: {
type: 'string',
},
type: {
type: 'string',
},
name: {
type: 'string',
},
owners: { //integer
collection:'user',
via:'pets'
}
}
};
But when i hit the blueprint api http://localhost:1337/user or http://localhost:1337/pet i dont get associated records
Here is the output in ARC or postman for user and pet
[
{
"pets": [],
"firstName": "a",
"lastName": "aa",
"id": 1
},
{
"pets": [],
"firstName": "b",
"lastName": "bb",
"id": 2
},
{
"pets": [],
"firstName": "c",
"lastName": "cc",
"id": 3
},
{
"pets": [],
"firstName": "d",
"lastName": "dd",
"id": 4
},
{
"pets": [],
"firstName": "e",
"lastName": "ee",
"id": 5
}
],
[
{
"owners": [],
"id": 1,
"breed": "dalmatian",
"type": "male",
"name": "tom"
},
{
"owners": [],
"id": 2,
"breed": "labrador",
"type": "female",
"name": "kg"
},
{
"owners": [],
"id": 3,
"breed": "doberman",
"type": "male",
"name": "tq"
},
{
"owners": [],
"id": 4,
"breed": "puppy",
"type": "male",
"name": "yu"
}
]
One to many association works fine but don't know what am i missing in many to many ?
i tried removing the id column from both the models but that didn't help
Try to add a pet to an owner with the add Blueprint API:
POST /:model/:id/:association/:fk
example :
http://localhost:1337/user/1/pets/2
This will add the pet with id 2 to the owner with id 1.