I'd like to validate my documents in MongoDB. As described here , the validator query can be specified during the createCollection
command.
My application is a simple todo list, so the collection list
has many documents like this:
{
"name": "the list name",
"color: "red",
"items": [
{
"title": "item1",
"isDone": false,
"text": "the item description"
}
]
}
How can I sure that all documents has this shape?
EDIT: I'm using MongoDB 3.4
You can't.
There's no way to avoid new property. If this needed is required, you should consider to migrate a SQL solution.
Otherwise, your document will contain at least those members and those members will be validated.
I assume that all fields are mandatory, so no list can be added neither without a color nor having an item without a title.
I assume that color is an enum also.
{
validator: {
name: { $type: 'string', $exists: true },
color: { $in: ['red', 'green', 'blue'], $exists: true },
items: { $exists: true },
$or: [
{ items: { $size: 0 } },
{
$and: [
{ 'items.0': { $exists: true } },
{
items: {
$not: {
$elemMatch: {
$not: { $type: 'object' }
}
}
}
},
{
items: {
$not: {
$elemMatch: {
$or: [
{ title: { $not: { $type: 'string' } } },
{ title: { $not: { $exists: true } } },
{ isDone: { $not: { $type: 'bool' } } },
{ isDone: { $not: { $exists: true } } },
{ text: { $not: { $type: 'string' } } },
{ text: { $not: { $exists: true } } }
]
}
}
}
}
]
}
]
}
}
Explanation
The solution is based on the second-order logic . Indeed, the assertion "all elements have to match A" is equal to "no element should match !A" or, if you have many queries, "all element have to match A and B" becomes "no element should match !A or !B", where !
means the negation of the following predicate.
Long explanation
The first two query items assert that all documents have name
and color
, the third one the items
property always exists. NB: in the third query property, there's not the $type
check because mongodb treats the array
type in a odd way .
The $or
operation below is needed because the item
property can be empty or filled with some data.
First clause of $or
checks if the array is empty. If the array is not empty, three checks should be done:
So the first $and
operator element checks that at least one element is present. The second one checks that all array elements are objects. The third one asserts that all objects have a specified shape.
In mongodb, there's an operator for checking if all array elements match a query. So, using the second order logic, the check should be turned over.
In fact, the last two clauses of $and
check that no elements match none of the described query. Each sub query is the negate of a wanted query.
Example
fails
{ name: 'foo' }
{ color: 'red' }
{ color: 'unknown color' }
{ name: 'foo', color: 'red' }
{ name: 'foo', color: 'red', item: 3 }
{ name: 'foo', color: 'red', item: [ 3 ] }
{ name: 'foo', color: 'red', item: [ { } ] }
{ name: 'foo', color: 'red', item: [ { title: 'ww' } ] }
{ name: 'foo', color: 'red', item: [ { title: 'ww', isDone: false } ] }
{ name: 'foo', color: 'red', item: [ { title: 44, isDone: false, text: 'the text' } ] }
pass
{ name: 'foo', color: 'red', items: [ ] },
{ name: 'foo', color: 'red', items: [ ] },
{ name: 'foo', color: 'red', items: [ { title: 'the title', isDone: false, text: 'the text' }, { title: 'the title1', isDone: true, text: 'the text' } ] }
This solution is still valid using MongoDB 3.2
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.