My XSLT now completes a filter on the 4 sets of checboxes and then a filter on the search string.
The problem I have is the sets of checkboxes are doing an "AND" filter rather than an "OR" filter. So as an example if you tick Company A and Company B in the Company set you will only get matches where employees work at both companies rather than either company. Ooops !
My problem is similar to the following post, however I have not been able to get it to work.
<!-- return a list of the attribute @id's of the currently possible nodes as the final set of matched nodes --> <xsl:variable name="nodeIDListFilter"> <xsl:text>;</xsl:text>
<xsl:for-each select="$possibleNodes"> <!-- @id for this node --> <xsl:value-of select="@id"/> <xsl:text>;</xsl:text> </xsl:for-each> </xsl:variable>
<!-- return the actual list of id's --> <xsl:value-of select="$nodeIDListFilter"/>
</xsl:template>
Is this even possible ?
The above template is called 4 times within the search so am trying to make it reuseable and for performance sakes hoping I can avoid looping / etc.
Can I suggest using the function Exslt.ExsltSets:hassamenode(node-set 1, node-set 2). You would need to turn your comma separated list into node-sets first, but that is simple with another exslt function Exslt.ExsltStrings:tokenize(string,delimiters).
Not sure how it would exaclty work in your set up, I think something like
For performance, it might be worth turning the comma separated list of selected checkboxes into a node-set in a variable outside of the template for re-use.
HI Nigel - I guess it could now be a problem with the actual nodes, rather than the content - the test will only match if the nodes are the same. The exslt tokenize calls its nodes tokens, and umbraco.library:Split (which does the same thing calls its nodes value so
So you should use the same tokenize function for doing both lists - or you need to do some extra work to make sure the node sets can be the same! It all depends on what your temporary nodeset looks like.
Nigel - just had another thought - is this a checkbox list in umbraco being compared to a string in a separate XML file? if so the checkbox list comes out as a list of ids, rather than a comma separated list of strings so:
<xsl:value-of select="$selCheckboxes"/>
probably produces something along the lines of
1104,1105,1110,1134
in which case you need to loop through them and use
umbraco.library:GetPreValueAsString(1104);
something like this to get a semi-colon separated list into a variable which you can manipulate
Hi Nigel - turns out to be worse than I thought - the HasSameNode function of EXSLT is checking to see if it is the exact same node from the same node-set - rather than if it appears to be the same node from its content, so it is of no use to use whatsoever.
Anyway - my best solution is to add in some javascript to the xml file, and use that (make sure to add the implements prefix to the namespace declarations, and to exclude the prefix from the output):
<msxml:script language="javascript" implements-prefix="bc"> <![CDATA[ function compareCommaStrings(s,t) { var sarr = s.split(','); var tarr = t.split(','); var match = false; for (var i=0; i<sarr.length;i++) { for (var j=0; j<tarr.length;j++) { if (tarr[j] == sarr[i]) { match = true; } } } return match; } ]]> </msxml:script>
I am going to have up to 5 sets of checkboxes on my search form and ideally the above template should not need to be called if no checkboxes are selected, but for now this is the only way I have been able to get it working.
For reference purposes here is the code that calls the template - if anyone can suggest how to avoid calling the template when no checkboxes are selected ($selectedCompanies) I'd be very grateful.
<!-- Filter on group companies --> <xsl:variable name="possibleCompanyMatches"> <xsl:call-template name="checkBoxMatches"> <xsl:with-param name="nodeSetOne" select="$possibleNodes"/> <xsl:with-param name="selCheckboxes" select="$selectedCompanies"/> <xsl:with-param name="fieldToSearch">EmployerID</xsl:with-param> </xsl:call-template> </xsl:variable>
<!-- get the actual matching nodes as a nodeset --> <xsl:variable name="matchedNodesCompanyFilter" select="$possibleNodes[contains($possibleCompanyMatches, concat(';', concat(@id, ';')))]" /> ...
I have tried altering the above as follows but get an error message (Error parsing XSLT file: \xslt\XSLTEmployeeSearchMembers.xslt) and have not been able to figure out where the error is caused.
Just found this post after coming a cropper on this...
"HasSameNode function of EXSLT is checking to see if it is the exact same node from the same node-set - rather than if it appears to be the same node from its content, so it is of no use to use whatsoever"
..myself, while matching tags :o)
I don't really like extension functions so here is a VERY QUICK AND DIRTY matching template. You could improve this in all manner of ways; probably using recursion to be able to quit the loop on the first match would be my first change, but the site this is for is very small, there will not be many tags to match and I'm on a deadline...
<xsl:template name="displayTaggedItems"> <!-- Tags we are searching for - this would be passed in in the actual macro --> <xsl:param name="tags" select="Exslt.ExsltStrings:split($currentPage/tagsToMatchAgainst, ',')" /> <ul class="portfolio_examples"> <xsl:for-each select="umbraco.library:GetXmlAll()/yourTaggedDocumentType"> <xsl:variable name="item" select="."/> <!-- Tags on current matched document --> <xsl:variable name="itemTags" select="Exslt.ExsltStrings:split($item/tags, ',')" /> <!-- Run match against node sets from split() --> <xsl:variable name="isMatch"> <xsl:call-template name="tokensMatch"> <xsl:with-param name="set1" select="$tags"/> <xsl:with-param name="set2" select="$itemTags"/> </xsl:call-template> </xsl:variable> <!-- tokensMatch template will return a string with an X for each tag matched - so an empty string is no match, any non-empty string a match --> <xsl:if test="string($isMatch) != ''"> <xsl:call-template name="displayItem"> <xsl:with-param name="item" select="$item"/> </xsl:call-template> </xsl:if> </xsl:for-each> </ul> </xsl:template>
One problem I'm having though,,, When you get to displaying the matched nodes, how do you limit the number outputted? You can't use position() because that refers to position further up the logic - it might be 7, 12, 24, 29 ...
If I understand you correctly, you should be able to put the whole outer for-each loop in a variable, then do a for-each with position() on that - although I suspect you may need to use the node-set() function.
Compare 2 comma separated strings
I have modified XSLT search to include 4 sets of checkboxes as well as the textbox - see http://our.umbraco.org/forum/developers/xslt/9534-Modify-XSLT-Search-to-Read-External-XML-File
My XSLT now completes a filter on the 4 sets of checboxes and then a filter on the search string.
The problem I have is the sets of checkboxes are doing an "AND" filter rather than an "OR" filter. So as an example if you tick Company A and Company B in the Company set you will only get matches where employees work at both companies rather than either company. Ooops !
My problem is similar to the following post, however I have not been able to get it to work.
http://our.umbraco.org/forum/developers/xslt/7981-Matching-multiple-comma-separated-values
My code is as follows:
Is this even possible ?
The above template is called 4 times within the search so am trying to make it reuseable and for performance sakes hoping I can avoid looping / etc.
Thanks
Nigel
Can I suggest using the function Exslt.ExsltSets:hassamenode(node-set 1, node-set 2). You would need to turn your comma separated list into node-sets first, but that is simple with another exslt function Exslt.ExsltStrings:tokenize(string,delimiters).
Not sure how it would exaclty work in your set up, I think something like
For performance, it might be worth turning the comma separated list of selected checkboxes into a node-set in a variable outside of the template for re-use.
Hi Josh
Sorry for the delay in responding.
I have just tried your suggestion and at the moment it doesn't seem to be working.
I am not getting an error but the script does not error so maybe that is a positive.
I will do some testing / outputting of data and go from there.
Thanks again
Nigel
HI Nigel - I guess it could now be a problem with the actual nodes, rather than the content - the test will only match if the nodes are the same. The exslt tokenize calls its nodes tokens, and umbraco.library:Split (which does the same thing calls its nodes value so
produces a node set like so:
and to equivalent exslt function
produces a node set like so:
So you should use the same tokenize function for doing both lists - or you need to do some extra work to make sure the node sets can be the same! It all depends on what your temporary nodeset looks like.
/Josh
Nigel - just had another thought - is this a checkbox list in umbraco being compared to a string in a separate XML file? if so the checkbox list comes out as a list of ids, rather than a comma separated list of strings so:
probably produces something along the lines of
in which case you need to loop through them and use
something like this to get a semi-colon separated list into a variable which you can manipulate
Hi Josh
Delayed response again - only just getting back onto this task now . . .
Still no joy but keen to persevere.
To clarify:
My XML data is as follows:
The string being passed in from the checkboxes selected as part of the form is as follows:
So I am trying to determine if compare the EmployerID node contains one of the selected checkbox values.
So from your posts are you indicating the the string of checkboxes is being treated differently by the XSLT ?
I'll keep scratching . . .
Cheers
Nigel
Just realised the above example was a bad one to quote - you can only ever have 1 Employer (at least for the purposes of what I am working on!)
So a better example would be:
And so the checkbox comma separated list of ID's needs to do a compare to the BannerGroups node data
Cheers
Nigel
Hi Nigel - turns out to be worse than I thought - the HasSameNode function of EXSLT is checking to see if it is the exact same node from the same node-set - rather than if it appears to be the same node from its content, so it is of no use to use whatsoever.
Anyway - my best solution is to add in some javascript to the xml file, and use that (make sure to add the implements prefix to the namespace declarations, and to exclude the prefix from the output):
Hi Josh
Wicked wicked wicked . . . It seems to be working perfectly !
The only remaining issue I need to sort out is if there are no checkboxes ticked - I've no problems dealing with this aspect.
Your assistance is greatly appreciated - a huge thanks for your help.
Nigel
OK - here is my amended code to get around the issue of no results being returned when no checkboxes are selected:
I am going to have up to 5 sets of checkboxes on my search form and ideally the above template should not need to be called if no checkboxes are selected, but for now this is the only way I have been able to get it working.
For reference purposes here is the code that calls the template - if anyone can suggest how to avoid calling the template when no checkboxes are selected ($selectedCompanies) I'd be very grateful.
I have tried altering the above as follows but get an error message (Error parsing XSLT file: \xslt\XSLTEmployeeSearchMembers.xslt) and have not been able to figure out where the error is caused.
Any suggestions ?
Thanks
Nigel
I'm not sure can can declare a variable inside a variable. I suggest the best thing to do is to adjust possibleCompanyMatches:
Thats the way I did it for xslt search, and it doesn't seem too bad in terms of performance
Just found this post after coming a cropper on this...
"HasSameNode function of EXSLT is checking to see if it is the exact same node from the same node-set - rather than if it appears to be the same node from its content, so it is of no use to use whatsoever"
..myself, while matching tags :o)
I don't really like extension functions so here is a VERY QUICK AND DIRTY matching template. You could improve this in all manner of ways; probably using recursion to be able to quit the loop on the first match would be my first change, but the site this is for is very small, there will not be many tags to match and I'm on a deadline...
<xsl:template name="tokensMatch">
<xsl:param name="set1"/>
<xsl:param name="set2"/>
<xsl:for-each select="$set1">
<xsl:variable name="v1" select="."/>
<xsl:for-each select="$set2">
<xsl:variable name="v2" select="."/>
<xsl:if test="string($v1) = string($v2)">X</xsl:if>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
Use with the split function like so:
Hope someone finds this helpful!
I found it useful so thanks for posting!
One problem I'm having though,,, When you get to displaying the matched nodes, how do you limit the number outputted? You can't use position() because that refers to position further up the logic - it might be 7, 12, 24, 29 ...
Any ideas?
If I understand you correctly, you should be able to put the whole outer for-each loop in a variable, then do a for-each with position() on that - although I suspect you may need to use the node-set() function.
is working on a reply...