0
votes

I have the below XML structure :

<Books>
    <Book>
        <ItemReference>
            <ClassificationCode listID="Name">Book1</ClassificationCode>
            <ClassificationCode listID="Property2"/>
            <ClassificationCode listID="Property3"/>
        </ItemReference>
    </Book>

    <Book>
        <ItemReference>
            <ClassificationCode listID="Name">Book2</ClassificationCode>
            <ClassificationCode listID="Property2"/>
            <ClassificationCode listID="Property3"/>
        </ItemReference>
    </Book>

    <Book>
        <ItemReference>
            <ClassificationCode listID="Name">Book1</ClassificationCode>
            <ClassificationCode listID="Property2"/>
            <ClassificationCode listID="Property3"/>
        </ItemReference>
    </Book>

    <Book>
        <ItemReference>
            <ClassificationCode listID="Name">Book4</ClassificationCode>
            <ClassificationCode listID="Property2"/>
            <ClassificationCode listID="Property3"/>
        </ItemReference>
    </Book>
</Books>

What is the proper way to form my for loop using XQuery,in order to create a validation rule to return as log a validation error when it founds duplicates in ClassificationCode[@listID = 'Name']/text() where the /text() = ('Book1', 'Book4')? That means i don't want to use the same validation for Book2. my problem is that i care about particular items if they will be double or not. To give more details my Validation XQuery looks like this :

    declare function local:BooksValidation (
    $books as element()*, 

    {

    for $book in $books 
        let $ItemRef := $book/ItemReference

    return (
        if (fn:exists($ItemRef)) then ()
        else 
            local:Issue($error, "No Items found", $currentPath)
            )
    }

    declare function local:Issue (
        $errorCode as xs:string, 
        $message as xs:string, 
        $xpath as xs:string) as xs:string* 
    {   
        concat($errorCode, ': ', $message, ' (XPath : ',  $xpath, ')')
    };

and i want to include in this for-loop an exact same if-statement that will check if there are double items

2
It is not clear which result you are looking for. I don't see any duplicates based on ClassificationCode[@listID = 'Name'][. = ('Book1', 'Book4')] as the first item has ClassificationCode listID="FulfillmentItemCode". So please explain in more detail what you are looking for and how the usual XQuery tools with grouping and/or distinct-values don't help. - Martin Honnen
The one with the "FulfillmentItemCode" was a mistake i fixed it. To be specific i want based on that property to check if ````ClassificationCode[@listID = 'Name']/text() = ('Book1', 'Book4') < 2 then () else local:log("Double Items")````` but i can't figure out the fn: that would help to achieve that. - pavloskkr

2 Answers

0
votes

Use below query for finding duplicate ' element values:

        let $text :=fn:distinct-values($xml/Book/ItemReference/ClassificationCode[@listID = "Name"]/text())
         for $each in $text
             let $seq := if(count($xml//ClassificationCode[@listID = "Name"]/text()[. = $each]) > 1)
                 then(concat($each, " Values are duplicate"))
                 else()
        return $seq
0
votes

To identify duplicates in XQuery (3 and later) you use the group by clause of a FLOWR expression to group the items and the where clause to check whether the grouping has found more than one item in a group:

declare variable $names as xs:string* external := ('Book1', 'Book4');

for $name in //ClassificationCode[@listID = 'Name'][. = $names]
group by $v := $name
where tail($name)
return 'duplicates for ' || $v

https://xqueryfiddle.liberty-development.net/bFukv8q

With XQuery 1 you can use

declare variable $names as xs:string* external := ('Book1', 'Book4');

let $names := //ClassificationCode[@listID = 'Name'][. = $names],
    $keys := distinct-values($names)
for $key in $keys
where $names[. = $key][2]
return concat('duplicates for ', $key)

https://xqueryfiddle.liberty-development.net/bFukv8q/1