jsonschema mongodb not validate from .js file - javascript

I have mongo.js commands to create collection, here is in my .js file:
let organizationSchema = {
bsonType: "object",
properties: {
name: {
bsonType: "string",
description: "must be a string and required"
},
active: {
bsonType: "bool",
},
location: {
bsonType: "string",
},
contactInfo: {
// this one doest working for validate
pattern: "/^\+?\d+(?:-\d+)*(?:\(\d+\)-\d+)*$/"
},
createdBy: {
bsonType: "string",
},
createdAt: {
bsonType: "date",
},
updatedAt: {
bsonType: "date",
},
deletedAt: {
bsonType: "date",
},
}
}
db.runCommand( { collMod: "organizations", validator:{ $jsonSchema: organizationSchema }, validationLevel: "strict"} )
db.organizations.createIndex( { "name": 1, "createdBy": 1, "location": 1, "active": 1} )
then I run mongo "mongodb+srv://url" --username dev -p 'dev' < mongo.js
on the contactInfo I do pattern for validation phone number, but when I insert data on my MongoDB Client, with invalid phone number, it was inserted, is that something wrong on that syntax on my js should I use strings on each keys there ??
const r = /^\+?\d+(?:-\d+)*(?:\(\d+\)-\d+)*$/;
console.log(r.test("+1110988888"))
console.log(r.test("zzzzuuuu"))

Related

Mongoose update subdocument by key

for the following collection:
id: Number
name:[
new Schema({
language: { type: String, enum: ['en-US', 'fr-CA'] },
text: String,
},{ _id: false }
);
],
isActive: Boolean
and sample data like the following:
{
id:1,
"name": [
{"language": "en-US", "text": "Book"},
{"language": "fr-CA", "text": "livre"}
],
isActive:true
// and so many other fields
},
{
id:2,
"name": [
{"language": "en-US", "text": "Pen"}
],
isActive:true
// and so many other fields
}
I would like to update the document by Id:1 and change the text only for french, I tried by:
const input={ "id":"1", "isActive": false}
const name= { "name": [{"language": "fr-CA", "text": "Nouvel article"}]}
await langs.findByIdAndUpdate(
{ _id: input.id },
{ ...input, $addToSet: { name } },
{ new: true, upsert: true }
);
but the result is: (added another french item)
{
id:1,
"name": [
{"language": "en-US", "text": "Book"},
{"language": "fr-CA", "text": "livre"},
{"language": "fr-CA", "text": "Nouvel article"}
],
isActive:false
},
This is based on Brit comment:
https://mongoplayground.net/p/atlw5ZKoYiI
Please advise.
As long as that attribute already exists on the document, you can do something like:
Collection.findOneAndUpdate({id: 1}, {
$set: {
"name.$[elem]": name
}
},
{
arrayFilters: [ { "elem.language": name.language } ],
upsert: true,
new: true
})

AJV if-then-else conditional type based on enum

I've searched examples of using if-then-else in AJV schemas but haven't found a specific case where the property type and required list change based on the value of another property.
Case:
I need to upgrade userSchema so that if property role = superuser, then customer_id is both nullable and not required.
const userSchema: Schema<UserItem> = {
$schema: 'http://json-schema.org/draft-07/schema#',
type: 'object',
required: ['id', 'email', 'customer_id'],
additionalProperties: false,
properties: {
id: {
type: 'string',
format: 'uuid'
},
email: {
type: 'string',
format: 'email'
},
customer_id: {
type: 'string',
format: 'uuid'
},
role: {
anyOf: [
{ type: 'null' },
{ enum: Object.values(UserRole) }
]
}
}
}
I tried...
const userSchemaNullableCustomerId: Schema<UserItem> = {
...userSchema,
if: {
properties: {
role: { const: UserRole.Superuser }
}
},
then: {
properties: {
customer_id: {
anyOf: [
{ type: 'null' },
{ type: 'string', format: 'uuid' }
]
}
},
not: {
required: ['customer_id']
}
}
}
but it still complains that data.customer_id should be string. How can this be solved?
The following should be true:
// Valid
{
"id": "id",
"email": "foo#bar.com",
"role": "superuser",
"customer_id": null
},
{
"id": "id",
"email": "foo#bar.com",
"role": "superuser"
},
{
"id": "id",
"email": "foo#bar.com",
"role": "null",
"customer_id": 'some-uuid...'
},
{
"id": "id",
"email": "foo#bar.com",
"role": "user",
"customer_id": 'some-uuid...'
}
// Invalid
{
"id": "id",
"email": "foo#bar.com",
"role": "user",
"customer_id": null
},
{
"id": "id",
"email": "foo#bar.com",
"role": "user"
},
{
"id": "id",
"email": "foo#bar.com",
"role": "superuser",
"customer_id": 'nonUuidString'
}
After much experimentation, I discovered the trick is that the customer_id property has to be initialized as empty and the role property requires a dependency check.
...
properties: {
id: {
type: 'string',
format: 'uuid'
},
email: {
type: 'string',
format: 'email'
}
customer_id: {},
role: {
anyOf: [
{ type: 'null' },
{ enum: Object.values(UserRole) }
]
}
},
if: {
dependencies: { role: ['role'] },
properties: { role: { const: UserRole.Superuser } }
},
then: {
properties: { customer_id: { anyOf: [{ type: 'null' }, { type: 'string', format: 'uuid' }] } }
},
else: {
properties: { customer_id: { type: 'string', format: 'uuid' } }
}

MongoDB E11000 duplicate key error collection (duplicate key error)

I am trying to write an array to my mongodb and i keep getting this error...
UnhandledPromiseRejectionWarning: MongoError: E11000 duplicate key
error collection: test.strains index: id dup key: { _id:
ObjectId('5ec18ebc5c7b4f6149499d93') }
adding to db
const upsertStrain = async (strainObj) => {
const DB_URL =
"mongodb_url";
if (mongoose.connection.readyState == 0) {
mongoose.connect(DB_URL);
}
// if this strain exists, update the entry, don't insert
let conditions = { name: strainObj.name };
let options = { upsert: true, new: true, setDefaultsOnInsert: true };
await Strain.findOneAndUpdate(
conditions,
strainObj,
options,
(err, result) => {
if (err) throw err;
}
);
};
calling function
for(strain of strains) {
await upsertStrain({
name: strain.name,
mood: strain.mood,
dateCrawled: new Date(),
});
}
Schema:
let strainSchema = new mongoose.Schema(
{
name: {
type: String,
sparse: true,
unique: true
},
mood: {
type: String,
sparse: true,
unique: false
},
dateCrawled: {
type: Date,
sparse: true
},
}
);
The the array that i am trying to add looks like this...
[
{
"name": "Blackberry Hashplant",
"mood": "Sleepy"
},
{
"name": "Walrus Kush",
"mood": "Happy"
},
{
"name": "Cherry Cheesecake",
"mood": "Sleepy"
},
{
"name": "Gorilla Cake",
"mood": "Hungry"
},
{
"name": "Divorce Cake",
"mood": ""
},
{
"name": "Biscotti",
"mood": ""
},
{
"name": "Kush Mints",
"mood": ""
},
{
"name": "Cookie Dawg",
"mood": ""
},
{
"name": "Cake Batter",
"mood": ""
},
{
"name": "Crazy Glue",
"mood": ""
},
{
"name": "Grapefruit Sour Dream",
"mood": ""
},
{
"name": "Gelato Cake",
"mood": ""
},
{
"name": "Garanimals",
"mood": ""
}
]
please help...been stuck on this for a while

Trouble using mongoose 'Populate'

Good Morning,
So I seem to have an issue with populating my fields with Node.js and Mongoose. It is just turning up a blank array:
Result: (JSON)
[
{
"courseassignments": [],
"_id": "5db56ceb4cc2c92824ae2651",
"name": "Garibaldi",
"website": "www.garibaldi.org.uk",
"__v": 0
},
{
"courseassignments": [],
"_id": "5db56d074cc2c92824ae2652",
"name": "Kirk Hallam",
"website": "www.kirkhallamacademy.co.uk",
"__v": 0
}
]
Below is the code I am using the call the populate function, plus the models that I am using for each of the data. It is very weird.
*school.js (Model) *
// Require modules within our file:
const mongoose = require('mongoose')
const schoolSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true,
trim: true
},
website: {
type: String,
required: true,
trim: true
},
logo: {
type: Buffer
},
courseassignments: [{
type: mongoose.Schema.Types.ObjectID,
ref: 'CourseAssignment'
}]
})
// Export the user to use within other files:
const school = mongoose.model('School', schoolSchema)
module.exports = school
courseassignment.js (Model)
// Require modules within our file:
const mongoose = require('mongoose')
const courseAssignmentSchema = new mongoose.Schema({
school: {
type: mongoose.Schema.Types.ObjectID,
required: true,
ref: 'School'
},
course: {
type: mongoose.Schema.Types.ObjectID,
required: true,
ref: 'Course'
}
})
// Export the user to use within other files:
const courseAssignment = mongoose.model('CourseAssignment', courseAssignmentSchema)
module.exports = courseAssignment
* Code to fetch data: (within app.js)*
router.get('/school', async (req, res) => {
const schools = await School.find({}).populate({ path: 'courseassignments' })
res.send(schools)
})
I would remove the courseassigment ref from the School model, and would take advantage of virtual populate.
So here are the steps:
school.js (school model - as you see I removed courseassignments ref, and added options for virtual features)
const mongoose = require('mongoose')
const schoolSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true,
trim: true
},
website: {
type: String,
required: true,
trim: true
},
logo: {
type: Buffer
}
}, {
toJSON: { virtuals: true },
toObject: { virtuals: true }
})
schoolSchema.virtual("courseassignments", {
ref: "CourseAssignment",
foreignField: "school",
localField: "_id"
})
const school = mongoose.model('School', schoolSchema)
module.exports = school
At this point when you hit the schools endpoint, your response will be like this.
( I only show one item to be short.)
[
{
"_id": "5db5a809cfc9951770d5078a",
"name": "school 1",
"website": "school 1 website",
"__v": 0,
"courseassignments": [
{
"_id": "5db5a892cfc9951770d50790",
"school": "5db5a809cfc9951770d5078a",
"course": "5db5a847cfc9951770d5078d",
"__v": 0
},
{
"_id": "5db5a89ccfc9951770d50791",
"school": "5db5a809cfc9951770d5078a",
"course": "5db5a851cfc9951770d5078e",
"__v": 0
},
{
"_id": "5db5a8a1cfc9951770d50792",
"school": "5db5a809cfc9951770d5078a",
"course": "5db5a858cfc9951770d5078f",
"__v": 0
}
],
"id": "5db5a809cfc9951770d5078a"
},
...
...
]
And if you also want to access to the Course name (which I think would be good),
courseassigment.js
const mongoose = require('mongoose')
const courseAssignmentSchema = new mongoose.Schema({
school: {
type: mongoose.Schema.Types.ObjectID,
required: true,
ref: 'School'
},
course: {
type: mongoose.Schema.Types.ObjectID,
required: true,
ref: 'Course'
}
}, {
toJSON: { virtuals: true },
toObject: { virtuals: true }
})
courseAssignmentSchema.pre(/^find/, function (next) {
this.populate({
path: 'course'
});
next();
});
// an index may be required like this
//courseAssignmentSchema.index({ school: 1, course: 1 }, { unique: true });
const courseAssignment = mongoose.model('CourseAssignment', courseAssignmentSchema)
module.exports = courseAssignment
And with this the result will contain the course related fields like course name.
[[
{
"_id": "5db5a809cfc9951770d5078a",
"name": "school 1",
"website": "school 1 website",
"__v": 0,
"courseassignments": [
{
"_id": "5db5a892cfc9951770d50790",
"school": "5db5a809cfc9951770d5078a",
"course": {
"_id": "5db5a847cfc9951770d5078d",
"name": "course 1",
"__v": 0
},
"__v": 0,
"id": "5db5a892cfc9951770d50790"
},
{
"_id": "5db5a89ccfc9951770d50791",
"school": "5db5a809cfc9951770d5078a",
"course": {
"_id": "5db5a851cfc9951770d5078e",
"name": "course 2",
"__v": 0
},
"__v": 0,
"id": "5db5a89ccfc9951770d50791"
},
{
"_id": "5db5a8a1cfc9951770d50792",
"school": "5db5a809cfc9951770d5078a",
"course": {
"_id": "5db5a858cfc9951770d5078f",
"name": "course 3",
"__v": 0
},
"__v": 0,
"id": "5db5a8a1cfc9951770d50792"
}
],
"id": "5db5a809cfc9951770d5078a"
},
...
...
]
Docs:
https://mongoosejs.com/docs/tutorials/virtuals.html
https://mongoosejs.com/docs/populate.html#populate-virtuals

Phrase Searching in ElasticSearch with English Analyzer

I'm currently using elastic search and have several type of queries, among them I use the match_phrase query. The index I'm using this on uses an english analyzer for text messages. When I search for phrases I'm expecting exact results, but if my search term has an english word - like remove - it also marks words like "removed", "removing" "removes".
How do I prevent this with my phrase matching? Is there a better option other than match_phrase for queries like this?
Is this possible without changing the analyzer? Below is my query (structured so it can do other things):
query: {
fields : ['_id', 'ownerId'],
from: 0,
size: 20,
query: {
filtered: {
filter: {
and: [group ids]
},
query: {
bool: {
must: {
match_phrase: {
text: "remove"
}
}
}
}
}
}
}
And here is my index:
[MappingTypes.MESSAGE]: {
properties: {
text: {
type: 'string',
index: 'analyzed',
analyzer: 'english',
term_vector: 'with_positions_offsets'
},
ownerId: {
type: 'string',
index: 'not_analyzed',
store: true
},
groupId: {
type: 'string',
index: 'not_analyzed',
store: true
},
itemId: {
type: 'string',
index: 'not_analyzed',
store: true
},
createdAt: {
type: 'date'
},
editedAt: {
type: 'date'
},
type: {
type: 'string',
index: 'not_analyzed'
}
}
}
You can use multi-fields to use a field in different ways(one for exact match and one for partial match etc).
You can get rid of stemming with standard analyzer which is also a default analyzer. You could create your index with following mapping
POST test_index
{
"mappings": {
"test_type": {
"properties": {
"text": {
"type": "string",
"index": "analyzed",
"analyzer": "english",
"term_vector": "with_positions_offsets",
"fields": {
"standard": {
"type": "string"
}
}
},
"ownerId": {
"type": "string",
"index": "not_analyzed",
"store": true
},
"groupId": {
"type": "string",
"index": "not_analyzed",
"store": true
},
"itemId": {
"type": "string",
"index": "not_analyzed",
"store": true
},
"createdAt": {
"type": "date"
},
"editedAt": {
"type": "date"
},
"type": {
"type": "string",
"index": "not_analyzed"
}
}
}
}
}
After that whenever you want exact match you need to use text.standard and when you want to perform stemming(want to match removed removes) you could revert to text
You could also update the current mapping but you would have to reindex your data in both cases.
PUT test_index/_mapping/test_type
{
"properties": {
"text": {
"type": "string",
"index": "analyzed",
"analyzer": "english",
"term_vector": "with_positions_offsets",
"fields": {
"standard": {
"type": "string"
}
}
}
}
}
Does this help?

Categories