How do I encode a single dictionary item to JSON in KDB? - kdb

In KDB:
.j.j `something`else!(1 2)
produces {"something": 1, "else": 2} as expected
However, doing
.j.j `something!1
just produces ,"1", with no labels.
What am I doing wrong? Or is this is a bug in .j.j?

You need to enlist the keys and values in the dictionary
q).j.j enlist[`something]!enlist 1
"{\"something\":1}"
Otherwise your input is not a dictionary (type 99h), but an enumeration
q)type `something!1
-20h

Related

Convert a string into array of strings

In a postgresql query, I would like to turn a given string, split it by comma, and return the results as an array of strings.
Input:
'a,b,c'
Expected output:
{'a','b','c'}
Using STRING_TO_ARRAY('a,b,c') yields the result {a,b,c} which is an array of objects, not strings.
How can I achieve array of strings?

MongoDB should report error when negative integer is used in dot notation?

MongoDB allows to use dot notation to do queries on JSON sub-keys or array elements (see ref1 or ref2). For instance, if a is an array in the documents the following query:
db.c.find({"a.1": "foo"})
returns all documents in which 2nd element in the a arrary is the "foo" string.
So far, so good.
What is a bit surprissing in that MongoDB accepts using negative values for the index, e.g.:
db.c.find({"a.-1": "foo"})
That doesn't return anything (makes sense if it an unsupported syntax) but what I wonder if why MongoDB doesn't return error upon this operation or if it has some sense at the end. Documentation (as far as I've checked) doesn't provide any clue.
Any information on this is welcome!
That is not an error. The BSON spec defines a key name as
Zero or more modified UTF-8 encoded characters followed by '\x00'. The (byte*) MUST NOT contain '\x00', hence it is not full UTF-8.
Since "-1" is a valid string by that definition, it is a valid key name.
Quick demo:
> db.test.find({"a.-1":{$exists:true}})
{ "_id" : 0, "a" : { "-1" : 3 } }
Playground
Also note how that spec defines array:
Array - The document for an array is a normal BSON document with integer values for the keys, starting with 0 and continuing sequentially. For example, the array ['red', 'blue'] would be encoded as the document {'0': 'red', '1': 'blue'}. The keys must be in ascending numerical order.

Compare if one string is greater than another in kdb

I want to check which of the two strings in greater, for which I have was using below logic but it fails in few cases
q){$[1b in x>=y;x;y]}["b";"b"]
"b"
q){$[1b in x>=y;x;y]}["c";"b"]
"c"
q){$[1b in x>=y;x;y]}["azz";"dff"] // Wrong output (Reason for failure - "azz">"dff" --> 011b)
"azz" / desired output dff
Please suggest another way to get the greatest string of the provided strings?
Since comparison operator compares character by character hence in "azz" and "dff" if the output can be displayed as "dff" only after comparison for "d" from "dff" and "a" from "azz" as "a" is less than "d".
You can convert the string to symbol and use <, >, etc.. These operators perform lexicographic comparisons for symbols.
https://code.kx.com//q4m3/4_Operators/
q) `azz < `dff
1b
If you insist on strings then you can leverage iasc to creat a "smaller-or-equal"-like function:
q) not first iasc ("azz"; "dff")
1b
Using 1b in is equivalent to any in this case as "azz">"dff" equates to 011b. Your conditional will evaluate true as 2 letters in "azz" are greater than "dff".
It is better to cast x and y to symbols and compare as this will evaluate with 1 boolean:
(`$"azz")>=`$"dff"
0b
{$[(`$x)>=`$y;x;y]}["azz";"dff"]
"dff"
Alternatively you could sort desc order and take the first result:
{first desc(x;y)}["azz";"dff"]
"dff"

Behaviour of the project stage operator in projecting Arrays

My question is closely related to this, but not similar.
I have a sample document in my collection:
db.t.insert({"a":1,"b":2});
My intent is to project a field named combined of type array with the values of both a and b together.([1,2]).
I simply try to aggregate with a $project stage:
db.t.aggregate([
{$project:{"combined":[]}}
])
MongoDB throws an error: disallowed field type Array in object expression.
Which means a field cannot be projected as a array.
But when i use a $cond operator to project an array, the field gets projected.
db.t.aggregate([
{$project:{"combined":{$cond:[{$eq:[1,1]},["$a","$b"],"$a"]}}}
])
I get the o/p: {"combined" : [ "$a", "$b" ] }.
If you notice the output, the value of a and b are treated as if they were literals and not a field paths.
Can anyone please explain to me this behavior?, When i make the condition to fail,
db.t.aggregate([
{$project:{"combined":{$cond:[{$eq:[1,2]},["$a","$b"],"$a"]}}}
])
I get the expected output where $a is treated as a field path, since $a is not enclosed as an array element.
I've run into this before too and it's annoying, but it's actually working as documented for literal ["$a", "$b"]; the first error about disallowed field type is...not as clear why it complains. You have to follow the description of the grammar of the $project stage spread out in the documentation, however. I'll try to do that here. Starting at $project,
The $project stage has the following prototype form:
{ $project: { <specifications> } }
and specifications can be one of the following:
1. <field> : <1 or true or 0 or false>
2. <field> : <expression>
What's an expression? From aggregation expressions,
Expressions can include field paths and system variables, literals, expression objects, and operator expressions.
What are each of those things? A field path/system variable should be familiar: it's a string literal prefixed with $ or $$. An expression object has the form
{ <field1>: <expression1>, ... }
while an operator expression has one of the forms
{ <operator>: [ <argument1>, <argument2> ... ] }
{ <operator>: <argument> }
for some enumerated list of values for <operator>.
What's an <argument>? The documentation isn't clear on it, but from my experience I think it's any expression, subject to the syntax rules of the given operator (examine the operator expression "cond" : ... in the question).
Arrays fit in only as containers for argument lists and as literals. Literals are literals - their content is not evaluated for field paths or system variables, which is why the array literal argument in the $cond ends up with the value [ "$a", "$b" ]. The expressions in the argument array are evaluated.
The first error about Array being a disallowed value type is a bit odd to me, since an array literal is a valid expression, so according to the documentation it can be a value in an object expression. I don't see any ambiguity in parsing it as part of an object expression, either. It looks like it's just a rule they made to make the parsing easier? You can "dodge" it using $literal to put in a constant array value:
db.collection.project([{ "$project" : { "combined" : { "$literal" : [1, 2] } } }])
I hope this helps explain why things work this way. I was surprised the first time I tried to do something like [ "$a", "$b" ] and it didn't work as I expected. It'd be nice if there were a feature to pack field paths into an array, at least. I've found uses for it when $grouping on ordered pairs of values, as well.
There's a JIRA ticket, SERVER-8141, requesting an $array operator to help with cases like this.

Perl : map statement

I would like your help, because I am not able to understand what the following line means:
map {#$_[1 .. 4]} #msft
found in the example code of GD::Graph::ohlc.
Could you please provide me with a hint?
Thank you.
#msft is an array of arrays where each inner array contains 5 items (date, open/low/high/close prices).
The map takes each element of #msft, which is an array reference stored in $_ and dereferences it via #$_ and takes a slice of that array (namely the second through fifth items since the array is 0-based) via the [1..4]. It then returns those four items. map concatenates them into a single list.
In essence, it is flattening the array of arrays of five elements into a single array made up of the 2nd through 5th items of each subarray.
The elements of #msft are array references. The code collects elements 1 through 4 from each array into a single list:
my #msft = (
[0,1,2,3,4,5],
[0,11,22,33,44,55],
[0,111,222,333,444,555],
);
my #result = map {#$_[1 .. 4]} #msft;
print "#result\n"; # 1 2 3 4 11 22 33 44 111 222 333 444
From the documentation for map:
Evaluates the BLOCK or EXPR for each
element of LIST (locally setting $_ to
each element) and returns the list
value composed of the results of each
such evaluation.