Dynamically create a TW object in IBM BPM - javascript

I am using IBM BPM 8.6
I have an input string as follows:
"\"RECORD_CONTACT\":\"Maram\" , \"DRUG\":\"Panadol\"
In a script on server side, I want to dynamically create a business object like this:
tw.local.recordContact = Maram;
tw.local.drug = Panadol;
How can I dynamically create the business object?

There are a few problems with your request. The first is that you are not creating a business object, you are creating variables. In IBM BPM the variables have to be declared at design time or you will get an error, so invoking attempting to call something like -
tw.local.myVariable = 'Bob';
Will throw an exception if tw.local.myVariable has not been declared. Base on your other question you asked here (link), I'm going to assume you actually have an ANY variable declared called "return" so that
tw.local.return.myVariable = 'Bob'
will work. Given that I based on Sven's answer I think something like the following will work (you will need to validate)
var str = "\"RECORD_CONTACT\":\"Maram\" , \"DRUG\":\"Panadol\"";
var jsonStr = "{" + str.replace(/\\\"/g,'\"') + "}";
var tempValue = JSON.parse(jsonStr);
var keyArray = Object.keys(tempValue);
var valueArray = Object.values(tempValue);
for(var keyCount=0; keyCount<keyArray.length; keyCount++{
var evalString = "tw.local.return."+keyArray[keyCount]+"="+valueArray[keyCount];
eval(evalString);
}
I'll note that doing this is a very bad idea as it would be very brittle code and that using eval() in this manner opens you up to all sorts of possible exploits. It will also fail badly if the value for one of the keys is not a simple type.
-Andrew Paier

One should know what you are going to do with dynamically created Business Objects (BO) to answer you better. Like a very generic way would be - creating JSON object instead of BO.
But if you want to stick with BO then this is only possible when you know all the BO structure (schema) beforehand during design time.
var str = "\"RECORD_CONTACT\":\"Maram\" , \"DRUG\":\"Panadol\"";
vat objArray = str.split("reg ex to split each object string")
foreach (obj in objArray ){
if(obj.indexOf( "RECORD_CONTACT")!=-1)
tw.local.recordContact = new tw.object.RECORD_CONTACT();
//below goes code get value of each attribute of BPM from string
}
else if(obj.indexOf( "DRUG")!=-1){
//similar code to create BO DRUG
}
Don't forget to create BO before using those :)

Related

Writing beautiful JSON using multiple line strings?

Context
For personal use, im creating a small project that documents my learning about data structures. I want to dynamically pull strings from a JSON file into a custom Vue component that displays code that can be run and edited in the browser by the user.
The Probem
I find the above image far easier to, not only type, but also to read, than something like this (including variations including escape strings on a single line):
"BasicArithmic" : "var a = 1;var b = 2;var c = a + 5*b;return c;"
Ultimately i just want to be able to type the string out as multiple lines inside the code so that i'm not wasting my time trying to work out what code is supposed to do.
Clarification
Im passing in code as a string to an eval(), since i couldn't work out how to pass code between components and THEN convert the code to a string.
Use ` instead of "
console.log(`
a = 1;
b = 2;
`
)
Or you could add \ to the end of each line:
x = { "BasicArithmic" : "\
var a = 1;\
var b = 2;\
var c = a + 5*b;\
return c;\
"}
console.log(x.BasicArithmic);

How do I pass a Java Map<Long, Integer> into scala-play?

In my play framework project I have a confirmed functional Java map of the form Java that is passed to a html page home.scala.html
The map variable is passed in as other (working) variables are, at the top of the page:
#(workingVar1: String, workingVar2: Int, mapVar: Map[Long, Integer])
But developer tools in google chrome highlights this part of the javascript (embedded in home.scala.html's head):
var myMap = #mapVar;
With the error Uncaught SyntaxError: Unexpected token =
So none of the javascript works. What is the correct way to pass this map in?
You can use Java/Scala variables in Twirl Scala templates and both are executed on the server side.
Now on server side Twirl engine translates Java object to something (which probably isn't what you want) and in this form is passed to client, and then this JavaScript is executed.
You want to make sure that client will receive valid JavaScript code.
To assign proper value, you will have to mix some JSON libraries, which will help you assign value in a proper way.
Eg. on the controller side:
...
Map<Long, Integer> map = new HashMap<>();
map.put(1L, 2);
map.put(3L, 3);
String yourMap = Json.stringify(Json.toJson(map));
Now you want to pass yourMap to view, and then you will assign to myMap
using #Html as we want it as raw content fragment:
#(workingVar1: String, workingVar2: Int, mapVar: String)
var myMap = #Html(mapVar);
Try and let me know if it helped.
An inelegant but functional solution is as follows:
Bring in the Java Map as a string:
var stringMap = "#mapVar";
Removes the braces and spaces inserted into the string unnecessarily
stringMap = stringMap.replace(/{/g,'');
stringMap = stringMap.replace(/}/g,'');
stringMap = stringMap.replace(/ /g,'');
Split the mapString by , and for every pair split again by =, extracting keys and values as you go. These will need to be parsed to their correct data-types before adding to a javascript array jsArr:
var pairArray = mapString.split(",");
pairArray.forEach(function(pair) {
var values = pair.split("=");
var longString = values[0];
var intString = values[1];
var myLong = parseFloat(longString);
var myInt = parseInt(intString);
jsArr.myLong = myInt;
}
Where jsArr has been defined previously.

Delete multiple documents

The following code is working but extremely slow. Up till the search function all goes well. First, the search function returns a sequence and not an array (why?!). Second, the array consists of nodes and I need URI's for the delete. And third, the deleteDocument function takes a string and not an array of URI's.
What would be the better way to do this? I need to delete year+ old documents.
Here I use xdmp.log in stead of document.delete just te be safe.
var now = new Date();
var yearBack = now.setDate(now.getDate() - 365);
var date = new Date(yearBack);
var b = cts.jsonPropertyRangeQuery("Dtm", "<", date);
var c = cts.search(b, ['unfiltered']).toArray();
for (i=0; i<fn.count(c); i++) {
xdmp.log(fn.documentUri(c[i]), "info");
};
Doing the same with cts.uris:
var now = new Date();
var yearBack = now.setDate(now.getDate() - 365);
var date = new Date(yearBack);
var b = cts.jsonPropertyRangeQuery("Dtm", "<", date);
var c = cts.uris("", [], b);
while (true) {
var uri = c.next();
if (uri.done == true){
break;
}
xdmp.log(uri.value, "info");
}
HTH!
Using toArray will work but is most likely were your slowness is. The cts.search() function returns an iterator. So All you have to do is loop over it and do your deleting until there is no more items in it. Also You might want to limit your search to 1,000 items. A transaction with a large number of deletes will take a while and might time out.
Here is an example of looping over the iterator
var now = new Date();
var yearBack = now.setDate(now.getDate() - 365);
var date = new Date(yearBack);
var b = cts.jsonPropertyRangeQuery("Dtm", "<", date);
var c = cts.search(b, ['unfiltered']);
while (true) {
var doc = c.next();
if (doc.done == true){
break;
}
xdmp.log(fn.documentUri(doc), "info");
}
here is an example if you wanted to limit to the first 1,000.
fn.subsequence(cts.search(b, ['unfiltered']), 1, 1000);
Several things to consider.
1) If you are searching for the purpose of deleting or anything that doesnt require the document body, using a search that returns URIs instead of nodes can be much faster. If that isnt convenient then getting the URI as close to the search expression can achieve similar results. You want to avoid having the server have to fetch and expand the document just to get the URI to delete it.
2) While there is full coverage in the JavaScript API's for all MarkLogic features, the JavaScript API's are based on the same underlying functions that the XQuery API's use. Its useful to understand that, and take a look at the equivalent XQuery API docs to get the big picture. For example Arrays vs Iterators - If the JS search API's returned Arrays it could be a huge performance problem because the underlying code is based on 'lazy evaluation' of sequences. For example a search could return 1 million rows but if you only look at the first one the server can often avoid accessing the remaining 999,999,999 documents. Similarly, as you iterate only the in scope referenced data needs to be in available. If they had to be put into an array then all results would have to be pre-fetched and put put in memory upfront.
3) Always keep in mind that operations which return lists of things may only be bounded by how big your database is. That is why cts.search() and other functions have built in 'pagination'. You should code for that from the start.
By reading the users guides you can get a better understanding of not only how to do something, but how to do it efficiently - or even at all - once your database becomes larger than memory. In general its a good idea to always code for paginated results - it is a lot more efficient and your code will still work just as well after you add 100 docs or a million.
4) take a look at xdmp.nodeUrl https://docs.marklogic.com/xdmp.nodeUri,
This function, unlike fn.documentUri(), will work on any node even if its not document node. If you can put this right next to the search instead of next to the delete then the system can optimize much better. The examples in the JavaScript guide are a good start https://docs.marklogic.com/guide/getting-started/javascript#chapter
In your case I suggest something like this to experiment with both pagination and extracting the URIs without having to expand the documents ..
var uris = []
for (var result of fn.subsequence(cts.search( ... ), 1 , 100 )
uris.push(xdmp.nodeUri(result))
for( i in uris )
xdmp.log( uris[i] )

Saving Javascript object

I have tree of javascript objects. Let's call it "family. It can contain any number of certain objects ("parents") which can each contain any number of "child" objects. The number of levels in this structure is known and each level of the tree only contains objects of one certain type.
All the objects have data and methods.
I want to save the structured data in the databese. JSON.stringify() does it perfect extracting the data and also saving the structure. But how to get back to objects? JSON.parse() fails, because it recreates the object without methods.
What should I do in this case? Should I write my own function for recreating the object from string? Or should I save the data together with methods somehow (seems a waste).
As I know the structure, it would be very handy if there would be a possibility to point to an object and tell "that's a parent object" and it would get the methods. I could easily cycle through it then. But I don't know how to that and I'm also afraid that my constructors could set some values to the default ones.
The objects constructors would look something like this:
function lines()
{
this.lines = [];
this.height = 0.5*theMargin;
this.addLine = addLine;
function addLine(newline)
{
this.lines.push(newline);
this.height += newline.height;
}
}
function aLine()
{
this.dots = [];
this.height = 0;
this.length = indent;
this.insertDot = insertDot;
function insertDot(pos,newDot)
{
this.dots.splice(pos,0,newDot);
this.length += newDot.length;
this.height = Math.max(this.height,newDot.height);
if (this.length > maxLineLength)
{ "I will not go into details here" }
}
}
Then I would do like:
var a = new lines();
var testline = new aLine();
var testdot = new aDot();
testdot.height = 10;
testdot.length = 15;
testline.insertDot(0,testdot);
a.addLine(testline);
a.addLine(testline);
Then I want to save the data about lengths and heights. And the structure, to know which dot belongs in which line.
I send that data to the webserver. I think these are the key lines to understand the used approach:
post = "name=" + name + "&tab=" + JSON.stringify(file);
req.open("POST", "saveFile.php", true);
req.send(post);
The saved file saves exactly what I wanted - the structure and data. But I don't know how to make it become an object again. I am not insisting to use JSON.stringify() method. I would enjoy any approach that would let me save the content without repeatedly saving the methods.
If you are really hooked on the idea of saving the entire object for some reason then I suggest you use the toString() method of which will essentially return the code body of a function in the form of a string when called on a function.
var obj = { func: function() { alert('hello!'); };
for(var key in obj)
if (typeof obj[key] === 'function')
alert(obj[key].toString());
You would just have to add code to serialize and store this information in addition to the json data.
All that said, you really should be simply storing the state of your objects and reloading them into your application.
EDIT: Reconstructing the object client-side
Disclaimer: I am not a PHP guy so you will be left to finding an actually coding example but I'm confident there is one out there with the power of the almighty Google.
You simply need to use your serializing/deserializing class to serialize the data back into your object.
So imagine the section of pseudo code is the php file for the particular page in question:
<?php
<html>
<head>
<script type="text/javascript">
var model = /* Use serializing class on your server-side object */;
//Now you just need to build a function into your objects that is much like a constructor that can receive this model and rebuild the object
function rebuildObject(yourObject, jsonModel) {
//assign first property
//assign second etc...
}
</script>
</head>
<body>
</body>
</html>
?>
You are essentially templating the json data back to the page in a script tag so you can access it client-side. The javascript interpretter will automatically convert the json into an actual js object that your code can use so no issue there.
In the end I chose the straightforward way to recreate all the objects and copy the data. It turned out to be shorter and nicer than I had imagined before. In case it is useful for anyone else, here's how I did it:
data = JSON.parse(file);
a = new lines();
a.height = data.height;
for (var i=0; i<data.lines.length; i++)
{
a.lines.push(new aLine());
a.lines[i].height = data.lines[i].height;
a.lines[i].length = data.lines[i].length;
for (var j=0; j<data.lines[i].dots.length; j++)
{
a.lines[i].dots.push(new aDot());
[... and so on ...]
}
}

How to store integer indexed data in Javascript?

I'm coming from working in PHP for many years and having trouble wrapping my head around creating some more complicated data structures in JS for objects that are integer IDed. I need to build an object the stores these simpler objects hierarchically keyed on their integer ids. So if I have the following objectes each of which has a unique integer id:
section, element, item, entry
in php I would do something like
$arr[$section_id][$element_id][$item_id][$entry_id] = $entry;
In javascript this does not work. I know I could technically wrap those IDs in quotes to force it but that seems like a bad idea. Similarly I could create an object and use the quoted integer approach but again that seems hacky.
Right now I am storing the data in regular integer indexed arrays and then using caolan's async detect to look up a particular member by ID. This works but seems extremely messy compared to the php equivalent. I'm hoping there's a cleaner way to do this in JS.
TIA!
since javascript cannot save an array with string index, i use these :
var namespace = function(name, separator, container){
var ns = name.split(separator || '.')
, o = container || window
, i = 0;
while (i++ < ns.length) o = o[ns[i - 1]] = o[ns[i - 1]] || {};
return o;
}
namespace(arr + '.' + section_id + '.' + element_id + '.' + item_id + '.' + entry_id) = entry;
// ex : namespace('arr.1.3.2.6') will product arr.1.3.2.6 object
This is a little ugly, but it will get you pretty close to what you want.
You can add a method to the JavaScript Array class like so:
Array.prototype.g = function(index) {
if (this[index] == undefined) {
this[index] = [];
}
return this[index];
}
Then, to set something you would do this:
var test = [];
test.g(5).g(7)[5] = 1;
Unfortunately, for the last entry you'd have to remember to use the regular notation to set the value.
You would retrieve it like you expect:
test[5][7][5]; //1
Obviously I just pulled g out of thin air, you could come up with your own function name.
Disclaimer: People tend to frown on extending the built in types using the prototype chain, but as far as I know most major frameworks no longer do this so unless you or some third party JS is using the same name to extend Array somewhere else you should be fine.

Categories