Planning to use AJV
for validating user inputs. AJV needs data model JSON Schema to validate user inputs. So, we need to derive JSON Schema from Sequelize model. Is there a way to get JSON schema from Sequelize model programatically?
A late answer, but I ended up creating sequelize-to-json-schema to solve this for our needs.
It offers more customisation in terms of which attributes you include in your schema and adding virtual attributes that might be used by your create method or similar.
Example
// assuming you have a user model with the properties
// name (string) and status (enum: real, imagined)
const schemaFactory = require('sequelize-to-json-schema');
const factory = new SchemaFactory({
customSchema: {
user: {
name: { description: "The user's name" },
status: { description: 'Was it all just a dream?' },
},
}
hrefBase: 'http://schema.example',
});
const schemaGenerator = factory.getSchemaGenerator(User);
const schema = schemaGenerator.getSchema();
// Results in
schema = {
{
title: 'User',
'$id': 'http://schema.example/user.json',
type: 'object',
'$schema': 'http://json-schema.org/draft-06/schema#',
properties: {
name: {
'$id': '/properties/fullname',
type: 'string',
examples: [],
title: 'Name',
description: "The user's name",
},
status: {
'$id': '/properties/status',
type: 'string',
examples: ['REAL', 'IMAGINED'],
enum: ['REAL', 'IMAGINED'],
title: 'Status',
description: 'Was it all just a dream?'
}
}
}
}
Note: sequelize-to-json-schema generates draft-06 schemas, to use that with AJV, their README says you'll need to do:
ajv.addMetaSchema(require('ajv/lib/refs/json-schema-draft-06.json'));
Related
I am using migrate-mongo for managing my database migration and I am trying to create a new migration that create a collection with a validator and insert values in it. I want to use a UUID for the _id property and I am using the uuid-mongodb library to generate it. My problem is that I am not able to set the bsonType of my _id in the validator without causing the data insertion failure. Is there any way to make sure that the id of the documents inserted in the collection is a UUID? I know that mongoose could help me to solve this issue, but I would like the validation to be done at the database level. Also, when I do not specify the _id's bsonType in the validator, the insertion works, it fails validation only when I specify it.
Here is my migration code
const MUUID = require("uuid-mongodb");
module.exports = {
async up(db) {
//Will use https://docs.mongodb.com/manual/tutorial/model-tree-structures-with-materialized-paths/
await db.createCollection("itemCategories", {
validator: {
$jsonSchema: {
required: ["name"],
bsonType: "object",
properties: {
_id: {"object"}, //I also tried with binData
name: {
bsonType: "string",
maxLength: 50,
},
path: {
bsonType: ["string", "null"],
pattern: "^,([^,]+,)+$"
}
},
additionalProperties: false,
}
},
});
await db.collection("itemCategories").createIndex({"name": 1}, {unique: true});
await db.collection("itemCategories").insertMany([
{_id: MUUID.v4(), name: "Sport", path: null},
{_id: MUUID.v4(), name: "Tool", path: null},
{_id: MUUID.v4(), name: "Entertainment", path: null}
]);
},
async down(db) {
await db.dropCollection("itemCategories");
}
};
And here is the error I get when running it
ERROR: Could not migrate up 20210627041314-create-categories.js: Document failed validation BulkWriteError: Document failed validation
at OrderedBulkOperation.handleWriteError (C:\Users\username\projectDirectory\node_modules\mongodb\lib\bulk\common.js:1352:9)
at resultHandler (C:\Users\username\projectDirectory\node_modules\mongodb\lib\bulk\common.js:579:23)
at handler (C:\Users\username\projectDirectory\node_modules\mongodb\lib\core\sdam\topology.js:943:24)
at C:\Users\username\projectDirectory\node_modules\mongodb\lib\cmap\connection_pool.js:350:13
at handleOperationResult (C:\Users\username\projectDirectory\node_modules\mongodb\lib\core\sdam\server.js:558:5)
at MessageStream.messageHandler (C:\Users\username\projectDirectory\node_modules\mongodb\lib\cmap\connection.js:281:5)
at MessageStream.emit (events.js:321:20)
at processIncomingData (C:\Users\username\projectDirectory\node_modules\mongodb\lib\cmap\message_stream.js:144:12)
at MessageStream._write (C:\Users\username\projectDirectory\node_modules\mongodb\lib\cmap\message_stream.js:42:5)
at doWrite (_stream_writable.js:441:12)
Assuming collection name user_demo and having 2 fields only ( _id, name )
Create collection Schema Validator
db.createCollection("user_demo", {
validator: {
$jsonSchema: {
bsonType: "object",
title: "User Object Validation",
required: [ "_id","name"],
properties: {
_id: {
bsonType: "binData",
description: "Unique identifier,I am using it instead of objectId for portibility",
pattern: "^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$"
},
name: {
bsonType: "string",
description: "'name' must be a string and is required",
maxLength: 50,
minLength: 1
}
}
}
}
} )
Insert data in collection
a) If you already have a uuid4
db.user_demo.insertOne({_id: UUID("a5750db3-1616-45a4-bf92-6a44c3e67342"), name:"shiva"})
b) If you want random uuid4
db.user_demo.insertOne({_id: UUID(), name:"explore"})
Tested with mongo version 6.0.3
I'm working on realm to make it work offline as local db in Electron. Now I want to make join(aggregation), so I defined a relationship between two schema's but then the data is not getting synced. It would be great to get help.
Here's my schema:
const articleMetaSchema = {
name: 'articlemeta',
properties: {
_id: 'objectId?',
catalog_id: 'objectId?',
content: 'objectId?',
createdAt: 'date?',
description: 'string?',
main_image_url: 'string?',
origin_type: 'string?',
pub_date: 'date?',
publication: 'objectId?',
sub_title: 'string?',
title: 'string?',
updatedAt: 'date?',
url: 'string?'
},
primaryKey: '_id'
}
const articleSchema = {
name: 'article',
properties: {
_id: 'objectId?',
active_search: 'bool?',
article_meta: 'articlemeta?',
catalog_id: 'objectId?',
content: 'objectId?',
createdAt: 'date?',
flagged: 'bool?',
owner_id: 'objectId?',
rating: 'int?',
read: 'bool?',
status: 'string?',
status_updated_at: 'date?',
updatedAt: 'date?'
},
primaryKey: '_id'
}
config = {
schema,
path: getDBPath(),
sync: {
user: app.currentUser,
partitionValue: new ObjectID(getCatalogId()),
error: (error) => {
console.log(error.name, error.message)
}
}
}
let realm = await Realm.open(config)
// query
I want to query and get the article result and in article schema we have defined a key 'article_meta' which is and objectId. Now I want article result with all the properties with article_meta as an object fetched on the basis of (article.article_meta = articlemeta._id)
Expected result:
[{ _id: '1', catalog_id: '', content: '', createdAt: '', main_image_url: '', origin_type: '', pub_date: '', publication: '', sub_title: '', title: '', updatedAt: '', url: '', article_meta: {} }, {..}, {..}]
Edit:
As pointed out in a comment, there is a bit of ambiguity in the question as the text doesn't match the code. The code shows the parent object article having a child object articlemeta which is generally correct and how relationships are built in Realm. Accessing child objects by querying their objectId is generally unnecessary if the relationship is built in a "Realmy" way as shown in this answer.
-- Original Answer below --
Realm objects can reference 'joined' realm objects via dot notation. from the docs
Filter on Related and Embedded Object Properties
To filter a query based on a property of an embedded object or a
related object, use dot-notation as if it were in a regular, nested
object.
So for example, a Person object has a Dog object as one of it's properties and you want to get that pesons' dogs name.
Let's get a specific person by their _id
const person = realm.objectForPrimaryKey("Person", 1234)
if we then want the persons dogs name
const dogName = person.dog.dog_name
In your case you can get a specific article, and then access all of the articlemeta properties using dot notation
const article = realm.objectForPrimaryKey("article", 1234)
and then access the rest of the properties through dot notiation
const id = article._id
const catalogId = article.catalog_id
const mainImageUrl = article.article_meta.main_image_url
As you can see, a query is not needed (for this example) as the Realm objects know their own child properties and can be accessed via dot notation.
so basically I have this and I am trying to update the STATUS part of an array.
However, everything I try does nothing. I have tried findOneAndUpdate also. I am trying to identify the specific item in the array by the number then update the status part of that specific array
(Sorry for formatting, I have no idea how to do that on the site yet ...) (Full code can be found here: https://sourceb.in/0811b5f805)
Code
const ticketObj = {
number: placeholderNumber,
userID: message.author.id,
message: m.content,
status: 'unresolved'
}
let tnumber = parseInt(args[1])
let statuss = "In Progress"
await Mail.updateOne({
"number": tnumber
}, { $set: { "status": statuss } })
Schema
const mongoose = require('mongoose')
const mailSchema = new mongoose.Schema({
guildID: { type: String, required: true },
ticketCount: { type: Number, required: true },
tickets: { type: Array, default: [] }
}, { timestamps: true });
module.exports = mongoose.model('Mail', mailSchema)
You need to use something like Mail.updateOne({"guildID": message.guild.id}, {$set: {`tickets.${tnumber}.status`: statuss}})
or for all objects in array:
Mail.updateOne({"guildID": message.guild.id}, {$set: {'tickets.$[].status': statuss}})
Also, you need to create a schema for the tickets, as it is described in docs:
one important reason to use subdocuments is to create a path where there would otherwise not be one to allow for validation over a group of fields
I wrote the below schema. But while running it gives me an error -- throw new TypeError(Invalid schema configuration: \${name}` is not ` + --- can someone help me why does this error comes?. Below shown is my schema, can someone figure out if my schema have some mistakes.
$
jsonSchema: {
bsonType: "object",
properties: {
name: {
bsonType: "string",
description: "must be a string"
},
teacherId: {
bsonType: "objectId",
description: "must be a Object ID"
}
}
}
You are using the MongoDB native syntax to define your schema. If you are using mongoose, mongoose has its own syntax. Please refer to mongoose documentation. For your schema it would be:
var studentSchema = new mongoose.Schema({
name: { type: String },
teacherId: { type: mongoose.Schema.Types.ObjectId }
})
Mmm how I resolved this was to use the Types defined within the Schema import. For some reason importing the Types module directly didn't work -- well for Map at least.
import { Document, Schema, model, Query, Model } from "mongoose";
{
...
metadata: { type: Schema.Types.Map, of: String, required: false },
}
"name" is not a valid attribute anymore in mongoose schemas. Replace it with "type" it will work.
Oh man this is related to wrong name of the schema fields in my case:
participants: [
{
typeof: Types.ObjectId,
ref: "User",
},
]
and I got same error as you. I was so struggled and this typeof - type f* VS code I'm using it at the moment cause this computer can't deal with jet brains, :(
I had a mongoose object at server side:
...
item = {
name: "Test",
id: 1
}
// item's an mongo schema
// id and name defined in model as String and Number
Then I add into item new field mentions:
item.mention = [{ id: 1, ... }]
But I can't get mention at client side.
My response code:
res,json({ status: 1, message: 'success', data: item })
The response was data: { name: "Test", id: 1 }
I don't want to add mention into my mongo schema.
So, what's my problem?
How can I fix that?
Thanks!
The problem is that mongoose will not allow you to modify item document if a field you are trying to set value for does not exists in schema of the model, which is your case is "mention" field.
If you want to add "mention" field you have to access _doc field of the item document. Example :-
item._doc.mention = [{ id: 1, ... }]
Response should be:-
{ name: "Test", id: 1, mention: [{ id: 1, ... }] }
It is not usually recommended, but if you want to change schema like this.
you should pass strict false parameter to mongoose model while setting schema like this.
var ModelSchema = new Schema({
number: String,
}, { strict: false });
Now, it will change schema if you give any other parameter which is not in schema, it will add that one.