I'm writing an application that has a function for tipping points. I want to make a conditional update which is only executed if the resulting value of a user's wallet would be 0 or higher. If the value is negative the update should not happen.
The function works without the conditional expression but when I add it, it breaks.
ConditionExpression: 'teleUser.wallet.points -:a > -1',
In the above line :a is a passed in integer. I'll post the context below, but the above line is where my problem occurs.
The error returned is ValidationException: Invalid ConditionExpression: Syntax error; token: "-", near: "points -:a".
Full function for context:
function removeFromWallet(msg, amount) {
console.log("remove");
let params = {
TableName: tableName,
Key: {"id": msg.from.id},
UpdateExpression: 'set teleUser.wallet.points = teleUser.wallet.points -:a',
ExpressionAttributeValues:{
":a": parseInt(amount)
},
ConditionExpression: 'teleUser.wallet.points -:a > -1',
ReturnValues:"UPDATED_NEW"
};
docClient.update(params, function(err, data) {
if (err) {
console.log(err);
} else {
const { Items } = data;
console.log(data.Attributes.teleUser.wallet.points);
addToWallet(msg, amount);
}
});
}
You can't perform calculations in ConditionExpression (see grammar for ConditionExpression)
condition-expression ::=
operand comparator operand
| operand BETWEEN operand AND operand
| operand IN ( operand (',' operand (, ...) ))
| function
| condition AND condition
| condition OR condition
| NOT condition
| ( condition )
comparator ::=
=
| <>
| <
| <=
| >
| >=
function ::=
attribute_exists (path)
| attribute_not_exists (path)
| attribute_type (path, type)
| begins_with (path, substr)
| contains (path, operand)
| size (path)
You can perform calculations in ExpressionAttributeValues, but in this particular case you'll probably have to use teleUser.wallet.points >= :a since column values aren't available in ExpressionAttributeValues
Related
I need a function wide_to_long that turns a wide table into a long table and that accepts an argument id_vars for which the values have to be repeated (see example).
Sample input
let T_wide = datatable(name: string, timestamp: datetime, A: int, B: int) [
'abc','2022-01-01 12:00:00',1,2,
'def','2022-01-01 13:00:00',3,4
];
Desired output
Calling wide_to_long(T_wide, dynamic(['name', 'timestamp'])) should produce the following table.
let T_long = datatable(name: string, timestamp: datetime, variable: string, value: int) [
'abc','2022-01-01 12:00:00','A',1,
'abc','2022-01-01 12:00:00','B',2,
'def','2022-01-01 13:00:00','A',3,
'def','2022-01-01 13:00:00','B',4
];
Attempt
I've come pretty far with the following code.
let wide_to_long = (T:(*), id_vars: dynamic) {
// get names of keys to remove later
let all_columns = toscalar(T | getschema | summarize make_list(ColumnName));
let remove = set_difference(all_columns, id_vars);
// expand columns not contained in id_vars
T
| extend packed1 = pack_all()
| extend packed1 = bag_remove_keys(packed1, id_vars)
| mv-expand kind=array packed1
| extend variable = packed1[0], value = packed1[1]
// remove unwanted columns
| project packed2 = pack_all()
| project packed2 = bag_remove_keys(packed2, remove)
| evaluate bag_unpack(packed2)
| project-away packed1
};
The problems are that the solution feels clunky (is there a better way?) and the columns in the result are ordered randomly. The second issue is minor, but annoying.
So I have big json, where I need to take some subtree and copy it to other place, but with some properties updated (a lot of them). So for example:
{
"items": [
{ "id": 1, "other": "abc"},
{ "id": 2, "other": "def"},
{ "id": 3, "other": "ghi"}
]
}
and say, that i'd like to duplicate record having id == 2, and replace char e in other field with char x using regex. That could go (I'm sure there is a better way, but I'm beginner) something like:
jq '.items |= . + [.[]|select (.id == 2) as $orig | .id=4 | .other=($orig.other | sub("e";"x"))]'<sample.json
producing
{
"items": [
{
"id": 1,
"other": "abc"
},
{
"id": 2,
"other": "def"
},
{
"id": 3,
"other": "ghi"
},
{
"id": 4,
"other": "dxf"
}
]
}
Now that's great. But suppose, that there ins't just one other field. There are multitude of them, and over deep tree. Well I can issue multiple sub operations, but assuming, that replacement pattern is sufficiently selective, maybe we can turn the whole JSON subtree to string (trivial, tostring method) and replace all occurences using singe sub call. But how to turn that substituted string back to — is it call object? — to be able to add it back to items array?
Here's a program that might be a solution to the general problem you are describing, but if not at least illustrates how problems of this type can be solved. Note in particular that there is no explicit reference to a field named "other", and that (thanks to walk) the update function is applied to all candidate JSON objects in the input.
def update($n):
if .items | length > 0
then ((.items[0]|keys_unsorted) - ["id"]) as $keys
| if ($keys | length) == 1
then $keys[0] as $key
| (.items|map(.id) | max + 1) as $newid
| .items |= . + [.[] | select(.id == $n) as $orig | .id=$newid | .[$key]=($orig[$key] | sub("e";"x"))]
else .
end
else .
end;
walk(if type == "object" and has("items") then update(2) else . end)
I've got the following code:
use actix_service::Service;
use actix_web::{web, App, HttpServer, Responder};
use actix_router::{Path, Url};
use actix_web::dev::{ServiceRequest, ServiceResponse};
use actix_web::error::ResponseError;
use actix_web::{http, http::StatusCode, Error, HttpRequest, HttpResponse};
async fn greet(req: HttpRequest) -> impl Responder {
let name = req.match_info().get("name").unwrap_or("World");
format!("Hello {}!", &name)
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
HttpServer::new(|| {
let app = App::new()
.wrap_fn(|req, srv| {
let passed: bool;
// change to false to simulate a failed check
let check = true;
if *&req.path().contains("/item/") {
passed = check;
} else {
passed = true;
}
let fresh_result = match passed {
true => {
let fut = srv.call(req);
Box::pin(async {
let result = fut.await?;
Ok(result)
})
}
false => Box::pin(async {
let result = req.into_response(
HttpResponse::Found()
.header(http::header::LOCATION, "/login")
.finish()
.into_body(),
);
Ok(result)
}),
};
async {
let last_outcome = fresh_result.await?;
Ok(last_outcome)
}
})
.route("/", web::get().to(greet));
return app;
})
.bind("127.0.0.1:8000")?
.run()
.await
}
However, I get the following error:
110 | let fresh_result = match passed {
| ________________________________________-
111 | | true => {
112 | | let fut = srv.call(req);
113 | | Box::pin(
| _|_____________________________-
114 | | | async {
115 | | | let result = fut.await?;
116 | | | Ok(result)
117 | | | }
118 | | | )
| |_|_____________________________- this is found to be of type `std::pin::Pin<std::boxed::Box<impl core::future::future::Future>>`
... |
121 | / | Box::pin(
122 | | | async {
123 | | | let result = req.into_response(
124 | | | HttpResponse::Found()
... | |
129 | | | }
130 | | | )
| |_|_____________________________^ expected generator, found a different generator
131 | | }
132 | | };
| |_____________________- `match` arms have incompatible types
|
::: /Users/maxwellflitton/.rustup/toolchains/stable-x86_64-apple-darwin/lib/rustlib/src/rust/src/libcore/future/mod.rs:55:43
|
55 | pub const fn from_generator<T>(gen: T) -> impl Future<Output = T::Return>
| ------------------------------- the found opaque type
|
= note: expected type `std::pin::Pin<std::boxed::Box<impl core::future::future::Future>>` (generator)
found struct `std::pin::Pin<std::boxed::Box<impl core::future::future::Future>>` (generator)
I am completely stuck on this. I don't know how to ensure it's a type. If there is no match statement and there is just one async block then it all runs fine. This is for middleware in an Actix-web server. I am trying to redirect the viewer if credentials are not working.
You use Box::pin to create the boxed futures,
but Rust still thinks you want the impl Future<...> version (meaning the future is boxed to the heap, but doesn't use dynamic dispatch).
This is why the type in the error is Pin<Box<impl Future<...>>>, and because any two impl Future<...>s are of different types, you get the expected/found error.
You need to tell Rust you are interested in dynamic dispatch (because you have two different futures that can be stored in the Box, and which one really is stored there can only be known at runtime), for example by explicitly specifying the return type like this:
use std::pin::Pin;
use std::future::Future;
let fresh_result: Pin<Box<dyn Future<Output=_>>> = match passed {
// ...
}
Now you will get a new error about cannot infer type for type parameter E which can be resolved by specifying the Output of the future as well,
so the new line should be:
let fresh_result: Pin<Box<dyn Future<Output=Result<ServiceResponse, Error>>>> = match passed {
// ...
}
which will compile successfully!
My JSON looks like this.
"[{
changes": [
{
"change": "{users=[7], submitted=true}",
"date": "2016-11-13T14:34:27.353Z",
"user": "abcd"
}
]
}]
Expected Output:
{
id: null,
date: "2016-11-13T14:34:27.353Z",
type: "submission",
user: abcd,
_processDate: todaysDate
}
JQ I tried
[.[][] as $source |
$source.changes[] as $log |
$log.change |
{
submitted: .| (scan("submitted=(?<submitted>[^,}]+)") // [""] ) | .[0],
rejected: .| (scan("rejected=(?<rejected>[^,}]+)") // [""] ) | .[0]
} as $change |
[
(
select($change.submitted == "true") |
{
id: $source.id,
date: $log.date,
type: "submission",
user: $log.user,
_processDate: now | todate
}),
(select($change.rejected == "true") |
{
id: $source.id,
date: $log.date,
type: "rejection",
user: $log.user,
_processDate: now | todate
}
)
] |
.[]]
There could 'rejections' in the json and the output should display the rejections .
My JQ is not yielding expected output.
Any pointers on how to fix this query.
Thank you for your help.
Appreciate it.
I'd take a completely different approach and do this instead.
(now | todate) as $_processDate | [
.[].changes[]
| (.change
| scan("\\b(submitted|rejected)=true\\b")[]
| {submitted:"submission",rejected:"rejection"}[.]) as $type
| {id, date, $type, user, $_processDate}
]
First take note of the current date at the beginning. Then determine what type of change it is. Assuming that "submitted" and "rejected" (or other "types") are mutually exclusive, easier to match on the key name. Then build up the result. This will keep the results in an array.
https://jqplay.org/s/S65ySzbF30
I made a simple macro that returns the taken parameter.
macro_rules! n {
($n:expr) => {{
let val: usize = $n;
match val {
0 => 0,
_ => n!(val - 1),
}
}};
}
When I compile this code with the option external-macro-backtrace, it raises an error:
error: recursion limit reached while expanding the macro `n`
--> src/main.rs:15:18
|
10 | macro_rules! n {
| _-
| |_|
| |
11 | | ($n:expr) => {{
12 | | let val: usize = $n;
13 | | match val {
14 | | 0 => 0,
15 | | _ => n!(val - 1),
| | ^^^^^^^^^^^
| | |
| | in this macro invocation
16 | | }
17 | | }};
18 | | }
| | -
| |_|
| |_in this expansion of `n!`
| in this expansion of `n!`
...
31 | | n!(1);
| | ------ in this macro invocation
|
= help: consider adding a `#![recursion_limit="128"]` attribute to your crate
I changed the recursion_limit to 128 and higher, but the compiler error message just increase as well. Even when I call n!(0) it makes the same error. I think it is infinite recursion, but I can't find the reason.
Well, it really is an infinite recursion. Check what your macro invocation n!(0) will be expanded into:
{
let val: usize = 0;
match val {
0 => 0,
_ => n!(0 - 1),
}
}
...and since there's no way for argument of n! to stop growing negative, it'll repeat (with n!(0 - 1 - 1) in the second match arm, then n!(0 - 1 - 1 - 1) etc.) infinitely.
The key point here is that the macro expansion happens in compile-time, while the match statement you're trying to use to limit the recursion is invoked only at run-time and can't stop anything from appear before that. Unhappily, there's no easy way to do this, since Rust won't evaluate macro arguments (even if it's a constant expression), and so just adding the (0) => {0} branch to the macro won't work, since the macro will be invoked as (for example) n!(1 - 1).