I'm using ajax as my data source, as you can see from the code below. However, I also need to programmatically insert nodes into the tree. When I do this, jsTree is making an ajax callback after the node is created. Is there any way to suppress this based on node attributes? In my particular case, I have nodes that are listed in a CORBA name service and nodes that are not. The ones that are in the CORBA name service are working perfectly, and are available via AJAX. I want to insert arbitrary nodes, with the attribute 'ns'='none' into the tree and NOT have it make an ajax call. Here is my code:
function renderJsonTree() {
$("#JsonTree").jstree({
"plugins" : [ "json_data", "types", "themes", "ui", "cookies" ],
"ui" : {"select_limit" : 1},
"json_data" : {
"ajax" :
{
"url":"lookup",
"data":function(n) {
return {"server":n.attr?n.attr('data-ns'):'',
"longName":n.attr?n.attr('data-longname'):'',"isObj":n.attr?n.attr('data-isObj'):'n'};
}
}
},
"types": {
"valid_children" : [ "nameServer" ],
"types" : {
"nameServer": {
"icon": {"image":"/corba_browser/static/images/drive.png"},
"valid_children" : [ "default" ],
},
"global": {
"icon": {"image":"/corba_browser/static/images/globe.png"},
"valid_children" : [ "default" ]
},
"object": {
"icon": {"image":"/corba_browser/static/images/alien.png"},
"valid_children" : [ "default" ]
},
"default" : {"valid_children" : [ "default" ]}
}
}
}).bind("loaded.jstree", function (event, data) {
jQuery("li").css('line-height', "15pt");
jQuery("li").css('font-size', "12pt");
}).bind("open_node.jstree close_node.jstree", function (e,data) {
jQuery("li").css('line-height', "15pt");
jQuery("li").css('font-size', "12pt");
var currentNode = data.args[0];
if(e.type === "close_node") {
currentNode.addClass("to-be-refreshed");
}
if(e.type === "open_node") {
if (currentNode.attr('ns')!='none') {
if (currentNode.hasClass("to-be-refreshed")) {
var tree = $.jstree._reference("#JsonTree");
tree.refresh(currentNode);
}
}
}
});
};
Related
I have mongo collection with survey answers submitted by each user. I would like to get the count of users selected as an option. Only one user has selected the option O12. The output should be 1.
{
"_id" : ObjectId("5ea179eb39ff117948f19266"),
"_class" : "model.survey.Answer",
"survey_id" : "5ea178c239ff117948f19265",
"survey_user" : [
{
"user_id" : 1072,
"user_option" : [
{
"question_id" : "Q1",
"option_id" : "O11"
},
{
"question_id" : "Q2",
"option_id" : "O21"
},
{
"question_id" : "Q3",
"option_id" : "O31"
},
{
"question_id" : "Q4",
"option_id" : "O41"
}
]
},
{
"user_id" : 1073,
"user_option" : [
{
"question_id" : "Q1",
"option_id" : "O12"
},
{
"question_id" : "Q2",
"option_id" : "O21"
},
{
"question_id" : "Q3",
"option_id" : "O31"
},
{
"question_id" : "Q4",
"option_id" : "O41"
}
]
}
]
}
You can do that using MongoDB's aggregation-pipeline :
Different ways to do it, One way is to use $unwind:
Type 1 - Query 1 :
db.collection.aggregate([
/** Optional but will be good on huge collections to lessen data for further stages */
{
$match: { "survey_user.user_option.option_id": "O12" }
},
{
$unwind: "$survey_user"
},
/** When you unwind a each object/element in array gets it's own document after `unwind` stage */
{
$match: { "survey_user.user_option.option_id": "O12" }
},
/** After match you'll only have objects which met the criteria in `survey_user` array */
/** group on `_id` & push entire original doc to data field */
{
$group: { _id: "$_id", survey_user: { $push: "$survey_user" }, data: { $first: "$$ROOT" } }
},
/** Add `survey_user` array to `data.survey_user` & it's size to `data.optedCount` field */
{
$addFields: { "data.survey_user": "$survey_user", "data.optedCount": { $size: "$survey_user" } }
},
/** Make `data` as new root to doc */
{
$replaceRoot: { newRoot: "$data" }
}
])
Test : mongoplayground
Just in case if you just need count but not needed the entire doc to be returned there will be a minor change in above query :
Type 1 - Query 2 :
db.collection.aggregate([
{
$match: { "survey_user.user_option.option_id": "O12" }
},
{
$unwind: "$survey_user"
},
{
$match: { "survey_user.user_option.option_id": "O12" }
},
/** Just group on `_id` & count no.of docs, maintain `survey_id` */
{
$group: { _id: "$_id", optedCount: { $sum: 1 }, survey_id: { $first: "$survey_id" } }
}
])
Test : mongoplayground
Using array iterator $reduce, which might be helpful if your collections data is so huge, as unwind will explode your docs.
Type 2 - Query :
db.collection.aggregate([
{
$match: {
"survey_user.user_option.option_id": "O12",
},
},
/** Instead of `$addFields`, you can use `$project` to project fewer needed fields (which can be help improve query with performance benefits ) */
{
$addFields: {
optedCount: {
$reduce: {
input: "$survey_user",
initialValue: 0,
in: {
$cond: [
{ $in: ["O12", "$$this.user_option.option_id"] },
{ $add: ["$$value", 1] },
"$$value",
]
}
}
}
}
}
]);
Test : mongoplayground
Need help to build dynamic MongoDB query.
everything inside the "$or" Array is dynamic.
db.group.find({
"version" : NumberLong(0),
"$or" : [{
"$and" : [
{
"object_type" : "D"
},
{
"type" : "R"
},
{
"name" : "1"
}
]
},{
"$and" : [
{
"object_type" : "D"
},
{
"type" : "E"
},
{
"name" : "2"
}
]
]
});
Did the below spring data query but doesn't work
Criteria criteria = Criteria.where("version").is("123");
List<Criteria> docCriterias = new ArrayList<Criteria>();
groups.stream().forEach(grp -> {
docCriterias.add(Criteria.where("type").is(grp.get("type").toString())
.andOperator(Criteria.where("object_type").is(grp.get("objectType").toString()))
.andOperator(Criteria.where("name").is(grp.get("name").toString())));
});
criteria.orOperator((Criteria[]) docCriterias.toArray());
Query q = new Query(criteria);
Thanks for the help
You should pay attention to how you combine the operators.
The ff code should work for you (note this is groovy remember to change the closure into to java lambda expression):
List<Criteria> docCriterias = new ArrayList<Criteria>();
List groups = [
[
type: "type1",
object_type: "object_type1",
name: "name1"
],
[
type: "type2",
object_type: "object_type2",
name: "name2"
],
[
type: "type3",
object_type: "object_type3",
name: "name3"
],
]
groups.stream().each {grp ->
docCriterias.add(new Criteria().andOperator(
Criteria.where("type").is(grp.get("type")),
Criteria.where("object_type").is(grp.get("object_type")),
Criteria.where("name").is(grp.get("name"))
))
};
Criteria criteria = new Criteria().andOperator(
Criteria.where("version").is("123"),
new Criteria().orOperator(docCriterias.toArray(new Criteria[docCriterias.size()]))
);
Query q = new Query(criteria);
Which will give you this query:
{
"$and":[
{
"version":"123"
},
{
"$or":[
{
"$and":[
{
"type":"type1"
},
{
"object_type":"object_type1"
},
{
"name":"name1"
}
]
},
{
"$and":[
{
"type":"type2"
},
{
"object_type":"object_type2"
},
{
"name":"name2"
}
]
},
{
"$and":[
{
"type":"type3"
},
{
"object_type":"object_type3"
},
{
"name":"name3"
}
]
}
]
}
]
},
Fields:{
},
Sort:{
}
You could reach this using MongoDB Aggregation Pipeline in Json and Apache Velocity to customize more the Query, then execute this using db.runCommand using Spring MongoTemplate.
Example:
monodb_client_dynamic_query.vm
{
"aggregate": "client",
"pipeline": [
{
"$match" : {
"$and" : [
{
"is_removed" : {
"$ne" : [
true
]
}
},
{
"errors" : {
"$size" : 0.0
}
},
{
"client_id": "$velocityMap.client_id"
}
]
}
},
{
"$project" : {
"_id" : -1.0,
"account" : "$_id.account",
"person_id" : "$_id.person_id",
"begin_date": { $dateToString: { format: "%Y-%m-%d", date: "$value.begin_date" } },
"end_date": { $dateToString: { format: "%Y-%m-%d", date: "$value.end_date" } }
}
}
]
}
Then execute using MondoTemplate:
String script = ...load from file the script monodb_client_dynamic_query.vm
Map parameters = ... put all variables to replace in the mongodb script
String scriptNoSql = VelocityUtil.loadTemplateVM(script, parameters);
DBObject dbObject = (BasicDBObject) JSON.parse(scriptNoSql);
if (null == dbObject) {
return;
}
DB db = mongoTemplate.getDb();
CommandResult result = db.command(dbObject);
if(!result.ok()) {
throw result.getException();
}
Basically I am trying to use REST hreat to create two variables issuerId and sectionName. I am trying to get data from the URI mentioned below
http://ftc-wbpyrdb201:8080/statdata/InsStatData/_aggrs/getDataByIssuerAndSectionName?avars={"issuerId":19038},{"sectionName":"ASSETS"}
My aggregation definition is a below
{"$push" : {
"aggrs":
{
"type": "pipeline",
"uri": "getDataByIssuerAndSectionName",
"stages": [
{
"_$match": {
"_$and": [
{"issuerId": {"_$var": "issuerId" }},
{
"sectionName": {"_$var": "sectionName"}
}
]
}
},
{
"_$unwind": "$sections"
},
{
"_$unwind": "$sections.data"
},
{
"_$unwind": "$sections.data.values"
},
{
"_$match": {
"_$and": [
{"issuerId": {"_$var": "issuerId" }},
{
"sectionName": {"_$var": "sectionName"}
}
]
}
}
]
}
}
}
I am sure I am doing something wring in either defining the aggregation or while requesting the URI. There is not enough in the documentation. Please help me with this request.
so what i'm trying to do is to call some data from a json file to fill my table. But instead of having a lot of json files, i'd like to keep some of the data in a single file. For example, i have 2 types of clients (person and company) and i want to keep them both in the same json, but as different objects, because each one goes in different table.
The problem is: I can't access the data in the JSON file.
I can use it normally if i use the 2 type of clients in different files (person.json and company.json - This works fine).
I did tried these 2 examples, but none of them worked for me.
https://www.datatables.net/examples/ajax/deep.html
https://www.datatables.net/examples/ajax/objects_subarrays.html
So, how can i do get the data from the file? Here is my json and my datatable code:
{
"clients": {
"person": [
{
"cd":0,
"id":"C-0010",
"nm_cliente":"Name AAA",
"dt_nasc":"02/11/1990",
"info":"Some basic info"
},
{
"cd":1,
"id":"C-0013",
"nm_cliente":"Name BBB",
"dt_nasc":"02/11/1990",
"info":"Some basic info"
},
{
"cd":2,
"id":"C-0017",
"nm_cliente":"Name CCC",
"dt_nasc":"02/11/1990",
"info":"Some basic info"
}
],
"company": [
{
"cd":0,
"id":"C-0032",
"nm_cliente":"Name Client",
"num_cnpj":"111.222.3333/0001-22",
"nm_cidade":"City AAA"
},
{
"cd":1,
"id":"C-0033",
"nm_cliente":"Client Name",
"num_cnpj":"111.222.3333/0001-22",
"nm_cidade":"City BBB"
},
{
"cd":2,
"id":"C-0035",
"nm_cliente":"jEmpresa teste",
"num_cnpj":"111.222.3333/0001-22",
"nm_cidade":"City CCC"
}
]
}
}
I already used jsonlint.com to verify, and everything is ok with my JSON. And here is how i'm trying to call this data into my tables.
TableA - Client type Person
//rest of the code goes here...
"aoColumns" : [
{ "mData": "person.id" },
{ "mData": "person.nm_cliente" },
{ "mData": "person.dt_nasc" },
{ "mData": "person.info" }
]
TableB - Client type Company
//rest of the code goes here...
"aoColumns" : [
{ "mData": "company.id" },
{ "mData": "company.nm_cliente" },
{ "mData": "company.nm_cnpj" },
{ "mData": "company.nm_cidade" }
]
I keep getting error like "lenght not defined"
Can anyone help me?
You must refer to the person and company arrays themselves, not the items. Updated to demonstrate initialization after load of JSON and how to reuse and manipulate the options :
//an options object, some example settings
var options = {
bPaginate: true,
sPaginationType: "full_numbers",
aLengthMenu: [25,50,100,500]
//etc
}
function initialize(json) {
options.aaData = json.clients.person;
options.aoColumns = [
{ "mData": "id" },
{ "mData": "nm_cliente" },
{ "mData": "dt_nasc" },
{ "mData": "info" }
];
$("#tableA").dataTable(options);
options.aaData = json.clients.company;
options.aoColumns = [
{ "mData": "id" },
{ "mData": "nm_cliente" },
{ "mData": "num_cnpj" },
{ "mData": "nm_cidade" }
];
$("#tableB").dataTable(options);
}
$.getJSON("your.json", function(json) {
initialize(json);
});
original demo showing how to inject the JSON -> http://jsfiddle.net/3g5wcyet/
reuseable options demo -> http://jsfiddle.net/bpgvfefd/
I'm trying to get a jsTree working with on demand loading of subnodes. My code is this:
jQuery('#introspection_tree').jstree({
"json_data" : {
"ajax" : {
url : "http://localhost/introspection/introspection/product"
}
},
"plugins" : [ "themes", "json_data", "ui" ]
});
The json returned from the call is
[
{
"data": "Kit 1",
"attr": {
"id": "1"
},
"children": [
[
{
"data": "Hardware",
"attr": {
"id": "2"
},
"children": [
]
}
],
[
{
"data": "Software",
"attr": {
"id": "3"
},
"children": [
]
}
]
]
}
.....
]
Each element could have a lot of children, the tree is going to be big. Currently this is loading the whole tree at once, which could take some time. What do I have to do to implement on-demand-loading of child-nodes when they are opened by the user?
Thanks in advance.
Irishka pointed me in the right direction, but does not fully resolve my problem. I fiddled around with her answer and came up with this. Using two different server functions is done only for clarity. The first one lists all products at top level, the second one lists all children of a given productid:
jQuery("#introspection_tree").jstree({
"plugins" : ["themes", "json_data", "ui"],
"json_data" : {
"ajax" : {
"type": 'GET',
"url": function (node) {
var nodeId = "";
var url = ""
if (node == -1)
{
url = "http://localhost/introspection/introspection/product/";
}
else
{
nodeId = node.attr('id');
url = "http://localhost/introspection/introspection/children/" + nodeId;
}
return url;
},
"success": function (new_data) {
return new_data;
}
}
}
});
The json data returned from the functions is like this (notice the state=closed in each node):
[
{
"data": "Kit 1",
"attr": {
"id": "1"
},
"state": "closed"
},
{
"data": "KPCM 049",
"attr": {
"id": "4"
},
"state": "closed"
},
{
"data": "Linux BSP",
"attr": {
"id": "8"
},
"state": "closed"
}
]
No static data is needed, the tree is now fully dynamic on each level.
I guess it would be nice to display by default first level nodes and then the children will be loaded on demand. In that case the only thing you have to modify is to add "state" : "closed" to the nodes whose child nodes are going to be loaded on demand.
You might wish to send node's id in ajax call so you modify your code
"json_data": {
//root elements to be displayed by default on the first load
"data": [
{
"data": 'Kit 1',
"attr": {
"id": 'kit1'
},
"state": "closed"
},
{
"data": 'Another node of level 1',
"attr": {
"id": 'kit1'
},
"state": "closed"
}
],
"ajax": {
url: "http://localhost/introspection/introspection/product",
data: function (n) {
return {
"nodeid": $.trim(n.attr('id'))
}
}
}
}
From jsTree documentation
NOTE:
If both data and ajax are set the initial tree is rendered from the data string. When opening a closed node (that has no loaded children) an AJAX request is made.
you need to set root elements as tree data on page load and then you will be able to retrieve their children with an ajax request
$("#introspection_tree").jstree({
"plugins": ["themes", "json_data", "ui"],
"json_data": {
//root elements
"data": [{"data": 'Kit 1', "attr": {"id": 'kit1'}} /*, ... */], //the 'id' can not start with a number
"ajax": {
"type": 'POST',
"data": {"action": 'getChildren'},
"url": function (node) {
var nodeId = node.attr('id'); //id="kit1"
return 'yuorPathTo/GetChildrenScript/' + nodeId;
},
"success": function (new_data) {
//where new_data = node children
//e.g.: [{'data':'Hardware','attr':{'id':'child2'}}, {'data':'Software','attr':{'id':'child3'}}]
return new_data;
}
}
}
});
See my answer to a similar question here (the old part) for more details
I spended hours on this problem. Finally i got it that way:
$("#resourceTree").jstree({
"types": {
"default": {
"icon": "fa fa-folder-open treeFolderIcon",
}
},
"plugins": ["json_data", "types", "wholerow", "search"],
"core": {
"multiple": false,
"data": {
"url" : function(node){
var url = "rootTree.json";
if(node.id === "specialChildSubTree")
url = "specialChildSubTree.json";
return url;
},
"data" : function(node){
return {"id" : node.id};
}
}
},
});
rootTree.json:
[
{
"text": "Opened root folder",
"state": {
"opened": true
},
"children": [
{
"id" : "specialChildSubTree",
"state": "closed",
"children":true
}
]
}
]
specialChildSubTree.json:
[
"Child 1",
{
"text": "Child 2",
"children": [
"One more"
]
}
]
So i mark the node that become the parent of the ajax loaded subtree with an id, i watch for in the core configuration.
NOTE:
That node must have the "state" : "closed" parameter and it must have
the parameter "children" : true.
I am using jsTree.js in version 3.3.3
Above solution is all fine. Here I am also providing similar working solution and very simple for lazy loading of nodes using ajax call vakata. When your API works like
https://www.jstree.com/fiddle/?lazy
and for getting any child nodes
https://www.jstree.com/fiddle/?lazy&id=2
for explanation and for complete solution you can have a look at https://everyething.com/Example-of-jsTree-with-lazy-loading-and-AJAX-call
<script type="text/javascript">
$(function () {
$('#SimpleJSTree').jstree({
'core' : {
'data' : {
'url' : "https://www.jstree.com/fiddle/?lazy",
'data' : function (node) {
return { 'id' : node.id };
}
}
}
});
});
</script>