I have an assignment to create an xml file and query it using XQuery. The file looks roughly like this:
<library>
<book>
<title>Title1</title>
<author>Author1</author>
<author>Author2</author>
<subject>Subject1</subject>
</book>
<book>
<title>Title2</title>
<author>Author3</author>
<subject>Subject1</subject>
</book>
</library>
For each author, I'm supposed to return the title of the book, so obviously Title 1 should be returned twice.
I've been trying something like this:
let $a:=doc("/home/user/library.xml")/library/book
for $x in $a/title, $y in $a/author
return $x
The results I'm getting are:
Title1
Title2
Title1
Title2
Title1
Title2
I see that the problem is that for every author, I'm returning every book title, but I'm not sure how to get it to return only the book titles that correspond to a particular author. Any suggestions?
Thanks!
Use:
for $author in distinct-values(/*/*/author)
return
/*/book[$author = author]/title/string()
When this XPath expression (that is also XQuery, because XPath is a subset of XQuery) is evaluated against the provided XML document:
<library>
<book>
<title>Title1</title>
<author>Author1</author>
<author>Author2</author>
<subject>Subject1</subject>
</book>
<book>
<title>Title2</title>
<author>Author3</author>
<subject>Subject1</subject>
</book>
</library>
the wanted, correct result is produced:
Title1 Title1 Title2
Related
test.xml:
<?xml version="1.0" encoding="UTF-8"?>
<breakfast_menu>
<food>
<name>French Toast aaa</name>
<price>$5.95</price>
<description>Our famous Belgian Waffles with plenty of real maple syrup</description>
<calories>650</calories>
</food>
<food>
<name>French Toast</name>
<price>$4.50</price>
<description>Thick slices made from our homemade sourdough bread</description>
<calories>600</calories>
</food>
<food>
<name>Homestyle Breakfast</name>
<price>$6.95</price>
<description>Two eggs, bacon or sausage, toast, and our ever-popular hash browns</description>
<calories>950</calories>
</food>
</breakfast_menu>
test.xqy:
for $x in doc('test.xml')//*
return update insert attribute id {'abcd'} into $x
For each XML markup I add a new attribute.
The xqy file is pretty simple. And I got:
[XPST0003] Unexpected end of query: 'insert attribut...'.
Any help?
You're having two issues here:
misuse of the update statement and
missing node keyword.
The BaseX-specific update statement is only meant to be used with the copy/modify construct; you don't need it here. Then, the operator for inserting any kinds of nodes is always insert node $node [positional clause] into $target with an optional [positional clause]. Instead of a node variable $node, you can of course also use a node constructor like attribute id {'abcd'}.
The correct query is:
for $x in doc('test.xml')//*
return insert node attribute id {'abcd'} into $x
How could I get the list of title books where country location is unique in the next XML?
<BooksLib>
<Book Title="Murder in NY" Year="1980">
<BookLocations>
<Location City="New York" Country="USA"/>
<Location City="Virginia" Country="USA"/>
</BookLocations>
</Book >
<Book Title="Dracula" Year="2000">
<BookLocations>
<Location City="Sydney" Country="Australia"/>
<Location City="Moab" Country="USA"/>
<Location City="Calvados" Country="France"/>
</BookLocations>
</Book>
<Book Title="Romance in calvados" Year="2012">
<BookLocations>
<Location City="Calvados" Country="France"/>
</BookLocations>
</Book >
</BooksLib>
For example, in this XML would be "Dracula", because Australia only appears once
Now I got this:
for $book in doc("books.xml")//Book
where count(distinct-values($Book/BookLocations/Location/#Country)) eq 1
return $Book/data(#Title)
But this gives me the titles where the county is the same.
I would do it slightly differently. First, because it feels more natural to me how one actually thinks and second, because it will be faster on a large set. So first I would try to identify which countries are just present once. And then based on this filter out the results. The following should work:
let $single-loc :=
for $loc in doc("books.xml")//Location/#Country/string()
where count(//Location/#Country[. = $loc]) = 1
return $loc
for $book in doc("books.xml")//Book
where $book/BookLocations/Location/#Country = $single-loc
return $book/data(#Title)
Please note, that your input XML was not well-formed (I edited your post). Also, your XQuery is wrong as $book and $Book are two different variables.
A chapter-metadata.xml store in each book isbn folder(there are 100 isbn folder so there is 100 chapter-metadata.xml) which store in marklogic database server and chapter-metadata. Xml either contain data of one chapter or empty. If chapter-metadata.xml contain only one chapter information then I want to add more chapter information(my chapter infomation is common for all chapter) under chapter element with attribute and value of that chapter up to how many chapter store in book isbn folder(that I can fetch and store in a variable $chapter_sequence like ch001 ch002 ch003 ch004..) or if chapter-metadata.xml does not have any chaper information then it will create chapter element with attribute and value of chapter number and add my information, below I have put some xml structure if there is one chapter information and my information is from element keywordset
<?xml version="1.0" encoding="UTF-8" ?>
<chaptermetadata>
<bookisbn>isbn number</bookisbn>
<booktitle>Copyright</booktitle>
<chapter id="ch001"">
<keywordset>
<keyword role="primary">context</keyword>
<keyword role="secondary">Copyright</keyword>
<keyword role="tertiary">subject</keyword>
</keywordset>
</chapter>
</chaptermetadata>
I want like below:
<?xml version="1.0" encoding="UTF-8" ?>
<chaptermetadata>
<bookisbn>isbn number</bookisbn>
<booktitle>Copyright</booktitle>
<chapter id="ch001"">
<keywordset>
<keyword role="primary">context</keyword>
<keyword role="secondary">Copyright</keyword>
<keyword role="tertiary">subject</keyword>
</keywordset>
</chapter>
<chapter id="ch002"">
<keywordset>
<keyword role="primary">context</keyword>
<keyword role="secondary">Copyright</keyword>
<keyword role="tertiary">subject</keyword>
</keywordset>
</chapter>
so on to last chapter which I store in veriable
</chaptermetadata>
thanks,
raj
The question is hard to follow, but start with http://docs.marklogic.com/xdmp:directory and a FLWOR expression. Let's say you put this into a function. I'll handwave a few helper functions that you would also have to implement, but the function might look something like this:
declare function chaptermetadata($isbn as xs:string)
as element(chaptermetadata) {
<chaptermetadata>
{
<bookisbn>{ $isbn }</bookisbn>
<booktitle>{ title($isbn) }</booktitle>
for $chapter in xdmp:directory(isbn-uri($isbn), 'infinity')
return element { fn:node-name($chapter) } {
$chapter/#*,
$chapter/keywordset }
}
<chaptermetadata>
};
Now, this code won't help much unless you understand everything that it's doing so you can modify it to suit your needs. This is a variation on one of the XQuery use cases, so you might find helpful to work through and understand those: http://www.w3.org/TR/xquery-use-cases/
Lets say this is the XML file I have.
<bookstore>
<book year="1994">
<title>blah</title>
<price>66</price>
</book>
<book year="1998">
<title>blahblah</title>
<price>99</price>
</book>
</bookstore>
How do I select all books where the year attribute is <1995 and price is <70.
This is what I have:
for $x in doc("bkstr.xml")/bookstore/book
where $x/price<70 and ??
return $x
How do i check the value of the year attribute?
Attributes are addressed by using #.
for $x in doc("bkstr.xml")/bookstore/book
where $x/price<70 and $x/#year<1995
return $x
You can also use the much shorter equivalent
doc("bkstr.xml")/bookstore/book[price<70 and #year<1995]
Using Xquery, how can I search the file below (consisting of many items), for all items with 'XC' in the part-number (there are many), then for matches return all 3 of the interesting data elements (part-number, part-name, and name)? The return is the main problem--my attempts result in every permutation of the interesting data elements. Thank you!
<catalog>
<item>
<description>
<partref>
<part-id>
<part-number>XC51222</part-number>
</part-id>
</partref>
<part-name>DSP, Network Vectoring<part-name>
<vendors>
<vendor1>
<pay-to>
<name>JCOF Industries</name>
</pay-to>
</vendor1>
</vendors>
</description>
</item>
<item>
</item>
[many items…]
</catalog>
xquery version "1.0";
let $sep := ','
for $x in catalog/item
where fn:matches($x/description/partref/part-id/part-number, 'XC')
return fn:string-join( ($x/description/partref/part-id/part-number/text(), $x/description/part-name/text(), $x/description/vendors/vendor1/pay-to/name/text()), $sep)