Mongo shell insert statement for array of objects failing validation - mongodb

I have the below collection schema which has been created and passed the validation:
db.createCollection("Settings",
{ validator: { $or:
[
{cID:{ $type: "int" } },
{sID : { $type: "int" } },
{default :
[
{default1:
{
name : { $type: "string" },
priority : { $type: "string" },
autoMoveToCompleted : { $type: "string" },
notifyInAdvance : { $type: "string" }
}
},
{default2 :
{
name : { $type: "string" },
priority : { $type: "string" },
autoMoveToCompleted : { $type: "string" }
}
}
]
},
{applied: [
{applied1:
{ name : { $type: "string" },
priority : { $type: "string" },
autoMoveToCompleted : { $type: "string" },
notifyInAdvance : { $type: "string" }
}
},
{applied2 :
{
name : { $type: "string" },
priority : { $type: "string" },
autoMoveToCompleted : { $type: "string" }
}
}
]}
]
}
}
)
But, I cannot generate a valid insert statement for above schema as it involves arrays. I tried to find the resolution in google but could not find anything related to prepare an insert statement similar to above schema.
Please help generating the insert statement for this schema for one mongo document, to run the insert query on mongo shell.

I only can propose to change validation schema like:
db.createCollection("Settings",
{ validator: { $or:
[
{cID:{ $type: "int" } },
{sID : { $type: "int" } },
{"default.default1.name" : { $type: "string" },
"default.default1.priority" : { $type: "string" },
"default.default1.autoMoveToCompleted" : { $type: "string" },
"default.default1.notifyInAdvance" : { $type: "string" },
"default.default2.name" : { $type: "string" },
"default.default2.priority" : { $type: "string" },
"default.default2.autoMoveToCompleted" : { $type: "string" }
}
]
}
}
)
It must work

Related

MongoDB query to replace NULL values

I'm currently working with a MongoDB database and I have fields that have a value of NULL is there a way to run a query that will replace these NULL fields with a value of "Missing" instead?
An example of the document is:
{
"_id" : 1,
"Users" : [
{
"name" : "John Davies",
"age" : null,
"place_of_birth" : "Cardigan"
},
{
"name" : "Edward Jones",
"age" : null,
"place_of_birth" : null
},
{
"name" : "Daniel Rhys",
"age" : NumberLong(63),
"place_of_birth" : "Cardigan"
},
{
"name" : null,
"age" : NumberLong(61),
"place_of_birth" : "Cardigan"
},
{
"name" : "John Davies ",
"age" : null,
"place_of_birth" : "Cardigan"
}
]
}
Demo - https://mongoplayground.net/p/dsI5G6zfbLr
Use $[]
db.collection.update(
{},
{ $set: { "Users.$[u].age": "missing" } },
{ arrayFilters: [ { "u.age": null } ], multi: true}
)
Combine multiple queries into 1 using db.collection.bulkWrite
db.collection.bulkWrite( [
{ updateMany :
{
"filter": {},
"update": { $set: { "Users.$[u].age": "missing" } },
"arrayFilters": [ { "u.age": null } ],
}
},
{ updateMany :
{
"filter": {},
"update": { $set: { "Users.$[u].name": "missing" } },
"arrayFilters": [ { "u.name": null } ],
}
},
{ updateMany :
{
"filter": {},
"update": { $set: { "Users.$[u].place_of_birth": "missing" } },
"arrayFilters": [ { "u.place_of_birth": null } ],
}
}
] )
Update for MongoDB Version 3.2+
while (db.collection.find({$or:[{"Users.age":null},{"Users.name":null},{"Users.place_of_birth":null}]}).count()) {
db.collection.bulkWrite( [
{ updateMany :
{
"filter": { "Users.age": null },
"update": { $set: { "Users.$.age": "missing" } }
}
},
{ updateMany :
{
"filter": { "Users.name": null },
"update": { $set: { "Users.$.name": "missing" } },
}
},
{ updateMany :
{
"filter": { "Users.place_of_birth": null },
"update": { $set: { "Users.$.place_of_birth": "missing" } },
}
}
] )
}
Try update with aggregation pipeline starting from MongoDB 4.2,
$map to iterate loop of Users array
$objectToArray to convert current object in $map to array key-value pair
$map to iterate loop of above converted array
$ifNull to check if value is null then replace Missing otherwise remain same
$arrayToObject convert above key-value array to object format
db.collection.update({},
[{
$set: {
Users: {
$map: {
input: "$Users",
in: {
$arrayToObject: {
$map: {
input: { $objectToArray: "$$this" },
in: {
k: "$$this.k",
v: { $ifNull: ["$$this.v", "Missing"] }
}
}
}
}
}
}
}
}],
{ multi: true }
)
Playground
MongoDB version 3.2 or above:
set default value for replacement in variable nullReplace
find() query to get all documents from your collection and loop through forEach
for loop of user object and check condition if value is null then replace nullReplace variable
return user oibject
updateOne() to update updated Users array in your collection
var nullReplace = "Missing";
db.collection.find({}).forEach(function(doc) {
var Users = doc.Users.map(function(u) {
for (var u in userObj) {
if (userObj[u] === null) userObj[u] = nullReplace;
}
return userObj;
})
db.collection.updateOne({ _id: doc._id }, { $set: { Users: Users } });
});

Query datevalue of a inner Array element

Need help with some MongoDB query:
The document I have is below and I am trying to search based on 2 conditions
The meta.tags.code = "ABC"
Its LastSyncDateTime should
meta.extension.value == "" (OR)
the meta.extension.value is less than meta.lastUpdated
Data :
{
"meta" : {
"extension" : [
{
"url" : "LastSyncDateTime",
"value" : "20190206-00:49:25.694"
},
{
"url" : "RetryCount",
"value" : "0"
}
],
"lastUpdate" : "20190207-01:21:41.095",
"tags" : [
{
"code" : "ABC",
"system" : "type"
},
{
"code" : "XYZ",
"system" : "SourceSystem"
}
]
}
}
Query:
db.proc_patients_service.find({
"meta.tags.code": "ABC",
$or: [{
"meta.extension.value": ""
}, {
$expr: { "$lt": [{ "mgfunc": "ISODate", "params": [{ "$arrayElemAt": ["$meta.extension.value", 0] }] }, { "mgfunc": "ISODate", "params": ["$meta.lastUpdate"] }] }
}]
})
But it is only fetching ABC Patients whose LastSyncDateTime is empty and ignores the other condition.
Using MongoDB Aggregation, I have converted your string to date with operator $dateFromString and then compare the value as per your criteria.
db.proc_patients_service.aggregate([
{ $match: { "meta.tags.code": "ABC", } },
{ $unwind: "$meta.extension" },
{
$project: {
'meta.tags': '$meta.tags',
'meta.lastUpdate': { '$dateFromString': { 'dateString': '$meta.lastUpdate', format: "%Y%m%d-%H:%M:%S.%L" } },
'meta.extension.url': '$meta.extension.url',
'meta.extension.value': {
$cond: {
if: { $ne: ["$meta.extension.value", "0"] }, then: { '$dateFromString': { 'dateString': '$meta.extension.value', format: "%Y%m%d-%H:%M:%S.%L" } }, else: 0
}
}
}
},
{
$match: {
$or: [
{ "meta.extension.value": 0 },
{ $expr: { $lt: ["$meta.extension.value", "$meta.lastUpdate"] } }
]
}
},
{
$group: { _id: '_id', 'extension': { $push: '$meta.extension' }, "lastUpdate": { $first: '$meta.lastUpdate' }, 'tags': { $first: '$meta.tags' } }
},
{
$project: { meta: { 'extension': '$extension', lastUpdate: '$lastUpdate', 'tags': '$tags' } }
}
])

Aggregate and $ne Not Working as Expected

I'm trying the aggregation below, but do not appear to be getting the expected result using $ne and null.
I have tried other solutions like using a combination of $cond, $not, and $eq to no avail. Using $gt:[ "$unloadeddate", null] seems to give some results, but that does not seem to be proper syntax and I am concerned it's not trustworthy to run properly over entire dataset.
Also, querying as follows:
db.getCollection('esInvoices').find({"esBlendTickets.loadeddate":{$ne:null}})
... returns results so not sure why the same query in the aggregate function not working.
Any help is appreciated!!
The first part works...
"unloadeddate": { "$switch": {
branches:[ {
case: {
"$ne":[ "$esBlendTickets.ticketdate", null]
},
then: "$esBlendTickets.ticketdate"
}, {
case: {
"$ne":[ "$esDeliveryTickets.ticketdate", null]
},
then: "$esDeliveryTickets.ticketdate"
}],
default: null
}
},
"loadeddate": { "$switch": {
branches:[ {
case: {
"$ne":[ "$esBlendTickets.loadeddate", null]
},
then: "$esBlendTickets.loadeddate"
}, {
case: {
"$ne":[ "$esDeliveryTickets.loadeddate", null]
},
then: "$esDeliveryTickets.loadeddate"
}],
default: null
}
},
... but this second part (essentially the same logic except for the resulting value) does not work as expected...
"stagename": { "$switch": {
branches:[ {
case: {
"$ne":[ "$esDeliveryTickets.ticketdate", null]
},
then: "Invoiced"
}, {
case: {
"$ne":[ "$esBlendTickets.ticketdate", null]
},
then: "Invoiced"
}],
default: "Invoiced-Only"
}
}
Complete Aggregation:
db.esInvoices.aggregate([ {
$addFields: {
// A single invoice will not have both a blend ticket and delivery ticket associated so looping tough each case should work.
"unloadeddate": { "$switch": {
branches:[ {
case: {
"$ne":[ "$esBlendTickets.ticketdate", null]
},
then: "$esBlendTickets.ticketdate"
}, {
case: {
"$ne":[ "$esDeliveryTickets.ticketdate", null]
},
then: "$esDeliveryTickets.ticketdate"
}],
default: null
}
},
"loadeddate": { "$switch": {
branches:[ {
case: {
"$ne":[ "$esBlendTickets.loadeddate", null]
},
then: "$esBlendTickets.loadeddate"
}, {
case: {
"$ne":[ "$esDeliveryTickets.loadeddate", null]
},
then: "$esDeliveryTickets.loadeddate"
}],
default: null
}
},
"stagedate": "$InvoiceHeader.InvDate",
"stagename": { "$switch": {
branches:[ {
case: {
"$ne":[ "$esDeliveryTickets.ticketdate", null]
},
then: "Invoiced"
}, {
case: {
"$ne":[ "$esBlendTickets.ticketdate", null]
},
then: "Invoiced"
}],
default: "Invoiced-Only"
}
}
}}])
Think I just ran into the same problem you were having. Using Mongo 3.4 at the moment. From what I can tell the query $ne behaves differently from the aggregate $ne when you are comparing it to null. Threw me off for a little bit.
Specifically, a { $ne: [ '$field', null ] } predicate in the aggregate pipeline will return true when $field is undefined.
However, when using queries (not $aggregate), the { field: { $ne: null } predicate will return false for those same documents when $field is undefined.
My work-around was to use a $project with { field: { $ifNull: [ '$field': null ] } } in a prior step to turn undefined instances of that field into explicit nulls, which will then make the aggregate $ne work as I needed. For whatever reason, $ifNull works with null, undefined, and missing fields. Not sure why $ne is different.
Here's an example to reproduce.
db.test.insertMany([
{ a: 1, b: 'string' },
{ a: 2, b: null },
{ a: 3 },
])
db.test.find({ b: { $ne: null }}, { _id: 0 })
/*
returns:
{
"a" : 1.0,
"b" : "string"
}
*/
db.test.aggregate([
{ $project: {
_id: 0,
a: 1,
b: 1,
switched: { $switch: {
branches: [
{ case: { $ne: [ '$b', null ] }, then: 'cased' },
],
default: 'default',
}}
}}
])
/*
returns:
{
"a" : 1.0,
"b" : "string",
"switched" : "cased"
},
{
"a" : 2.0,
"b" : null,
"switched" : "default"
},
{
"a" : 3.0,
"switched" : "cased" <--
}
*/
The problem is missing fields are undefined while null fields are, well, nulls. When you write "$ne":[ "$esDeliveryTickets.ticketdate", null] you don't filter the former but only the latter.
But undefined is "smaller" than null. In order to filter them both you just need to make lte/gt instead of eq/ne. So this query returns all existing values:
See example:
db.test.insertOne(
{
"a" : 10,
"b" : null
})
...
db.getCollection('mytest').find( {},
{
"a" : { $lte : ["$a", null] },
"b" : { $lte : ["$b", null] },
"c" : { $lte : ["$c", null] },
})
// {
// "_id" : ObjectId("606724f38ec4d26b981b5a1c"),
// "a" : false,
// "b" : true,
// "c" : true
// }
In your case:
"stagename": { "$switch": {
branches:[ {
case: {
"$gt":[ "$esDeliveryTickets.ticketdate", null]
},
then: "Invoiced"
}, {
case: {
"$gt":[ "$esBlendTickets.ticketdate", null]
},
then: "Invoiced"
}],
default: "Invoiced-Only"
}
}
and this one returns only missing (or null) values:
"stagename": { "$switch": {
branches:[ {
case: {
"$lte":[ "$esDeliveryTickets.ticketdate", null]
},
then: "Invoiced"
}, {
case: {
"$lte":[ "$esBlendTickets.ticketdate", null]
},
then: "Invoiced"
}],
default: "Invoiced-Only"
}
}

How do I get the current validator of a mongo collection?

How do I get the current validator of a mongo collection?
Or is there no way, and I just need to overwrite it?
You can get it with the db.getCollectionInfos() method.
Example:
Create a collection in an empty database:
db.createCollection( "contacts",
{
validator: { $or:
[
{ phone: { $type: "string" } },
{ email: { $regex: /#mongodb\.com$/ } },
{ status: { $in: [ "Unknown", "Incomplete" ] } }
]
},
validationAction: "warn"
}
)
{ "ok" : 1 }
Run the command:
db.getCollectionInfos()[0].options.validator
Result:
{
"$or" : [
{
"phone": {
"$type": "string"
}
},
{
"email": {
"$regex": /#mongodb\.com$/
}
},
{
"status": {
"$in": [
"Unknown",
"Incomplete"
]
}
}
]
}
The validator is found as an object in the options object.

Mongodb document validation

I need your help on mongodb document validation. I am trying to create the mongodb document structure below:
db.createCollection("People2", {
validator: {
$and: [
{
Identity: {
Number: {
$type: "string",
$exists: true
},
Type: {
$type: "string",
$exists: true
}
}
},
{
"lastName": {
$type: "string",
$exists: true
}
},
{
"email": {
$type: "string",
$exists: true,
}
},
{
UserVerification: { $in: [ "Rejected", "Validated" ] }
}
]
}
})
Below command to test document insertion:
db.People2.insert(
{
IdentityCard: {
Number: "#1234",
Type: "typeA"
},
lastName: "toto",
email: "toto#mydom.com",
UserVerification: "Validated"
}
);
But a get this error:
WriteResult({
"nInserted" : 0,
"writeError" : {
"code" : 121,
"errmsg" : "Document failed validation"
}
})
I think The problem come from the object "Identity" validation declaration which is not correct.
Please try this and your statement is using IdentityCard but not Identity.
db.createCollection("People2", {
validator: {
$and: [
{
"IdentityCard.Number": {
$type: "string",
$exists: true
},
"IdentityCard.Type": {
$type: "string",
$exists: true
}
},
{
"lastName": {
$type: "string",
$exists: true
}
},
{
"email": {
$type: "string",
$exists: true,
}
},
{
UserVerification: { $in: [ "Rejected", "Validated" ] }
}
]
}
})