SAPUI5 TreeTable - flat OData - json

i have an issue or lack of idea how to build TreeTable from flat OData data. The data looks like this (columns):
Document Number,
Year,
Month,
Status,
several more columns
The hierarchy should like like this:
DOCUMENT
`- YEAR
`- Month
`- STATUS 1
- data for status 1 #1
- data for status 1 #2
`- STATUS 2
- data for status 2 #1
- data for status 2 #2
ODATA returns:
DOCUMENT | YEAR | MONTH | STATUS1 | Data for status 1 #1
DOCUMENT | YEAR | MONTH | STATUS1 | Data for status 1 #1
DOCUMENT | YEAR | MONTH | STATUS2 | Data for status 2 #1
DOCUMENT | YEAR | MONTH | STATUS2 | Data for status 2 #2
The question would be - is there any callback function/whatever i could use in order to reorganize the data for example by putting it in JSON format ? Or any other Idea ?
I have a CDS View and generated OData behind, so need to do this in UI5.
Any help would be highly appreciated !
Cheers
Kubas

If as you said there should be an edit possibility for the table, you can use a stanard TreeTable OData binding, but the backend must provide you the hierarchical data (it will cost nothing to implement the editability due to standard two-way binding), in your case the OData structure will look like this:
[
{
ID: "",
NodeID: 1,
type: "document",
title: "",
HierarchyLevel: 1,
ParentNodeID: null
},
{
ID: "",
NodeID: 2,
type: "year",
title: "",
HierarchyLevel: 2,
ParentNodeID: 1
},
{
ID: "",
NodeID: 3,
type: "month",
title: "",
HierarchyLevel: 3,
ParentNodeID: 2
},
{
ID: "",
NodeID: 4,
type: "status",
title: "",
HierarchyLevel: 4,
ParentNodeID: 3
},
{
ID: "",
NodeID: 5,
type: "data for status",
data: {},
title: "",
HierarchyLevel: 5,
ParentNodeID: 4
}
]
If there is no chance for the backend to provide such structure, you can parse your backend response and construct JSON model (the parsing will be just for a grouping reasons, you need to group your flat structure into the hierarchical considering the keys: document ids, years, months, statuses..).
JSON data structure:
[
{
DocumentId: "",
items: [
{
year: "",
items: [
{
month: "",
items: [
{
status: ""
}
]
}
]
}
]
}
]
But this JSON-approach makes harder to track the changes to be send to the backend side. So the task is to find the nodes that were changed by the user and here you can think about making the diff between original parsed data and the data in a state after the changes applied by the user. Or you can keep a kind of a map of the changes user made to the document id's and turn this data into the odata calls once user wants to submit the changes.
UPD 1:
Implementation of "lazy loading" for the tree structure that is defined by the flat one (via backend) will be problematic. Because to my understanding, the lazy loading for the tree will look like the following:
initially UI loads all the items from the 1-st level. Then after clicking on the "expand" button of th 1st level, UI makes a call to fetch the 2nd level and so on.
The lazy loading for the "flat" list works differently, while user scrolling the table down and reaches the very bottom side (based on the "threshold" property), table can request more data via new $skip & $top properties in OData call.
So I'm afraid that it won't be possible to make the lazy loading without property tree structure from backend.
Possible workaround might be providing a kind of a filter to reduce amount of items.
As to your question about "where to put this parsing" - you need to perform the "read" request manually in the controller, then in "success" method call the parsing, then construct JSONModel based on parsed data and finally bind the table to this model.

Related

Is there a way to enrich JSON field in MySQL?

Let's take a simple schema with two tables, one that describes an simple entity item (id, name)
id | name
------------
1 | foo
2 | bar
and another, lets call it collection, that references to an item, but inside a JSON Object in something like
{
items: [
{
id: 1,
quantity: 2
}
]
}
I'm looking for a way to eventually enrich this field (kind of like populate in Mongo) in the collection with the item element referenced, to retrieve something like
{
...
items: [
{
item: {
id: 1,
name: foo
},
quantity: 2
}
]
...
}
If you have a solution with PostgreSQL, I take it as well.
If I understood correctly, your requirement is to convert an Input JSON data into MySQL table so that you can work with JSON but leverage the power of SQL.
Mysql8 recently released JSONTABLE function. By using this function, you can store your JSON in the table directly and then query it like any other SQL query.
It should serve your immediate case, but this means that your table schema will have a JSON column instead of traditional MySQL columns. You will need to check if it serves your purpose.
This is a good tutorial for the same.

Best way to handle data list of REST web service with foreign key(one to many)

I am going to implement the REST base CRUD modal in my my app.I wan to display the list of product data with edit and delete link
Product
id, title, unit_id, product_type_id, currency_id,price
Q1: what should be json response look like?
There are two formats comes in my mind to place the data in Json as a response of REST Get call
[
{
id:1,
title:"T-Shirt",
unit_id:20,
unit_title: "abc"
product_type_id:30,
product_type_title:"xyz"
currency_id: 10,
currency_name: "USD"
min_price:20
},
{...}
]
and the another one is
[
{
id:1,
title:"T-Shirt",
unit: {
id: 20,
title: "abc"
},
product_type: {
id: 30,
title: "xyz"
},
currency_id: {
id:10,
name: "USD"
},
min_price:20
},
{...}
]
what is the better and standard way to handle the above scenario?
Furthermore, let suppose I have 10 more properties in product table which will never display on list page. but i needed it when user going to edit the specific item.
Q2: Should I the load all data once at the time of displaying product list and pass the data to edit component.
or
Load only the needed propeties of product table and pass the id to produt edit component and a new REST GET call with id to get the properties of product.
I am using React + Redux for my front end
Typically, you would create additional methods for API consumers to retrieve the values that populate the lists of currency, product_type and unit when editing in a UI.
I wouldn't return more data than necessary for an individual Product object.

How to fill in several dropdowns subsequently from JSON in Elm?

We are stuck with filling in form from JSON data and need help. The component is about selecting the ward in the district of the given city.
The data structure is a tree of Cities, Districts, and Wards with approximately following structure (everything is wrapped in GeoJSON):
// Cities: '/api/cities/berlin'
{
features: [
{
type: "Feature",
properties: {
slug: "berlin",
name: "Berlin",
districts: [
{name: "Neukölln", slug: "neukolln", ...}
]
},
geometries: {...}
}
]
}
// Districts: '/api/cities/berlin/districts/neukolln'
{
features: [
{
type: "Feature",
properties: {
slug: "neukolln",
name: "Neukölln",
wards: [
{name: "Britz", slug: "britz", ...}
]
},
geometries: {...}
}
]
}
// Wards: '/api/cities/berlin/districts/neukolln/wards'
{
features: [
{
type: "Feature",
properties: {
slug: "britz",
name: "Britz",
},
geometries: {...}
}
]
}
In the view, three are three dropdown boxes for selecting City, District and Ward, thus, when User select City, then District dropdown is filled from the properties.districts field of the JSON response.
Same is applied for the Districts dropdown: wards are filled in from the properties.wards
When the page is loaded it already has an injected JSON of all available Cities (and, accordingly their districts)
What strategy would you advise on:
1) how to get currently selected city and hit server for next administrative divisions? I.e. when a user selects District, get its slug and query server for the wards?
2) how to fill subsequent select from the response or injected JSON on the page? I.e. when user select another City, fill District select box with respective Districts?
Here's how I have done something similar in the past (in elm v0.17.0):
dropdown : List City -> Html Msg
dropdown cities =
div []
[ div []
[ span [] [ text "dropdown label name" ]
, selectOptions cities
]
]
selectOptions : List City -> Html Msg
selectOptions cities =
select [ on "change" ( Json.map (\city -> GetDistrictsMsg city) targetValue ) ]
(List.map setOption cities)
setOption : City -> Html Msg
setOption city =
option [ value city.name ]
[ text city.name ]
And you will repeat the same for districts to get wards.
Start-up
If selected city is known, before the start-up, you can pass in as a flag to Html.App.programWithFlags
The same thing you can do to the list of cities.
Please see the http example, it covers most of the stuff.
If you want to send xhr request on start-up, you might use a little neat trick for that:
init : String -> (Model, Cmd Msg)
init topic =
( Model topic "waiting.gif"
, getRandomGif topic
)
Where getRandomGif will execute the xhr request on start-up, assuming that you have gotten some data for that from passing them as flags or from user input.
On every FetchSucceed, you should send the next xhr to grab the data for the next step.
The flow
Please consider this flow chart, illustrating the flow of your multi step form. Dashed arrows point to steps, where you can restart the cycle, if you want to change the city/district at some point.
Caching layer is optional, Elm offers a variety of data structures for that.

Is mongodb (or other nosql dbs) the best solution for the following scenario?

Considering the following data structures what would be better to QUERY the data once stored in a database system (rdbms or nosql)? The fields within the metadata field are user defined and will differ from user to user. Possible values are Strings, Number, "Dates" or even arrays.
var file1 = {
id: 123, name: "mypicture", owner: 1
metadata: {
people: ["Ben", "Tom"],
created: 2013/01/01,
license: "free",
rating: 4
...
},
tags: ["tag1", "tag2", "tag3", "tag4"]
}
var file2 = {
id: 155, name: "otherpicture", owner: 1
metadata: {
people: ["Tom", "Carla"],
created: 2013/02/02,
license: "free",
rating: 4
...
},
tags: ["tag4", "tag5"]
}
var file1OtherUser = {
id: 345, name: "mydocument", owner: 2
metadata: {
autors: ["Mike"],
published: 2013/02/02,
…
},
tags: ["othertag"]
}
Our users should have the ability to search/filter their files:
User 1: Show all files where "Tom" is in "people" array
User 1: Show all files "created" between 2013/01/01 and 2013/02/01
User 1: Show all files having "license" "free" and "rating" greater 2
User 2: Show all files "published" in "2012" and tagged with "important"
...
Results should be filtered in way like you can do in OS X with intelligent folders. The individual metadata fields are defined before files are being uploaded/stored. But they also may change after that, e.g. User 1 may rename the metadata field "people" to "cast".
As #WiredPrairie said, the field within the metadata field look variable, maybe dependant upon what the user enters which is supported by:
User 1 may rename the metadata field "people" to "cast".
MongoDB cannot create variable indexes whereby you just say that every new field in metadata gets added to the compound index, however you could do a key-value type structure like so:
var file1 = {
id: 123, name: "mypicture", owner: 1
metadata: [
{k: people, v:["Ben", "Tom"]},
{k: created, v:2013/01/01},
],
tags: ["tag1", "tag2", "tag3", "tag4"]
}
That is one method of doing this, allowing you to index on both k and v dynamically within the metadata field. You would then query by this like so:
db.col.find({metadata:{$elemMatch:{k:people,v:["Ben"]}}})
However this does introduce another problem. $elemMatch works on top level, not nested elements. Imagine you wanted to find all files where "Ben" was one of the people, you can't use $elemMatch here so you would have to do:
db.col.find({metadata.k:people,metadata.v:"Ben"})
The immediate problem with this query is in the way MongoDB queries. When it queries the metadata field it will say: where one field of "k" equals "people" and a field of "v" equals "Ben".
Since this is a multi-value field you could run into the problem where even though "Ben" is not in the peoples list, because he exists in another field on the metadata you actually pick out the wrong documents; i.e. this query would pick up:
var file1 = {
id: 123, name: "mypicture", owner: 1
metadata: [
{k: people, v:["Tom"]},
{k: created, v:2013/01/01},
{k: person, v: "Ben"}
],
tags: ["tag1", "tag2", "tag3", "tag4"]
}
The only real way to solve this is to factor off the dynamic fields to another collection where you don't have this problem.
This creates a new problem though, you can no longer get a full file with a single round trip and nor can you aggregate both the file row and its user defined fields in one go. So all in all you loose a lot of abilities by dong this.
That being said you can still perform quite a few queries, i.e.:
User 1: Show all files where "Tom" is in "people" array
User 1: Show all files "created" between 2013/01/01 and 2013/02/01
User 1: Show all files having "license" "free" and "rating" greater 2
User 2: Show all files "published" in "2012" and tagged with "important"
All of those would still be possible with this schema.
As for which is better -RDBMS or NoSQL; it is difficult to say here, I would say both could be quite good, if done right, at querying this structure.

I can't manage to create 3rd level of dijit.Tree

I wanted to create a 3 level dijit.Tree, like that:
-root
|
--level1
|
--level2
I thought it would be really simple since there's a code snippet in this tutorial (example 1). But somehow I manage to fail.
This is my dojo code (variable names are in Polish, I hope it's not a problem):
modelRaportow = new dijit.tree.ForestStoreModel({
store: new dojo.data.ItemFileReadStore({
url: "logika/getJSON/getStatusRaportow.php"
}),
query: {typ: 'galaz'},
rootId: 'statusRaportuRoot',
rootLabel: 'Status raportu',
childrenAttrs: 'raporty'
});
drzewoRaportow = new dijit.Tree({
openOnClick: true,
model: modelRaportow,
showRoot: true,
persist: false
}, "target-status-raportow");
drzewoRaportow.startup();
This is my JSON returned by logika/getJSON/getStatusRaportow.php (again, names are in Polish):
{
"identifier":"id",
"label":"status",
"items": [
{"id":0,"status":"zaakceptowane","typ":"galaz"
"raporty":[{"_reference":1},{"_reference":2},{"_reference":3}]},
{"id":1,"data":"24-10-2011","wykonujacy":"cblajszczak","idKlienta":3,"status":"Raport0","typ":"lisc"},
{"id":2,"data":"24-10-2011","wykonujacy":"cblajszczak","idKlienta":1,"status":"Raport1","typ":"lisc"},
{"id":3,"data":"24-10-2011","wykonujacy":"cblajszczak","idKlienta":3,"status":"Raport2","typ":"lisc"},
{"id":4,"status":"odrzucone","typ":"galaz"
"raporty":[{"_reference":5},{"_reference":6},{"_reference":7}]},
{"id":5,"data":"24-10-2011","wykonujacy":"cblajszczak","idKlienta":1,"status":"Raport3","typ":"lisc"},
{"id":6,"data":"24-10-2011","wykonujacy":"cblajszczak","idKlienta":3,"status":"Raport4","typ":"lisc"},
{"id":7,"data":"24-10-2011","wykonujacy":"cblajszczak","idKlienta":3,"status":"Raport5","typ":"lisc"}
]}
And finally, this is what I'm getting: img - root node and lvl 1 nodes returned by query, no child nodes.
The question is - where is my mistake? Can anyone see it?
You have no comma between the typ and raporty value pair.
I have a partial answer: by stepping through the code in a similar situation, I've discovered that it expects childrenAttrs to be an array, so it should be:
childrenAttrs: ['raporty']
but I still cannot get the third level to appear in my case.