As the title suggests, is there an operator similar to MongoDB's $addToSet in CockroachDB? I want to append a value to an array if it's not present.
I browsed the docs but didn't find an $addToSet operator.
The array_append function should work for this case, see this doc: https://www.cockroachlabs.com/docs/stable/array.html#using-the-array_append-function
You'll need two functions to do this with arrays. Either of these should work:
array_append(array_remove(myArray, newElement), newElement)
IF(array_position(myArray,newElement) IS NULL, array_append(myArray,newElement), myArray)
If what you're representing should never contain duplicates, you might be better off using a JSON object type than an array type, since their keys are automatically unique. Here's an example:
create table unique_groups(id int primary key, vals jsonb default '{}');
insert into unique_groups values (1, '{}'), (2, '{"a": true}');
-- Add "b" to each set
update unique_groups set vals = jsonb_set(vals, '{b}', 'true') where true;
select * from unique_groups;
id | vals
-----+-----------------------
1 | {"b": true}
2 | {"a": true, "b": true}
-- Add "a" to each set
update unique_groups set vals = jsonb_set(vals, '{a}', 'true') where true;
select * from unique_groups;
id | vals
-----+-------------------------
1 | {"a": true, "b": true}
2 | {"a": true, "b": true}
Related
i have a simple array of objects like this
{
"a": [1,2],
"b": [3,4]
}
and I want to put it in this form
[
{ "a": 1, "b": 3},
{ "a": 2, "b": 4}
]
I want to find the right approach here.
Seems like this is a transpose operation where a table is given as an object where the keys are the column names and the values is the column data. The output is supposed to be an array of table rows where each row is an object with the column names are its keys.
(
/* define function that iterates over all columns, gets the i-th array index and merges the results */
$getRow := function($x, $i){
$merge($each($x, function($v, $k) {
{$k: $v[$i]}
}))
};
/* this is the first key that is found, "a" in the example */
$firstKey := $.$keys()[0];
/* this is the first column, [1,2] in the example */
$firstArray := $lookup($, $firstKey);
/* loop over the first array (index is $i) and call $getRow($, $i) */
$map($firstArray, function($v, $i){
$getRow($,$i)
} );
)
I have a table in DynamoDB with a composite key
user_id (PartitionKey)
timestamp (Sort Key)
I want to retrieve a group of items, I have a set of user_ids I want to retrieve but I also need the timestamp to be the MAX for every single user_id.
For example:
user_id
timestamp
attr
1
1614910613
value1
1
1614902527
value 2
2
1614910683
value 2
2
1614881311
value 2
3
1614902527
value 2
I want to retrieve the rows for user_id 1 and 2, with the max timestamp for each user. In this case, my result set should be:
user_id
timestamp
attr
1
1614910613
value1
2
1614910683
value 2
I can do this now on an item-by-item basis doing this Key expression:
cond := expression.KeyAnd(
expression.Key("user_id").Equal(expression.Value(userId)),
expression.Key("timestamp").LessThanEqual(expression.Value(int32(time.Now().Unix()))),
)
expr, err:= expression.NewBuilder().WithKeyCondition(cond).Build()
if err!=nil {
panic(err)
}
input := &dynamodb.QueryInput{
ExpressionAttributeNames: expr.Names(),
ExpressionAttributeValues: expr.Values(),
KeyConditionExpression: expr.KeyCondition(),
TableName: aws.String("td.notes"),
Limit: aws.Int64(1),
ScanIndexForward: aws.Bool(false),
}
My problem is, I don't know how to pass a set of values for the user_id key instead of a single value. I took a look at the BatchGetItem, I know I can do something like this:
mapOfAttrKeys := []map[string]*dynamodb.AttributeValue{}
for _, place := range userIDs {
mapOfAttrKeys = append(mapOfAttrKeys, map[string]*dynamodb.AttributeValue{
"user_id": &dynamodb.AttributeValue{
S: aws.String(place),
},
"timestamp": &dynamodb.AttributeValue{
N: aws.String(timestamp),
},
})
}
input := &dynamodb.BatchGetItemInput{
RequestItems: map[string]*dynamodb.KeysAndAttributes{
tableName: &dynamodb.KeysAndAttributes{
Keys: mapOfAttrKeys,
},
},
}
But I cannot put a "less than" condition in the timestamp.
Thanks!
GetItem and BatchGetItem require the exact primary key.
You'll need to issue separate Query() for each user you want to return.
The key to returning max, is as you've found
Limit: aws.Int64(1),
ScanIndexForward: aws.Bool(false),
You really shouldn't even need this
expression.Key("timestamp").LessThanEqual(expression.Value(int32(time.Now().Unix())))
I have two lists in elixir. One list (list1) has values which get consumed in another list (list2). So I need to iterate over list2 and update values in list1 as well as list2.
list1 = [
%{
reg_no: 10,
to_assign: 100,
allocated: 50
},
%{
reg_no: 11,
to_assign: 100,
allocated: 30
},
%{
reg_no: 12,
to_assign: 100,
allocated: 20
}
]
list2 = [
%{
student: student1,
quantity: 60,
reg_nos: [reg_no_10, reg_no_11]
},
%{
student: student2,
quantity: 40,
reg_nos: [reg_no_11, reg_no_12]
},
%{
student: student3,
quantity: 30,
reg_nos: nil
}
]
I need to assign values from list1 to quantity field of list2 till quantity is fulfilled. e.g. student1 quantity is 60 which will need reg_no 10 and reg_no 11.
With Enum.map I cannot pass updated list1 for 2nd iteration of list2 and assign value reg_nos: reg_no_11, reg_no_12for student2.
So, my question is how can I send updated list1 to 2nd iteration in list2?
I am using recursion to get quantity correct for each element in list2. But again, should I use recursion only to send updated list1 in list2? With this approach, there will be 2 nested recursions. Is that a good approach?
If I understand your question correctly, you want to change values in a given list x, based on a list of values in another list y.
What you describe is not possible in a functional language due to immutability, but you can use a reduce operation where x is the state or so-called "accumulator".
Below is an example where I have a ledger with bank accounts, and a list with transactions. If I want to update the ledger based on the transactions I need to reduce over the transactions and update the ledger per transaction, and pass the updated ledger on to the next transaction. This is the problem you are seeing as well.
As you can see in the example, in contrast to map you have a second parameter in the user-defined function (ledger). This is the "state" you build up while traversing the list of transactions. Each time you process a transaction you have a change to return a modified version of the state. This state is then used to process the second transaction, which in turn can change it as well.
The final result of a reduce call is the accumulator. In this case, the updated ledger.
def example do
# A ledger, where we assume the id's are unique!
ledger = [%{:id => 1, :amount => 100}, %{:id => 2, :amount => 50}]
transactions = [{:transaction, 1, 2, 10}]
transactions
|> Enum.reduce(ledger, fn transaction, ledger ->
{:transaction, from, to, amount} = transaction
# Update the ledger.
Enum.map(ledger, fn entry ->
cond do
entry.id == from -> %{entry | amount: entry.amount - amount}
entry.id == to -> %{entry | amount: entry.amount + amount}
end
end)
end)
end
I want to select several records from Tarantool in one call, but don't see how can I pass several keys to space:get or space:select
You can do it using Lua as well as SQL.
1) Use a stored procedure in Lua, like this one:
function select_several(space_name, index_name, keys)
local obj = index_name == nil and box.space[space_name] or box.space[space_name].index[index_name]
local result = {}
for _, key in pairs(keys) do
table.insert(result, obj:get(key))
end
return result
end
...
select_several('test', nil, {1, 2})
2) Starting with Tarantool 2.0, you can use SQL (provided that you have space format):
box.execute('select * from "test" where "id" in (1, 3);')
One more variant equivalent to the SQL query select * from "test" where "id" in (1, 3) using LuaFun:
tarantool> box.space.test:pairs():filter(function (tuple) return tuple.id == 1 or tuple.id == 3 end):totable()
It is a generic variant if there is no 'id' index in the space, and it implies performing fullscan.
If a unique index named "id" exists, a more efficient variant is possible:
tarantool> fun.iter({1, 3}):map(function (value) return box.space.test.id:get(value) end):totable()
Otherwise if the index is not unique, then it will look like
tarantool> fun.iter({1, 3}):map(function (value) return box.space.test.id:select(value) end):reduce(function (result, data) for _, rec in ipairs(data) do table.insert(result, rec) end return result end, {})
I have a question about accessing data in a Lua table.
Say, there is a large Lua table like following:
tbl = {
{
blockIdx = 5,
key1 = "val1",
key2 = "val2",
...
},
{
blockIdx = 30,
key1 = "val11",
key2 = "val12",
...
},
{
blockIdx = 83,
key1 = "val21",
key2 = "val22",
...
},
...
}
And now I want to find one of the block that blockIdx is , for example, 38.
So normally, I would like to use for to find the block:
for k,v in pairs(tbl) do
if v.blockIdx == 38 then
blahFunction(v)
end
end
But I don't think it is a good idea especially for large table.
So I modify the table a bit:
tbl = {
[5] = {
key1 = "val1",
key2 = "val2",
...
},
[30] = {
key1 = "val11",
key2 = "val12",
...
},
[83] = {
key1 = "val21",
key2 = "val22",
...
},
...
}
Then I can easily access my block with one line:
blahFunction(tbl[38])
So my question is, is there any performance different between two method?
Maybe doing tbl[38] actually did a for loop inside Lua?
Or just like an array in C/C++ we can direct access memory using [ ] without for loop,
witch obviously has much better performance.
The performance is different, the second method is more efficient.
Internally, a Lua table contains an array part and a hash part, if the table is a sequence, then the sequence part is implemented by the array part. But the table in your second example is not a sequence, it's probably implemented by the hash part. The performance in this case is not like accessing arrays in C/C++, but like accessing a hash, which is still pretty fast.
So in conclusion, the second piece of code is faster, because it doesn't iterate through the elements like in your first example.