BI Publisher conditional field masking - peoplesoft

I have the following code on a field in a Peoplesoft BI Publisher RTF template where it is masking the last 4 digits of the Bank Account number.
<?xdofx:lpad('',length(Bank_Account__)-4,'*')?>
<?xdoxslt:rtrim(xdoxslt:right(Bank_Account__,4))?>
The problem is that sometimes the total Bank Account number length is less than 4 digits and when this happens it causes an negative array error on the lpad function to occur.
Can I wrap some kind of conditional IF statement around this where it will check the length of the bank account number and if it is longer than 5 digits than mask the last 4 digits, else (for Bank Account numbers less than 5 digits) just mask the last 2 digits. What would this look like?
Thanks in advance!
EDIT:
I should add that the existing code above is already wrapped in the following IF statement:
<?if#inlines:Bank_Account__!=''?>
So the entire statement is:
<?if#inlines:Bank_Account__!=''?>
<?xdofx:lpad('',length(Bank_Account__)-4,'*')?>
<?xdoxslt:rtrim(xdoxslt:right(Bank_Account__,4))?>
<?end if?>
I would just like to add in the conditional logic to check the bank account length and subsequently perform either of the above masking.
EDIT 2:
Here is my setup with your suggested changes, but I don't think I have the logic nested right, and the syntax may also be an issue.
Edit 3:
Here is the modified code, and the resulting error message:

The if statements can be nested, but since BIP does not have an else clause, the second if conditions has to check for the negative case.
Maybe this might work:
<?if#inlines:Bank_Account__!=''?>
<?if#inlines:string-length(Bank_Account__)>4?>
<?xdofx:lpad('',length(Bank_Account__)-4,'*')?><?xdoxslt:rtrim(xdoxslt:right(Bank_Account__,4))?>
<?end if?>
<?if#inlines:string-length(Bank_Account__)<=4?>
<?xdofx:lpad('','2','*')?><?xdoxslt:rtrim(xdoxslt:right(Bank_Account__,string-length(Bank_Account__)-2))?>
<?end if?>
<?end if?>
Update: Here is a screenshot of what I got:
Here is the xml snippet I used.
<?xml version="1.0"?>
<root>
<record>
<Bank_Account__>123456</Bank_Account__>
</record>
<record>
<Bank_Account__>12345</Bank_Account__>
</record>
<record>
<Bank_Account__>1234</Bank_Account__>
</record>
<record>
<Bank_Account__>123</Bank_Account__>
</record>
<record>
<Bank_Account__>12</Bank_Account__>
</record>
</root>
Download working files from here
There are some more functions available for other ways to implement this requirement.

Related

Marklogic: what are field range query and path range query cts functions using xquery

I have been following the documentation to understand cts:field-range-query and cts:path-range-query. These are the links i used to understand.
https://docs.marklogic.com/cts:field-range-query
https://docs.marklogic.com/cts:path-range-query
In cts:path-range-query, i didnt understand the output. How do you compare a string with < or >?
cts:search(doc(),cts:path-range-query("/name/fname",">","Jim"),"filtered")
=>
<?xml version="1.0" encoding="UTF-8"?>
<name><fname>John</fname><mname>Rob</mname><lname>Goldings</lname></name>
<?xml version="1.0" encoding="UTF-8"?>
<name><fname>Ooi</fname><mname>Ben</mname><lname>Fu</lname></name>
In cts:field-range-query, here aswell i didnt get the output.
ts:search(doc(),cts:field-range-query("aname",">","Jim Kurla"));
(:
returns the following:
<?xml version="1.0" encoding="UTF-8"?>
<name>
<fname>John</fname>
<mname>Rob</mname>
<lname>Goldings</lname>
</name>
<?xml version="1.0" encoding="UTF-8"?>
<name>
<fname>Ooi</fname>
<mname>Ben</mname>
<lname>Fu</lname>
</name>
:)
Sorry, if it is silly but i have been trying to understand this little thing since several days but somehow i don't get it. Really appreciate the help
String comparison is based on alphanumeric comparison. It actually depends on the collation, but the default is based on Unicode (UCA Root Collation with case and diacritic sensitivity). A comes before B, but a comes after B, and also alpha comes after Zeta. More confusingly, 10 comes before 2 as well.
In your examples the path query only looks at fname where Jim comes before both John and Ooi.
The second example is likely a field with multiple paths, including fname, mname, and lname. The > satisfies if there is any name value in the document that is larger than Jim. Goldings, Ben, and Fu come before Jim alphabetically, but there are other names like John and Ooi that come after. So that returns both those values as well.
It is more fun to repeat the queries with Lee. The path query will then return 1 result only (the second), but the field is likely still returning both.

How to dynamically fetch value using cts:seach in Marklogic?

My Database is having "n" number of documents and i need to search for document dynamically using the elements and value i am providing. I am explaining it below-
Sample documents in my database-
document1-
<root>
<id1>12345</id1>
<value>Country</value>
<node1>somevalue</node1>
<node2>somevalue</node2>
<node3>somevalue</node3>
<node4>somevalue</node4>
.......................
</root>
document2-
<root>
<id2>34567</id2>
<value>Fruits</value>
<node1>somevalue</node1>
<node2>somevalue</node2>
<node3>somevalue</node3>
<node4>somevalue</node4>
.......................
</root>
I need to give input parameters as Rest End Point to perform my operation and the input to rest xml document is as below-
INPUT XML-
<root>
<id>id1</id>
<idvalue>12345</idvalue>
.......................
</root>
Output i need is shown in example-
Example- Search for all the documents from the database which is having Id=Id1 and it's value=12345
Any Suggestions ?
You can explore Query By Example (QBE) of MarkLogic. For more details go to URL https://docs.marklogic.com/guide/search-dev/qbe
XPath can extract the input values for constructing a cts.elementValueQuery().
Something similar to the following should work in SJS:
cts.search(cts.elementValueQuery(
xs.QName(fn.string(input.xpath('/root/id'))),
fn.string(input.xpath('/root/idvalue'))
))
Or similar to the following in XQuery:
cts:search(fn:collection(), cts:element-value-query(
xs:QName(fn:string($input/root/id)),
fn:string($input/root/idvalue)
))
For more information, see http://docs.marklogic.com/cts.elementValueQuery
Hoping that helps,

R parsing plist XML

Sorry, edited with one more little nuance! I had simplified my raw file a little too much in the example I provided, so while your solution works beautifully as-is, what if there are a few extra things thrown into the second line? Those seem to throw off the xml_find_all(page, "//event"), since now it can't find that node. How can I get the script to ignore the extras (or maybe what is the right search term to incorporate them?) Thanks!!!
I'm new to working with xml, and I have some speech xml files that I'm trying to flatten into dataframes in R, but I can't get them to be read using some of the standard functions in the XML package. I think the problem is the plist format, because some of the other answers that I've tried to apply don't work on these files.
My files look as follows (*****second line edited):
<?xml version="1.0" encoding="us-ascii"?>
<event id="111" extraInfo="CivilwarSpeeches" xmlns = "someurl>
<meta>
<title>Gettysburg</title>
<date>1863-11-19</date>
<organizations>
<org>Union</org>
</organizations>
<people>
<person id="0" type="President">Honest Abe</person>
</people>
</meta>
<body>
<section name="Address">
<speaker id="0">
<plist>
<p>Four score and seven years ago</p>
</plist>
</speaker>
</section>
</body>
</event>
And I would like to end up with a dataframe that links some of the info in the two sections, something like
Section|Speaker|Speaker Type| Speaker Name|Body
Address|0 |President | Honest Abe |Four score and seven years ago
I found this answer fairly helpful, but it still can't seem to unpack my data. Parsing XML file with known structure and repeating elements
Any help would be appreciated!
I prefer to use the xml2 library over the xml library.
This is a pretty straight forward problem. Read the data in, parse out the desired attributes and nodes and assemble into a data frame.
library(xml2)
page<-read_xml('<?xml version="1.0" encoding="us-ascii"?>
<event id="111">
<meta>
<title>Gettysburg</title>
<date>1863-11-19</date>
<organizations>
<org>Union</org>
</organizations>
<people>
<person id="0" type="President">Honest Abe</person>
</people>
</meta>
<body>
<section name="Address">
<speaker id="0">
<plist>
<p>Four score and seven years ago</p>
</plist> </speaker> </section> </body> </event>')
#get the nodes
nodes<-xml_find_all(page, "//event")
#parse the requested information out of each node
Section<- xml_attr(xml_find_first(nodes, ".//section"), "name")
Speaker<- xml_attr(xml_find_first(nodes, ".//person"), "id")
SpeakerType<- xml_attr(xml_find_first(nodes, ".//person"), "type")
SpeakerName<- xml_text(xml_find_first(nodes, ".//person"))
Body<- xml_text(xml_find_first(nodes, ".//plist/p"))
#put together into a data.frame
answer<-data.frame(Section, Speaker, SpeakerType, SpeakerName, Body)
The code is set up to parse a series of "event" nodes. For clarity I am using 5 steps to parse out each requested information field out separately and then combine into the final dataframe.
Part of the justification for this is to maintain alignment in case the "event" nodes are missing some of the requested information. This could be simplified, but if your dataset is small, there shouldn't be much of a performance impact.

Do not include repeated data in facets with MarkLogic

I'm doing a search using facets with the new api search:search but I have the next problem:
My source:
File #1
<root>
<location>
<university>
<name>Yale</name>
<country>USA</country>
</university>
</location>
<location>
<university>
<name>MIT</name>
<country>USA</country>
</university>
</location>
<location>
<university>
<name>Santander</name>
<country>Spain</country>
</university>
</location>
</root>
File #2
<root>
<location>
<university>
<name>MIT</name>
<country>USA</country>
</university>
</location>
</root>
I need to know the number of universities by each country, but the facets return me the number of files that include one country or the number of locations in all files repeat universities, so in the last example of data it returns me this with the 2 options.
First Option (using frequency-order)
USA - 2 (Number of Files with at least one location with USA)
SPAIN - 1
Second Option (Using item-frequency)
USA - 3
SPAIN - 1
When the result should be this:
USA - 2 (because in the two files there are only two universities)
SPAIN - 1
How can I do this???
I think you need the item-frequency option, instead of the default fragment-frequency option. You add it to a constraint as a so-called facet-option. More details, and examples can be found on CMC: http://community.marklogic.com/pubs/5.0/apidocs/SearchAPI.html#search:search
-- edit --
I think I didn't read your question thoroughly enough. The search library focusses on search results, and the facet counts on fragments. Easiest way to improve the counts is by defining the location element as a fragment root. However, I don't think that really returns the numbers you are looking for. The country facet really only counts the country occurrences, and not the universities within countries. You can't achieve that with the search library. It isn't difficult to do it yourself though:
for $country in cts:element-values(xs:QName('country'))
let $universities := cts:element-values(xs:QName('university'), (), cts:element-value-query(xs:QName('country'), $country))
return fn:concat($country, ' - ', fn:count($universities))
Note: Untested code, but it at least shows the essential steps. It also require countries to not occur within same fragments. You need to add location as fragment root in the ML admin interface.
HTH!
Try cts:element-value-co-occurrences with name and country

how to use the BizTalk Flat File Mapping Wizard for nested repeating items?

I have a flat file with some repeating sections in it, and I'm confused how to create the schema via the BT flat file mapping wizard. The file looks like this:
001,bunch of data
002,bunch of data
006,bunch of data
006A,bunch of data
006B,bunch of data
006B,bunch of data
006,bunch of data
006A,bunch of data
006B,bunch of data
As you can see, the 006* records can repeat. I'm going to want to wind up with XML that looks like this:
<001Stuff>...</001Stuff>
<002Stuff>...</002Stuff>
<006Loop>
<006Stuff>...</006Stuff>
<006AStuff>...</006AStuff>
<006BStuff>...</006BStuff>
<006BStuff>...</006BStuff>
</006Loop>
<006Loop>
<006Stuff>...</006Stuff>
<006AStuff>...</006AStuff>
<006BStuff>...</006BStuff>
</006Loop>
Obviously I can't just set the first group of 006* records to "Repeating record" and Ignore the second set. I'm used to dealing with single repeating rows via the wizard (i.e. another 006 row right after the first one) and not nested things like this - any suggestions on how to proceed? Thanks!
Working with the Flat File Schema Wizard is quite hard and there is only so much it can help you with. I always seem to have to tweak its output a little bit.
In order to make things a little bit easier, I suggest you should restrict your sample document to a single occurrence of the whole <006> structure. You will not have to set many lines to Ignored using the Flat File Schema Wizard :
001,bunch of data
002,bunch of data
006,bunch of data
006A,bunch of data
006B,bunch of data
006B,bunch of data
Next, each repeating structure should be wrapped inside a corresponding Repeating Record in the definition of your Xml Schema.
Please, note that you can always run the Flat File Schema Wizard recursively on nested structures to have more fine-grained control. So I would suggest, first, to run the wizard with an all-encompassing repeating <006> structure, like so :
Then, you can right click on the structure, and provide a more detailed definition of nested child structures, only highlighting a subset of the sample contents, like so:
Then, the most important part: you need to tweak the Child Order property to Conditional Default for both repeating structures, because there is only one empty line at the end of your document file and the Wizard cannot help you out with this situation.
For reference, your resulting structure should look like so:
With the following settings:
BunchOfStuff (Root) : Delimited, 0x0D 0x0A, Suffix.
_001Stuff : Delimited, ,, Prefix, Tag Identifier 001.
_002Stuff : Delimited, ,, Prefix, Tag Identifier 002.
_006Loop : Delimited, 0x0D 0x0A, Conditional Default.
_006Stuff : Delimited, ,, Prefix, Tag Identifier 006.
_006AStuff : Delimited, ,, Prefix, Tag Identifier 006A.
_006BLoop : Delimited, 0x0D 0x0A, Conditional Default.
_006BStuff : Delimited, ,, Prefix, Tag Identifier 006B.
Hope this helps.
Treat everything from the first start of the first 006, record to the start of the second 006, record as one record. When you define 006 record, set it up as a repeating record also. This should create a node for each 660, group and nodes for each 600 under it.
That is what I would try.
Here is my output after 2 minutes of work. Except for the node/element names I think it is what you want. You would still have to create seperate elements for each of the fields in your data.
<_x0030_01 xmlns="">001,bunch of data
<_x0030_02 xmlns="">002,bunch of data
<_x0030_06 xmlns="">
<_x0030_06_Child1>bunch of data
<_x0030_06_Child2>
<_x0030_06_Child2_Child1>A,bunch of data
<_x0030_06_Child2>
<_x0030_06_Child2_Child1>B,bunch of data
<_x0030_06_Child2>
<_x0030_06_Child2_Child1>B,bunch of data
<_x0030_06 xmlns="">
<_x0030_06_Child1>bunch of data
<_x0030_06_Child2>
<_x0030_06_Child2_Child1>A,bunch of data
<_x0030_06_Child2>
<_x0030_06_Child2_Child1>B,bunch of data

Resources