xquery distinct-values and group by and selecting xquery 1.0 - xquery

I have a structure then have elements with same value, but any elements change the value.
I need group the information and mount the output.
<root>
<cadastro>
<id>2</id>
<nome>abcd</nome>
<links>
<link>example7</link>
</links>
</cadastro>
<cadastro>
<id>2</id>
<nome>abc</nome>
<links>
<link>example5</link>
<link>example3</link>
</links>
</cadastro>
<cadastro>
<id>5</id>
<nome>xpto</nome>
<links>
<link>example1</link>
</links>
</cadastro>
</root>
I must to return only 2 outputs
<results>
<result>
<idResult>2</idResult>
<nome>abc</nome>
<links>
<url>example7</url>
<url>example5</url>
<url>example3</url>
</links>
</result>
<result>
<idResult>5</idResult>
<nome>xpto</nome>
<links>
<url>example1</url>
</links>
</result>
<results>
I tried to use the distinct-values but i have differents values to same id at payload.
How i can to group by at xquery 1.0?

You can use distinct-values to iterate over unique IDs, and query for every matching element with that ID.
However, note one difference in this output: the <nome> values for elements matching 2 are different, and your example only returns one of them. But it's not clear which one is the correct one, so this solution outputs both. If you want only one match, then you need to add a constraint, which could be as simple as ($id-results/nome)[1], but there's no guarantee that will select the one you want.
element results {
let $ids := $data/cadastro/id
for $id in distinct-values($ids)
let $id-results := $data/cadastro[id = $id]
return element result {
element idResult { $id },
$id-results/nome,
element links {
$id-results/links/link
}
}
=>
<results>
<result>
<idResult>2</idResult>
<nome>abcd</nome>
<nome>abc</nome>
<links>
<link>example7</link>
<link>example5</link>
<link>example3</link>
</links>
</result>
<result>
<idResult>5</idResult>
<nome>xpto</nome>
<links>
<link>example1</link>
</links>
</result>
</results>

Related

How to create/define a sub resource via XML in API-Platform?

I am trying to create a sub resource via XML using Api Platform.
When I define the sub resource via a annotation on the entity, everything works as expected:
Entity/SocialProfile/SocialProfile.php
/**
* #ApiSubresource()
*
* #ORM\OneToMany(
* targetEntity="SoapSyliusSocialPlugin\Entity\Follow\Follow",
* mappedBy="follower",
* cascade={ "persist", "remove" }
* )
*/
protected $following;
Everything works as expected and I can then access the sub resource via the below path:
/api/v2/social-profiles/35471/followings
But when I try define this route/endpoint via .xml like the below:
Resources/config/api_resources/SocialProfile.xml
<?xml version="1.0" ?>
<resources xmlns="https://api-platform.com/schema/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata https://api-platform.com/schema/metadata/metadata-2.0.xsd"
>
<resource class="SoapSyliusSocialPlugin\Entity\SocialProfile\SocialProfile" shortName="SocialProfile">
<attribute name="validation_groups">sylius</attribute>
<subresourceOperations>
<subresourceOperation name="api_social_profiles_followings_get_subresource">
<attribute name="method">GET</attribute>
</subresourceOperation>
</subresourceOperations>
<property name="following" writable="false" readable="true">
<subresource resourceClass="SoapSyliusSocialPlugin\Entity\Follow\Follow" />
</property>
</resource>
</resources>
I am getting a:
404 No route found
I have tested my SocialProfile.xml file with a itemOperation & everything is working as expected.
I have updated my Resources/config/api_resources/SocialProfile.xml to look like the below, but I am still receiving a
404 route not found
<?xml version="1.0" ?>
<resources xmlns="https://api-platform.com/schema/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata https://api-platform.com/schema/metadata/metadata-2.0.xsd"
>
<resource class="SoapSyliusSocialPlugin\Entity\SocialProfile\SocialProfile" shortName="SocialProfile">
<attribute name="validation_groups">sylius</attribute>
<itemOperations></itemOperations>
<property name="following" writable="false" readable="true">
<subresource resourceClass="SoapSyliusSocialPlugin\Entity\Follow\Follow" collection="true"/>
</property>
</resource>
</resources>
The configuration for the entity holding the subresource (SocialProfile, in this example).
<?xml version="1.0" ?>
<resources xmlns="https://api-platform.com/schema/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata https://api-platform.com/schema/metadata/metadata-2.0.xsd"
>
<resource class="SoapSyliusSocialPlugin\Entity\SocialProfile\SocialProfile" shortName="SocialProfile">
<attribute name="validation_groups">sylius</attribute>
<property name="following" writable="false" readable="true">
<subresource resourceClass="SoapSyliusSocialPlugin\Entity\Follow\Follow" />
</property>
</resource>
</resources>
To configure things like normalization groups for the subresource, you do it in the other end of the relationship:
<?xml version="1.0" ?>
<resources xmlns="https://api-platform.com/schema/metadata"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="https://api-platform.com/schema/metadata https://api-platform.com/schema/metadata/metadata-2.0.xsd"
>
<resource class="SoapSyliusSocialPlugin\Entity\Follow\Follow" shortName="Follow">
<subresourceOperations>
<subresourceOperation name="api_social_profiles_followings_get_subresource">
<attribute name="method">GET</attribute>
</subresourceOperation>
</subresourceOperations>
</resource>
</resources>
Try with this. I have a few setup this way and working. If there is something wrong above should be because something does not match your class/resource names exactly, but you should be able to tweak that to fix it.
Note that in the second version of the configuration in your question you removed all itemOperations. You should have at least the basic get item operation so the library is able to build IRIs.

Write expression inside xml comment

I'd like to put xml comment in the output and put some expression inside the comment. How can I do that in xquery? If I have
<!-- {$var} -->
it is inserted literally, but I'd like to have a comment tag on the output with the value of $var
XQuery has a comment constructor, for example:
<test>
{
let $x := 'hello world!'
return comment {$x}
}
</test>
yields:
<?xml version="1.0" encoding="UTF-8"?>
<test><!--hello world!--></test>
Would something like this work?
for $x in /* return <x><![CDATA[<!--]]>{$x}<![CDATA[-->]]> </x>
input
<test>hello comment</test>
output
<!--hello comment-->

Add an attribute to an element in an existing XML using Xquery

I need to add an attribute to an element of my response XML using XQuery.
Take the below XML as input,
<xyz:RootNode xmlns:abc="url1" xmlns:xyz="url2">
<abc:OtherNodes/>
<abc:messageHeader att1="val1" att2="val2">
<abc:childNodes/>
</abc:messageHeader>
<abc:OtherNodes/>
</xyz:RootNode>
Need an Xquery that add one more attribute newAtt with value newVal and give the result as,
<xyz:RootNode xmlns:abc="url1" xmlns:xyz="url2">
<abc:OtherNodes/>
<abc:messageHeader att1="val1" att2="val2" newAtt="newVal">
<abc:childNodes/>
</abc:messageHeader>
<abc:OtherNodes>
</xyz:RootNode>
Each time the number of attributes of message header may change. So the query should add a new attribute along with all the existing attributes and return the whole document.
Try the following:
xquery version "3.0";
module namespace foo="http://exist-db.org/apps/ns/foo";
declare function foo:process-node($node as node()?, $model as map()) {
if ($node) then
typeswitch($node)
case text() return $node
case element(messageHeader) return foo:messageHeader($node, $model)
default return element { $node/name() }
{ $node/#*, foo:recurse($node, $model) }
else ()
};
declare function foo:recurse($node as node()?, $model as map()) as item()* {
if ($node)
then
for $cnode in $node/node()
return foo:process-node($cnode, $model)
else ()
};
declare function foo:messageHeader($node as node(), $model as map()) {
element { $node/name() }
{ $node/#*,
attribute { 'newAtt' } { 'newVal' },
foo:recurse($node, $model)
}
};
You then call foo:process-node on the RootNode
You can always re-use the wheel that is there for such things, XSLT. Especially if you want to minimise risk in your code.
eXist supports XSL Transformations and here is an example of how to run an XSLT transformation that does the job that you want:
xquery version "3.0";
declare function local:add-attribute($input as node()?, $attributeName as xs:string, $attributeValue as xs:string?) {
let $xslt := <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:template xmlns:abc="url1" match="abc:messageHeader">
<xsl:copy>
<xsl:apply-templates select="#*"/>
<xsl:attribute name="{$attributeName}">{$attributeValue}</xsl:attribute>
<xsl:apply-templates select="node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="#*|node()">
<xsl:copy>
<xsl:apply-templates select="#*|node()"/>
</xsl:copy>
</xsl:template>
</xsl:stylesheet>
return transform:transform($input, $xslt, ())
};
let $input := <xyz:RootNode xmlns:abc="url1" xmlns:xyz="url2">
<abc:OtherNodes/>
<abc:messageHeader att1="val1" att2="val2">
<abc:childNodes/>
</abc:messageHeader>
<abc:OtherNodes/>
</xyz:RootNode>
return local:add-attribute($input, "hey", "bam")
You can also make use of the $parameters attribute if you want to bring your XSLT out into its own file. Will make things even more testable and modular.

How to customize the Document List in Alfresco?

In the repository there be will different document lists.i.e there will be Data dictionary, user homes,Guest homes etc. when I change the view to "detailed view", it displays Favorite, like, comments links. where will I have to modify if I dont want to show them. Can you tell in which file i have to comment the code for not displaying those links. Thank you in Advance.
I wanted a "modular" answer to this question, this answer is to show how I handled this issue.
Context: Alfresco 4.2.f, project Maven from the org.alfresco.maven.archetype:alfresco-amp-archetype:1.1.1 archetype, I put everything in the embedded JAR when possible.
Create a module extension for share (see this blog for more details). Here is my extension file:
src/main/resources/alfresco/site-data/extensions/my-custom-extension.xml
<extension>
<modules>
<module>
<id>Main module of my custom extension</id>
<version>${project.version}</version>
<auto-deploy>true</auto-deploy>
<customizations>
<customization>
<!-- Order matters here! target before source, always! -->
<targetPackageRoot>org.alfresco</targetPackageRoot>
<sourcePackageRoot>my-custom.main</sourcePackageRoot>
</customization>
</customizations>
</module>
</modules>
</extension>
In the documentlibrary component of your module package, create this FTL in order to declare a javascript:
src/main/resources/alfresco/site-webscripts/my-custom/main/components/documentlibrary/documentlist-v2.get.html.ftl
<#-- Add a Javascript declaration -->
<#markup id="my-custom-js" target="js" action="after">
<#script type="text/javascript" group="documentlibrary"
src="${url.context}/res/my-custom/main/components/documentlibrary/documentlist.js"/>
</#>
In the resources (META-INF), under documentlibrary component, create the Javascript:
src/main/resources/META-INF/my-custom/main/components/documentlibrary/documentlist.js
YAHOO.lang.augmentObject(Alfresco.DocumentList.prototype, {
// Possible values: i18nLabel, lockBanner, syncFailed, syncTransientError
// date, size, name, version, description, tags, categories
myCustomDisabledRenderers: ["description", "version", "tags"],
// Possible values: favourites, likes, comments, quickShare
myCustomDisabledSocials: ["favourites", "comments", "likes", "quickShare"],
myCustomIsSocialDisabled: function(propertyName) {
return Alfresco.util.arrayContains(
this.myCustomDisabledSocials, propertyName);
},
myCustomIsRendererDisabled: function(propertyName) {
if (Alfresco.util.arrayContains(
this.myCustomDisabledRenderers, propertyName)) {
return true;
}
// Disable the social renderer when all the social features are
// disabled
if (propertyName === "social" && this.myCustomDisabledSocials.length == 4) {
return true;
}
return false;
},
/** Helper function to disable socials
* propertyName must be one of "favourites", "comments", "likes", "quickShare"
*/
myCustomDisableSocial: function(propertyName) {
if (!Alfresco.util.arrayContains(
this.myCustomDisabledSocials, propertyName)) {
this.myCustomDisabledSocials.push(propertyName);
}
},
// Custom registerRenderer for social features, originally defined in:
// webapps/share/components/documentlibrary/documentlist.js:2134
myCustomSocialRegisterRenderer: function(record) {
var jsNode = record.jsNode;
var html = "";
// Current usage of the separator variable allow to change the order
// of the different social features (the 'if' blocks below) without
// changing their content
var separator = "";
/* Favourite / Likes / Comments */
if (!this.myCustomIsSocialDisabled("favourites")) {
html += '<span class="item item-social' + separator + '">' +
Alfresco.DocumentList.generateFavourite(this, record) +
'</span>';
separator = " item-separator";
}
if (!this.myCustomIsSocialDisabled("likes")) {
html += '<span class="item item-social' + separator + '">' +
Alfresco.DocumentList.generateLikes(this, record) +
'</span>';
separator = " item-separator";
}
if (!this.myCustomIsSocialDisabled("comments") &&
jsNode.permissions.user.CreateChildren) {
html += '<span class="item item-social' + separator + '">' +
Alfresco.DocumentList.generateComments(this, record) +
'</span>';
separator = " item-separator";
}
if (!this.myCustomIsSocialDisabled("quickShare") && !record.node.isContainer &&
Alfresco.constants.QUICKSHARE_URL) {
html += '<span class="item' + separator + '">' +
Alfresco.DocumentList.generateQuickShare(this, record) +
'</span>';
separator = " item-separator";
}
return html;
},
// Overwrite registerRenderer which was originally defined in:
// webapps/share/components/documentlibrary/documentlist.js:1789
registerRenderer: function DL_registerRenderer(propertyName, renderer) {
if (Alfresco.util.isValueSet(propertyName) &&
Alfresco.util.isValueSet(renderer) &&
!this.myCustomIsRendererDisabled(propertyName)) {
if (propertyName === "social") {
this.renderers[propertyName] = this.myCustomSocialRegisterRenderer;
} else {
this.renderers[propertyName] = renderer;
}
return true;
}
return false;
}
}, true);
Then you can disable the links by updating myCustomDisabledRenderers and/or mySocialDisabledRenderers.
This way also allows you to create a module that disable (for example) the "comments on documents" or "likes on document" feature independently in only 6 easy steps!
Example, how to make a module that only disable comments on documents in 6 steps
Important: first remove the "comment disabling" from the documentlist.js of the main module.
myCustomDisabledSocials: ["favourites", "likes", "quickShare"],
Create a new module "my-custom.nocomment" with the same structure.
<extension>
<modules>
<module>
<id>Main module of my custom extension</id>
[...]
</module>
<module>
<id>No comment module of my custom extension</id>
<version>${project.version}</version>
<customizations>
<customization>
<targetPackageRoot>org.alfresco</targetPackageRoot>
<sourcePackageRoot>my-custom.nocomment</sourcePackageRoot>
</customization>
</customizations>
</module>
</modules>
</extension>
Add the FTL...
src/main/resources/alfresco/site-webscripts/my-custom/nocomment/components/documentlibrary/documentlist-v2.get.html.ftl
<#-- Add a Javascript declaration -->
<#markup id="my-custom-js" target="js" action="after">
<#script type="text/javascript" group="documentlibrary"
src="${url.context}/res/my-custom/nocomment/components/documentlibrary/documentlist.js"/>
</#>
then the Javascript...
src/main/resources/META-INF/my-custom/nocomment/components/documentlibrary/documentlist.js
Alfresco.DocumentList.prototype.myCustomDisableSocial("comment");
and then I'm happy, clap along if you feel like everything's just got smooth!
Notes:
The nocomment module depends on the main module.
It is important for the nocomment module to be loaded after the main module (in http://localhost:8080/share/page/modules/deploy).
In order for the nocomment module to be complete, you also need to disable comments from the document details page, see below.
Disable comments from the document details page
Even if, this one is documented elsewhere, I spent so much time searching around these few days that I feel like I need to be as comprehensive as possible.
src/main/resources/alfresco/site-data/extensions/my-custom-extension.xml
Add this to your my-custom.nocomment module declaration and you will get rid of the comments form and list from the document details page.
[...]
<module>
<id>No comment module of my custom extension</id>
[...]
<components>
<component>
<region-id>comments</region-id>
<source-id>document-details</source-id>
<scope>template</scope>
<sub-components>
<sub-component id="default">
<evaluations>
<evaluation id="guaranteedToHide">
<render>false</render>
</evaluation>
</evaluations>
</sub-component>
</sub-components>
</component>
</components>
</module>
[...]
src/main/resources/alfresco/site-webscripts/my-custom/nocomment/components/node-details/node-header.get.js
This is for disabling the button on the header of the document details page.
// Disable comments
for (var i = 0; i < model.widgets.length; i++) {
if (model.widgets[i].id == "NodeHeader") {
model.widgets[i].options.showComments = false;
}
}
// And since it does not work, disable comments this way too
model.showComments = "false";
Note: I did not test these snippets, they have been taken from my project after "anonymization" (basically renaming the module). Let me know if you find mistakes.
What you are looking for is more than likely generated by client-side JavaScript. You should use share-config-custom.xml to set Share to development mode, like this:
<alfresco-config>
<!-- Put Share Client in debug mode -->
<config replace="true">
<flags>
<client-debug>true</client-debug>
<client-debug-autologging>false</client-debug-autologging>
</flags>
</config>
</alfresco-config>
Then, use firebug or your browser's developer console to step through the client-side JavaScript. You should be able to find the point where the document library elements are rendered.
You can override Alfresco's client-side JavaScript components with your own components. Please put them in your own namespace to avoid collisions with Alfresco's.
I did it by commenting the {social} line in file share-documentlibrary-config.xml in share/src/alfresco/share-document-config
...
<metadata-templates>
<!-- Default (fallback) -->
<template id="default">
<line index="10" id="date">{date}{size}</line>
<line index="20" id="description" view="detailed">{description}</line>
<line index="30" id="tags" view="detailed">{tags}</line>
<line index="40" id="categories" view="detailed" evaluator="evaluator.doclib.metadata.hasCategories">{categories}</line> -->
<!-- <line index="50" id="social" view="detailed">{social}</line> -->
</template>
...
It works!
I'm not sure if I understand well your question - you're trying to hide some columns from particular view in alfresco explorer? If so, you need to edit /jsp/browse/browse.jsp file, but I think that's not a good idea. Maybe implementing your own NodePropertyResolver should be better way, have look at my older blogpost on this topic: http://www.shmoula.cz/adding-columns-to-custom-browse-jsp/
It looks like all of it is in: \opt\alfresco-4.0.d\tomcat\webapps\share\components\documentlibrary\documentlist.js
I think the trick is in this.registerRenderer("social"...) to return html before line 1981 (after favorites before likes) supposing you want to keep at least faorite

ASP.NET/XML/LINQ: Extract Attribute from Element

I have the following in an XML file:
<entry>
...
<link href="http://www.example.com/somelink" />
...
</entry>
I want to extract the "href" attribute. I've been getting my list of elements like so:
Dim productsDoc = XDocument.Parse(productXML.ToString())
Dim feed = From entry In productsDoc...<entry> Select entry
For Each entry In feed
Dim product As New CartItem
...
product._productid = entry.<id>.Value
product._permalink = entry.<link>.Value 'HOW DO I GET THE ATTRIBUTE?
allItems.Add(product)
Next
How do I extract the <link> href attribute?
Thanks!
Hi to get the link you should use the "XML Attribute Axis Property". This allows you to easily access attributes.
You can use it like this:
'this is how you get the href string
product._permalink = entry.<link>.#href
Here's a full working example of usage:
Module Module1
' some products xml to use for this example
Dim productsXml As XElement = <Xml>
<Product>
<Name>Mountain Bike</Name>
<Price>59.99</Price>
<Link href="http://example.com/bike">Click here for a Mountain Bike</Link>
</Product>
<Product>
<Name>Arsenal Football</Name>
<Price>9.99</Price>
<Link href="http://example.com/arsenalfootball">Click here for a Arsenal Football</Link>
</Product>
<Product>
<Name>Formula One Cap</Name>
<Price>14.99</Price>
<Link href="http://example.com/f1cap">Click here for a Formula One Cap</Link>
</Product>
<Product>
<Name>Robin Hood Bow</Name>
<Price>8.99</Price>
<Link href="http://example.com/robinhoodbow">Click here for a Robin Hood Bow</Link>
</Product>
</Xml>
Sub Main()
' get all <Product> elements from the XDocument
Dim products = From product In productsXml...<Product> _
Select product
' go through each product
For Each product In products
' output the value of the <Name> element within product
Console.WriteLine("Product name is {0}", product.<Name>.Value)
' output the value of the <Price> element within product
Console.WriteLine("Product price is {0}", product.<Price>.Value)
' output the value of the <Link> element within product
Console.WriteLine("Product link text is {0}", product.<Link>.Value)
' output the value of the <Link> href attribute within product
Console.WriteLine("Product href is {0}", product.<Link>.#href)
Next
End Sub
End Module

Resources