开发者

xpath query returning wrong values when using greater and less than?

开发者 https://www.devze.com 2023-01-21 22:26 出处:网络
This query is returning values less than 1000. It should only be returning values between 1000 and 1100. Why is that?

This query is returning values less than 1000. It should only be returning values between 1000 and 1100. Why is that?

//results/Building[ 1 = 1 and (( Vacancy/sqft > 1000 ) and ( Vacancy/sqft < 1100 ) ) ]

The query will return the following building, which has vacancies less than 1000 square feet and greater than 1100 square feet:

<Building>
  <Vacancy><sqft>900</sqft></Vacancy>
  <Vacancy><sqft>1000</sqft></Vacancy>
  <Vacancy><sqft>2000</sqft></Vacancy>
  <Vacancy><sqft>500</sqft></Vacancy>
</Building>

Why is it included in the results?

Sample data:

<resu开发者_Python百科lts>
  <Building><!--Shouldn't be selected.--></Building>

  <Building><!--Should be selected-->
    <Vacancy><sqft>1050</sqft></Vacancy>
  </Building>

  <Building><!--Should be selected-->
    <Vacancy><sqft>1025</sqft></Vacancy>
    <Vacancy><sqft>1075</sqft></Vacancy>
  </Building>

  <Building><!--Shouldn't be selected-->
    <Vacancy><sqft>10</sqft></Vacancy>
    <Vacancy><sqft>50</sqft></Vacancy>
  </Building>

  <Building><!--Should be selected.-->
    <Vacancy><sqft>1050</sqft></Vacancy>
    <Vacancy><sqft>2000</sqft></Vacancy>
  </Building>

  <Building><!--Should be selected.-->
    <Vacancy><sqft>900</sqft></Vacancy>
    <Vacancy><sqft>1040</sqft></Vacancy>
  </Building>

  <Building><!--Shouldn't be selected-->
    <Vacancy><sqft>10500</sqft></Vacancy>
  </Building>

  <Building><!--Shouldn't be selected-->
    <Vacancy><sqft>900</sqft></Vacancy>
    <Vacancy><sqft>1000</sqft></Vacancy>
    <Vacancy><sqft>2000</sqft></Vacancy>
    <Vacancy><sqft>500</sqft></Vacancy>
  </Building>

</results>

Thanks.


The sample Building has a Vacancy child with sqft of 2000, so Vacancy/sqft > 1000 succeeds. It has a child with sqft of 1000 (and 900 and 500), so Vacancy/sqft < 1100 succeeds. Thus the xpath selects the Building.

The comparison expressions (such as Vacancy/sqft <= 1000) are implicitly qualified with "there exists"–as in "there exists a Vacancy child that has a sqft child with value > 1000"–because Vacancy/sqft is a set of nodes, rather than a single node. Moreover, each comparison has its own qualification, so the sqft in Vacancy/sqft > 1000 doesn't need to be the same sqft as in Vacancy/sqft < 1100. Note that //results/Buildings is a node set; the predicate [...] applies separately to each item in the set, which is why there isn't an issue with qualifiers. Translating your original xpath into English, we get:

Select the buildings (in the results) such that 1=1 and there exists a vacancy square footage > 1000 and there exists a vacancy square footage < 1100.

Let's take the English statement of the desired query and make it a little closer to a statement of logic, arriving at one of:

Select the buildings (in the results) such that there exists a vacancy with square footage such that it's > 1000 and it's < 1100

Select the buildings (in the results) such that there exists a vacancy such that the square footage > 1000 and the square footage < 1100

The former leads to jasso's solution, the latter to:

//results/Building[ Vacancy[1000 < sqft and sqft < 1100] ]

Original solution

(Note: this answered the original question, when it wasn't clear what the OP wanted. The technique may prove useful to others with a similar problem but different requirements, so I'm leaving it in.)

Try the logical double-negation of the condition:

//results/Building[ Vacancy and not (Vacancy/sqft <= 1000 or Vacancy/sqft >= 1100) ]

This predicate includes a test for Vacancy children to filter out cases that are otherwise trivially true, i.e. buildings with no vacancies. The English equivalent of this solution is:

Select buildings (in the results) such that the building has a vacancy and it's not the case that there exists a vacancy square footage <= 1000 or there exists a vacancy square footage >= 1100

In fewer words:

Select all buildings with vacancies where no vacancy has <= 1000 square feet or >= 1100 square feet.

In fewer words still:

Select all buildings with vacancies where all vacancies are between 1000 and 1100 square feet.


Do you also need to match buildings with some sqft outside your criteria but at least one sqft between 1000-1100 like this

  <Building>Should this be selected too?
    <Vacancy><sqft>1000</sqft></Vacancy>
    <Vacancy><sqft>1050</sqft></Vacancy>
    <Vacancy><sqft>2000</sqft></Vacancy>
  </Building>

If yes, then use XPath expression

/results/Building[Vacancy/sqft[. > 1000 and 1100 > . ] or not(Vacancy)]

It also selects buildings with no <Vacancy> element (as requested).


//results
  /Building[1 = 1 and 
            (( Vacancy/sqft > 1000 ) and (Vacancy/sqft < 1100 ))]

This query is returning values less than 1000. It should only be returning values between 1000 and 1100. Why is that?

From http://www.w3.org/TR/xpath/#booleans

If one object to be compared is a node-set and the other is a number, then the comparison will be true if and only if there is a node in the node-set such that the result of performing the comparison on the number to be compared and on the result of converting the string-value of that node to a number using the number function is true.

Node set comparisons are existencial comparisons. Vacancy/sqft > 1000 means: Is there at least one Vacancy/sqft greater than 1000?

If you want to select Building elements having Vacancy/sqft grand children, and all of them in the range (1000,1100), this XPath expression:

/results/Building[Vacancy/sqft and not(Vacancy/sqft[1000 >= . or . >= 1100])]


Try changing your xpath to this:

//results/Building[number(Vacancy/sqft) > 1000  and  number(Vacancy/sqft) < 1100 ]

I suspect it's treating your Vacancy/sqft node like text which could be causing some weirdness...

I removed your 1=1 and extra parens because I didn't see a need for them. The main point is to try the number function.

UPDATE

This one is a little odd but it grabs the ones you want plus the one you aren't sure if you want (Should this be selected?):

//results/Building[count(Vacancy[sqft > 1000  and  sqft < 1100 ]) = count(Vacancy)]

and if you want to exclude that one:

//results/Building[(count(Vacancy[sqft > 1000  and  sqft < 1100 ]) = count(Vacancy)) and count(Vacancy) > 0]

Also I am using this site to text my xpaths, if there is some sort of fundamental difference between how they do it and how objective-c does let me know...


Here are two XPath expressions:

1. The following selects all nodes that you believe should be selected:

/*/*[Vacancy and not(Vacancy[. < 1000 or . > 1100])]
  1. The following selects all nodes that you believe should be selected and all those you are not certain about. It doesn't select any node that you are certain should not be selected:

/*/*[not(Vacancy) or Vacancy[. > 1000 and not(. > 1100)]]

This XSLT transformation can be used to verifu the correctness of the XPath expressions:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
 <xsl:output omit-xml-declaration="yes" indent="yes"/>
 <xsl:strip-space elements="*"/>

 <xsl:template match="/">
  <xsl:copy-of select=
  "/*/*[Vacancy and not(Vacancy[. &lt; 1000 or . > 1100])]
  "/>

===============================
  <xsl:copy-of select=
  "/*/*[not(Vacancy) or Vacancy[. > 1000 and not(. > 1100)]]
  "/>

 </xsl:template>
</xsl:stylesheet>

when applied on the provided XML document:

<results>
  <Building><!--Should this be selected?--></Building>

  <Building><!--Should be selected-->
    <Vacancy><sqft>1050</sqft></Vacancy>
  </Building>

  <Building><!--Should be selected-->
    <Vacancy><sqft>1025</sqft></Vacancy>
    <Vacancy><sqft>1075</sqft></Vacancy>
  </Building>

  <Building><!--Shouldn't be selected-->
    <Vacancy><sqft>10</sqft></Vacancy>
    <Vacancy><sqft>50</sqft></Vacancy>
  </Building>

  <Building><!--Should this be selected?-->
    <Vacancy><sqft>1050</sqft></Vacancy>
    <Vacancy><sqft>2000</sqft></Vacancy>
  </Building>

  <Building><!--Should this be selected?-->
    <Vacancy><sqft>900</sqft></Vacancy>
    <Vacancy><sqft>1040</sqft></Vacancy>
  </Building>

  <Building><!--Shouldn't be selected-->
    <Vacancy><sqft>10500</sqft></Vacancy>
  </Building>

  <Building><!--Shouldn't be selected-->
    <Vacancy><sqft>900</sqft></Vacancy>
    <Vacancy><sqft>1000</sqft></Vacancy>
    <Vacancy><sqft>2000</sqft></Vacancy>
    <Vacancy><sqft>500</sqft></Vacancy>
  </Building>

</results>

the wanted, correct results are produced:

<Building><!--Should be selected-->
   <Vacancy>
      <sqft>1050</sqft>
   </Vacancy>
</Building>
<Building><!--Should be selected-->
   <Vacancy>
      <sqft>1025</sqft>
   </Vacancy>
   <Vacancy>
      <sqft>1075</sqft>
   </Vacancy>
</Building>

===============================
  <Building><!--Should this be selected?--></Building>
<Building><!--Should be selected-->
   <Vacancy>
      <sqft>1050</sqft>
   </Vacancy>
</Building>
<Building><!--Should be selected-->
   <Vacancy>
      <sqft>1025</sqft>
   </Vacancy>
   <Vacancy>
      <sqft>1075</sqft>
   </Vacancy>
</Building>
<Building><!--Should this be selected?-->
   <Vacancy>
      <sqft>1050</sqft>
   </Vacancy>
   <Vacancy>
      <sqft>2000</sqft>
   </Vacancy>
</Building>
<Building><!--Should this be selected?-->
   <Vacancy>
      <sqft>900</sqft>
   </Vacancy>
   <Vacancy>
      <sqft>1040</sqft>
   </Vacancy>
</Building>
0

精彩评论

暂无评论...
验证码 换一张
取 消