I am using BaseX to store XML data with multiple nodes in the following format:
<root>
<item id="65816" parent_id="45761" type="test">
<content>
<name>Name of my node on the tree</name>
</content>
</item>
</root>
The code above is essentially one typical node under 'root'.
Now, I am trying to delete a node based on the 'id' property of the 'Item' object.
I looked at the documentation on BaseX.org but that does not explicitly tell me how to deal with nodes which have IDs linked to it. I am trying to something like this:
XQUERY delete node //root/item.id="65816"
Note: The above line doesn't work. That is just to give an idea of what I am trying to achieve.
Related
I'm still new to xQuery / MarkLogic and I'm having trouble understanding how to query based on the number of elements in the XML document. For example, imagine I have a database of XML documents roughly similar to the following:
<?xml version="1.0" encoding="UTF-8"?>
<bookstore>
<book category="cooking">
<title lang="en">Everyday Italian</title>
<author>Giada De Laurentiis</author>
<year>2005</year>
<price>30.00</price>
</book>
<book category="children">
<title lang="en">Harry Potter</title>
<author>J K. Rowling</author>
<year>2005</year>
</book>
<book category="web">
<title lang="en">Learning XML</title>
<author>Erik T. Ray</author>
<year>2003</year>
<price>39.95</price>
</book>
</bookstore>
As you can see in book[2], price is missing. Most documents in the database I'm working with would either have the child element price for each book or no price element attached to any of the book elements. My goal is to find only the documents where some of the child elements are missing (like the above XML); and ignore the documents where either all the child elements exist or where none of the child elements exist. So in my head the logic is something along the lines of "return results where the number of price elements is < the number of book elements AND > 0."
The best I can do so far is the following query:
let $some-docs := cts:search(fn:collection('/my/collection'),
cts:and-query((
cts:element-query(xs:QName("book"), cts:true-query()),
cts:not-query(cts:element-query(xs:QName("price"), cts:true-query()))
)))
return (xdmp:node-uri($some-docs))
But this obviously only returns documents where book elements exist and no price elements exist. I need a way of indicating I want the documents where the price element exists, but is missing for some books.
I prefer a solution that is using the cts:search function, but any help is appreciated
I need a way of indicating I want the documents where the price element exists, but is missing for some books.
So basically you need to find documents that have both <bookstore><book><price/></book></bookstore> and ones missing the child <price/> element?
The simplest thing to do is modify the existing documents using a tool like CORB to include an element indicating that document matches your criteria or perhaps place them in a distinct collection. Then just use CTS to return documents with that added indicator.
If you don't want to touch the dataset you could create a field range index on /bookstore/book/price and /bookstore/book[not(./price)]/title. Then you just need to query for documents where both indexes are present with something like:
cts:and-query((
cts:field-word-query("field1", "*", ("wildcarded")),
cts:field-word-query("field2", "*", ("wildcarded"))
))
Getting the count of elements within a document isn't something that is exposed and available for a query. You could apply a predicate filter and test if there are any book that do not have a price for the docs returned from the search for those bookstore docs:
cts:search(fn:collection('/my/collection'),
cts:element-query(xs:QName("book"), cts:true-query())
)[bookstore/book[not(price)]]
return results where the number of price elements is < the number of book elements AND > 0
You could write not(count(//price) = (count(//book), 0))
or perhaps
empty(//price) or empty(//book[not(price)]
It seems a very strange query though. Perhaps you should be using a schema for validation?
I am trying to map parts of the following source structure that has two sets of properties - one flat and one looped:
Source Document
<root>
<flat>
<prop1>foo</prop1>
<prop2>bar</prop2>
...
</flat>
<loop>
<prop>
<qual>propA</qual>
<data>baz</data>
<more>blah</more>
</prop>
<prop>
<qual>propB</qual>
<data>qux</data>
<more>bhal</more>
</prop>
...
</loop>
</root>
Specifically, the flat part is the PO1 segment of an X12 850 EDI document, and the looping properties are the subsequent REF segments.
These should be mapped to a looping destination structure of key-value pairs that looks like this:
Destination Document
<root>
<props>
<prop>
<name>prop1</name>
<value>foo</value>
</prop>
<prop>
<name>propA</name>
<value>baz</value>
</prop>
</props>
</root>
I would like to map only some of the values, depending on the property name.
What I've Tried
I have successfully mapped the flat portion to the destination using a table looping functoid and two table extractor functoids:
I have also successfully mapped the looping portion to the destination using a looping functoid and some equality checks to select only certain qual values:
When I attempt to include both of these mappings at the same time, the map succeeds, but doesn't generate the combined output.
The Question
How I can I map both sections of the source document to the same looping section in the destination document?
Update 1
Turns out I had oversimplified the problem; the flat group of properties actually contains the property name in one node and the value in another node. This is what they actually look like:
<flat>
<name1>prop1</name1>
<value1>foo</value1>
<name2>prop2</name2>
<value2>bar</value2>
...
</flat>
The concept of #Dijkgraaf's answer still works with this change if you use a Value Mapping (Flattening) functoid to get the property name from the correct location.
Usually the only way to solve this is with either
Inline Custom XSLT via the Scripting Functoid
Custom XSLT setting Custom XSLT Path for the whole map
Having an intermediate schema that contains two Option nodes and having two maps. The first that maps the flat structure to one node and the looping to the second. Then a second map that loops across both and maps to to the same node.
In your case however, you need to have both (prop1,prop2,..) and the looping prop linked to the same looping functoid, and linking to the name and value and setting the link properties on the links from prop1,prop2 etc. to Copy name instead of value.
With your sample input that gives
<root>
<props>
<prop>
<name>prop1</name>
<value>foo</value>
</prop>
<prop>
<name>prop2</name>
<value>bar</value>
</prop>
<prop>
<name>propA</name>
<value>baz</value>
</prop>
<prop>
<name>propB</name>
<value>qux</value>
</prop>
</props>
</root>
I'm using MarkLogic v8.
I am trying to apply a container constraint on a structured query to return only documents with value x in element c (nested within elements a and b).
queryBuilder.containerConstraint() takes a parameter for an option name and a StructuredQueryDefinition. My option looks like this:
<options xmlns='http://marklogic.com/appservices/search'>
<constraint name='language'>
<element name=\"name\" ns=\"\"/>
</constraint>
</options>
"name" is the name of the innermost element (c) containing the value I want to reference against. Is this how the option should be constructed, or should 'name' instead be the name of the outermost element?
How should the StructuredQueryDefinition (that is accepted as a parameter by containerConstraint()) be constructed? Should I be writing raw XML, or are there contruction methods to be passed in?
Is there a better way to do this? I already have a working Term search, I just need to be able to filter by a property set inside the document.
I think I found an answer:
Option was as follows:
<search:options
xmlns:search='http://marklogic.com/appservices/search'>
<search:constraint name='language'>
<search:word>
<search:element name='name' ns=''/>
</search:word>
</search:constraint>
</search:options>
Then called the option in a Word Constraint:
queryBuilder.wordConstraint("language", MY_LANGUAGE)
This appears to do what I wanted it to.
I'm configuring Work Execution. The Work Order History query that is called when retrieving past work orders for assets or locations is open-ended. Consequently, several thousand rows are retrieved each time and the application times out. I can attach where clause (see below) to limit it to records with actfinish after a specific date. However, what I want to do is something like this...
spi_wm:actfinish>now()-30
<!--WorkOrder History Asset Resource-->
<resource id="workOrderHistoryAssetLoc" class="application.business.WorkOrderObject" defaultOrderBy="wonum asc" describedBy="http://jazz.net/ns/ism/work/smarter_physical_infrastructure#WorkOrder" name="workOrderHistoryAssetLoc" pageSize="50" providedBy="/oslc/sp/WorkManagement">
<attributes id="workOrderHistoryAsset_attributes1">
<attribute describedByProperty="dcterms:identifier" id="workOrderHistoryAsset_identifier_dctermsidentifier1" index="true" name="identifier"/>
<attribute describedByProperty="oslc:shortTitle" id="workOrderHistoryAsset_wonum_oslcshortTitle1" index="true" name="wonum"/>
<attribute describedByProperty="dcterms:title" id="workOrderHistoryAsset_description_dctermstitle1" index="true" method="descriptionChanged" name="description"/>
<attribute describedByProperty="spi:status" id="workOrderHistoryAsset_status_spistatus" index="true" method="statusChanged" name="status"/>
<localAttribute dataType="string" id="workOrderHistoryAsset_statusdesc_string" name="statusdesc"/>
</attributes>
<queryBases id="workOrderHistoryAsset_queryBasesh">
<queryBase defaultForSearch="true" id="workOrderHistoryAsset_queryBase_searchAllWorkOrdersh" name="searchAllWorkOrdersAsset" queryUri="/oslc/os/oslcwodetail?savedQuery=getWithComplexQuery"/>
<!-- TODO AWH 20170130 - add where clause to this query -->
</queryBases>
<whereClause clause="spi:status in ['COMP','CLOSE'] and spi_wm:actfinish>'2016-10-10T09:50:00-04:00'" id="workOrderHistoryAssetLoc_whereClause"/>
</resource>
I see elsewhere where there are formulas in the app.xml but I don't know what types of operators or language is available to accomplish something like this. I was hoping the whereClause attribute had the ability to use a resolverClass and resolverFunction so that I could replace a named parameter with a value derived from a javascript function... no dice. Any help would be appreciated!
It looks like you are attempting to set the where clause in the app.xml. While I think this could work, it would probably be a million times easier to do the following.
duplicate the resource, then comment out the original
Create a saved query in Maximo with the where clause you need
a. spi:status in ['COMP','CLOSE'] and spi_wm:actfinish>'2016-10-10T09:50:00-04:00'
Name the saved query "ANYWHERE_WOHIST" or something like that.
Modify the duplicate resource to point to your new saved query.
<queryBase defaultForSearch="true" id="workOrderHistoryAsset_queryBase_searchAllWorkOrdersh" name="searchAllWorkOrdersAsset" queryUri="/oslc/os/oslcwodetail?savedQuery=ANYWHERE_WOHIST"/>
Also, this allows the query where clause to be managed in the backend, so when your users decide they want to see something else here you can mange the query within Maximo. We're nearing the end of our project with Anywhere, so feel free to reach out if you'd like to swap war stories.
I have some data I want to fill in when my form opens, so I created an XML-file-based secondary data connection (it has to be a secondary connection, not the primary) with some sample values with the intent of later removing them and replacing them with real values. It basically looks like this:
<root>
<entry>
fields here
</entry>
<entry>
fields here
</entry>
</root>
(The second entry is so that InfoPath knows it has repeating values.)
I then bound <entry> to a repeating table in my form.
But when I open the form, there are exactly two values, and the options to allow adding/removing entries are greyed out. InfoPath knows that this is a repeating element, otherwise I wouldn't be able to bind it to a repeating table.
How can I change this to allow adding and removing entries?
Apparently, secondary data sources cannot be modified. I found this information in the InfoPath help.
Modifying secondary data sources
Because the fields and groups in secondary data sources are based on data connections to external data sources, you cannot modify the fields and groups in a secondary data source. When you view a secondary data source in the Data Source task pane, the fields have the locked field icon and the groups have the locked group icon to indicate that you cannot modify them.