Is it possible to create a JSON Schema that can validate a hashmap / dictionary as opposed to an object? - dictionary

So if I have an object, let's call it a Person, like:
{
"email": "foo#bar.com",
"first"" "foo",
"last": "bar"
}
This Person object can all be validated quite well with JSON Schema. The issue is when multiple of these are arranged into a dictionary where the email field is used as the key and the object is the value. For example:
{
"foo#bar.com": {
"email": "foo#bar.com",
"first"" "foo",
"last": "bar"
},
"you#your.com": {
"email": "you#your.com",
"first": "Bob",
"last": "Bobton"
},
"me#mine.com": {
"email": "me#mine.com",
"first": "Deb",
"last": "Debbington"
}
}
This is a common way to structure data. Beyond the validation of the Person values, which can be handled well by a JSON Schema, there are a number of validations that would be useful on the dictionary:
The key is an email and can be validated as one.
The value is always a Person.
The key is always identical to the value's email field.
All the keys are unique.
Is it possible to implement these dictionary validations using JSON Schema?

The first two requirements, yes.
You can use patternProperties.
The other two you ask are not possible using JSON Schema. Sorry.
My expectation would be you receive the data in the first form from an API, validate, then map reduce to your desired structure.

Related

ConditionExpression for PutItem not evaluating to false

I am trying to guarantee uniqueness in my DynamoDB table, across the partition key and other attributes (but not the sort key). Something is wrong with my ConditionExpression, because it is evaluating to true and the same values are getting inserted, leading to data duplication.
Here is my table design:
email: partition key (String)
id: sort key (Number)
firstName (String)
lastName (String)
Note: The id (sort key) holds randomly generated unique number. I know... this looks like a bad design, but that is the use case I have to support.
Here is the NodeJS code with PutItem:
const dynamodb = new AWS.DynamoDB({apiVersion: '2012-08-10'})
const params = {
TableName: <table-name>,
Item: {
"email": { "S": "<email>" },
"id": { "N": "<someUniqueRandomNumber>" },
"firstName": { "S": "<firstName>" },
"lastName": { "S": "<lastName>" }
},
ConditionExpression: "attribute_not_exists(email) AND attribute_not_exists(firstName) AND attribute_not_exists(lastName)"
}
dynamodb.putItem(params, function(err, data) {
if (err) {
console.error("Put failed")
}
else {
console.log("Put succeeded")
}
})
The documentation https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.OperatorsAndFunctions.html says the following:
attribute_not_exists (path)
True if the attribute specified by path does not exist in the item.
Example: Check whether an item has a Manufacturer attribute.
attribute_not_exists (Manufacturer)
it specifically says "item" not "items" or "any item", so I think it really means that it checks only the item being overwritten. As you have a random sort key, it will always create a new item and the condition will be always true.
Any implementation which would check against a column which is not an index and would test all the records would cause a scan of all items and that is something what would not perform very well.
Here is an interesting article which covers how to deal with unique attributes in dynamodb https://advancedweb.hu/how-to-properly-implement-unique-constraints-in-dynamodb/ - the single table design together with transactions would be a possible solution for you if you can allow the additional partition keys in your table. Any other solution may be challenging under your current schema. DynamoDB has its own way of doing things and it may be frustrating to try to push to do things which it is not designed for.

How to query nested objects in firestore [duplicate]

This question already has answers here:
Firestore - Nested query
(2 answers)
Closed 2 years ago.
I want to store data in following format:
{
"chatName": "Football",
"chatMembers":
[
{
"userId": "nSWnbKwL6GW9fqIQKREZENTdVyq2",
"name": "Niklas"
},
{
"userId": "V3QONGrVegQBnnINYHzXtnG1kXu1",
"name": "Timo"
},
]
}
My goal is to get all chats, where the signed in user with a userId is in the chatMembers list. If the userId of the signed in user is not in the chatMembers property, then that chat should be ignored. Is this possible?
If this is not possible, how can i achive this with subcollections?
My development language is dart, but you can also post solutions in other languages.
My current attempt is this, but this is not working:
_firestore.collection(collectionName).where("chatMembers.userId", isEqualTo: userId).snapshots()
Since August 2018 there is the new array_contains operator which allows filtering based on array values. The doc is here: https://firebase.google.com/docs/firestore/query-data/queries#array_membership
It works very well with arrays of string. However, I think it is not possible to query for a specific property of an object stored in the array. One workaround is to query for the entire object, as follows (in Javascript). Of course this may not be feasible in every situation....
var db = firebase.firestore();
var query = db.collection('chatDocs').where("chatMembers", "array-contains", { userId: "xyz", userName: "abc" });
Renaud Tarnec's, which complete, doesn't work in every case. Situations where only one or not all of the fields are known won't return the expected documents. However, by restructuring the data in the document, a query can be made to work where only one field is known, like a user identifier (uid).
Here's the data structure inside one of the document:
{
"members": {
"user4": {
"active": true,
"userName": "King Edward III",
"avatar": "www.photos.gov/animeGirl5.png"
},
"user7": {
"active": true,
"userName": "Dave K.",
"avatar": "www.photos.gov/gunsAmericanFlag.png"
}
}
Here's the query:
uid = 'user4';
collectionQuery = collectionReference.where(`members.${uid}.active`,"==", true);
In this example, "user4" represents a user who may or may not be in a group. This will return all documents in the collection where the uid "user4" is an active member. This works by only needing to know the UID of the member and works without needing to know their name or avatar uri ahead of time.

Can I iterate through an object type document field when querying a collection in cloud firestore?

I have a collection "foo". Each document in the collection has a property/field "bar" that is an array of objects like so:
foo = [{
bar: [{
id: "random_string",
"status": "string"
}, {
id: "random_string",
"status": "string2"
}]
}, {
bar: [{
id: "random_string",
"status": "string"
}, {
id: "random_string",
"status": "string2"
}]
}]
What I want to achieve is I want to be able to query the db so that I can get two different collections, one with all the documents in the "foo" collection that have "string" as a value of at least one of the objects in the "bar" array, and another collection for all the documents that have "string2" as a value of at least one of the objects in the "bar" array.
Is that even possible? I'm struggling quite a lot to this one, so any help would be greatly appreciated. I'm also happy to change the db schema if needed, totally open to suggestions!
You can't do this with a single query because Firestore currently doesn't support logical OR conditions. In other words, you can't have a query that gives you all the documents where any one of a set of conditions is true.
Also, you need to be able to call out a particular field in a document in order to perform a query against it. Without a specific field to use, the query can't use an index to speed things up, and the query would never scale at the magnitude offered by Firestore.

Validate arbitrary length object with simple schema

I have a Players collection and a Games collection. I want to construct a data structure that looks as follows:
{
"_id": "1234",
"accounts": {
"battlenet": "blah#1234"
},
"games": {
"overwatch": {
"class": "Hanzo",
"timePlayed": ISODate
},
"world-of-warcraft": {
"class": "Shaman",
"timePlayed": ISODate
}
}
}
games is an object, where every key refers to a specific document in the Games collection's slug attribute. Every value is a sub-schema definition with autoValues.
I can't find any good way to create validation in such a way that it updates an autoform correctly without weird coersion of data. Is there any way to accomplish this validation with simple schema?

Firebase orderByChild query not returning ordered results

I am trying to use the Firebase orderByChild() function per the documentation example but the queries are not coming back filtered or sorted.
Here is the sample data structure:
"notifications": {
"001": {
"member_id": "abc123",
"created_at": 1424357680681,
"new": true,
"first_name": "Jon",
"last_name": "Snow",
"message": "likes your photo"
},
"002": {
"member_id": "abc456",
"created_at": 1424357680681,
"new": true,
"first_name": "Clark",
"last_name": "Kent",
"message": "likes your comment"
}
}
When a user logs in, I want to query the notifications hash for only the notifications with a member_id that matches the current user's id.
I have tried both versions with and without the "child_added".
Option A:
ref.orderByChild("member_id").equalTo(memberId);
Option B:
ref.orderByChild("member_id").equalTo(memberId)
.on("child_added", function(data) {
console.log("new child id = " + data.key());
});
When I query the notifications hash, the query returns an array of all of the notifications. They are neither limited to the member_id of the current user, nor sorted by member_id. (I can delete the member_id key entirely and the notification will still be returned).
I can always filter the returned array by member_id in-app, but I would really like to get the query working per the documentation.
Any help is appreciated!
I found the problem!
I was under the impression that you could add the query modifiers in place.
I was doing:
ref.orderByChild("member_id").equalTo(memberId);
var sync = $firebase(ref);
It should actually be:
var sync = $firebase(ref.orderByChild("member_id").equalTo(memberId));
Most likely your memberId variable is a number, while you're storing the corresponding value as a string.
If you store numbers as a string, this query won't give any results:
ref.orderByChild("member_id").equalTo(456)
But this one will:
ref.orderByChild("member_id").equalTo('456')
The easiest fix in your snippet of code is coercing the memberId to a string like this:
ref.orderByChild("member_id").equalTo(''+memberId)
See this fiddle for a working sample: http://jsfiddle.net/695rf0vy/

Resources