Imagine I have the data in Cosmos
[
{
"id": "FCEC01CD-A6E9-4DEA-8DD5-89711B5B05A1",
"sub": [
{
"id": 1,
"v": false
},
{
"id": 2,
"v": false
}
]
]
and I want to query for all id's that have all (sibbeling) 'sub' items having v=false
what query syntax would work?
(ARRAY_CONTAINS would not work, since that gives an 'any' result)
Thanks!
You need a user-defined function for this.
function arrayAllMatch(arr) {
for(i=0; i < arr.length; i++) {
if (arr[i].v === true) {
return false;
}
}
return true;
}
Then call within query (also include ARRAY_CONTAINS because it can use the index to reduce the number of calls to the UDF):
SELECT *
FROM c
WHERE ARRAY_CONTAINS(c.sub, {"v" : false }, true)
AND udf.arrayAllMatch(c.sub)
Related
Is there a generic mechanism within jq to select arbitrary elements of a JSON file but return the full structural context of those elements? For example, if I have the following:
{
"foo": {
"one": true,
"two": false,
"three": {
"hello": "world"
},
"four": true
},
"bar": [
1,
4,
5
],
"baz": true
}
using the filter .foo,.baz would normally result in:
{
"one": true,
"two": false,
"three": {
"hello": "world"
},
"four": true
}
true
but what I'd like is to get:
{
"foo": {
"one": true,
"two": false,
"three": {
"hello": "world"
},
"four": true
},
"baz": true
}
I can solve that specifically for the given filter using select, but I'd like something generic, to be able to run the same code with a different filter and get the same type of result, e.g. running with the filter .foo.three,.bar[1] would result in:
{
"foo": {
"three": {
"hello": "world"
}
},
"bar": [
4
]
}
Thanks!
This would give exactly the result you wanted for .foo.three,.bar[1] :
jq 'def extract(f):
. as $input |
reduce path(f) as $path (
null;
if ($path | last | type) == "string"
then setpath($path; $input | getpath($path))
else setpath(($path|.[:-1]);
getpath($path|.[:-1]) +
[$input | getpath($path)]
)
end
);
extract(.foo.three, .bar[1])' data.json
At best you could do an object construction directly by naming the key names under {..} and apply a further transformation to get only the desired paths
{foo, bar} | .foo |= {three} | .bar |= [.[1]]
jqplay demo
You could convert your queries into paths, and the input into a stream, select the pieces matching the query path, and rebuild it to a single output:
def extract(f):
reduce (
path(f) as $path | tostream
| select(length > 1 and (.[0] | index($path) == 0))
) as $set (
null;
setpath($set[0]; $set[1])
);
First example using .foo and .baz:
jq 'def extract(f): …; extract(.foo, .baz)'
{
"foo": {
"one": true,
"two": false,
"three": {
"hello": "world"
},
"four": true
},
"baz": true
}
Demo
As with sparse arrays though, it'll fill up the missing items with null, as otherwise the index wouldn't match anymore. Second example using .foo.three and .bar[1]:
jq 'def extract(f): …; extract(.foo.three, .bar[1])'
{
"foo": {
"three": {
"hello": "world"
}
},
"bar": [
null,
4
]
}
Demo
I have a list of defined objects:
[{"name":"name1", "age":25}, {"name":"name2", "age":27}]
and I would like to return records that are the same
Is there any way in Cosmos DB to perform the following query?
select * from c
where c in ({"name":"name1", "age":25}, {"name":"name2", "age":27})
my records are like this:
[
{
"name":"name1",
"age":25,
"height":165
},
{
"name":"name2",
"age":27,
"height":169
},
{
"name":"name3",
"age":35,
"height":185
}
]
The query would return this result:
[
{
"name":"name1",
"age":25,
"height":165
},
{
"name":"name2",
"age":27,
"height":169
}
]
You can use ARRAY_CONTAINS
SELECT * FROM c where ARRAY_CONTAINS([{"name":"name1", "age":25}, {"name":"name2", "age":27}],{"name":c.name,"age":c.age})
Note : Might be useful for others as in the comment.
reference: DocumentDB SQL with ARRAY_CONTAINS
Question: Is there a better and more efficient query for what is happening below?
In the above reference question, the UDF was written to check if an object in the array had a match to the passed in string. In this variant, I am passing into the UDF an array of strings.
Now I have a working O(N^2) version that I would hope CosmosDB had a more efficient solution for.
function ScopesContainsNames(scopes, names){
var s, _i,_j, _ilen, _jLen;
for (_i = 0, _ilen = scopes.length; _i < _ilen; _i++) {
for (_j = 0, _jLen = names.length; _j < _jLen; _j++) {
s = scopes[_i];
n = names[_j];
if (s.name === n) {
return true;
}
}
}
return false;
}
My QUERY looks like this.
SELECT * FROM c WHERE udf.ScopesContainsNames(c.scopes, ["apples", "strawberries", "bananas"])
The following is an example of my Document:
{
"scopes": [
{
"name": "apples",
"displayName": "3048b61e-06d8-4dbf-a4ab-d4c2ba0a8943/a"
},
{
"name": "bananas",
"displayName": "3048b61e-06d8-4dbf-a4ab-d4c2ba0a8943/a"
}
],
"enabled": true,
"name": "dc1e4c12-95c1-4b7f-bf27-f60f0c29bf52/a",
"displayName": "218aea3d-4492-447e-93be-2d3646802ac6/a",
"description": "4aa62367-7421-4fb6-88c7-2699c9c309dd/a",
"userClaims": [
"98988d5b-38b5-400c-aecf-da57d2b66433/a"
],
"properties": {
"437d7bab-a4fb-4b1d-b0b9-f5111d01882a/a": "863defc1-c177-4ba5-b699-15f4fee78ea5/a"
},
"id": "677d4a49-a46c-4613-b3f6-f390ab0d013a",
"_rid": "q6I9AOf180hJAAAAAAAAAA==",
"_self": "dbs/q6I9AA==/colls/q6I9AOf180g=/docs/q6I9AOf180hJAAAAAAAAAA==/",
"_etag": "\"00000000-0000-0000-1ede-f2bc622201d5\"",
"_attachments": "attachments/",
"_ts": 1560097098
}
If i don't misunderstanding your requirement,you need to search the results where any name property of scopes array is included by the ["apples", "strawberries", "bananas"].
No need to use udf, please see the sample documents i made as below:
Using sql:
SELECT distinct c.scopes FROM c
join fruit in c.scopes
where Array_contains(["apples", "strawberries", "bananas"],fruit.name,false)
Result:
I would like to turn this resultset
[
{
"Document": {
"JsonData": "{\"key\":\"value1\"}"
}
},
{
"Document": {
"JsonData": "{\"key\":\"value2\"}"
}
}
]
into this
[
{
"key": "value1"
},
{
"key": "value2"
}
]
I can get close by using a query like
select value c.Document.JsonData from c
however, I end up with
[
"{\"key\":\"value1\"}",
"{\"key\":\"value2\"}"
]
How can I cast each value to an individual JSON fragment using the SQL API?
As David Makogon said above, we need to transform such data within our app. We can do as below:
string data = "[{\"key\":\"value1\"},{\"key\":\"value2\"}]";
List<Object> t = JsonConvert.DeserializeObject<List<Object>>(data);
string jsonData = JsonConvert.SerializeObject(t);
Screenshot of result:
I am stuck since 2 days, as I am not to firm with pointers and recursion. I have an array of path like structures, lets say:
s:=[]string {
"a/b/c",
"a/b/g",
"a/d",
}
With a data structure like this:
type Node struct {
Name string `json:"name"`
Children []Node `json:"children"`
}
I would like to end up with something like this:
{
"name": "a",
"children": [
{
"name": "b",
"children": [
{
"name": "c",
"children": []
},
{
"name": "g",
"children": []
}
]
},
{
"name": "d",
"children": []
}
]
}
I tried to build it with a recursion, which works kind of fine, but only for one string (e.g. "a/b/c"), as soon as I try to implement something which should add missing nodes ("g" in "a/b/g") to a tree I am stuck.
I had something like:
func appendChild(root Node, children []string) Node {
if len(children) == 1 {
return Node{children[0], nil}
} else {
t := root
t.Name=children[0]
t.Children = append(t.Children, appendChild(root, children[1:]))
return t
}
}
Could someone point me to an efficient solution?
https://play.golang.org/p/9pER5cwChF
func AddToTree(root []Node, names []string) []Node {
if len(names) > 0 {
var i int
for i = 0; i < len(root); i++ {
if root[i].Name == names[0] { //already in tree
break
}
}
if i == len(root) {
root = append(root, Node{Name: names[0]})
}
root[i].Children = AddToTree(root[i].Children, names[1:])
}
return root
}
Example output (note that I used omitempty on the children field, because I don't like null entries in my JSONs):
[{
"name": "a",
"children": [{
"name": "b",
"children": [{
"name": "c"
}, {
"name": "g"
}]
}, {
"name": "d"
}]
}]
Notable difference from your version:
It operates on a list of nodes instead of the children of a single node. This is important, as your version assumes that all of the trees have the same single root node (a), when this might not be the case. The only way to handle that in your version is to have a "fake" node at the root.
It does NOT reuse the input node. This is one of the primary problems with your code. If len(children) > 1, you update the input node's name, append to it's children, then recurse. This means that every prior level of the slice becomes part of the children. You need to create a new node instead.
It actually searches the tree. You're not searching the tree to see if the item being inserted already exists, so you duplicate nodes (specifically, node b)