XQuery 3 merging two node sequences and de-duplicating - xquery

In XQuery 3.1 (under eXistDB 4.4) I have two functions which return two sets of nodes that describe family relations of a person. The results of the two functions can overlap.
A first function person:person-relationship-as-object("#Guilhem_Faure_SML-AU") returns:
<person relation="Peire_Faure_SML-AU">
<span class="en">Sibling(s)</span>
<span class="fr">Frère(s)/sœur(s)</span>
</person>
<person relation="Arnald_Faure_SML-AU">
<span class="en">Sibling(s)</span>
<span class="fr">Frère(s)/sœur(s)</span>
</person>
<person relation="Esteve_Faure_SML-AU">
<span class="en">Sibling(s)</span>
<span class="fr">Frère(s)/sœur(s)</span>
</person>
<person relation="Martin_de_Verazilh_SML-AU">
<span class="en">Aunt(s)/Uncle(s)</span>
<span class="fr">Tante(s)/Oncle(s)</span>
</person>
<person relation="Raimund_de_Verazilh_SML-AU">
<span class="en">Aunt(s)/Uncle(s)</span>
<span class="fr">Tante(s)/Oncle(s)</span>
</person>
A second function person:person-relationship-as-subject("#Guilhem_Faure_SML-AU") returns
<person relation="Arnald_Faure_SML-AU">
<span class="en">Sibling(s)</span>
<span class="fr">Frère(s)/sœur(s)</span>
</person>
<person relation="Peire_Faure_SML-AU">
<span class="en">Sibling(s)</span>
<span class="fr">Frère(s)/sœur(s)</span>
</person>
<person relation="Esteve_Faure_SML-AU">
<span class="en">Sibling(s)</span>
<span class="fr">Frère(s)/sœur(s)</span>
</person>
<person relation="Bernarda_Guilhem_Faure_SML-AU">
<span class="en">Spouse(s)</span>
<span class="fr">Époux/épouse(s)</span>
</person>
Now I want to merge the two sets of nodes into one, and return each distinct node only once.
I attempted a rough SQL-like structure, but failed:
let $x := person:person-relationship-as-object("#Guilhem_Faure_SML-AU")
| person:person-relationship-as-subject("#Guilhem_Faure_SML-AU")
for $y in $x
order by $y/#relation ascending
group by $y/#relation, $y/span[#class="en"], $y/span[#class="fr"]
return $y
I don't think I really understand why the group by doesn't work here. Moreover, if group by doesn't do the job, how else can I approach what (I think?) should be a simple exercise in grouping/distinct results?

With XQuery and group by, the variable $y in your return clause is bound to the whole group you have created so to eliminate duplicates you need to use return $y[1]. See https://www.w3.org/TR/xquery-31/#id-group-by which says
In the post-grouping tuple generated for a given group, each
non-grouping variable is bound to a sequence containing the
concatenated values of that variable in all the pre-grouping tuples
that were assigned to that group.
and then
This behavior may be surprising to SQL programmers, since SQL reduces
the equivalent of a non-grouping variable to one representative value.

Related

map single source schema element to multiple element with attribute

I have a source schema as follows:
Users
Id
Name
Department
and destination schema as follows:
Employee
Name
Number
number_type(attribute)
I need to do the following mapping
Name ---> Name
Id ---> Number (number_type = "Id")
Department_no --> Number (number_type = "dept")
I need to map both Id and department number i.e. 2 elements to 1 element i.e. Number in dest schema but for both attribute value should be different.
Input
<Users>
<Id>123</Id>
<Name>abc</Name>
<Department_no>456</Department_no>
</Users>
Output:
<Employee>
<Name>abc</Name>
<Number number_type = "Id">123</Number>
<Number number_type = "dept">456</Number>
</Employee>
How can I achieve in BizTalk or what could be the inline XSLT for the same?
You nearly have it
Name ---> Name
Id ---> Number
Department --> Number
Then also link both Id & Department to a looping functoid that goes to Number
Plus to do the attributes there are some things you can try such as
Id ---> number_type
Department --> number_type
But click on the links select Copy Name instead of the Copy Text value.
From an input
<ns0:Root xmlns:ns0="http://Scratch.SO55049939Input">
<Users>
<Id>Id_0</Id>
<Name>Name_0</Name>
<Department>Department_0</Department>
</Users>
</ns0:Root>
You will get output
<ns0:Root xmlns:ns0="http://Scratch.SO55049939output">
<Employee>
<Name>Name_0</Name>
<Number number_type="Id">Id_0</Number>
<Number number_type="Department">Department_0</Number>
</Employee>
</ns0:Root>

odoo multiple measures together in line graph

For my model with the following fields:
name = fields.Char()
date = fields.Date()
value1 = fields.Float()
value2 = fields.Float()
I am trying to show multiple lines in the same graph this way:
<graph string="Results" type="line">
<field name="date" type="row" interval="day"/>
<field name="value1" type="measure" operator="+"/>
<field name="value2" type="measure" operator="+"/>
</graph>
But it shows only one line. Every time I have to select the measure I want to see and deselect the other, while I need to see both at the same time.
The only way I was able to show the two values separately at the same time, is through a bar graph grouped by name field, but it is not what I need (I still had to show only one measure but at least I could achieve to have different colors for each group).
Any ideas?
I did it:
<graph string="Results" type="line">
<field name="date" type="row" interval="day"/>
<field name="name" type="col"/>
<field name="value1" type="measure" operator="+"/>
</graph>
I just need to put all values into value1 and separate them by using name, obtaining different lines with same name

Applying suggestion-source constraints for typeahead in Marklogic

Say, I have 5 documents that belong to either of different collections such as:
Biography, Fiction, Humour, Adventure
For instance, document 1 is in Adventure Collection:
<Book>
<title>Harry Potter and the Deathly Hallows</title>
<Author>J.K.Rowling</Author>
<year>2007</year>
</Book>
document 2, is in Biography Collection:
<Book>
<title>Steve Jobs</title>
<Author>Walter Issacson</Author>
<year>2011</year>
</Book>
Now I want to apply suggest on year element. I want to apply this on collections i.e. for example-suggest on Biography collections
I have defined element range index on year and ingested sample documents using application loader and configured collections for documents in document settings.
Follwing is my XQUERY code:
xquery version "1.0-ml";
import module namespace search = "http://marklogic.com/appservices/search"
at "/MarkLogic/appservices/search/search.xqy";
let $options :=
<options xmlns="http://marklogic.com/appservices/search">
<constraint name="Group">
<collection prefix="Biography/"/>
</constraint>
<suggestion-source ref="Group">
<range collation="http://marklogic.com/collation/"
type="xs:string" >
<element name="year"/>
</range>
</suggestion-source>
</options>
return
search:suggest("Group:20", $options)
On running this query I am getting suggestions for both 2011 and 2007 which is not what I am expecting.
Expected suggestion is 2011 (as only Biography collection should be searched).
I have refered this doc on search:suggest but I am not able to find out what exactly is the mistake.
Where am I doing wrong?
Shrey:
The values from the suggestion source are substituted for the values from the referenced constraint. In other words, the options say that input qualified by the group constraint should come from the year range index, which is what you're seeing. For more detail, please see:
http://docs.marklogic.com/guide/search-dev/search-api#id_40748
To filter a set of suggestions, you can call search:suggest() with two string queries. The first provides the input for the suggestions. The second provides the filtering constraint. In the case above, the filtering constraint would be the Biography collection. For more detail, please see:
http://docs.marklogic.com/guide/search-dev/search-api#id_91911
Hoping that helps
However, I am able to achieve the required output using additional-query in options as follows:
xquery version "1.0-ml";
import module namespace search = "http://marklogic.com/appservices/search"
at "/MarkLogic/appservices/search/search.xqy";
let $options :=
<options xmlns="http://marklogic.com/appservices/search">
<additional-query>{cts:collection-query("Biography")}
</additional-query>
<constraint name="Search_Element">
<range collation="http://marklogic.com/collation/"
type="xs:string" >
<element name="year"/>
</range>
</constraint>
<suggestion-source ref="Search_Element">
<range collation="http://marklogic.com/collation/"
type="xs:string" >
<element name="year"/>
</range>
</suggestion-source>
</options>
return
search:suggest("Search_Element:20", $options)
Still, I was wondering how could it be done without using additional-query paramater because it would be more optimized to use collection constraint for above.

Sum using XQuery

I'm using XQuery to perform addition. Following is the structure of XML saved in database:
<Events>
<Event>
<id>1</id>
<code>1001</code>
<Amount>50,1</Amount>
</Event>
<Event>
<id>1</id>
<code>1002</code>
<Amount>5,5</Amount>
</Event>
<Event>
<id>1</id>
<code>1001</code>
<Amount>50,1</Amount>
</Event>
<Event>
<id>1</id>
<code>1002</code>
<Amount>5,5</Amount>
</Event>
</Events>
I want to get below output by using XQuery: the sum of amount having same code. Please note , is .. I need to replace , by . and the perform arithmetic operation.
<Total>
<1001> 100,2 </1001>
<1002> 11,0 </1002>
</Total>
If your XQuery processor supports XQuery 3.0, use the group by statement.
<Total>
{
for $i in //Event
let $code := $i/code
group by $code
return element {"code"} { attribute {"id"} {$code}, sum($i/Amount)}
}
</Total>
There are two differences to the XML snippets in your question: I changed the floating point seperator to points (which is required, of course you could do this using some XQuery string operations, too) and element names may not consist of numbers only, have a look at the element naming rules. I decided to return the code as id-attribute instead in my example.
This will get you the data as a result set.
declare #X xml
set #X =
'<Events>
<Event>
<id>1</id>
<code>1001</code>
<Amount>50,1</Amount>
</Event>
<Event>
<id>1</id>
<code>1002</code>
<Amount>5,5</Amount>
</Event>
<Event>
<id>1</id>
<code>1001</code>
<Amount>50,1</Amount>
</Event>
<Event>
<id>1</id>
<code>1002</code>
<Amount>5,5</Amount>
</Event>
</Events>'
select T.code,
sum(Amount) as Amount
from
(
select T.X.value('code[1]', 'int') as code,
cast(replace(T.X.value('Amount[1]', 'varchar(13)'), ',', '.') as float) as Amount
from #X.nodes('Events/Event') as T(X)
) as T
group by T.code
The following code will calculate the totals and output the result as XML, but not in your output (which is invalid):
SELECT Code AS 'Code', SUM(Value) AS 'Total'
FROM (
SELECT
CONVERT(DECIMAL(9,2), REPLACE(c.value('Amount[1]', 'VARCHAR(10)'), ',', '.')) AS Value
, c.value('code[1]', 'INT') AS Code
FROM #x.nodes('//Event') AS t(c)
) t
GROUP BY Code
FOR XML PATH('Total'), ROOT('Totals')
where #x is a XML variable containing your data.

How do I run PL/SQL code within SQLPlus?

I am trying to run the following code within SQLPlus:
exec lbacsys.sa_sysdba.create_policy(policy_name => 'ACCESS_LOCATIONS',
column_name => 'OLS_COLUMN',
default_options => 'READ_CONTROL,INSERT_CONTROL,UPDATE_CONTROL,DELETE_CONTROL,LABEL_DEFAULT,LABEL_UPDATE,CHECK_CONTROL,');
However, I'm getting the following error:
BEGIN lbacsys.sa_sysdba.create_policy(policy_name => 'ACCESS_LOCATIONS',; END;
*
ERROR at line 1:
ORA-06550: line 1, column 78:
PLS-00103: Encountered the symbol ";" when expecting one of the following:
( - + case mod new not null <an identifier>
<a double-quoted delimited-identifier> <a bind variable>
continue avg count current exists max min prior sql stddev
sum variance execute forall merge time timestamp interval
date <a string literal with character set specification>
<a number> <a single-quoted SQL string> pipe
<an alternatively-quoted string literal with character set specification>
<an alternatively
It seems to me to be something I'm doing wrong with the syntax. I'm just not sure what it is. Any help would be appreciated. Thanks :)
The EXEC statement takes a line of code and wraps it in a BEGIN/END block. In this case you want to split your call over several lines of code, so you'll probably find it easier to add the BEGIN/END yourself:
BEGIN
lbacsys.sa_sysdba.create_policy(policy_name => 'ACCESS_LOCATIONS',
column_name => 'OLS_COLUMN',
default_options =>
'READ_CONTROL,INSERT_CONTROL,'
|| 'UPDATE_CONTROL,DELETE_CONTROL,'
|| 'LABEL_DEFAULT,LABEL_UPDATE,CHECK_CONTROL,');
END;
/

Resources