[ArangoDB]: Get the first element of an object? - arangodb

I am trying to get the first element of the object scan. In my case, the first element's key changes. So I cannot call it with the key. Here is the AQL query I'm using, which is not working.
`FOR d in collection RETURN DISTINCT Object.keys(d.out.scan)[0]`
Object structure:
{
"out": {
"scan":{
"someKeyThatChanges":"someValue"
}
}
}
Is there a way to fetch the first key of scan?
Thank you

The relevant AQL functions for this issue are documented at
https://docs.arangodb.com/3.3/AQL/Functions/Document.html
In brief, if the object has only one user-defined key, then you will
be able to use VALUES(_, true) directly.
Otherwise, you could use ATTRIBUTES() to get an array of the object's
attributes. You may want to filter it to avoid keys with names that start with "_". Once you've selected a key, remember:
Attributes can also be accessed using the [] accessor
... the square brackets allow for expressions:
... u[attr1][0][attr2][ CONCAT("fir", "st") ]
Demo
LET x = {
"out": {
"scan":{
"someKeyThatChanges":"someValue"
}
}
}
LET y = x.out.scan
LET z = y[ ATTRIBUTES(y)[0] ]
RETURN z

To fetch just the name of the first key of out.scan, the following will work:
FOR d IN collection
RETURN ATTRIBUTES(d.out.scan)[0]
For returning the mapped value for that key, please refer to the other answer given.

Related

How to combine and sort key-value pair in Terraform

since the last update of the Logicmonitor provider in Terraform we're struggling with a sorting isse.
In LogicMonitor the properties of a device are a name-value pair, and they are presented alfabetically by name. Also in API requests the result is alphabetical. So far nothing fancy.
But... We build our Cloud devices using a module. Calling the module we provide some LogicMonitor properties specially for this device, and a lot more are provided in the module itself.
In the module this looks like this:
`
custom_properties = concat([
{
name = "host_fqdn"
value = "${var.name}.${var.dns_domain}"
},
{
name = "ocid"
value = oci_core_instance.server.id
},
{
name = "private_ip"
value = oci_core_instance.server.private_ip
},
{
name = "snmp.version"
value = "v2c"
}
],
var.logicmonitor_properties)
`
The first 4 properties are from the module and combined with anyting what is in var.logicmonitor_properties. On the creation of the device in LogicMonitor all properties are set in the order the are and no problem.
The issue arises when there is any update on a terraform file in this environment. Due to the fact the properties are presented in alphabetical order, Terraform is showing a lot of changes if finds (but which are in fact just a mixed due to sorting).
The big question is: How can I sort the complete list of properties bases on the "name".
Tried to work with maps, sort and several other functions and examples, but got nothing working on key-value pairs. Merging single key's works fine in a map, but how to deal with name/value pairs/
I think you were on the right track with maps and sorting. Terraform maps do not preserve any explicit ordering themselves, and so whenever Terraform needs to iterate over the elements of a map in some explicit sequence it always do so by sorting the keys lexically (by Unicode codepoints) first.
Therefore one answer is to project this into a map and then project it back into a list of objects again. The projection back into list of objects will implicitly sort the map elements by their keys, which I think will get the effect you wanted.
variable "logicmonitor_properties" {
type = list(object({
name = string
value = string
}))
}
locals {
base_properties = tomap({
host_fqdn = "${var.name}.${var.dns_domain}"
ocid = oci_core_instance.server.id
private_ip = oci_core_instance.server.private_ip
"snmp.version" = "v2c"
})
extra_properties = tomap({
for prop in var.logicmonitor_properties : prop.name => prop.value
})
final_properties = merge(local.base_properties, local.extra_properties)
# This final step will implicitly sort the final_properties
# map elements by their keys.
final_properties_list = tolist([
for k, v in local.final_properties : {
name = k
value = v
}
])
}
With all of the above, local.final_properties_list should be similar to the custom_properties structure you showed in your question except that the elements of the list will be sorted by their names.
This solution assumes that the property names will be unique across both base_properties and extra_properties. If there are any colliding keys between both of those maps then the merge function will prefer the value from extra_properties, overriding the element of the same key from base_properties.
First, use the sort() function to sort the keys in alphabetical order:
sorted_keys = sort(keys(var.my_map))
Next, use the map() function to create a new map with the sorted keys and corresponding values:
sorted_map = map(sorted_keys, key => var.my_map[key])
Finally, you can use the jsonencode() function to print the sorted map in JSON format:
jsonencode(sorted_map)```

Need to modify the list of Keys in a Groovy Map

I need to modify the key values from the given map below
Example:
Map map= ["abcd":["name":"x", "age":"22"],"xyz":["name":"y", "age":"12"]]
Need to modify the key values and my final Map should be like below:
Map map= ["modifiedkey":["name":"x", "age":"22"],"someanotherkey":["name":"y", "age":"12"]]
You can use collectEntries method from Groovy Collections API:
def defaultTransformation = { String key -> key }
def basicTransformation = { String key -> key.toUpperCase().reverse()
Map transformations = [abcd: basicTransformation, xyz: basicTransformation]
Map map= ["abcd":["name":"x", "age":"22"],"xyz":["name":"y", "age":"12"], "unchanged": ["name": "a", "age": "20"]]
Map newMap = map.collectEntries { [(transformations.getOrDefault(it.key, defaultTransformation).call(it.key)): it.value] }
In above example I use Closure that defines transformation - it expects single String parameter that is taken from current map entry key. As you can see Closure in Groovy is first class citizen, so we can pass it as e.g. a value in map. For this example I have created transformations map that defines mappings from old key to a new one. I have also created defaultTransformation closure - it will be used if mapping in transformations map for given key does not exist.
Running following script will produce newMap like this one:
[DCBA:[name:x, age:22], ZYX:[name:y, age:12], unchanged:[name:a, age:20]]
As you can see:
abcd key was transformed using basicTransformation closure
xyz key was also transformed using basicTransformation closure
unchanged key stayed the same, because there was no mapping in transformations map defined and the default one was used - a closure that returns key as is.
I hope it helps.

GORM - Update object without retrieving it first

I'd like to be able to update a previously persisted object for which I have an id without having to retrieve it first. The main thing that I'm trying to avoid is having to copy multiple values into the object's fields when that object has been retrieved from the database. I have these values in a map with keys corresponding to the field names so it's trivial to create the object via a constructor with the map as an argument. Unfortunately, an object created this way results in a new database record when saved even though the id field is set to that of an existing record.
I'm currently using a slight variation on one of the examples shown here for copying Groovy class properties but it's not a very elegant solution for multiple reasons.
Basically I'd like to be able to do something like this:
class Foo {
int a
String b
}
def data = [id: 99, a: 11, b: "bar"] //99 is the id of an existing record
def foo = new Foo(data)
foo.update() //or some other comparable persistence mechanism
Thanks
As long as your map keys have the same name as your object properties, you can use executeUpdate without specifying the individual property names with a closure or function like the following:
def updateString = { obj, map ->
def str = ""
map.each { key, value ->
str += "${obj}.${key}=:${key},"
}
return str[0..-2]
}
def data= [foo:"bar", machoMan:"RandySavage"]
In this case, println updateString("f", data) returns "f.foo=:foo,f.machoMan=:machoMan".
Then you can do this:
Foo.executeUpdate("update Foo f set ${updateString("f", data)}", data)
Or of course you could combine that all together into one closure or function.
You can use the executeUpdate method on the GORM domain class:
Foo.executeUpdate("update Foo f set f.a=:a, f.b=:b where f.id=:id", data)

What is in the reduce function arguments in CouchDB?

I understand that the reduce function is supposed to somewhat combine the results of the map function but what exactly is passed to the reduce function?
function(keys, values){
// what's in keys?
// what's in values?
}
I tried to explore this in the Futon temporary view builder but all I got were reduce_overflow_errors. So I can't even print the keys or values arguments to try to understand what they look like.
Thanks for your help.
Edit:
My problem is the following. I'm using the temporary view builder of Futon.
I have a set of document representing text files (it's for a script I want to use to make translation of documents easier).
text_file:
id // the id of the text file is its path on the file system
I also have some documents that represent text fragments appearing in the said files, and their position in each file.
text_fragment:
id
file_id // correspond to a text_file document
position
I'd like to get for each text_file, a list of the text fragments that appear in the said file.
Update
Note on JavaScript API change: Prior to Tue, 20 May 2008 (Subversion revision r658405) the function to emit a row to the map index, was named "map". It has now been changed to "emit".
That's the reason why there is mapused instead of emitit was renamed. Sorry I corrected my code to be valid in the recent version of CouchDB.
Edit
I think what you are looking for is a has-many relationship or a join in sql db language. Here is a blog article by Christopher Lenz that describes exactly what your options are for this kind of scenario in CouchDB.
In the last part there is a technique described that you can use for the list you want.
You need a map function of the following format
function(doc) {
if (doc.type == "text_file") {
emit([doc._id, 0], doc);
} else if (doc.type == "text_fragment") {
emit([doc.file_id, 1], doc);
}
}
Now you can query the view in the following way:
my_view?startkey=["text_file_id"]&endkey;=["text_file_id", 2]
This gives you a list of the form
text_file
text_fragement_1
text_fragement_2
..
Old Answer
Directly from the CouchDB Wiki
function (key, values, rereduce) {
return sum(values);
}
Reduce functions are passed three arguments in the order key, values and rereduce
Reduce functions must handle two cases:
When rereduce is false:
key will be an array whose elements are arrays of the form [key,id], where key is a key emitted by the map function and id is that of the document from which the key was generated.
values will be an array of the values emitted for the respective elements in keys
i.e. reduce([ [key1,id1], [key2,id2], [key3,id3] ], [value1,value2,value3], false)
When rereduce is true:
key will be null
values will be an array of values returned by previous calls to the reduce function
i.e. reduce(null, [intermediate1,intermediate2,intermediate3], true)
Reduce functions should return a single value, suitable for both the value field of the final view and as a member of the values array passed to the reduce function.

Groovy keyset and values

Is there a way to grab the key of one map and replace the value of the other with its value?
def wild = [animal1:"pet3", animal2:"dog", animal3:"pig"]
def pet = [pet1:"hamster", pet2:"fish", pet3:"cat"]
if(pet.containsKey(wild.animal1)) {
//replace wild.animal1 with the value contained in pet3 for example
//so wild.animal1 would equal "cat"
} else {
//dont change value
}
So basically I'm wondering if I am able to find a key based on a value in the map wild and replacing the value with the value of the key in the map pet.
Is there a simple way of going about this?
if(pet.containsKey(wild.animal1))
{
wild.animal1 = pet[wild.animal1];
}

Resources