Normalizr and recursive nested structure - redux

I'm using normalizr to flatten a structure like the following one:
{
"fields":[{
"id":"29",
"text": "something"
}, {
"id":"16",
"text": "something"
"fields":[{
"id":"17",
"text": "something"
}]
}, {
"id":"18",
"text": "something"
}
}
My structure has an array of fields and a field may also have nested fields. There's only one level of nesting allowed.
What I'm trying to do is this:
const block = new schema.Entity('fields')
const blockList = new schema.Array(block)
block.define({
fields: blockList
})
const normalizedData = normalize(originalData, blockList)
After running this snippet, normalizedData has a results property and it only has the first level of field ids, even though entities has all the fields normalized, including the nested ones.
I'd like to have in the results array all the ids, including the nested ones. What am I missing?

I'd like to have in the results array all the ids, including the nested ones. What am I missing?
This is how Normalizr works. The returned result is always in the same format of the input data. You won't be able to get what you are asking for from Normalizr.
If, however, you're just looking for a list of blocks, pull that from the entities:
const blockIds = Object.keys(normalizedData.entities.blocks);

You should consider using a normalized form for your data structure in redux.
This is advisable if your application need growth in complexity.
There is an interesting article on redux docs.
http://redux.js.org/docs/recipes/reducers/NormalizingStateShape.html
A normalized form take some ideas from db counterpart and Normalizr works in that way, so your request does not really match with how Normalizr works.
Please consider #Paul Armstrong answer for a work around if you really need get blocks in that way.

Related

How to query documents where contains an array and the value of the array is ["val1", "val2"] Firestore

How can I get a collection where the query should be applicable to an array inside the document.
Document example: I would like to know how to query the document where the brands are fiat and seat
{
"name":"test 1",
"brands":[
{
"brand":{
"id":1,
"name":"Fiat",
"slug":"fiat",
"image":null,
"year_end":null,
"year_start":null
},
"released_at":"2018-10-26"
},
{
"brand":{
"id":2,
"name":"Seat",
"slug":"seat",
"image":null,
"year_end":null,
"year_start":null
},
"released_at":"2018-10-26"
},
{
"brand":{
"id":3,
"name":"Mercedes",
"slug":"mercedes",
"image":null,
"year_end":null,
"year_start":null
},
"released_at":"2018-10-26"
},
{
"brand":{
"id":4,
"name":"Yamaha",
"slug":"yamaha",
"image":null,
"year_end":null,
"year_start":null
},
"released_at":"2018-10-26"
}
]
}
I have tried something like:
.collection("motors")
.where("brands.slug", "array-contains-any", ["fiat", "seat"])
but this is not working I cannot figure out by the documentation how to get this.
When using the array-contains-any operator, you can check the values of your array against the value of a property of type String and not an array. There is currently no way you can use array-contains-any operator on an array. There are two options, one would be to create two separate fields and create two separate queries or, been only a document, you can get the entire document and filter the data on the client.
Edit:
What #FrankvanPuffelen has commented is correct, I made some research and I found that we can check against any type and even complex types, not just against strings, as mentioned before. The key to solving this issue is to match the entire object, meaning all properties of that object and not just a partial match, for example, one of three properties.
What you are trying to achieve is not working with your current database structure because your slug property exists in an object that is nested within the actual object that exists in your array. A possible solution might also be to duplicate some data and add only the desired values into an array and use the array-contains-any operator on this new creatded array.

How can I update deeply nested object inside array?

Hello good people of the stack!
I am working on a react-redux application and I am trying to update a property on a deeply nested structure in my reducer. The data structure is as follows and I want to update the text property:
state = {
assessment: {
requirements: [
questions: [
{
text
}
]
]
}
}
so I have tried the following:
// reducer code...
return {
...state,
[assessmentId]: {
...state[assessmentId],
requirements: [
...state[assessmentId].requirements,
[requirementId]: [
...state[assessmentId].requirements[requirementsId],
questions: [
...state[assessmentId].requirements[requirementsId].questions,
[questionId]: {
text: action.payload.response.text
},
],
] ,
],
},
};
This is more pseudo code than actual code to remove complexity.
I do not see any change in redux dev tools so I am wondering if I have made a mistake the way I get the nested objects and array elements.
I was also curious about using combine reducers here. I asked a colleague and they suggested to use that but I am unsure how you would take that approach here. As always, any help is appreciated.
I recommend immer for deep state changes in your reducers.
It adds a little weight to your bundle, and you'll get better performance from using the spread operator, but if you can live with that it'll make your code easier to read and write.
import produce from "immer";
// reducer code...
return produce(state, draft => {
draft[assessmentId].requirements[requirementsId].questions[questionsIndex].text = action.payload.response.text;
});
I'd say your issue stems from questions being an array which will take a little more work to keep straight than object based state.
As it is you appear to be trying to set the question value as if questions was an object. Maybe you just need to drop the [questionId] syntax, eg
questions: [
...state[assessmentId].requirements[requirementsId].questions,
{ text: action.payload.response.text },
],
This will set the text object as a new item on the end of the array though.
Depending on what you need to do (ie what already exists in the array and whether you are trying to add or update) you'll want to have a read of:
https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns#inserting-and-removing-items-in-arrays
https://redux.js.org/recipes/structuring-reducers/immutable-update-patterns#updating-an-item-in-an-array

Ordering data does not actually order anything

I'm trying to get a dataset of messages out of my firebase database and want the messages sorted by added/timestamp. But for some reason no orderby I put in the code is actually used. I tried doing these 2 things.
_messagesRef = FirebaseDatabase.instance.reference().child('messages/'+key);
_membersSubscription = _messagesRef
.orderByChild('timestamp')
.onValue//On valuechange
.listen((Event event) => _messagesSubscriptionCallback(event));
_messagesRef = FirebaseDatabase.instance.reference().child('messages/'+key);
_membersSubscription = _messagesRef
.orderByKey()
.onValue//On valuechange
.listen((Event event) => _messagesSubscriptionCallback(event));
Both give me back the same dataset that is not ordered by timestamp or key in the callback. I've added the output underneath
{
-LA-Aw6crEAV53LxdujP:{
sender:1508,
message:test s9 2,
timestamp:1523642778089
},
-LA-Arby61T1UG5URMn6:{
sender:1508,
message:test s9,
timestamp:1523642759679
},
-LA-AyC7F8KAqceZBE3j:{
sender:1509,
message:test emu 1,
timestamp:1523642786632
},
-LA22WiUfL2tbh7-OjtM:{
sender:1508,
message:Blaj,
timestamp:1523690904480
},
-LA-B29RRXbdOPT1mG7m:{
sender:1508,
message:tesy3,
timestamp:1523642806940
}
}
This is how the data should be.
I hope someone can help me with this issue. I think I might misunderstand how ordering data works with Firebase
Kind regards,
Namanix
The result you show is a JSON object with other objects in there. JSON objects are never ordered as far as I know, only retrievable by key. JSON Arrays would be, but it doesn't look like you get that. When you would change this to an array the document IDs would have to be inside the document instead of being the object key. My guess would be that 'orderBy' is meant to be used for example to limit the number of items you get for pagination. Than you can order by timestamp, limit the number of items to 20 and search from the last timestamp you got.
I think if you want to order them I would put them in a new list of objects which can be ordered.
Most likely (it's hard to be sure without seeing _messagesSubscriptionCallback) you're throwing the ordering information away when you convert the data from Firebase into a plain JSON object, which (as Rene also says) doesn't have any defined order.
But the data your request from Firebase does have ordering information, you just have to be sure to not drop it.
The usual way to do this is to listen for onChildAdded instead of onValue. With that Firebase will invoke onChildAdded for each child in turn, and it will do so in the order you requested.
Also see this example and for example what FirebaseAnimatedList does here.
I now temporarily fixed it by doing this in my callback. But this feels like a very bad way to fix it. I hope to get some thoughts on this.
static void _messagesSubscriptionCallback(Event event) {
_messagesList.clear();
_messages.clear();
_messages = event.snapshot.value;
_messagesList = _messages.keys.toList();
_messagesList.sort((a, b) {
return b.compareTo(a) ;
});
onMessagesChange();
}

Why use an object when denormalising data?

In the recent blog post on denormalising data, it suggests logging all of a user's comments beneath each user like so:
comments: {
comment1: true,
comment2: true
}
Why is this not a list like so:
comments: [
"comment1",
"comment2",
]
What are the advantages? Is there any difference at all? While I'm at it, how would you go about generating unique references for these comments for a distributed app? I was imagining that with a list I'd just push them onto the end and let the array take care of the index.
Firebase only ever stores objects. The JS client converts arrays into objects using the index as a key. So, for instance if you store the following array using set:
comments: [
"comment1",
"comment2"
]
In Forge (the graphical debugger), it will show up as:
comments:
0: comment1
1: comment2
Given this, storing the ID of the comment directly as a key has the advantage that you can refer to it directly in the security rules, for example, with an expression like:
root.child('comments').hasChild($comment)
In order to generate unique references for these comments, please use push (https://www.firebase.com/docs/managing-lists.html):
var commentsRef = new Firebase("https://<example>.firebaseio.com/comments");
var id = commentsRef.push({content: "Hello world!", author: "Alice"});
console.log(id); // Unique identifier for the comment just added.

Using the deep:true query option with a dojo ObjectStore adapter

I'm new to Dojo, and I'm trying to get my head wrapped around some pretty basic concepts before my partner and I embark on a new project. I've been successful in using the ItemFileWriteStore to get data returned from an ajax request into a DataGrid, but I would like to use the new Memory store instead. I am using the ObjectStore adapter to wrap the Memory store, and data is being populated in the grid.
One thing has me worried. In the example code I'm running, there is some nested nodes in the JSON object I'm using to populate the grid. It's one of the Countries JSON object they use in the dojo documentation. Here's a little snippet of that object:
'items': [
{ 'name':'Africa', 'type':'continent', children:[
{ 'name':'Egypt', 'type':'country' },
{ 'name':'Kenya', 'type':'country', children:[
{ 'name':'Nairobi', 'type':'city' },
{ 'name':'Mombasa', 'type':'city' } ]
},
{ 'name':'Sudan', 'type':'country', 'children':
{ 'name':'Khartoum', 'type':'city' }
} ]
},
]
In the DataGrid instantiation, I send in a query and then I set the qureyOptions to {deep:true} like so:
var grid = new DataGrid({
style: "width: 500px; height: 300px;",
store: this._geoStore,
structure: layoutGeo,
rowSelector: '20px',
columnReordering: true,
query: {},
queryOptions: { deep: true },
rowsPerPage: 20
}, document.createElement("div"));
When I use the old ItemFileWriteStore, this works fine, and ALL the nested data is essentially flattened out and placed in the grid as I would expect. However, when I switch it over to a Memory store wrapped in an ObjectStore adapter, the only data displayed in the grid is the top most parent of the data object. In the case of the sample data, only the data associated with Africa, but none of its children, are displayed. So it seems that the queryOptions {deep:true} statement has no affect when using the Memory store.
Is there some way of getting nested data within the JSON objects into a data grid using a Memory store wrapped in an ObjectStore? I would have thought that the wrapper class would have taken care of this, and it might, but I don't know how to fix it.
For all who help, thank you very much for your time and willingness to share your expertise. I really have studied the dojo docs and web for a long time trying to figure this stuff out. I fear I may have glossed over something obvious and am hoping you all can help.
I blogged about the store changes at http://swingingcode.blogspot.com/2012/03/dojo-implementing-viewmodelstore.html
The code here uses the new store object but brings the reference notation from the data api. All objects would be considered top level objects and query-able. I did not implement the logic to pull out nested objects.

Resources