JSON schema for an array - arrays

I've a JSON Schema and a sample input. I need to write a generic schema which can handle the array regardless the length of the array. Currently, I need to write schema for each of the index in the array.
JSON Schema
{
"title":"Example",
"$schema":"http://json-schema.org/draft-04/schema#",
"type":"array",
"items":[
{
"oneOf":[
{
"multipleOf": 3
}
]
},
{
"oneOf":[
{
"multipleOf": 3
},
{
"multipleOf": 5
}
]
}
]
}
Sample Input
[
3,
5
]
I need a schema which can validate [1,3,5,6,3,5,4,......] (regardless the length)

If you put a schema directly in items, instead of using an array, then it applies to all array items:
{
"type": "array",
"items": {
"oneOf": [
{"multipleOf": 3},
{"multipleOf": 5}
]
}
}
If you want to describe an initial set of items with specific schemas, and all the following ones with a generic one, then use an array with items, and a schema in additionalItems:
{
"type": "array",
"items": [
{"multipleOf": 3},
...
],
"additionalItems": {
"oneOf": [
{"multipleOf": 3},
{"multipleOf": 5}
]
}
}

Related

json schema: how to specify schema for array with two object types and min/max occurance for each type

I would like to define a JSON schema for an array that can contain two different objects (type A and type B).
Only exactly the following combinations should be allowed:
[ A ]
[ A, B ]
[ B, A ]
[ B ]
my current schema draft
"myArray": {
"type": "array",
"minItems": 1,
"maxItems": 2,
"items": {
"oneOf": [
{
"title": "TypeA",
...
},
{
"title": "TypeB",
...
}
]
}
},
So far I have this schema, but this would also make an array with two times A valid.
How can I specify the above condition?

How would I use the value of one element in an array to access another array in React?

I have data from an API that is like:
[
{
"id": 1,
"name": "eventName1",
"related_events": [
2,
3
]
},
{
"id": 2,
"name": "eventName2",
"related_events": [
1,
3
]
},
{
"id": 3,
"name": "eventName3",
"related_events": [
1,
2,
4
]
}
]
Each element of the related_events array links to an id element. So, if the related_events has 2 and 3 then that means I have to call id 2 and 3.
If I want to get the name that corresponds to the related_events ids, how would I code it?
Example, if the related_events has 2 and 3 then I want to display the names "eventName2" and "eventName3".
You can search your data for the related_event ids by iterating through or by using Array.find function.
Something like this:
const testData = [
{
"id": 1,
"name": "eventName1",
"related_events": [
2,
3
]
},
{
"id": 2,
"name": "eventName2",
"related_events": [
1,
3
]
},
{
"id": 3,
"name": "eventName3",
"related_events": [
1,
2,
4
]
}
];
// the object to test from the data
const testObject = testData[0];
//we iterate over every id of related_events
testObject.related_events.forEach(targetId => {
//we search in the data for a matching element that corresponds to the related_event id
const targetElement = testData.find(element => {return element.id === targetId});
//check if that element actually exists, just to be sure
if (!!targetElement) {
console.log(targetElement.name);
}
else {
console.log("no matching object found");
}
});
There is really no difference when using React to just using basic javascript, as the logic is the same.

Reference value from positional element in array in update

Suppose I have a document that looks like this:
{
"id": 1,
"entries": [
{
"id": 100,
"urls": {
"a": "url-a",
"b": "url-b",
"c": "url-c"
},
"revisions": []
}
]
}
I am trying to add a new object to the revisions array that contains its own urls field. Two of the fields should be copied from the entry's urls, while the last one will be new. The result should look like this:
{
"id": 1,
"entries": [
{
"id": 100,
"urls": {
"a": "url-a",
"b": "url-b",
"c": "url-c"
},
"revisions": [
{
"id": 1000,
"urls": {
"a": "url-a", <-- copied
"b": "url-b", <-- copied
"c": "some-new-url" <-- new
}
}
]
}
]
}
I am on MongoDB 4.2+, so I know I can use $property on the update query to reference values. However, this does not seem to be working as I expect:
collection.updateOne(
{
id: 1,
"enntries.id": 100
},
{
$push: {
"entries.$.revisions": {
id: 1000,
urls: {
"a": "$entries.$.urls.a",
"b": "$entries.$.urls.b",
"c": "some-new-url"
}
}
}
}
);
The element gets added to the array, but all I see for the url values is the literal $entries.$.urls.a. value I suspect the issue is with combining the reference with selecting a specific positional array element. I have also tried using $($entries.$.urls.a), with the same result.
How can I make this work?
Starting from MongoDB version >= 4.2 you can use aggregation pipeline in updates which means your update part of query will be wrapped in [] where you can take advantage of executing aggregation in query & also use existing field values in updates.
Issue :
Since you've not wrapped update part in [] to say it's an aggregation pipeline, .updateOne() is considering "$entries.$.urls.a" as a string. I believe you'll not be able to use $ positional operator in updates which use aggregation pipeline.
Try below query which uses aggregation pipeline :
collection.updateOne(
{
id: 1,
"entries.id": 100 /** "entries.id" is optional but much needed to avoid execution of below aggregation for doc where `id :1` but no `"entries.id": 100` */,
}
[
{
$set: {
entries: {
$map: { // aggregation operator `map` iterate over array & creates new array with values.
input: "$entries",
in: {
$cond: [
{ $eq: ["$$this.id", 100] }, // `$$this` is current object in array iteration, if condition is true do below functionality for that object else return same object as is to array being created.
{
$mergeObjects: [
"$$this",
{
revisions: { $concatArrays: [ "$$this.revisions", [{ id: 1000, urls: { a: "$$this.urls.a", b: "$$this.urls.b", c: "some-new-url" } } ]] }
}
]
},
"$$this" // Returning same object as condition is not met.
]
}
}
}
}
}
]
);
$mergeObjects will replace existing revisions field in $$this (current) object with value of { $concatArrays: [ "$$this.revisions", { id: 1000, urls: { a: "$$this.urls.a", b: "$$this.urls.b", c: "some-new-url" } } ] }.
From the above field name revisions and as it being an array I've assumed there will multiple objects in that field & So we're using $concatArrays operator to push new objects into revisions array of particular entires object.
In any case, if your revisions array field does only contain one object make it as an object instead of array Or you can keep it as an array & use below query - We've removed $concatArrays cause we don't need to merge new object to existing revisions array as we'll only have one object every-time.
collection.update(
{
id: 1,
"entries.id": 100
}
[
{
$set: {
entries: {
$map: {
input: "$entries",
in: {
$cond: [
{ $eq: ["$$this.id", 100] },
{
$mergeObjects: [
"$$this",
{
revisions: [ { id: 1000, urls: { a: "$$this.urls.a", b: "$$this.urls.b", c: "some-new-url" } } ]
}
]
},
"$$this"
]
}
}
}
}
}
]
);
Test : Test your aggregation pipeline here : mongoplayground
Ref : .updateOne()
Note : If in any case .updateOne() throws in an error due to in-compatible client or shell, try this query with .update(). This execution of aggregation pipeline in updates helps to save multiple DB calls & can be much useful on arrays with less no.of elements.

json schema validate all array items against one or another schema

Given the following schema:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"worktype": {
"blue": {
"enum": [1, 2, 3]
},
"red": {
"enum": [4, 5, 6]
}
}
}
}
I can validate an array to be either consisting of all "blue" items or "red" items like this:
{
"type": "array",
"items": {
"$ref": "#/definitions/worktype/red" // or worktype/blue
}
}
But how can I validate an array to have only either "blue" or "red" worktypes, but not mixed ones?
For example:
[1, 2]: Valid (only blue)
[5]: Valid (only red)
[1, 6]: Invalid (mixed blue and red)
My first idea was using oneOf:
{
"type": "array",
"items": {
"oneOf": [
{"$ref": "#/definitions/worktype/red"},
{"$ref": "#/definitions/worktype/blue"},
]
}
}
But this checks each entry for itself against the schemas, so for example [1, 6] is valid, too. (Afaik in this case the oneOf does not mean "all items must be valid against oneOf together" but "each item must be valid against oneOf for itself").
How can I write a schema for an array that either has only "blue" or "red" worktypes, but not both? Is this possible?
I got it, my first idea was on the right way, but oneOf was simply nested one level too deep. This works like I want:
{
"type": "array",
"oneOf": [
{
"items": {
"$ref": "#/definitions/worktype/red"
}
},
{
"items": {
"$ref": "#/definitions/worktype/blue"
}
}
]
}

MongoDB - Select multiple sub-dicuments from array using $elemMatch

I have a collection like the following:-
{
_id: 5,
"org_name": "abc",
"items": [
{
"item_id": "10",
"data": [
// Values goes here
]
},
{
"item_id": "11",
"data": [
// Values goes here
]
}
]
},
// Another sub document
{
_id: 6,
"org_name": "sony",
"items": [
{
"item_id": "10",
"data": [
// Values goes here
]
},
{
"item_id": "11",
"data": [
// Values goes here
]
}
]
}
Each sub document corresponds to individual organizations and each organization has an array of items in them.
What I need is to get the select individual elements from the items array, by providing item_id.
I already tried this:-
db.organizations.find({"_id": 5}, {items: {$elemMatch: {"item_id": {$in: ["10", "11"]}}}})
But it is returning either the item list with *item_id* "10" OR the item list with *item_id* "11".
What I need is is the get values for both item_id 10 and 11 for the organization "abc". Please help.
update2:
db.organizations.aggregate([
// you can remove this to return all your data
{$match:{_id:5}},
// unwind array of items
{$unwind:"$items"},
// filter out all items not in 10, 11
{$match:{"items.item_id":{"$in":["10", "11"]}}},
// aggregate again into array
{$group:{_id:"$_id", "items":{$push:"$items"}}}
])
update:
db.organizations.find({
"_id": 5,
items: {$elemMatch: {"item_id": {$in: ["10", "11"]}}}
})
old Looks like you need aggregation framework, particularly $unwind operator:
db.organizations.aggregate([
{$match:{_id:5}}, // you can remove this to return all your data
{$unwind:"$items"}
])

Resources