0

I have a lot of XML files and I need to output the file names based on user-defined input from the command line.

Here is a simple example of XQuery which captures the essence of the problem.

File1.xml

<?xml version="1.0" encoding="utf-8"?>
<Description>  
  <FileName>File1</FileName>
  <Persons>
    <Person>
      <Name>Anna</Name>
      <Age>25</Age>
    </Person>
    <Person>
      <Name>Marco</Name>
      <Age>25</Age>
    </Person>
    <Person>
      <Name>Mary</Name>
      <Age>13</Age>
    </Person>
  </Persons>
</Description>

File2.xml

<?xml version="1.0" encoding="utf-8"?>
<Description>
  <FileName>File2</FileName>
  <Persons>
    <Person>
      <Name>Anna</Name>
      <Age>25</Age>
    </Person>
    <Person>
      <Name>Marco</Name>
      <Age>11</Age>
    </Person>
  </Persons>
</Description>

For example, the user needs to get the name of the file which contains persons: Anna of age 25 and Marco of age 25. I wrote a dirty solution. I am wondering if it can be done in generic form for an arbitrary number of input pairs (Name, Age).

Here, the inputs are hardcoded in search.xq.

search.xq

(: person and age are user defined inputs of arbitrary length :)
let $person:= ('Anna', 'Marco')
let $age:= (25, 25)

(: Output needs to be name of file which have person (Anna, 25) and (Marco,25):)
return collection('db')/Description/Persons/Person[(Name=$person[1] and Age=$age[1])]/../Person[(Name=$person[2] and Age=$age[2])]/../../FileName

output:

<FileName>File1</FileName>

Thanks in advance.

1 Answer 1

2

Here is a possible solution (which assumes that your XQuery processor allows you to pass on maps as user-defined input):

declare variable external $INPUT := map {
  'Anna': 25,
  'Marco': 25
};

for $description in collection('db')/Description
where every $test in map:for-each($INPUT, function($name, $age) {
  exists($description/Persons/Person[Name = $name and Age = $age])
}) satisfies $test
return $description/FileName

The second alternative is closer to your original solution. Names and ages are bound to separate variables:

declare variable $NAMES external := ('Anna', 'Marco');
declare variable $AGES external := (25, 25);

for $description in collection('db')/Description
where every $test in for-each-pair($NAMES, $AGES, function($name, $age) {
  exists($description/Persons/Person[Name = $name and Age = $age])
}) satisfies $test
return $description/FileName
Sign up to request clarification or add additional context in comments.

3 Comments

Thank you! I have only one problem, in a real case, I have 6 variables, not 2 like in this case with name and age.
I think the idea will be exactly the same.
I have learned a lot from your answer , thank you once again.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.