writing more complex json schemas that have dependencies upon other keys - javascript

I've been writing simple JSON schemas but I ran into an API input call that is a bit more complex. I have one restful end route that can take 3 very different types of JSON:
localhost/foo
can take:
{ "type" : "ice_cream", "cone" : "waffle" ...}
or
{"type" : "hot_dog", "bun" : "wheat" ...}
If the "type" key contains "ice_cream", I only ever want to see the key "cone" and not the key "bun". Similiarly if "type" contains "hot_dog" I only want to see "bun" and not "cone". I know I can pattern match to make sure I only ever see type "ice_cream" or type "hot_dog", but I don't know how to force the requirement of certain other fields if that key is set to that value. I see that there is a json schema field called "dependency" but I haven't found any good examples on how to use it.
BTW, I'm not sure if this input JSON is good form (overloading the type of JSON structure it takes, effectively), but I don't have the option of changing the api.

I finally got some information about this - it turns out you can make a union of several different objects that are valid like so:
{
"description" : "Food",
"type" : [
{
"type" : "object",
"additionalProperties" : false,
"properties" : {
"type" : {
"type" : "string",
"required" : true,
"enum": [
"hot_dog"
]
},
"bun" : {
"type" : "string",
"required" : true
},
"ketchup" : {
"type" : "string",
"required" : true
}
}
},
{
"type" : "object",
"additionalProperties" : false,
"properties" : {
"type" : {
"type" : "string",
"required" : true,
"enum": [
"ice_cream"
]
},
"cone" : {
"type" : "string",
"required" : true
},
"chocolate_sauce" : {
"type" : "string",
"required" : true
}
}
}
]
}
I'm still not sure if this is valid JSON, since my Schemavalidator dies on some invalid input, but it accepts the valid input as expected.

Related

Firebase realtime database - orderByChild not returning any result

I am using the Firebase Realtime Database functions .orderByChild() and .equalTo() to fetch data nodes to be updated. However, I cannot seem to get any results back from my queries.
My database structure
{
"4QEg0TWDbESiMX8Cu8cvUCm17so2" : {
"-LmGtXsgJbAvVS8gv5-E" : {
"createdAt" : 1565815876803,
"message" : "Hello",
"isSender" : false,
"sender" : {
"alias" : "",
"name" : "Person A",
"sid" : "mUO3DtYY2yRw3zkv4EmTlfldB3S2"
},
"sysStatus" : 0
},
"-LmGtt4nyuygG9B4s6__" : {
"createdAt" : 1565815967746,
"message" : "Hej!",
"isSender" : true,
"sender" : {
"alias" : "",
"name" : "Person B",
"sid" : "4QEg0TWDbESiMX8Cu8cvUCm17so2"
},
"sysStatus" : 0
},
"-LmJxcvL_Y7JmojxPiiK" : {
"createdAt" : 1565867281849,
"isSender" : true,
"message" : "111",
"sender" : {
"alias" : "",
"name" : "Person B",
"sid" : "4QEg0TWDbESiMX8Cu8cvUCm17so2"
},
"sysStatus" : 0
}
},
"mUO3DtYY2yRw3zkv4EmTlfldB3S2" : {
"222" : {
"createdAt" : 1565867281849,
"isSender" : true,
"message" : "Test",
"sender" : {
"alias" : "",
"name" : "Person A",
"sid" : "mUO3DtYY2yRw3zkv4EmTlfldB3S2"
},
"sysStatus" : 0
},
"333" : { <-- This is one node I wish to fetch
"createdAt" : 1565815967746,
"isSender" : false,
"message" : "123",
"sender" : {
"alias" : "",
"name" : "Person B",
"sid" : "4QEg0TWDbESiMX8Cu8cvUCm17so2" <-- This is the value I am trying to match
},
"sysStatus" : 0
}
},
"rKNUGgdKqdP68T0ne6wmgJzcCE82" : {
"-Lm_GTmNHyxqCqQl4D4Z" : { <-- This is one node I wish to fetch
"createdAt" : 1566140917160,
"isSender" : false,
"message" : "Sesame",
"sender" : {
"alias" : "",
"name" : "Person B",
"sid" : "4QEg0TWDbESiMX8Cu8cvUCm17so2" <-- This is the value I am trying to match
},
"sysStatus" : 5
}
}
}
My code
dbRoot.child('messages')
.orderByChild('sid')
.equalTo(userId)
.once('value', (senderSnapshot) => {
console.log('senderSnapshot', senderSnapshot.val())
console.log('senderSnapshot amount', senderSnapshot.numChildren())
senderSnapshot.forEach((sender)=>{
//Do the work!
})
})
The code logs
senderSnapshot null
senderSnapshot amount 0
I have manually checked that there are several nodes where "sid" is set to the "userId" I am looking for.
Why am I not getting any results back from my query?
It seems like I have to search dbRoot.child('messages/rKNUGgdKqdP68T0ne6wmgJzcCE82') to get my value. :/ (And then repeat the search for each user)
How much extra data overhead would it be to download/collect all contacts and then loop thru each users contact?
Firebase Database queries allow you to search through the direct child nodes at a certain location for a value at a fixed path under each child. So: if you know the user whose message you want to search through, you can do:
dbRoot
.child('messages')
.child('mUO3DtYY2yRw3zkv4EmTlfldB3S2') // the user ID
.orderByChild('sender/sid')
.equalTo(userId)
Note the two changes I made here from your code:
We now start the search from messages/mUO3DtYY2yRw3zkv4EmTlfldB3S2, so that it only searches the messages for that one user.
We now order/filter on the sender/sid property for each child under messages/mUO3DtYY2yRw3zkv4EmTlfldB3S2.
By combining these we are essentially searching a flat list of child nodes, for a specific matching value at a path under each child node.
There is no way in your current data structure to find all messages for a specific sender/sid value across all users. To allow that you'll have to add an additional data structure, where you essentially invert the current data.
"sender_messages": {
"mUO3DtYY2yRw3zkv4EmTlfldB3S2": {
"4QEg0TWDbESiMX8Cu8cvUCm17so2/-LmGtXsgJbAvVS8gv5-E": true,
},
"4QEg0TWDbESiMX8Cu8cvUCm17so2": {
"4QEg0TWDbESiMX8Cu8cvUCm17so2/-LmGtXsgJbAvVS8gv5-E": true,
"4QEg0TWDbESiMX8Cu8cvUCm17so2/-LmGtt4nyuygG9B4s6__": true,
"4QEg0TWDbESiMX8Cu8cvUCm17so2/-LmJxcvL_Y7JmojxPiiK": true,
"mUO3DtYY2yRw3zkv4EmTlfldB3S2/333": true,
"rKNUGgdKqdP68T0ne6wmgJzcCE82/Lm_GTmNHyxqCqQl4D4Z": true
},
"mUO3DtYY2yRw3zkv4EmTlfldB3S2": {
"mUO3DtYY2yRw3zkv4EmTlfldB3S2/222": true,
}
}
Now you can find the messages for a specific sender/sid by reading:
dbRoot
.child('sender_messages')
.child('4QEg0TWDbESiMX8Cu8cvUCm17so2') // the sender ID
And then looping over the results, and loading the individual messages from each path as needed.
This is quite common in NoSQL databases: you'll often have to modify your data structure to allow the use-cases you want to add to your app.
See also:
Firebase query if child of child contains a value
Firebase Query Double Nested
Speed up fetching posts for my social network app by using query instead of observing a single event repeatedly (to learn why loading the additional messages is not nearly as slow as you may initially think)

loopback model with object

Good evening,
I just started with loopback (literally), so I went for the commands explained in the documentation:
Started the loopback project with the command:
slc loopback
Generated a model with the loopback model generator with the command:
slc loopback:model
When the generator starts, it asks for model name, storing method, base class, plural, whether we want it to be a common model or a server model. After that it asks for the properties of the model.
I have a model that could be like this:
MODEL NAME
Property1 : String,
Property2 : String,
Property3 : Number,
Property4 : {
obj.Property1 : String,
obj.Property2 : String,
obj.Property3 : String
},
Property5 : String
I thought that by choosing "Object" as the type of property it would ask me for additional properties on that object, but it didn't. Now I have no idea how to create those additional properties that are inside the object of this model.
How would I go about creating the properties that are nested inside the Property4 Object? Am I missing something from the loopback:model generator?
Well slc loopback:model doesn't do that. You just have to specify yourself in the generated json file(possibly in common/models/ directory) in the properties object:
"properties": {
...
"Property4": {
"type": {
"Property1" : "String",
"Property2" : "String",
"Property3" : "String"
}
},
...
}
If you want any of the properties to be required you can do it like this:
"properties": {
...
"Property4": {
"type": {
"Property1" : {
"type": "String"
"required": true
},
"Property2" : "String",
"Property3" : "String"
}
},
...
}
If the object doesn't have a "type" property, you can just do this:
"properties": {
...
"Property4": {
"Property1" : "String",
"Property2" : "String",
"Property3" : "String"
},
...
}
You can also put this definition into a another model and reference that here:
"properties": {
...
"Property4": "AnotherModel",
...
}
I suggest you read through the 'properties' section of this document and also 'object types' section of this document.

Firebase query taking forever

My firebase data looks like this...
In JSON down below...
I've been trying to use what I've found in their docs to query the db so that I end up with of all the terms and their keys. How do I query just the term property and key for every node under vocab? I'm using this to populate an autosuggest. Also, the 0 and 1 under the vocab node are keys... they showed up that way when I imported the data as a JSON file. Any new vocab goes in with unique long key values though.
I've tried both .on() and .equalTo()... But I have had no luck whatsoever. It always seems to be grabbing everything in the entire vocab node of the database and taking on the order of 7 whole seconds to do it each time. Here is an example of one of my attempts where I try to put just the terms into an array...
let termList = [];
const termsRef = firebase.database().ref('vocab/');
termsRef.on('child_added', snap => termList.push(snap.val().term));
This doesn't even get the keys (which I need) and it still takes around 7 seconds to do it. I've seen videos where people do things like this in one line and it goes quick... not my luck. Any help would be much appreciated.
As requested here is a JSON sample of one of the database nodes... there are over 400 like this... the relevant property is term... all the way at the bottom. I need an array of terms and firebase database keys. How do I get them with a query?
{
"date" : "2016-07-26T14:50:10.906Z",
"defs" : [ {
"AddedBy" : "",
"date" : "2016-07-26T14:50:10.906Z",
"def" : "it's your need to fulfill or to obtain your full potential, and have meaningful goals.",
"image" : ""
}, {
"AddedBy" : "",
"date" : "2016-07-26T14:50:10.906Z",
"def" : "the very last stage in priority in order to be happy.",
"image" : ""
}, {
"AddedBy" : "",
"date" : "2016-07-26T14:50:10.906Z",
"def" : "the need to be able to set goals and reach them and be able to accept one's self.",
"image" : ""
} ],
"examples" : [ {
"AddedBy" : "",
"addDate" : "2016-07-26T14:50:10.906Z",
"approved" : true,
"checkDate" : "2016-07-26T14:50:10.906Z",
"example" : "a self-actualizing person is a person who accepts themselves, sets realistic and meaningful goals and accepts change.",
"feedback" : {
"comment" : "",
"grammarTrouble" : false,
"incorrect" : false,
"moreSpecific" : false,
"noContext" : false
},
"nonExample" : false,
"seenRecently" : [ {
"sawOn" : "",
"user" : ""
} ]
}, {
"AddedBy" : "",
"addDate" : "2016-07-26T14:50:10.906Z",
"approved" : true,
"checkDate" : "2016-07-26T14:50:10.906Z",
"example" : "who is not self-actualizing is a person who dislikes themselves, has no goals in life, and refuses to accept change.",
"feedback" : {
"comment" : "",
"grammarTrouble" : false,
"incorrect" : false,
"moreSpecific" : false,
"noContext" : false
},
"nonExample" : false,
"seenRecently" : [ {
"sawOn" : "",
"user" : ""
} ]
}, {
"AddedBy" : "",
"addDate" : "2016-07-26T14:50:10.906Z",
"approved" : true,
"checkDate" : "2016-07-26T14:50:10.906Z",
"example" : "Someone who achieves to their best personal ability and faces every situation head on.",
"feedback" : {
"comment" : "",
"grammarTrouble" : false,
"incorrect" : false,
"moreSpecific" : false,
"noContext" : false
},
"nonExample" : false,
"seenRecently" : [ {
"sawOn" : "",
"user" : ""
} ]
} ],
"term" : "Self Actualization",
"unit" : 0
},

updating mongodb using $push operator in angular-meteor

I am trying to push arrays from one collection to another.
this is the code I used in my server js
updateSettings: function(generalValue) {
let userId = Meteor.userId();
let settingsDetails = GeneralSettings.findOne({
"_id": generalValue
});
Meteor.users.update({
_id: userId
}, {
$push: {
"settings._id": generalValue,
"settings.name": settingsDetails.name,
"settings.description": settingsDetails.description,
"settings.tag": settingsDetails.tag,
"settings.type": settingsDetails.type,
"settings.status": settingsDetails.status
}
})
}
updateSettings is the meteor method. GeneralSettings is the first collection and user is the second collection. I want to push arrays from GeneralSettings to users collection. While I try this the result i got is like
"settings" : {
"_id" : [
"GdfaHPoT5FXW78aw5",
"GdfaHPoT5FXW78awi"
],
"name" : [
"URL",
"TEXT"
],
"description" : [
"https://docs.mongodb.org/manual/reference/method/db.collection.update/",
"this is a random text"
],
"tag" : [
"This is a demo of an Url selected",
"demo for random text"
],
"type" : [
"url",
"text"
],
"status" : [
false,
false
]
}
But the output I want is
"settings" : {
"_id" : "GdfaHPoT5FXW78aw5",
"name" : "URL",
"description" :
"https://docs.mongodb.org/manual/reference/method/db.collection.update/",
"tag" :"This is a demo of an Url selected",
"type" : "url",
"status" : false
},
What changes to be made in my server.js inorder to get this output
This is one case where you "do not want" to use "dot notation". The $push operator expects and Object or is basically going to add the "right side" to the array named in the "left side key":
// Make your life easy and just update this object
settingDetails._id = generalValue;
// Then you are just "pushing" the whole thing into the "settings" array
Meteor.users.update(
{ _id: userId },
{ "$push": { "settings": settingDetails } }
)
When you used "dot notation" on each item, then that is asking to create "arrays" for "each" of the individual "keys" provided. So it's just "one" array key, and the object to add as the argument.

trouble parsing json string

I am trying to parse this simple json string:
var dataJSON = {};
var data;
dataJSON = {
"status": "OK",
"messages" : [{
"user" : {
"id" : "4",
"status" : "offline",
"name" : "dummy",
"pictures" : ["pic.jpg"]
},
"message" : "Hey",
"timestamp" : 1395660658
}, {
"user" : {
"id" : "2",
"status" : "online",
"name" : "dummy1",
"pictures" : ["pic1.jpg"]
},
"message" : "hello",
"timestamp" : 1395660658
}]
};
console.log('test');
console.log(dataJSON);
//parse data
data = JSON.parse(dataJSON);
but I am getting the following error:
"unable to parse json string"
I have mo idea why, cheers.
You don't have to parse it at all; it's a JavaScript object already.
The acronym "JSON" stands for JavaScript Object Notation. It's a restricted form of the native syntax in JavaScript for creating objects "on the fly". To say that another way, JavaScript's native object literal syntax is a superset of JSON. What you've typed in there, as the value of your "dataJSON" variable, is a JavaScript object literal expression. The value of such an expression is a reference to an object. No parsing necessary, since the JavaScript parser itself has already done so.
edit — if you really do need a JSON string for testing purposes, then I think the easiest way to do that is to use JSON.stringify() to convert an object into a string, and then pass it into the test code. In your example, that'd look like:
dataJSON = JSON.stringify({
"status": "OK",
"messages" : [{
"user" : {
"id" : "4",
"status" : "offline",
"name" : "dummy",
"pictures" : ["pic.jpg"]
},
"message" : "Hey",
"timestamp" : 1395660658
}, {
"user" : {
"id" : "2",
"status" : "online",
"name" : "dummy1",
"pictures" : ["pic1.jpg"]
},
"message" : "hello",
"timestamp" : 1395660658
}]
});
It's a little easier than trying to construct the string by hand because of the "quote quoting" nuisance. Of course the object you pass in should be one that actually can be represented as JSON, but your sample above is definitely OK.

Categories