How to write the program in JSONiq if the table is empty? - jsoniq

let $query_a := (for $st in $student
where
(some $t in $transcript
satisfies ($t.ssn = $st.ssn and $t.dcode = "CS" and $t.cno = 530))
return {ssn: $st.ssn, name: $st.name, major: $st.major, status: $st.status}
)
Here the table student table is empty, its returning query_a as null.
how to write a code if the table is empty i.e student[].

I think the issue in the query is that the return clause corresponding to the let clause is missing. The query has a FLWOR expression (for $st...) nested in another FLWOR expression (let $query_a...) and there must be two return clauses.
Here is a fixed query, with the indentation set to facilitate readability (it is assuming that $student and $transcript are properly bound to sequences of student objects resp. of transcript objects):
let $query_a :=
for $st in $student
where (
some $t in $transcript
satisfies ($t.ssn = $st.ssn and $t.dcode = "CS" and $t.cno = 530)
)
return {
ssn: $st.ssn,
name: $st.name,
major: $st.major,
status: $st.status
}
return $query_a
Note that $query_a cannot be the "null" value (which is a sequence of one atomic item, the null item): if no item in the sequence $student passes the predicate filter, then $query_a will be the empty sequence of items.
You can then test if a sequence is empty or not with the function empty:
if(empty($sequence))
then ...
else ...

Related

Kusto: Handling null parameter in user defined funtion

I am writing a custom User defined function in kusto where I want to have some optional parameters to the function which will be used in the "where" clause. I want to dynamically handle this. For example: If the value is present, then my where clause should consider that column filter, else it should not be included in the "where" clause>
Eg (psuedo code where value is not null):
function Calculate(string:test)
{
T | where test == test | order by timestamp
}
Eg (psuedo code where value is null or empty. My final query should look like this):
function Calculate(string:test)
{
T | order by timestamp
}
What is the efficient way to implement this. I will call this function from my c# class.
you can define a function with a default value for its argument, and use the logical or operator and the isempty() function to implement the condition you've described.
for example:
(note: the following is demonstrated using a let statement, but can be applied similarly to stored functions)
let T = range x from 1 to 5 step 1 | project x = tostring(x)
;
let F = (_x: string = "") {
T
| where isempty(_x) or _x == x
| order by x desc
}
;
F("abc")
if you run F() or F("") (i.e. no argument, or an empty string as the argument) - it will return all values 1-5.
if you run F("3") - it will return a single record with the value 3.
if you run F("abc") - it will return no records.

XQuery: map to array of sequences

I have the following map:
let $input := map { 'a-key': 'a-value', 'b-key': ['b-value-1', 'b-value-2'] }
(the length of the b-key array can vary, or be absent; a-key can be present or absent)
I need to create the following array:
[ ('a', 'b', 'b'), 'a-value', 'b-value-1', 'b-value-2' ]
The number of bs in the first sequence should correspond to the number of b-values.
I've tried just about every combination of iterating/map:for-each, etc., and the array either ends up with too much nesting or completely flat...
(n.b. the array is to be passed to a function -- not mine!-- using fn:apply, so needs to be in this form)
It seems hard to build an array containing a sequence that is constructed dynamically, I think you first need to construct the sequence into a variable and then use the square bracket array constructor [$seq] to construct the array with the sequence as the single item. Then you can array:join the other values:
let $input := map { 'a-key': 'a-value', 'b-key': ['b-value-1', 'b-value-2'] }
let $seq := ($input?a-key!'a', (1 to array:size($input?b-key))!'b')
return
array:join(([$seq], array {$input?a-key, $input?b-key?* }))
https://xqueryfiddle.liberty-development.net/nbUY4ku/2
If you are comfortable with the functional-programming side of XQuery, you can create the whole output in two nested "loops" (i.e. folds), one over the keys and the other one over those values that are arrays:
(: the input :)
let $input := map { 'a-key': 'a-value', 'b-key': ['b-value-1', 'b-value-2'] }
(: utility function that adds a single key/value pair to the output array :)
let $update-arr :=
function($arr, $k, $v) {
array:append(array:put($arr, 1, ($arr(1), $k)), $v)
}
(: nested folds over keys and values :)
return fold-left(map:keys($input), [()], function($arr, $key) {
let $k := substring-before($key, '-key')
let $val := $input($key)
return typeswitch($val)
case array(*)
return array:fold-left($val, $arr, function($arr2, $v) {
$update-arr($arr2, $k, $v)
})
default
return $update-arr($arr, $k, $val)
})
You can even abbreviate the array(*) case as return array:fold-left($val, $arr, $update-arr(?, $k, ?)) if you want.
The result is [("a", "b", "b"), "a-value", "b-value-1", "b-value-2"] as expected.

Parsing an array of dates in XQuery (MarkLogic v8 flavour)

My question seems trivial but could not figure out how to parse a string that contains a list of dates separated by commas. The parsing part of individual dates is not an issue but the empty values are. The trouble is the order of the dates are significant and some dates can be omitted. The dates are expected to be formatted in YYYY-mm-dd
So, the following are valid inputs and expected return values:
,2000-12-12,2012-05-03, ➔ ( NULL, 2000-12-12, 2012-05-03, NULL )
2000-12-12,,2012-05-03 ➔ ( 2000-12-12, NULL, 2012-05-03 )
And here is my function signature
declare function local:assert-date-array-param(
$input as xs:string
, $accept-nulls as xs:boolean?
) as xs:date*
I recognised the problem after realizing that there seems to be no equivalent of of NULL in XQuery for the returned values as placeholders for omitted dates, if you want to return a sequence, that is. Because empty sequences wrapped inside sequences are flattened to nothing.
I suppose, my fallback would be to use date like 1900-01-01 as the placeholder or return a map instead of a sequence, but I sure hope to find a more elegant way
Thank you,
K.
PS. I am working with MarkLogic v8 (and v9 soon) and any solution should execute with their XQuery processor.
UPDATE: thanks for both answers, in the end I chose to go with a placeholder date as XQuery works so nicely with sequences and anything else would have required some changes in other places. But the problem remains for the cases where the required return values are numerics. In this case, use of placeholder values would probably be not feasible. A null literal for xs:anyAtomicType would have solved the problem nicely, but alas.
You could consider returning a json:array(), or an array-node{} with null-node{}'s inside. But perhaps a null-date placeholder is not as bad as it sounds:
declare variable $null-date := xs:date("0001-01-01");
declare function local:assert-date-array-param(
$input as xs:string,
$accept-nulls as xs:boolean?
) as xs:date*
{
for $d in fn:tokenize($input, "\s*,\s*")
return
if ($d eq "") then
if ($accept-nulls) then
$null-date
else
fn:error(xs:QName("NULL-NOT-ALLOWED"), "Date is required")
else
if ($d castable as xs:date) then
xs:date($d)
else if ($d castable as xs:dateTime) then
xs:date(xs:dateTime($d))
else
fn:error(xs:QName("INVALID-DATE"), "Invalid date format: " || $d)
};
declare function local:print-date-array($dates) {
string-join(for $d in $dates return if ($d eq $null-date) then "NULL" else fn:string($d), ", ")
};
local:print-date-array(
local:assert-date-array-param(",2000-12-12,2012-05-03,", fn:true())
),
local:print-date-array(
local:assert-date-array-param("2000-12-12,,2012-05-03", fn:true())
)
HTH!
Multiple options .. in addition to above.
return a sequence of functions which when invoked return dates
for $i in string-tokenize-to-sequence-of-strings()
let $dt := my-parse-date($i)
return function() { $dt ;}
or
return function() { return my-parse-date($i) ;
return tokenized and validated but not parsed strings. Use "" for 'not valid', e.g.:
( "2014-01-22","","2017-03-30","" )
then there's arrays', maps, arrays of maps, and ... XML
parseFunction() as xs:element()*:
for ... return <date>{ parse-and-validate($value) } </date>

Pass returned list Erlang

I want to pass my returned list into case but I'm getting the
"->" Error
Here is my code:
parse ( Element, [] ) -> false;
parse(Element,N) -> re:split(N,"([*+])",[{return,list}]),
parse ( Element, [ Item | ListTail ] ) ->
case ( Item == Element ) of
true -> true;
false -> listFind(Element, ListTail)
end.
I don't know how to pass the returned list into the case, Can anyone help?
If you want to test the result of re:split/3 in a case, you need to get rid of the extraneous function head stuck inside the second clause of parse/2. You also don't need the first clause of parse/2, as it's unnecessary and not used. Just look through the result of re:split/3 for Element, like this:
parse(Element, N) ->
Elements = re:split(N,"([*+])",[{return,list}]),
lists:member(Element, Elements).
If for example the argument N has the value "a*b+c*d+e", the re:split/3 call returns ["a","*","b","+","c","*","d","+","e"] (and let's assume that's really what you want). The call to lists:member/2 then searches that result for the value of Element, returning true if found or false if not found.

How to use forAll method of Doctrine ArrayCollection?

Can anybody show example of using forAll method of Doctrine\Common\Collections\ArrayCollection?
It's pretty straightforward. The class you linked to implements the forAll method in a following manner:
foreach ($this->_elements as $key => $element) {
if ( ! $p($key, $element)) {
return false;
}
}
So, based on that you should invoke the forAll like:
$collection = ... #some data
$collection->forAll(function($key, $item){
// Your logic here, based on $key and $item
});
Hope this help....
EDIT (the example):
You have an object of entity Student, which has a OneToMany to student's marks.
You want to check if student has passed all the subjects he/she elected
$student = ....
$allPassed = $student->getMarks()->forAll(function($key, $mark){
return $mark->getValue() != 'F';
});
The $allPassed will hold TRUE if all marks were either 'A', 'B', 'C' or 'D'. Even if one of them were F if will be FALSE.
I would like to highlight that forAll method exits as soon as the inner function returns a false.
Following the example of Perovic, take for instance that the $student has 10 marks, where only one is an 'F'.
If the 'F' mark is the 10th element of the array, the function
$mark->getValue()
will be invoked 10 times.
On the other hand, if the 'F' mark is the first element of the array, $mark->getValue() will be invoked only once, since the function immediately returns false, and forAll method stops evaluating.

Resources