XQuery template in MarkLogic doesn't show any values, only attributes (TDE) - templates

I've created a .xml-file and a template to extract some data, but only the attributes show up.
This is my .xml-testfile:
<user id="1234" email="test.user#live.com" password="1234">
<type>Human</type>
<notes>
<note reference="5432" id="753" xmlns="http://testnamespace.de/note">
<text>example</text>
<username>John Doe</username>
<groups>
<group id="42">Avengers</group>
<group id="55">JLA</group>
</groups>
<distinctiveTitle>title</distinctiveTitle>
<personNameInverted>Doe John</personNameInverted>
</note>
</notes>
and here the corresponding template:
import module namespace tde = "http://marklogic.com/xdmp/tde" at "/MarkLogic/tde.xqy";
declare namespace testns = "http://testnamespace.de/note";
let $userNoteTDE:=
<template xmlns="http://marklogic.com/xdmp/tde" xmlns:testns="http://testnamespace.de/note">
<context>/user/notes/testns:note</context>
<rows>
<row>
<schema-name>user</schema-name>
<view-name>notes</view-name>
<columns>
<column>
<name>reference</name>
<scalar-type>string</scalar-type>
<val>#reference</val>
<nullable>true</nullable>
<default>""</default>
</column>
<column>
<name>id</name>
<scalar-type>string</scalar-type>
<val>#id</val>
<nullable>true</nullable>
<default>""</default>
</column>
<column>
<name>text</name>
<scalar-type>string</scalar-type>
<val>text</val>
<nullable>true</nullable>
<default>""</default>
</column>
<column>
<name>username</name>
<scalar-type>string</scalar-type>
<val>username</val>
<nullable>true</nullable>
<default>""</default>
</column>
<column>
<name>distinctiveTitle</name>
<scalar-type>string</scalar-type>
<val>distinctiveTitle</val>
<nullable>true</nullable>
<default>""</default>
</column>
<column>
<name>personNameInverted</name>
<scalar-type>string</scalar-type>
<val>personNameInverted</val>
<nullable>true</nullable>
<default>""</default>
</column>
</columns>
</row>
</rows>
</template>
I changed the context to use the correct (?) path and the namespace (because this part should be nested into another template):
<context>/user/notes/testns:note</context>
If I check the template with tde:node-data-extract(fn:doc (TESTFILE PATH), $userNoteTDE)
I get the following output:
{
"TESTFILE PATH": [
{
"row": {
"schema": "user",
"view": "notes",
"data": {
"rownum": "1",
"reference": "5432",
"id": "753",
"text": "",
"username": "",
"distinctiveTitle": "",
"personNameInverted": ""
}
}
}
]
}
This shows, that the attributes are displayed correctly, but somehow the values (text, username, distinctiveTitle, personNameInverted) of the elements do not work.
My guess is, that the values need a more refined path or expression, but I can't find any information.
If I change the text value for example to <val>testns:text</val> in my template, I get the error: XDMP-UNBPRFX: (err:XPST0081) Prefix testns has no namespace binding
So somehow the elements can't use the declared namespace, but the attributes can.
Also I skipped the <groups> section in my template, because they would need a context on their own, that shouldn't matter, should it?
Thanks in advance for any helpful insight!

The MarkLogic Support gave me the answer for this problem, so I want to share it here!
(Thanks Chris Hamlin!)
It was indeed a namespace problem. The MarkLogic Documentation shows that for multiple namespaces "path-namespaces" should be used.
After declaring
...
<path-namespaces>
<path-namespace>
<prefix>testns</prefix>
<namespace-uri>http://testnamespace.de/note</namespace-uri>
</path-namespace>
</path-namespaces>
...
between template and context, and using the testns prefix on my elements like testns:text, the elements are being correctly displayed!

Related

All row data is not getting reflected in Power BI from Marklogic

I have a csv file with 3 rows of data which is stored in Marklogic and available in collections.
I am trying to access the content of the csv file from Power BI.
But from Power BI I can only access 1 row of data at one time whereas requirement is to see all rows of data.
Here are the details of the process I followed to achieve the functionality:
Used Marklogic ODBC driver(64bit) to connect Marklogic SQL from Power BI
Created template(used xquery) to implement the table structure in Marklogic
Used mlcp to import the csv file in the marklogic database.
Here is the template I used:
xquery version "1.0-ml";
import module namespace tde = "http://marklogic.com/xdmp/tde" at "/MarkLogic/tde.xqy";
if (xdmp:database-name(xdmp:database()) = "Documents") then
let $shipment-CBE:=
<template xmlns="http://marklogic.com/xdmp/tde">
<context>/shipment</context>
<collections>
<collections-and>
<collection>PowerBI</collection>
<collection>Shipment</collection>
</collections-and>
</collections>
<rows>
<row>
<schema-name>Shipment</schema-name>
<view-name>CBE2</view-name>
<columns>
<column>
<name>DocType</name>
<scalar-type>string</scalar-type>
<val>DocType</val>
</column>
<column>
<name>User</name>
<scalar-type>string</scalar-type>
<val>User</val>
</column>
<column>
<name>ShipmentRef</name>
<scalar-type>string</scalar-type>
<val>ShipmentRef</val>
</column>
<column>
<name>Transmode</name>
<scalar-type>string</scalar-type>
<val>Trans_mode</val>
</column>
<column>
<name>Packagetype</name>
<scalar-type>string</scalar-type>
<val>Package_type</val>
</column>
<column>
<name>Customer_Party_Name</name>
<scalar-type>string</scalar-type>
<val>Customer_Party_Name</val>
</column>
</columns>
</row>
</rows>
</template>
return (
tde:template-insert("/powerbi/shipment-CBE2.xml",$shipment-CBE),
"shipment-CBE.xml OK"
)
else ("Please select the 'Documents' database.")
Here is the import command(saved as shipment.txt) used to import the csv file:
IMPORT
-input_file_path
../data/Shipment-CBE.csv
-input_file_type
delimited_text
-delimited_root_name
shipment
-username
admin
-password
*****
-host
owc-db01.owc.com
-port
8000
-output_uri_prefix
/powerbi/shipment/
-output_collections
PowerBI,Shipment
-uri_id
"DocType"
Running this MLCP command to execute the import command -
mlcp.bat -options_file shipment.txt
The issue appears to be that you are importing each of the 3 CSV rows and inserting with the same URI of /powerbi/shipment/Shipment
This is because you have configured the -uri_id to be the "DocType" column, and each of your rows have the same value: Shipment.
https://docs.marklogic.com/guide/mlcp/import#id_65814
Optionally, override the default document URI by setting -uri_id to the name of the element from which to derive the document URI.
You could instead configure it to use the User column, so that the docs would be inserted with unique URIs:
/powerbi/shipment/j_henderson
/powerbi/shipment/c_saunders
/powerbi/shipment/a_gatfield

XSLT - select a node from a complex xml file

I have an XML file below where I need to get the text inside of the <Description> tag under the <Checklist> tag where the <Sequence> tag has the text 40. How to achieve it?
<?xml version="1.0" encoding="UTF-8"?>
<SyncMaintenanceOrder>
<DataArea>
<MaintenanceOrder>
<MaintenanceOrderHeader>
<DocumentID>
<ID accountingEntity="AT">1105442</ID>
</DocumentID>
<Description>Routine Bridge Inspection - S6</Description>
<PriorityCode>2</PriorityCode>
<ReportedDateTime>2020-04-29T20:21:27Z</ReportedDateTime>
</MaintenanceOrderHeader>
<MaintenanceOrderLine>
<LineNumber>10</LineNumber>
<RemainingDuration>PT8H0M0S</RemainingDuration>
<ActivityDeferredIndicator>false</ActivityDeferredIndicator>
<UserArea>
<EamCheckListInfo>
<CheckList>
<CheckListItem>
<Sequence>40</Sequence>
<Description>Half joints (Superstructure elements)</Description>
</CheckListItem>
<CheckListItem>
<Sequence>160</Sequence>
<Description>Substructure drainage (Durability elements)</Description>
</CheckListItem>
<CheckListItem>
<Sequence>60</Sequence>
<Description>Parapet beam or cantilever (Superstructure elements)</Description>
</CheckListItem>
</CheckList>
</EamCheckListInfo>
</UserArea>
</MaintenanceOrderLine>
</MaintenanceOrder>
</DataArea>
</SyncMaintenanceOrder>
I need sample of an XSLT code for selecting only the text node described above.
I'm not sure I really get your question.
This will print the Description of the CheckListItem which has a Sequence of 40:
<xsl:value-of select="//CheckListItem/Sequence[text()='40']/../Description"/>
Try it here: https://xsltfiddle.liberty-development.net/ehVZvvZ
Try the below code
<xsl:value-of select="*[local-name(.)='SyncMaintenanceOrder']/*[local-name(.)='DataArea']/*[local-name(.)='MaintenanceOrder']/*[local-name(.)='MaintenanceOrderLine']/*[local-name(.)='UserArea']/*[local-name(.)='EamCheckListInfo']/*[local-name(.)='CheckList']/*[local-name(.)='CheckListItem']/*[local-name(.)='Sequence'][text() = '40']/../*[local-name(.)='Description']" />

Need the maximum value in label element in list using perl

The below provided list's as contains nested list. I need to fetch the maximum level value in <label> element and insert the value before the <list> element where the attribute appears as 'type="num"'.
For Example:
INPUT:
<list spitype="num" id="list1">
<list-item><label>1</label><para></para></list-item>
<list-item><label>2</label><para></para>
<list spitype="num" id="list1-1">
<list-item><label>1</label><para></para></list-item>
<list-item><label>2</label><para></para></list-item>
<list-item><label>3</label><para></para></list-item>
</list></list-item>
<list-item><label>3</label><para></para></list-item>
<list-item><label>4</label><para></para></list-item>
<list-item><label>5</label><para></para></list-item>
</list>
OUTPUT:
**<max-val-5/>**
<list spitype="num" id="list1">
<list-item><label>1</label><para></para></list-item>
<list-item><label>2</label><para></para>
**<max-val-3/>**
<list spitype="num" id="list1-1">
<list-item><label>1</label><para></para></list-item>
<list-item><label>2</label><para></para></list-item>
<list-item><label>3</label><para></para></list-item>
</list></list-item>
<list-item><label>3</label><para></para></list-item>
<list-item><label>4</label><para></para></list-item>
<list-item><label>5</label><para></para></list-item>
</list>
I have wrote this below code however I didn't get the output. The nested list doesn't not covered on this one. Could you please anyone can help on this will be appreciate.
$incnt=~s{(<list(?: |>)[^>]*>((?:(?!<\/list>).)*)<\/list>)}{
my $list=$1; my ($sbpre,$sbmatch,$sbpost,$slmatch,$LblWidthVal) = "";
if($list=~m/<list(?: |>)[^>]*type="num"[^>]*>/g)
{
$sbpre=$sbpre.$`; $sbmatch=$&; $sbpost=$';
$slmatch=$1 while($list=~m/<label>([^<>]*)<\/label>/sg);
$slmatch=~s/[.,:;\(\)\[\]\{\}]*//g;
$LblWidthVal = "<max-val-$slmatch/>";
$sbmatch = $LblWidthVal."\n".$sbmatch;
$sbpre = $sbpre.$sbmatch; $list = $sbpost;
}
if(length $sbpre) { $list = $sbpre.$sbpost; }
"$list";}igse;
While it is possible to add a new XML element with a name of max-val-5 it is highly irregular to put information into the name of an element. It's also illegal to have two root elements in an XML element, so I've wrapped your code in a <root>...</root> element and added a new element that looks like `
This solution uses the XML::Twig module, which I think is a little easier to use than XML::LibXML, especially for outputting XML, and has all of its documentation on a single page
I hope you'll agree that it's very straightforward compared with a regex solution
use strict;
use warnings 'all';
use XML::Twig;
use List::Util 'max';
my $twig = XML::Twig->new( pretty_print => 'indented' );
$twig->parsefile('test.xml');
my $max_label = max $twig->findvalues('//label');
my $max_element = XML::Twig::Elt->new( max => { value => $max_label } );
$max_element->paste( first_child => $twig->root);
$twig->print;
output
<root>
<max value="5"/>
<list id="list1" spitype="num">
<list-item>
<label>1</label>
<para></para>
</list-item>
<list-item>
<label>2</label>
<para></para>
<list id="list1-1" spitype="num">
<list-item>
<label>1</label>
<para></para>
</list-item>
<list-item>
<label>2</label>
<para></para>
</list-item>
<list-item>
<label>3</label>
<para></para>
</list-item>
</list>
</list-item>
<list-item>
<label>3</label>
<para></para>
</list-item>
<list-item>
<label>4</label>
<para></para>
</list-item>
<list-item>
<label>5</label>
<para></para>
</list-item>
</list>
</root>

XSLT to Concatenate Empty Values

We have a requirement that requires us to list out all the empty values from the incoming xml. I have searched but all I could find was listing non-null values, trying to use that for our xml is not returning the required results.
Here is the xml that we will receive and I want to be able to concatenate all the null values from this xml and print. Kindly assist.
<?xml version = "1.0" encoding = "UTF-8"?>
<Output>
<Rows>
<ns0:I2NA xmlns:ns0 = "http://www.example.com/schemas/Schema.xsd">
<ns0:Organization>108</ns0:Organization>
<ns0:AccountNumber>1231231231231231233 </ns0:AccountNumber>
<ns0:Status>0</ns0:Status>
<ns0:VipStatus>0</ns0:VipStatus>
<ns0:TypeOfIdNo>1</ns0:TypeOfIdNo>
<ns0:IdNo>2303111450 </ns0:IdNo>
<ns0:HomePhone>123456 </ns0:HomePhone>
<ns0:Employer> </ns0:Employer>
<ns0:EmployersPhone>123456 </ns0:EmployersPhone>
<ns0:FaxPhone>123456 </ns0:FaxPhone>
<ns0:MobileNo>0568520421 </ns0:MobileNo>
<ns0:CountryCode> </ns0:CountryCode>
<ns0:PostalCode> </ns0:PostalCode>
<ns0:Position> </ns0:Position>
<ns0:MaritalStatus>0</ns0:MaritalStatus>
<ns0:DateOfBirth>00000000</ns0:DateOfBirth>
<ns0:EmailAddrs> </ns0:EmailAddrs>
<ns0:UserCode1> </ns0:UserCode1>
<ns0:NationalityCode> </ns0:NationalityCode>
<ns0:NameLine1>ABC </ns0:NameLine1>
<ns0:NameLine2> </ns0:NameLine2>
<ns0:NameLine3> </ns0:NameLine3>
<ns0:ChDob> </ns0:ChDob>
<ns0:AddressLine1>USA </ns0:AddressLine1>
<ns0:AddressLine2>USA </ns0:AddressLine2>
<ns0:AddressLine3>USA </ns0:AddressLine3>
<ns0:AddressLine4>USA </ns0:AddressLine4>
<ns0:City> </ns0:City>
<ns0:State> </ns0:State>
<ns0:GenderCode>0</ns0:GenderCode>
<ns0:StatementNotifIndi> </ns0:StatementNotifIndi>
<ns0:Nationality> </ns0:Nationality>
<ns0:County> </ns0:County>
<ns0:LastName>John </ns0:LastName>
<ns0:MiddleName> </ns0:MiddleName>
<ns0:FirstName>SHAN MATHEW </ns0:FirstName>
<ns0:LangPref> </ns0:LangPref>
</ns0:I2NA>
</Rows>
<EOF>true</EOF>
When the XSLT is applied, we would like to receive the below string as output.
Status,Employer,CountryCode,PostalCode,Position,EmailAddrs,UserCode1,NationalityCode,NameLine2,NameLine3,ChDob,City,State,StatementNotifIndi,Nationality,County,MiddleName,LangPref
Thanks!
Use <xsl:value-of select="//*[not(*) and not(normalize-space())]/local-name()" separator=","/>. But I don't understand why your sample string starts with Status while the XML has a value <ns0:Status>0</ns0:Status> for that field.
With XSLT 1.0 you need a bit of more code:
<xsl:for-each select="//*[not(*) and not(normalize-space())]">
<xsl:if test="position() > 1"><xsl:text>.</xsl:text></xsl:if>
<xsl:value-of select="local-name()"/>
</xsl:for-each>

XSL - Display XML tags based on parameter from PHP

Ive got a problem displaying my desired XML tags based on an parameter. I'm fairly new to this.
XML Example:
<car name="Toyota">
<model nr="123" yeardate="2010">
<owner ssn="123456789" name="Tom"/>
<owned years="0" months="6"/>
</model>
</car>
<car name="Volvo">
<model nr="222" yeardate="2009">
<owner ssn="345364774" name="John"/>
<owned years="0" months="8"/>
</model>
</car>
<car name="Fiat">
<model nr="333" yeardate="2010">
<owned years="0" months="0"/>
</model>
</car>
The problem is that I want to be able to choose the car that is displayed BASED on an HTML form I made in my PHP document. So, I made a form in PHP, sent the value of POST back to my XSL document and now I want to display the car based on this parameter value. Also, notice that the car Fiat does not have an owner. I am able to get the value of POST in my XSL document, but I'm not sure how I go about using this parameter.
What I imagine this turning in to:
Lets say that Toyota is choosen in the form,
car name=Toyota
model nr=123 yeardate=2010
owner ssn=123456789 name=tom
owned years=0 months=6
I want to include the tag name as well as all the attributes.
If you're using PHP, maybe you don't need XSL. Have a look at SimpleXML.
<?php
$xmlString = '<root>
<car name="Toyota">
<model nr="123" yeardate="2010">
<owner ssn="123456789" name="Tom"/>
<owned years="0" months="6"/>
</model>
</car>
<car name="Fiat">
<model nr="333" yeardate="2010">
<owned years="0" months="0"/>
</model>
</car>
</root>';
$xml = new SimpleXMLElement($xmlString);
foreach ($xml->children() as $second_gen) {
if ( (string) $second_gen['name'] == "Toyota") {
echo $second_gen->asXML();
}
}
?>