Muenchian grouping difficulties - sharepoint

I am building a Sharepoint search results page to find the answers to questions asked in a 3rd party tool.
I have a bdc connection to their sql db.
I am crawling just one one table in the db.
I need to group question and answer results together using the muenchian method using several columns in the same table.
I cant change the db so I have to pull the results together in search to be something meaningful.
This table contains all the entries of questions asked and answers given.
I have to tie them together for us using search as the application to present the Q&A
overview of the data:
Each record returned in the core search results has an:
title - this is the question or the answer
activityid(int) - unique id of the entry
activitytypeid(int) - these identify if its a Q or A / 79 or 82
parentid(int) - this value contains the activityid of the original question - if the record is the original question, it will have a value of 0 for this column
isanswer(boolean) true or nothing (this is sort of redundant and does the same as activitytypeid but I was not sure if I could use it to help with the grouping)
Review
activitype id is either:
79 - question being asked - this will have a parentid of 0
or
82 - answer to question - this will have a parent id matching the activityid of the original question
I am trying but without much success to get my search results grouped/sorted to have the question returned first. In the example below it would be activityid 19142 and the answers to the question indented underneath with all grouped in a set
any help in reviewing my hack at this or examples would be great.
I am just trying to understand xsl and this has been a big learning curve. I have a long way to go and most certainly have jumped into the deep end on this.
raw xml of results i currently get without formatting the results
<All_Results>
<Result>
<id>1</id>
<title>Original Question</title>
<isanswer></isanswer>
<parentid>0</parentid>
<activitytype>79</activitytype>
<activityid>19142</activityid>
</Result>
<Result>
<id>2</id>
<title>Answer to original question</title>
<isanswer>true</isanswer>
<parentid>19142</parentid>
<activitytype>82</activitytype>
<activityid>19146</activityid>
</Result>
<Result>
<id>3</id>
<title>Another Question</title>
<isanswer></isanswer>
<parentid>0</parentid>
<activitytype>79</activitytype>
<activityid>19200</activityid>
<Result>
<Result>
<id>4</id>
<title>Second answer to original question</title>
<isanswer>true</isanswer>
<parentid>19142</parentid>
<activitytype>82</activitytype>
<activityid>19199</activityid>
</Result>
<Result>
<id>5</id>
<title>and another Question</title>
<isanswer></isanswer>
<parentid>0</parentid>
<activitytype>79</activitytype>
<activityid>19254</activityid>
<Result>
<Result>
<id>6</id>
<title>Answer to another question</title>
<isanswer>true</isanswer>
<parentid>19200</parentid>
<activitytype>82</activitytype>
<activityid>19265</activityid>
</Result>
</All_Results>
The output I am looking for would have all answers grouped with the corresponding question this is very simplified but I think it get s the point I am trying to get too.
<All_Results>
<Result>
<title>Original Question</title>
<title>Answer to original question</title>
<title>Second answer to original question</title>
</Result>
<Result>
<title>Another Question</title>
<title>Answer to another question</title>
</Result>
</All_Results>
my attempts at the template
<xsl:template match="All_Results">
<xsl:for-each select="Result[count(. | key('QAsked', 'activityid')[1]) > 0]">
<xsl:sort select="questionasked" />
<xsl:value-of select="title" /><br />
<xsl:value-of select="questionasked" /><br />
<xsl:value-of select="activityid" /><br /><br />
<xsl:for-each select="key('QAsked', '82')">
<xsl:sort select="questionasked" />
<xsl:if test="normalize-space(questionasked) = '82'"></xsl:if><br />
<xsl:value-of select="questionasked" /><br />
<xsl:value-of select="originalquestion" /><br />
<xsl:value-of select="likes" /><br /><br />
</xsl:for-each>
</xsl:for-each>
</xsl:template>

Try this template:
<xsl:key name="QAsked" match="Result[(activitytype = '82') or (activitytype = '79')]" use="concat(substring(parentid, 1, number(not(parentid = '0')) * string-length(parentid)),substring(activityid, 1, number(parentid = '0') * string-length(activityid)))"/>
<xsl:template match="All_Results">
<All_Results>
<xsl:for-each select="Result[count(. | key('QAsked', concat(substring(parentid, 1, number(not(parentid = '0')) * string-length(parentid)),substring(activityid, 1, number(parentid = '0') * string-length(activityid))))[1]) = 1]">
<xsl:sort select="id" />
<Result>
<xsl:for-each select="key('QAsked', concat(substring(parentid, 1, number(not(parentid = '0')) * string-length(parentid)),substring(activityid, 1, number(parentid = '0') * string-length(activityid))))">
<xsl:sort select="parentid" />
<title>
<xsl:value-of select="title" />
</title>
</xsl:for-each>
</Result>
</xsl:for-each>
</All_Results>
</xsl:template>
The output is xml structure which you provided as an example of the desired result.

Related

move an element to another element or create a new one if it does not exist using xslt-3

using xslt 3, i need to take all content elements' values, and move them to the title elements (if the title elements already exist in a record, they need to be appended with a separator like -) i now have inputted my real data, since the below solution does not solve the problem when implemented to something like:
example input:
<data>
<RECORD ID="31365">
<no>25099</no>
<seq>0</seq>
<date>2/4/2012</date>
<ver>2/4/2012</ver>
<access>021999</access>
<col>GS</col>
<call>889</call>
<pr>0</pr>
<days>0</days>
<stat>0</stat>
<ch>0</ch>
<title>1 title</title>
<content>1 content</content>
<sj>1956</sj>
</RECORD>
<RECORD ID="31366">
<no>25100</no>
<seq>0</seq>
<date>2/4/2012</date>
<ver>2/4/2012</ver>
<access>022004</access>
<col>GS</col>
<call>8764</call>
<pr>0</pr>
<days>0</days>
<stat>0</stat>
<ch>0</ch>
<sj>1956</sj>
<content>1 title</content>
</RECORD>
</data>
expected output:
<data>
<RECORD ID="31365">
<no>25099</no>
<seq>0</seq>
<date>2/4/2012</date>
<ver>2/4/2012</ver>
<access>021999</access>
<col>GS</col>
<call>889</call>
<pr>0</pr>
<days>0</days>
<stat>0</stat>
<ch>0</ch>
<title>1 title - 1 content</title>
<sj>1956</sj>
</RECORD>
<RECORD ID="31366">
<no>25100</no>
<seq>0</seq>
<date>2/4/2012</date>
<ver>2/4/2012</ver>
<access>022004</access>
<col>ΓΣ</col>
<call>8764</call>
<pr>0</pr>
<days>0</days>
<stat>0</stat>
<ch>0</ch>
<sj>1956</sj>
<title>1 title</title>
</RECORD>
<data>
with my attempt, i did not manage to move the elements, i just got an empty line where the content element existed, so please add the removal of blank lines in the suggested solution.
i believe the removal of blank lines could be fixed with the use of
<xsl:template match="text()"/>
One way to achieve this is the following template. It uses XSLT-3.0 content value templates.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="3.0" expand-text="true">
<xsl:output method="xml" indent="yes" />
<xsl:mode on-no-match="shallow-copy" />
<xsl:strip-space elements="*" /> <!-- Remove space between elements -->
<xsl:template match="RECORD">
<xsl:copy>
<xsl:copy-of select="#*" />
<title>{title[1]}{if (title[1]) then ' - ' else ''}<xsl:value-of select="content" separator=" " /></title>
<xsl:apply-templates select="node() except (title,content)" />
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
It's output is as desired.
If you want to separate the <content> elements with a -, too, you can simplify the core <title> expression to
<xsl:value-of select="title|content" separator=" - " />
EDIT:
All I changed was replacing chapter with RECORD, and it's working fine with Saxon-HE 9.9.1.4J. The only difference in the output is that the title element is always at the first position, but that shouldn't matter. I also added a directive to remove space between elements.

XSL parent node data in child node

Source XML:
<COVER_DETAIL>
<COVERDETAILS>
<COVERNAME>AAA</COVERNAME>
<EFFECTIVEDATE>2010-04-30</EFFECTIVEDATE>
<EXPIRYDATE>2022-03-31</EXPIRYDATE>
<COVERAMOUNT/>
<COVERPERCENT/>
<COVERCODE>60</COVERCODE>
</COVERDETAILS>
<COVERDETAILS>
<COVERNAME>BBB</COVERNAME>
<EFFECTIVEDATE>2010-04-30</EFFECTIVEDATE>
<EXPIRYDATE>2022-03-31</EXPIRYDATE>
<COVERAMOUNT/>
<COVERPERCENT/>
<COVERCODE>60</COVERCODE>
</COVERDETAILS>
<COVERDETAILS>
<COVERNAME>CCC</COVERNAME>
<EFFECTIVEDATE>2022-04-01</EFFECTIVEDATE>
<EXPIRYDATE>2032-03-31</EXPIRYDATE>
<COVERAMOUNT/>
<COVERPERCENT>100</COVERPERCENT>
<COVERCODE>62</COVERCODE>
</COVERDETAILS>
<COVERDETAILS>
<COVERNAME>DDD</COVERNAME>
<EFFECTIVEDATE>2022-04-01</EFFECTIVEDATE>
<EXPIRYDATE>2032-03-31</EXPIRYDATE>
<COVERAMOUNT/>
<COVERPERCENT/>
<COVERCODE>85</COVERCODE>
</COVERDETAILS>
</COVER_DETAIL>
XSLT:
<xsl:template name="COVERDETAILS">
<xsl:variable name="i" select="position()" />
<fo:table-row>
<fo:table-cell>
<fo:block>
<xsl:value-of select="$i"/>
<xsl:text>. </xsl:text>
<xsl:value-of select="COVERNAME"/>
</fo:block>
</fo:table-cell>
<fo:table-cell>
<fo:block> --- Here i want to display percentage </fo:block>
</fo:table-cell>
</fo:table-row>
</xsl:template>
How can I apply the coverpercent value of covercode 62 to as coverpercent value of all other covers in for each template. I want to apply the coverpecent of cover with covercode 62 to each and every other cover.
You could start by defining a variable as:
<xsl:variable name="perc" select="/COVER_DETAIL/COVERDETAILS[COVERCODE='62']/COVERPERCENT" />
Then use this variable inside your template, where you want to display the percentage as:
<xsl:value-of select="$perc"/>

XSLT finding matching node by comparing element text to substring result

I am processing this XML:
<Brand>
<Brand_Name>BLENDERM</Brand_Name>
<Brand_Code>1103</Brand_Code>
<Groups>
<Group>
<Group_Code>657</Group_Code>
<Parent_Code>0</Parent_Code>
<Group_Level>1</Group_Level>
<Group_Name>Brand Default</Group_Name>
<Product>
<Pip_code>0032359</Pip_code>
<Status>In Use</Status>
Using this XSLT:
<xsl:template match="Product" mode="phase-3">
<xsl:value-of select="document('rx_catmapping.xml')/descendant::mapping[source=substring(ancestor::Brand/Brand_Name,1,1)]/target"/>
</xsl:template>
Here is a sample of rx_catmapping.xml:
<Lookup xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<mapping>
<source>a</source>
<target>788</target>
</mapping>
<mapping>
<source>B</source>
<target>789</target>
</mapping>
</Lookup>
So, I am processing the Product element, which is a descendant of Brand. The first letter of Brand/Brand_Name in this case is B, and I am trying to output the value 789 by looking it up in rx_catmapping.xml. This should be really simple but I am completely stumped! I have tried changing the first part of the XPath to refer to document('rx_catmapping.xml')/Lookup/mapping, or document('rx_catmapping.xml')//mapping. I have also tried changing the first half of the comparison to string(source), or to source/text(), but neither of these works either. (The reason for trying this was that using source='B', for example, did seem to work, so I wondered if I was trying to compare two incompatible data types.)
Thanks in advance for your help.
Define a key
<xsl:key name="k1" match="mapping" use="source"/>
then use
<xsl:variable name="map-doc" select="document('rx_catmapping.xml')"/>
and
<xsl:variable name="letter" select="substring(ancestor::Brand/Brand_Name,1,1)"/>
<xsl:for-each select="$map-doc">
<xsl:value-of select="key('k1', $letter)/target"/>
</xsl:for-each>
With XSLT 2.0 you can simplify that to
<xsl:value-of select="key('k1', substring(ancestor::Brand/Brand_Name,1,1), $map-doc)"/>
The problem is, I believe, that at the point where you do ancestor::Brand/Brand_Name, the context is the mapping element in the external file. This worked for me
<xsl:template match="/">
<xsl:apply-templates select=".//Product"/>
</xsl:template>
<xsl:template match="Product">
<xsl:variable name="x" select="substring(ancestor::Brand/Brand_Name,1,1)"/>
<xsl:value-of select="document('file:///c:/temp/rx_catmapping.xml')//mapping[source=$x]/target"/>
</xsl:template>

Sharepoint XSL - break string into componant parts

I'm using a "choice" site column with the multiple-check option enabled so that users can tag a list item with several choices from the column.
This column then powers a design feature in a content query webpart - where the column choice is appended to create an image filename.
Choice1
Choice2
Choice3
Choice4
becomes
<img src="http://mysite/content-Choice1.jpg />
The problem I've got is that the XSL parser is fed a string which has semicolons (;) and hashes (#) separating the choice values. If all 4 options were ticked, the string fed into the XSLT parser would be:
;#Choice1;#Choice2;#Choice3;#Choice4
How can I work through the string and separate each choice into its own XSL variable?
I've tried various substring-before functions, but I can't get anything working.
Since XPath 1.0 does not support the tokenize() function, you'll have to do all the work yourself. For instance, you can generate the <img> elements recursively from the choices:
<xsl:template name="RecurseConvertChoicesToImages">
<xsl:param name="choices" />
<xsl:variable name="token"
select="substring-before($choices, ';#')" />
<xsl:variable name="nextToken"
select="substring-after($choices, ';#')" />
<xsl:if test="$token">
<img src="http://mysite/content-{$token}.jpg" />
</xsl:if>
<xsl:if test="$nextToken">
<xsl:call-template name="RecurseConvertChoicesToImages">
<xsl:with-param name="choices" select="$nextToken" />
</xsl:call-template>
</xsl:if>
</xsl:template>
I would recommend to use JavaScript to parse the string and accordingly display the image using JavaScript.

Outputting SharePoint Hyperlink Column as URL

I have some document URLs stored in a Sharepoint publishing column. When I output the info into a HTML page using:
<xsl:value-of select="#[ColumnName]" />
in ItemStyle.xml, I get [url], [document name] in the page. I would like to display this as a URL can anyone help with the XSL?
You could use:
<xsl:value-of select="substring-before(#[ColumnName],',')"/>
or whatever the separator is.
Thanks everyone, in the end I figured out the following based on a post at sguk
<xsl:variable name="Doc">
<xsl:call-template name="OuterTemplate.GetTitle">
<xsl:with-param name="Title" select="#DocumentLink1"/>
</xsl:call-template>
</xsl:variable>
with the following a tag code:
<a href="{substring-before($Doc,',')}">
<xsl:value-of select="substring-after($Doc,',')" />
</a>
or for an image:
<xsl:variable name="Image">
<xsl:call-template name="OuterTemplate.GetTitle">
<xsl:with-param name="Title" select="#img" />
</xsl:call-template>
</xsl:variable>
with the following img tag:
<img src="{substring-before($Image,',')}" alt="{substring-after($Image,',')}" />
I'm posting the solution back here as this proved ludicrously hard to figure out (probably my fault as I don't really 'get' XSL) but just in case anybody is looking for it, this code outputs images or links from the 'Hyperlink or Picture' column type in Sharepoint.
Another thing you can do is to take a list that shows URLs properly (like a Links list) and use SharePoint Designer to convert it to a DataView WebPart. In there will be the proper XSL for doing the conversion.
The easiest way to do this is with SharePoint designer:
click the field that shows "http://link, description"
a box with an > will appear, click it and you will get a "common xsl:value-of tasks" flyout.
It will have the field name and type, with the type set to "text".
Change the type to "hyperlink" and you will get a box allowing you to format the hyperlink. It will have all the necessary xsl already populated but you can input your own text or remove the link.
This hopefully helps. It shows "Project Site" when the hyperlink is entered and spaces when not.
<!--Project Site--><TD Class="{$IDAAO2UG}">
<xsl:variable name="Field" select="#Project_x0020_Site" />
<xsl:choose>
<xsl:when test="substring-before(#Project_x0020_Site, ', ')=''"><xsl:value-of select="substring-after(#Project_x0020_Site, ', ')" /></xsl:when>
<xsl:otherwise><A HREF="{substring-before(#Project_x0020_Site, ', ')}">
<xsl:choose>
<xsl:when test="substring-after(#Project_x0020_Site, ', ')=''"><xsl:value-of disable-output-escaping="no" select="substring-before(#Project_x0020_Site, ', ')" /></xsl:when>
<xsl:otherwise><xsl:value-of select="substring-after(#Project_x0020_Site, ', ')" /></xsl:otherwise>
</xsl:choose>
</A></xsl:otherwise>
</xsl:choose>
</TD>
In SharePoint 2013 you have to do things a bit differently because the #Url attribute is is no longer delimited with a comma. There is now a .desc sub property of #Url. Below is an example of how this works hopefully this saves someone else some time.
<xsl:template name="dvt_1.rowview">
<xsl:if test="string-length(#URL) > 0">
<div class="link-item item">
<a title="{#Comments}" target="_blank" href="{#URL}">
<xsl:value-of select="#URL.desc" />
</a>
</div>
</xsl:if>
</xsl:template>

Resources