I am working on a project where I want to show how various files within a website interact with each other. I thought that this would be a fairly simple task using D3, but now I am wondering about how my json data is arranged. Here is a sample of my data :
{
"pages" : [{
"ID" : "1",
"name" : "config.xml",
"class" : "dvs",
"path" : "/config.xml",
"children" : [{"dvs":"2","dvs":"3","sites":"4","sites":"5"}]
},{
"ID" : "2",
"name" : "site-pages.xml",
"class" : "dvs",
"path" : "/pages/site-pages.xml",
"children" : [{"dvs":"1"}]
},{
"ID" : "3",
"name" : "customscripts.vm",
"class" : "sites",
"path" : "components/customscripts.vm",
"children" : [{"dvs":"1","sites":"4","sites":"5"}]
},{
"ID" : "4",
"name" : "badge.vm",
"class" : "sites",
"path" : "components/badge.vm",
"children" : [{"dvs":"1","sites":"3"}]
},{
"ID" : "5",
"name" : "price.vm",
"class" : "sites",
"path" : "components/price.vm",
"children" : [{"dvs":"1","sites":"3"}]
}]
}
My plan was to use the ID's to show links between files and the classes to color code the types of files. When I started looking into d3.layout.tree() I started to realize that the layouts seem to be very dependant on having a specific data model, namely :
{ "name" : "foo",
"children" : [{
"name" : "bar",
"children" : [{
}]
}]
}
etc, etc, etc
Am I correct in thinking that the data being passed into d3.layout.tree() has to match that exact format?
Ultimately, I was hoping that I would be able to make each node of my data tree clickable, which would turn that node into the root node of the graph, but maybe that is just not possible?
Any guidance/advice on data formatting and the d3.layout.tree() would be much appreciated.
As for your desire to make a selected node the new root of the tree, I unearthed this fiddle that I started putting together sometime ago. It is simple minded and much can be done to improve it but it should provide you with a reasonable start.
function click(d) {
update(d.depth == 0 ? root : d)
}
Related
I have a collection that consists of posts. Inside those post objects are properties about the post, one of which being an array of tags associated with the post. Inside the array of tags are more objects, each of which are the tags with a name property. I would like to get back all the posts from this collection which have a given tag in them based on the name of the tag.
Here is an example of a post object within the collection:
{
"_id" : ObjectId("5c6a0478cba09c148b497fc6"),
"tags" : [
{
"_id" : "5c69d974e05511106048780e",
"name" : "Food",
"text_color" : "#ffffff",
"bg_color" : "#02569b",
"createdAt" : "2019-02-17T22:00:20.143Z",
"updatedAt" : "2019-02-17T22:00:20.143Z",
"__v" : 0
},
{
"_id" : "5c69d95de05511106048780d",
"name" : "Drinks",
"text_color" : "#ffffff",
"bg_color" : "#0175c2",
"createdAt" : "2019-02-17T21:59:57.758Z",
"updatedAt" : "2019-02-17T21:59:57.758Z",
"__v" : 0
}
],
"title" : "Title of the post",
"body" : "body of the post",
"author_id" : ObjectId("5c5e0d3b647f12e949cbea1e"),
"author_name" : "garrett",
"likes_count" : 1,
"createdAt" : ISODate("2019-02-18T01:03:52.497Z"),
"updatedAt" : ISODate("2019-02-28T00:25:21.969Z"),
"__v" : 0,
"dislikes_count" : 0
}
Of course I have other post objects in this collection, some of which may have different tags. How can I get all posts with the food tag to be returned?
A basic find query will do the job here. Here's how it looks
const tagName = 'food;
db.posts.find({ 'tags.name': tagName }).then(console.log);
I have a json object which has an array property 'partners' like below:
{
"_id" : ObjectId("578667b1bb14ca1c2773adaa"),
"customer" : null,
"created" : ISODate("2016-07-13T16:09:21.015+0000"),
"description" : "",
"subject" : "case 2",
"__v" : NumberInt(5),
"partners" : [
ObjectId("57857d93038aacd81ef3ad55"),
ObjectId("57857d93038aacd81ef3ad56")
]
}
I would like to check if an element already exists on the partner array using the following code:
_.includes(case_.partners, partner._id)
Just so u know I am using lodash for this but for some reason is not working and I have no clue why, anyone knows what is the problem here?
What I understanding, the partners sample you provided, is an javascript object, you should try to use primitive type value in the Json.
You can convert ObjectId to String by these way:
ObjectId("578667b1bb14ca1c2773adaa") + '';
or
ObjectId("578667b1bb14ca1c2773adaa").toHexString();
The Json object should be like this:
{
"_id" : "578667b1bb14ca1c2773adaa",
"customer" : null,
"created" : "2016-07-13T16:09:21.015+0000",
"description" : "",
"subject" : "case 2",
"__v" : 5,
"partners" : [
"57857d93038aacd81ef3ad55",
"57857d93038aacd81ef3ad56"
]}
So the solution will be , convert Javascript object into Json object, and then compare them.
I'm using a model tree structure for my collection. As references I'm using parent-fields. I need to get attributes from the current object and all its parents. The last element in a path has a field 'target'. So I start with
var result = parent = Articles.findOne({target: this.params._id});
do {
parent = Articles.findOne({_id: parent.parent}).parent;
for (var attrname in parent) { result[attrname] = parent[attrname]; }
}
while (parent.parent === null);
That seems to be very inefficient to me. Isn't it possible to do that with one line to get an object with all elements? Then I could process that object.
Example documents
{
"_id" : "LD6h5ZcDuJjexfKfx",
"title" : "title",
"publisher" : "public",
"author" : "author"
}
{
"_id" : "KSiyh8zHRq8RZQ2E6",
"edition" : "edition",
"year" : "2020",
"parent" : "LD6h5ZcDuJjexfKfx"
}
{
"_id" : "5yCk4y25wrLBLZhyY",
"pageNumbers" : "1-10",
"target" : "9sjhzPhyTuQ5Kbh6v",
"parent" : "KSiyh8zHRq8RZQ2E6"
}
So starting with "target" : "9sjhzPhyTuQ5Kbh6v" I would like to get the two parent documents (in this example).
At least I need the dataset
"title" : "title",
"publisher" : "public",
"author" : "author",
"edition" : "edition",
"year" : "2020",
"pageNumbers" : "1-10"
If you want to do this in a single query then you need to follow the array of ancestors pattern in Mongodb. Otherwise you need to recursively traverse the branches above the leaf node as you are doing. For hierarchies with low depth such as yours this is not a big penalty.
With an array of ancestors your doc tree would look like:
{
"_id" : "LD6h5ZcDuJjexfKfx",
"title" : "title",
"publisher" : "public",
"author" : "author",
}
{
"_id" : "KSiyh8zHRq8RZQ2E6",
"edition" : "edition",
"year" : "2020",
"ancestors" : ["LD6h5ZcDuJjexfKfx"],
"parent" : "LD6h5ZcDuJjexfKfx"
}
{
"_id" : "5yCk4y25wrLBLZhyY",
"pageNumbers" : "1-10",
"target" : "9sjhzPhyTuQ5Kbh6v",
"ancestors" : ["LD6h5ZcDuJjexfKfx","KSiyh8zHRq8RZQ2E6"],
"parent" : "KSiyh8zHRq8RZQ2E6"
}
To get the doc and its parents:
Articles.find({ $or: [ { target: target },
_id: { $in: Articles.findOne({ target: target }).ancestors }]});
I have a JSON file containing information for a tile based navigation app which uses the Router. Each tile could be a link directly to an external application, or it could contain subtiles which are displayed when the tile is clicked. Each of these could have their own subtiles, and so on. The JSON will eventually be delivered by an OData service so the app needs to dynamically create the navigation as it may change.
How can I implement the Router to have the URL looking like #tile1/tile1-1/tile1-1-3 which would indicate the user clicked on the first tile, which went to a screen where they clicked on the first tile there, followed by another screen on which they clicked the third tile? That route would, when coming from a bookmark, load the screen with subtiles from the tile1-1-3 node from the JSON.
The names 'tile1-1-3' etc are only to help visualise the position of the tile for this example. In the real version the names won't indicate the position in the tree, they will be more like #MyApps/MyApprovalApps.
I have a recursive function which crawls through every node and generates an individual route, but I'm unsure how to add the dynamic pattern like {tile}/{subtile}/{subtile} and also the parent route which I think will be needed to navigate between the levels properly.
I have a Home.view.xml which displays the top level tiles, and a Page1.view.xml for the rest of the levels of subtiles. Is this correct? How can I create the views dynamically if not?
Hopefully my goal is clear, I can elaborate more if needed.
Recursive function which creates the routes:
createRoutes: function(aData, oRouter){
for(var i=0; i<aData.length; i++){
oRouter.addRoute({name: aData[i].id,
pattern: aData[i].title,
view: "Page1"}); //parent?
if(aData[i].subtiles.length > 0){ // has subtiles
this.createRoutes(aData[i].subtiles, oRouter);
}
}
}
JSON:
{
"TilesCollection" : [
{
"id" : "tile1",
"title" : "tile1",
"target" : "#",
"subtiles" : [
{
"id" : "tile1-1",
"title" : "tile1--1",
"target" : "#",
"subtiles" : []
}
]
},
{
"id" : "tile2",
"title" : "tile2",
"target" : "#",
"subtiles" : [
{
"id" : "tile2-1",
"title" : "tile2--1",
"target" : "#",
"subtiles" : []
},
{
"id" : "tile2-2",
"title" : "tile2--2",
"target" : "#",
"subtiles" : []
},
{
"id" : "tile2-3",
"title" : "tile2--3",
"target" : "#",
"subtiles" : [
{
"id" : "tile2-3-1",
"title" : "tile2--3--1",
"target" : "#",
"subtiles" : []
},
{
"id" : "tile2-3-2",
"title" : "tile2--3--2",
"target" : "#",
"subtiles" : []
}
]
}
]
},
{
"id" : "tile3",
"title" : "tile3",
"target" : "#",
"subtiles" : []
},
{
"id" : "tile4",
"title" : "tile4",
"target" : "#",
"subtiles" : [
{
"id" : "tile4-1",
"title" : "tile4--1",
"target" : "#",
"subtiles" : []
}
]
}
]
}
How about this?
createRoutes: function(aData, oRouter, sParentPattern, iViewLevel) {
iViewLevel = iViewLevel || 0;
for (var i=0; i<aData.length; i++) {
var sPattern = sParentPattern ? sParentPattern +"/"+ aData[i].title : aData[i].title;
oRouter.addRoute({
name: aData[i].id,
pattern: sPattern,
view: "Page1",
viewLevel : iViewLevel
});
if (aData[i].subtiles.length > 0) {
this.createRoutes(aData[i].subtiles, oRouter, sPattern, iViewLevel+1);
}
}
}
In this example you just use the pattern to build the parent-child relationship just as you suggested.
If I understand you correctly you're asking how can you represent th clicking route as a string, whcih you can pass as prt of a URL ?
Referring to the tile IDs...
You could pass the text in as a JS array of objects I guess (ie include this JSON as part of the URL) :
a.b.com/xyz?route=[{"tile1-1",{"tile1-1"}},{"tile2",{"tile2-3"}}]
meaning they clicked tile1 -> tile1-1 -> tile2 -> tile2-3
Alternatively if the Ids are potentially secure or something, you could pass it by index no. as it's an aray :
a.b.com/xyz?route=[{0,{0}},{0,{2}}]
Then eval the passed string to turn it directly into Javascript object.
Or if you're concerned about people hacking, write a routine to interpret it.
(same clicking as above) - this relies on the tile arrangement never changing though.
I pass some json data in jstree and then I update the tree.
For example my initial data is
[
{ "id" : "demo_root_1", "text" : "Root 1", "children" : true, "type" : "root" },
{ "id" : "demo_root_2", "text" : "Root 2", "type" : "root" }
]
I update it a little (simple rename). "Root 1" becomes "UPDATED". How can I get the updated version of my data in json format like below?
[
{ "id" : "demo_root_1", "text" : "UPDATED", "children" : true, "type" : "root" },
{ "id" : "demo_root_2", "text" : "Root 2", "type" : "root" }
]
Okay..so basically if you want to refresh the tree with the updated data, then after the updation code, call the jstree callback:
$(tree).jstree('refresh');
This will refresh the tree with the new json data.
let me know in case of further issues.