Search code examples
xpathxpath-2.0

Advanced XPath query


I have an XML file that looks like this:

<?xml version="1.0" encoding="utf-8" ?>
<PrivateSchool>

     <Teacher id="teacher1">
         <Name>
           teacher1Name
         </Name>
    </Teacher>

    <Teacher id="teacher2">
        <Name>
            teacher2Name
        </Name>
    </Teacher>

  <Student id="student1">
    <Name>
      student1Name
    </Name>
  </Student>

  <Student id="student2">
    <Name>
      student2Name
    </Name>
  </Student>

    <Lesson student="student1" teacher="teacher1"  />
    <Lesson student="student2" teacher="teacher2"  />
    <Lesson student="student3" teacher="teacher3"  />
    <Lesson student="student1" teacher="teacher2"  />
    <Lesson student="student3" teacher="teacher3"  />
    <Lesson student="student1" teacher="teacher1"  />
    <Lesson student="student2" teacher="teacher4"  />
    <Lesson student="student1" teacher="teacher1"  />

</PrivateSchool>

There's also a DTD associated with this XML, but I assume it's not much relevant to my question. Let's assume all needed teachers and students are well defined.

What is the XPath query that returns the teachers' NAMES, that have at least one student that took more than 10 lessons with them?

I was looking at many XPath sites/examples. Nothing seemed advanced enough for this kind of question.


Solution

  • Doing a complex join in a single XPath may be possible, but you're banging your head against a brick wall. XQuery or XSLT are much more suited to this kind of thing. Here it is in XQuery:

    declare variable $doc as doc('data.xml');
    
    declare function local:numLessons($teacher, $student) {
      return count($doc//Lesson[@teacher = $teacher and @student = $student])
    };
    
    $doc//Teacher[some $s in //Lesson/@student satisfies local:numLessons(@id, $s) gt 10]/Name
    

    Having done that, if you are really determined you can reduce it to XPath 2.0:

    doc('data.xml')//Teacher[
       for $t in . return 
         some $s in //Lesson/@student satisfies 
           count(//Lesson[@teacher = $t and @student = $s]) gt 10] /Name
    

    Not tested.