Skip to content

apf:strSplit if used first, causes CSV rows to be skipped despite OPTIONAL #654

Description

@VladimirAlexiev

Query test1.fx works ok. You may notice that each CSV field is fetched with optional {?ROW then processed in the same OPTIONAL block:

base            <https://graph.swissgrid.ch/>
prefix apf: <http://jena.apache.org/ARQ/property#>
prefix fx: <http://sparql.xyz/facade-x/ns/>
prefix xyz: <http://sparql.xyz/facade-x/data/>
construct {
  graph ?System_URL                {?IRI_URL meta:systemReferenz ?Referenz}
  graph ?MastersystemPrimary_URL   {?IRI_URL meta:systemPraeferenz <enum/systemPraeferenz/primaer>}
  graph ?MastersystemSecondary_URL {?IRI_URL meta:systemPraeferenz <enum/enum/systemPraeferenz/sekundaer>}
  graph ?MastersystemFuture_URL    {?IRI_URL meta:systemPraeferenz <enum/enum/systemPraeferenz/zukuenftigePrimaer>}
} where {
  service <x-sparql-anything:> {
    fx:properties
      fx:location $_location ;
      fx:csv.headers "true" ;
      fx:csv.headers.sanitize "true" ;
      fx:csv.null-string "" ;
    .
    optional {?ROW xyz:IRI|xyz:Attribut_IRI ?IRI
      bind(iri(concat("otl/",?IRI)) as ?IRI_URL)
    }
    optional {?ROW xyz:Objekt_System_Referenz|xyz:Attribut_System_Referenz ?SystemReferenzen.
      ?SystemReferenz apf:strSplit (?SystemReferenzen ";")
      bind(iri(concat("profile/",replace(?SystemReferenz,"(.+):.*","$1"),"/graph")) as ?System_URL)
      bind(if(regex(?SystemReferenz,".+:(.+)"),replace(?SystemReferenz,".+:(.+)","$1"),?UNDEF) as ?Referenz)
    }
    optional {?ROW xyz:IRI_Mastersystem ?Mastersystem
      bind(iri(concat("profile/",if(?Mastersystem="engineeringbaseSap","engineeringbase",?Mastersystem),"/graph")) as ?MastersystemPrimary_URL)
      bind(iri(concat("profile/",if(?Mastersystem="engineeringbaseSap","sappm",          ?UNDEF),       "/graph")) as ?MastersystemSecondary_URL)
    }
    optional {?ROW xyz:IRI_Zukuenftiges_Mastersystem ?MastersystemFuture
      bind(iri(concat("profile/",?MastersystemFuture,"/graph")) as ?MastersystemFuture_URL)
    }
  }
}

Query test2.fx is very similar in structure, but it first fetches all fields (each in optional {?ROW) then processes them.
Notice that apf:strSplit is applied in OPTIONAL block after the argument ?Attribut_System_Referenz is fetched, not in the same block:

base            <https://graph.swissgrid.ch/>
prefix apf: <http://jena.apache.org/ARQ/property#>
prefix fx: <http://sparql.xyz/facade-x/ns/>
prefix xyz: <http://sparql.xyz/facade-x/data/>
construct {
  graph ?profile_Attribut_System_Referenz_SPLIT_SEMI_SYSREFSYS_graph_URL {
    ?otl_Attribut_IRI_URL meta:systemReferenz ?Attribut_System_Referenz_SPLIT_SEMI_SYSREFID}
  graph ?profile_IRI_Mastersystem_SYS_PRIMARY_graph_URL {
    ?otl_Attribut_IRI_URL meta:systemPraeferenz <enum/systemPraeferenz/primaer>}
  graph ?profile_IRI_Mastersystem_SYS_SECONDARY_graph_URL {
    ?otl_Attribut_IRI_URL meta:systemPraeferenz <enum/systemPraeferenz/sekundaer>}
  graph ?profile_IRI_Zukuenftiges_Mastersystem_graph_URL {
    ?otl_Attribut_IRI_URL meta:systemPraeferenz <enum/systemPraeferenz/zukuenftigePrimaer>}
}
where {
  service <x-sparql-anything:> {
    fx:properties
      fx:location $_location ;
      fx:csv.headers "true" ;
      fx:csv.headers.sanitize "true" ;
      fx:csv.null-string "" ;
    .
    optional {?ROW xyz:Attribut_System_Referenz ?Attribut_System_Referenz}
    optional {?ROW xyz:Attribut_IRI ?Attribut_IRI}
    optional {?ROW xyz:IRI_Mastersystem ?IRI_Mastersystem}
    optional {?ROW xyz:IRI_Zukuenftiges_Mastersystem ?IRI_Zukuenftiges_Mastersystem}
    optional {?Attribut_System_Referenz_SPLIT_SEMI apf:strSplit (?Attribut_System_Referenz ";")}
    bind(replace(?Attribut_System_Referenz_SPLIT_SEMI,"(.+):.*","$1") as ?Attribut_System_Referenz_SPLIT_SEMI_SYSREFSYS)
    bind(iri(concat("profile/",?Attribut_System_Referenz_SPLIT_SEMI_SYSREFSYS,"/graph")) as ?profile_Attribut_System_Referenz_SPLIT_SEMI_SYSREFSYS_graph_URL)
    bind(if(regex(?Attribut_System_Referenz_SPLIT_SEMI,".+:(.+)"),replace(?Attribut_System_Referenz_SPLIT_SEMI,".+:(.+)","$1"),?UNDEF) as ?Attribut_System_Referenz_SPLIT_SEMI_SYSREFID)
    bind(iri(concat("otl/",?Attribut_IRI)) as ?otl_Attribut_IRI_URL)
    bind(if(?IRI_Mastersystem="engineeringbaseSap","engineeringbase",?IRI_Mastersystem) as ?IRI_Mastersystem_SYS_PRIMARY)
    bind(iri(concat("profile/",?IRI_Mastersystem_SYS_PRIMARY,"/graph")) as ?profile_IRI_Mastersystem_SYS_PRIMARY_graph_URL)
    bind(if(?IRI_Mastersystem="engineeringbaseSap","sappm",?UNDEF) as ?IRI_Mastersystem_SYS_SECONDARY)
    bind(iri(concat("profile/",?IRI_Mastersystem_SYS_SECONDARY,"/graph")) as ?profile_IRI_Mastersystem_SYS_SECONDARY_graph_URL)
    bind(iri(concat("profile/",?IRI_Zukuenftiges_Mastersystem,"/graph")) as ?profile_IRI_Zukuenftiges_Mastersystem_graph_URL)
  }
}

Something strange happens with test2.fx:
it outputs quads only for rows that have xyz:Attribut_System_Referenz.

These CSV are progressively increasing subsets: test1.csv, test13.csv

Try the two scripts on these examples to see the problem. Eg

sparql-anything -q test1.fx -v location=test13.csv -f trig|grep -v PREFIX > out1.trig
sparql-anything -q test2.fx -v location=test13.csv -f trig|grep -v PREFIX > out2.trig

Update: the version is 1.2.0

Activity

  1. changed the title [-]OPTIONAL[/-] [+]unnested OPTIONAL causes some CSV rows to be skipped[/+] on Aug 30, 2026
  2. VladimirAlexiev commented on Aug 31, 2026

    @VladimirAlexiev
    Author

    I have reworked test2.fx to have the same structure as test1.fx: test3.fx:

    base         <https://graph.swissgrid.ch/>
    prefix meta: <meta/>
    prefix apf:  <http://jena.apache.org/ARQ/property#>
    prefix fx:   <http://sparql.xyz/facade-x/ns/>
    prefix xyz:  <http://sparql.xyz/facade-x/data/>
    construct {
      graph ?profile_Attribut_System_Referenz_SPLIT_SEMI_SYSREFSYS_graph_URL {
        ?otl_Attribut_IRI_URL meta:systemReferenz ?Attribut_System_Referenz_SPLIT_SEMI_SYSREFID}
      graph ?profile_IRI_Mastersystem_SYS_PRIMARY_graph_URL {
        ?otl_Attribut_IRI_URL meta:systemPraeferenz <enum/systemPraeferenz/primaer>}
      graph ?profile_IRI_Mastersystem_SYS_SECONDARY_graph_URL {
        ?otl_Attribut_IRI_URL meta:systemPraeferenz <enum/systemPraeferenz/sekundaer>}
      graph ?profile_IRI_Zukuenftiges_Mastersystem_graph_URL {
        ?otl_Attribut_IRI_URL meta:systemPraeferenz <enum/systemPraeferenz/zukuenftigePrimaer>}
    }
    where {
      service <x-sparql-anything:> {
        fx:properties
          fx:location $_location ;
          fx:csv.headers "true" ;
          fx:csv.headers.sanitize "true" ;
          fx:csv.null-string "" ;
        .
        optional {?ROW xyz:Attribut_System_Referenz ?Attribut_System_Referenz.
          ?Attribut_System_Referenz_SPLIT_SEMI apf:strSplit (?Attribut_System_Referenz ";")
          bind(replace(?Attribut_System_Referenz_SPLIT_SEMI,"(.+):.*","$1") as ?Attribut_System_Referenz_SPLIT_SEMI_SYSREFSYS)
          bind(iri(concat("profile/",?Attribut_System_Referenz_SPLIT_SEMI_SYSREFSYS,"/graph")) as ?profile_Attribut_System_Referenz_SPLIT_SEMI_SYSREFSYS_graph_URL)
          bind(if(regex(?Attribut_System_Referenz_SPLIT_SEMI,".+:(.+)"),replace(?Attribut_System_Referenz_SPLIT_SEMI,".+:(.+)","$1"),?UNDEF) as ?Attribut_System_Referenz_SPLIT_SEMI_SYSREFID)
        }
        optional {?ROW xyz:Attribut_IRI ?Attribut_IRI
          bind(iri(concat("otl/",?Attribut_IRI)) as ?otl_Attribut_IRI_URL)
        }
        optional {?ROW xyz:IRI_Mastersystem ?IRI_Mastersystem
          bind(if(?IRI_Mastersystem="engineeringbaseSap","engineeringbase",?IRI_Mastersystem) as ?IRI_Mastersystem_SYS_PRIMARY)
          bind(iri(concat("profile/",?IRI_Mastersystem_SYS_PRIMARY,"/graph")) as ?profile_IRI_Mastersystem_SYS_PRIMARY_graph_URL)
          bind(if(?IRI_Mastersystem="engineeringbaseSap","sappm",?UNDEF) as ?IRI_Mastersystem_SYS_SECONDARY)
          bind(iri(concat("profile/",?IRI_Mastersystem_SYS_SECONDARY,"/graph")) as ?profile_IRI_Mastersystem_SYS_SECONDARY_graph_URL)
        }
        optional {?ROW xyz:IRI_Zukuenftiges_Mastersystem ?IRI_Zukuenftiges_Mastersystem
          bind(iri(concat("profile/",?IRI_Zukuenftiges_Mastersystem,"/graph")) as ?profile_IRI_Zukuenftiges_Mastersystem_graph_URL)
        }
      }
    }

    But it still doesn't output the quads from a row that lacks xyz:Attribut_System_Referenz:

    <https://graph.swissgrid.ch/profile/plscadd/graph> {
        <https://graph.swissgrid.ch/otl/leiterquerschnittKern>
                meta:systemPraeferenz  <https://graph.swissgrid.ch/enum/systemPraeferenz/zukuenftigePrimaer> , <https://graph.swissgrid.ch/enum/systemPraeferenz/primaer> .
    }

    If I swap the order of the first 2 OPTIONAL blocks, it works ok.
    But they are absolutely independent of each other ?!?

  3. changed the title [-]unnested OPTIONAL causes some CSV rows to be skipped[/-] [+]apf:strSplit if used first, causes CSV rows to be skipped despite OPTIONAL[/+] on Aug 31, 2026
  4. VladimirAlexiev commented on Aug 31, 2026

    @VladimirAlexiev
    Author

    test4.fx does the apf:strSplit last, and it works ok:

    base         <https://graph.swissgrid.ch/>
    prefix meta: <meta/>
    prefix apf:  <http://jena.apache.org/ARQ/property#>
    prefix fx:   <http://sparql.xyz/facade-x/ns/>
    prefix xyz:  <http://sparql.xyz/facade-x/data/>
    construct {
      graph ?profile_Attribut_System_Referenz_SPLIT_SEMI_SYSREFSYS_graph_URL {
        ?otl_Attribut_IRI_URL meta:systemReferenz ?Attribut_System_Referenz_SPLIT_SEMI_SYSREFID}
      graph ?profile_IRI_Mastersystem_SYS_PRIMARY_graph_URL {
        ?otl_Attribut_IRI_URL meta:systemPraeferenz <enum/systemPraeferenz/primaer>}
      graph ?profile_IRI_Mastersystem_SYS_SECONDARY_graph_URL {
        ?otl_Attribut_IRI_URL meta:systemPraeferenz <enum/systemPraeferenz/sekundaer>}
      graph ?profile_IRI_Zukuenftiges_Mastersystem_graph_URL {
        ?otl_Attribut_IRI_URL meta:systemPraeferenz <enum/systemPraeferenz/zukuenftigePrimaer>}
    }
    where {
      service <x-sparql-anything:> {
        fx:properties
          fx:location $_location ;
          fx:csv.headers "true" ;
          fx:csv.headers.sanitize "true" ;
          fx:csv.null-string "" ;
        .
        optional {?ROW xyz:Attribut_IRI ?Attribut_IRI}
        optional {?ROW xyz:IRI_Mastersystem ?IRI_Mastersystem}
        optional {?ROW xyz:IRI_Zukuenftiges_Mastersystem ?IRI_Zukuenftiges_Mastersystem}
        optional {?ROW xyz:Attribut_System_Referenz ?Attribut_System_Referenz}
        optional {?Attribut_System_Referenz_SPLIT_SEMI apf:strSplit (?Attribut_System_Referenz ";")}
        bind(replace(?Attribut_System_Referenz_SPLIT_SEMI,"(.+):.*","$1") as ?Attribut_System_Referenz_SPLIT_SEMI_SYSREFSYS)
        bind(iri(concat("profile/",?Attribut_System_Referenz_SPLIT_SEMI_SYSREFSYS,"/graph")) as ?profile_Attribut_System_Referenz_SPLIT_SEMI_SYSREFSYS_graph_URL)
        bind(if(regex(?Attribut_System_Referenz_SPLIT_SEMI,".+:(.+)"),replace(?Attribut_System_Referenz_SPLIT_SEMI,".+:(.+)","$1"),?UNDEF) as ?Attribut_System_Referenz_SPLIT_SEMI_SYSREFID)
        bind(iri(concat("otl/",?Attribut_IRI)) as ?otl_Attribut_IRI_URL)
        bind(if(?IRI_Mastersystem="engineeringbaseSap","engineeringbase",?IRI_Mastersystem) as ?IRI_Mastersystem_SYS_PRIMARY)
        bind(iri(concat("profile/",?IRI_Mastersystem_SYS_PRIMARY,"/graph")) as ?profile_IRI_Mastersystem_SYS_PRIMARY_graph_URL)
        bind(if(?IRI_Mastersystem="engineeringbaseSap","sappm",?UNDEF) as ?IRI_Mastersystem_SYS_SECONDARY)
        bind(iri(concat("profile/",?IRI_Mastersystem_SYS_SECONDARY,"/graph")) as ?profile_IRI_Mastersystem_SYS_SECONDARY_graph_URL)
        bind(iri(concat("profile/",?IRI_Zukuenftiges_Mastersystem,"/graph")) as ?profile_IRI_Zukuenftiges_Mastersystem_graph_URL)
      }
    }
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions