Search code examples
xmlxquery

XQuery grouping of data (transforming one-to-one into one-to-many)


I have an XML element structure with a one-to-one relationship like this:

<CrewAllocation>
<Crew>Crew A</Crew>
<CrewMember>John Smith</CrewMember>
</CrewAllocation>
<CrewAllocation>
<Crew>Crew A</Crew>
<CrewMember>Jack Jones</CrewMember>
</CrewAllocation>
<CrewAllocation>
<Crew>Crew B</Crew>
<CrewMember>John Johnson</CrewMember>
</CrewAllocation>

I want to transform this into:

<CrewAllocation>
<Crew>Crew A</Crew>
<CrewMembers>
<CrewMember>John Smith</CrewMember>
<CrewMember>Jack Jones</CrewMember>
</CrewMembers>
</CrewAllocation>
<CrewAllocation>
<Crew>Crew B</Crew>
<CrewMembers>
<CrewMember>John Johnson</CrewMember>
</CrewMembers>
</CrewAllocation>

Is there an easy way to do this in XQuery? Or perhaps a better way of asking: what is the most efficient way to do this in XQuery?

Thanks!


Solution

  • Here is BaseX v.9.4.4 implementation.

    Also, I had to make input XML well-formed by adding a root tag.

    XQuery

    xquery version "3.1";
      
    declare context item := document {
    <root>
        <CrewAllocation>
            <Crew>Crew A</Crew>
            <CrewMember>John Smith</CrewMember>
        </CrewAllocation>
        <CrewAllocation>
            <Crew>Crew A</Crew>
            <CrewMember>Jack Jones</CrewMember>
        </CrewAllocation>
        <CrewAllocation>
            <Crew>Crew B</Crew>
            <CrewMember>John Johnson</CrewMember>
        </CrewAllocation>
    </root>
    };
    
    <root>
    {
      for $x in ./root/CrewAllocation
      let $crew := $x/Crew
      group by $crew
      order by $crew
      return <CrewAllocation>
                <Crew>{$crew}</Crew>
                <CrewMembers>{$x/CrewMember}</CrewMembers>
          </CrewAllocation>
    }
    </root>
    

    Output

    <root>
      <CrewAllocation>
        <Crew>Crew A</Crew>
        <CrewMembers>
          <CrewMember>John Smith</CrewMember>
          <CrewMember>Jack Jones</CrewMember>
        </CrewMembers>
      </CrewAllocation>
      <CrewAllocation>
        <Crew>Crew B</Crew>
        <CrewMembers>
          <CrewMember>John Johnson</CrewMember>
        </CrewMembers>
      </CrewAllocation>
    </root>