XQuery error: Attribute must follow the root element - xquery

Happy New Year to All!
I am learning XQuery with BaseX and face the following problem now.
I am parsing the factbook.xml file which is the part of the distribution.
The following query runs ok:
for $country in db:open('factbook')//country
where $country/#population < 1000000 and $country/#population > 500000
return <country name="{$country/name}" population="{$country/#population}">
{
for $city in $country/city
let $pop := number($city/population)
order by $pop descending
return <city population="{$city/population/text()}"> {$city/name/text()}
</city>
}
</country>
but while trying to generate a html running the second query - if I try to put the "{$country/#population}" in the <h2>Country population: </h2> tag I see an error message "Attribute must follow the root element".
<html><head><title>Some Countries</title></head><body>
{
for $country in db:open('factbook')//country
let $pop_c := $country/#population
where $pop_c < 1000000 and $pop_c > 500000
return
<p>
<h1>Country: {$country/name/text()}</h1>
<h2>Country population: #error comes if I put it here!#</h2>
{
for $city in $country/city
let $pop := number($city/population)
order by $pop descending
return ( <h3>City: {$city/name/text()}</h3>,
<p>City population: {$city/population/text()}</p>
)
}
</p>
}
</body></html>
Where is my mistake?
Thank you!

Just using:
{$country/#population}
copies the attribute population in the result. An attribute should follow immediately an element (or other attributes that follow the element) -- but this one follows a text node and this causes the error to be raised.
Use:
<h2>Country population: {string($country/#population)} </h2>

When you write {$country/#population}, you do not insert the text of the population attribute, but the attribute itself. If you did not had the "Country population text before it", using {$country/#population} would create something like`
If you want its value, use:
{data($country/#population)}
Or
{data($pop_c)}
since you have already have it in a variable. (the number or string functions can also be used instead of data, but I think data is the fastest)

Related

MDX error trying to compare one hierarchy level to another one

I have an MDX issue that I really don't understand with a 5 level hierarchy "SEGMENTATION" : AFFAIRE/NIVEAU 1/ NIVEAU 2/NIVEAU 3/NIVEAU 4
I want to compare "NIVEAU 1" sub-levels weight to "Niveau 1".
For instance, I want to know for each 'NIVEAU 3' members its contributions part for its "NIVEAU 1".
I've tried a bunch of things, but nothing works properly. I don't get the trick and is stucked to :
WITH MEMBER [Measures].[TEST] AS'
iif(ISEMPTY(([Segmentation].[Niveau1], [Measures].[Total])) OR ([Segmentation].[Niveau1],[Measures].[Total]) = 0
, NULL
,[Measures].[Total] / ([Segmentation].[Niveau1], [Measures].[Total])
)'
SELECT NON EMPTY { [Measures].[TEST],[Measures].[Total]} ON COLUMNS
, NON EMPTY { [Segmentation].[Niveau2]}
ON ROWS FROM ( SELECT ( { [Segmentation].[Niveau1].&[8589934592]&[1|DESC111] } ) ON COLUMNS FROM [CUBE]) // Only one "Niveau 1" focus
And I get :
<Niveau 2> TEST Total
SF - C... #Error 25143658
SF - M... #Error 1638913,5
ZZZ ... #Error 90468628
#Error : The EqualTo function expects a string or numeric expression for argument 1. A tuple set expression was used.
The expected result is :
<Niveau 2> TEST Total
SF - C... 21,44% 25143658
SF - M... 1,40% 1638913,5
ZZZ ... 77,16% 90468628
21,4% = 25143658/(25143658+1638913,5+90468628)
What's wrong with my MDX?
Is there a mistake among the dimension or hierarchy set up?
Tuples are written as comma separated lists of members. What you have is a dimension.
Try
[Segmentation].CurrentMember.Parent
Instead of
[Segmentation].[Niveau1]
On your measure definition.
[EDIT] As mentioned in a comment, the goal is a solution that works on all levels. The solution is to use
Ancestor( [Segmentation].CurrentMember, [Segmentation].[Niveau1] )
in the Tuple used in the custom measure definition.
Thanks to nsousa, I'm now using :
WITH MEMBER [Measures].[Total Niveau1] AS'
iif([Segmentation].CURRENTMEMBER.level.ordinal>=2
,(Ancestor([Segmentation].CurrentMember,[Segmentation].[Niveau1] ),[Measures].[Total])
,([Segmentation].CURRENTMEMBER, [Measures].[Total])
)
'
MEMBER [Measures].[TEST] AS'
DIVIDE([Measures].[Societe],[Measures].[Total Niveau1])
',FORMAT_STRING = 'Percent'
SELECT NON EMPTY { [Measures].[TEST],[Measures].[Societe],[Measures].[Total]} ON COLUMNS
, NON EMPTY { [Segmentation].[Niveau3]}
ON ROWS FROM [CUBE]

WC Ajax Product Filter - Price Range issue

I'm using WC Ajax Product Filter for filtering options .
Unfortunately the price range is showing :
Min Price: ₹NaN
Max Price: ₹NaN
The actual price is not displaying. Please help me out . I'm new to woocommerce .
The issue found out !
The minimum and maximum price range difference for the products was very less. For example - minimum was $65 and maximum was $66, that was the issue.
I've found a temporary solution, edit the plugin file: wcapf.php :you can find this in the root of the plugin directory: wc-ajax-product-filter
Now search for the code: getPriceRange
This occurs in 2 spots around line #467 and line #773
On both 2 spots you see an if statement below which uses sizeof() function.
There seems to be some sort of issue with this.
I have fixed the issues of NaN by commenting out the entire if and else statement that uses the sizeof() function, see below code for example.
It's not the best solution, but well it works, untill update.. I will post this to plugin creator as well.
#469:
//if (sizeof($unfiltered_price_range) === 2) {
#522:
//}
#781:
//if (sizeof($price_range) > 2) {
#840:
//} else {
// empty array
// return array();
//}
Solved it this way.
Edit widget-price-filter.php file.
It's in the folder /ajax-product-filter/widgets/
Replace the following code in line 125:
<span class="wcapf-slider-value" id="wcapf-noui-slider-value-min"></span> - <span class="wcapf-slider-value" id="wcapf-noui-slider-value-max"></span>
By:
if ($min_val!=0 && $max_val!=0 ) {
echo '<span class="wcapf-slider-value" id="wcapf-noui-slider-value-min"></span> - <span class="wcapf-slider-value" id="wcapf-noui-slider-value-max"></span>';
} else{ echo '-';}?>
It will replace the NaN values bellow the slider by (-), just change the last line if you want another text there.

Handlebars Using #root in Custom Helper

I have a scenario where I am trying to access a separate element in my custom helper from within a nested for loop. When I use root outside my for loop I don't have any issues, but I can't seem to use #root within my custom helper. I thought maybe ../ would work, but it appears that is only be moving up to the parent element, not a one that is separate
Here are my two objects:
category //Object being looped through
categoryQuery //Query object being compared to looped values
Here is my view (looping through ID's and then apply selected to the ID attached to categoryQuery:
{{#category}}
<option value="{{this.categoryId}}"{{selected this.categoryId #root.categoryQuery}}>{{this.categoryName}}</option>
{{/category}}
Preselected value if the values match:
/Preselect option value that is associated with edited record
hbs.registerHelper('selected', function(option, value){
if (option === value) {
return 'selected';
} else {
return '';
}
});
Updated:
when adding console.log('Option : ' + option + ' Value : ' + value); into the else statement of my registered helper, I receive the following, which shows that it isn't an issue that #root.category isn't pulling in the value, but it isn't equating correctly.
For example:
Option : 1 Value : 2
Option : 2 Value : 2
Option : 1 Value : undefined
I determined that the root cause of the issue was the strictness in the comparison operator. When changed to == I was able to correctly identify the ID's that matched

Why does my range search in XQuery not work and returns too many elements?

New to XQuery and probably a noob q. I installed a BaseX db as my sandbox (which included a sample file etc/factbook.xml). I constructed a simple query which I thought would return all 'cities' with population > 10million.
for $x in doc("etc/factbook.xml")/mondial/country
where $x/city/population > 10000000.0
return $x/city
but I'm getting cities with lower populations, any insight?
<city id="f0_1726" country="f0_553" longitude="126.967" latitude="37.5667">
<name>Seoul</name>
<population year="95">10229262</population>
</city>
<city id="f0_10300" country="f0_553">
<name>Kunsan</name>
<population year="95">266517</population>
</city>
(I've only included first two but many more both < and > 10million)
You're returning all countries that have a city with population larger than 10 millions. Loop over the cities instead (and please, use meaningful variable names):
for $city in doc("etc/factbook.xml")/mondial/country/city
where $city/population > 10000000
return $city
Or just go for an XPath expression doing the same:
doc("etc/factbook.xml")/mondial/country/city[population > 10000000]

XQuery equality of attributes in node and subnodes

I have this piece of XML-code:
<player name="John" points="50">
<game points="5">Beans</game>
<game points="40">Cucumbers</game>
<game points="50">Tomatos</game>
</player>
What I want to do is to get this piece, but only with those games where number of points (attribute of "game") is equal to points which is attribute of "player".
Thus, considering above example, I should get next XML-piece:
<player name="John" points="50">
<game points="50">Tomatos</game>
</player>
I write following XQuery:
for $a in doc("ex.xml")
where $a/xs:int(#points)=$a/game/xs:int(#points)
return $a
But I don't get any result. Could you please help me?
You cannot modify/filter a subtree by selecting parts of it (if not using XQuery Update). You will have to reconstruct the XML instead.
element player {
/player/#*,
/player/game[#points=/player/#points]
}
The first line creates a new element, the second line adds the attributes again, and the third line all games that fulfill the points condition.
If you've got multiple players in a document which you need to loop over, the code would look like that:
for $player in /player
return element player {
$player/#*,
$player/game[#points=$player/#points]
}
Now, we do not start all queries at the root level, but use the $player as context instead.
Using XQuery Update (if supported by your XQuery processor), you could also do something like this (actually not changing the original document, but only a copy):
copy $result := .
modify delete node $result//game[../#points != #points]
return $result

Resources