Is it possible to combine if_not_exists and list_append in update_item - amazon-dynamodb

I'm trying to use the update_item functionality for DynamoDB in boto3.
I'm struggling right now to update lists for items. I would like to create a new list if the list does not exist yet and otherwise append to the existing list.
Using an UpdateExpression of the form SET my_list = list_append(my_list, :my_value) returns an error "The provided expression refers to an attribute that does not exist in the item" if the list does not exist yet.
Any idea how I would have to modify my UpdateExpression?

You can use list_append(if_not_exists()) construction.
UpdateExpression:
'SET my_list2 = list_append(if_not_exists(my_list2, :empty_list), :my_value)'
ExpressionAttributeValues:
{ ":my_value":{"L": [{"S":"test"}]}, ":empty_list":{"L":[]} }
Update: as mentioned in the comments, boto3 now raises an error for the expression above and a version without explicit types works: { ":my_value": ["test"], ":empty_list":[] }.

An alternative to Boris solution could be to use set instead of list datatype and use the ADD keyword, it does exactly what you want.
With Add, the update expression becomes: ADD setName :s
And the expression attribute values can be like: {":s": {"SS":["First", "Second"]}}
http://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Expressions.UpdateExpressions.html#Expressions.UpdateExpressions.ADD

Related

Kotlin convert List with nullables to HashMap without nullables

I have incoming param List<Somedata>.Somedata class contains id field.
My goal is to make HashMap<Somedata.id, Somedata> from this list.
Is next approach correct or there is a better/safer way to do that?
list
.filter { it.id != null }
.associateTo(HashMap(), {it.id!! to it})
Actually, I cannot understand, why should I use !! keyword in associateTo method, when above I filtered it with non-null values only.
Or maybe there is a good way to perform this with ?. or ?.let keywords?
You can do:
list.mapNotNull { e -> e.id?.let { it to e } }.toMap()
Breakdown:
The call to .let with the ?. safe call operator will make the result null if the element is null.
So the lambda passed to mapNotNull is of type (Somedata) -> Pair<IdType, Somedata>.
mapNotNull discards the null pairs, and toMap turns the resulting List<Pair<IdType, Somedata>> into a Map<IdType, Somedata>.
If you want to avoid the creation of an intermediate list to hold the pairs, you can turn the list into a lazy Sequence from the start:
list.asSequence().mapNotNull { e -> e.id?.let { it to e } }.toMap()
Alternatively, since you asked:
why should I use !! keyword in associateTo method, when above I filtered it with non-null values only.
this is because the list is still of type List<Somedata> - this says nothing about the nullability of the field itself. The compiler does not know that the id fields are still not null, by the time your associateTo call is executed.

Firebase orderByPriority used with equalTo strange behavior

I try to use the second argument of equalTo(value, key) when using orderByPriority(), as documented here.
The problem is that results are inconsistent when using that second key argument.
Example dataset
items
key1
key2
key3
key4
All items have the same priority: 10 (this is just for the example, in my app there are other items with other priorities)
When issuing the following Firebase query to get the first two items with that priority:
dbRef.child('items').orderByPriority().equalTo(10).limitToFirst(2)
I get the following - expected - results :
{ "key1": ..., "key2": ... }
Then I try to get the results after the key2 item, as explained in the docs:
dbRef.child('items').orderByPriority().equalTo(10, 'key2').limitToFirst(2)
Result is pretty strange, with always only one item, the one with the provided key:
{ "key2": ... }
What i was expecting is two results starting at or after the key provided, so
{ "key2": ..., "key3": ... }
Or
{ "key3": ..., "key4": ... }
Question
How should I use the equalTo() filter with its second argument?
It seem this question was already asked, but it did not get any answers...
The equalTo() filter will only get items exactly matching the key specified. If you'd like to start at 'key2' and get multiple after that, use the startAt() filter. You cannot use multiple orderBy statements in the same query, though, so you may need to reformat your code. I would recommend that you save items by desired priority. Then you can query that group of items using the startAt() filter. Heres a dataset:
items10
key1
key2
items9
key1
key2
In the startAt() filter, you specify the starting key, and then it will query for any key with the same or greater value. Here's a link to the docs for this functionality: startAt().
Using this method, limitToFirst(2) should give you key2 and key3. Here's a code example, using priority 10.
dbRef.child('items10').orderByKey().startAt('key2').limitToFirst(2)
Note: you may also need to just grab an element, without knowing its priority. For this, create another key called items that just contains all keys (you would not use this for queries).

DynamoDB nested attribute querying support

Does Amazon DynamoDB scan operation allow you to query on nested attributes of type Array or Object? For example,
{
Id: 206,
Title: "20-Bicycle 206",
Description: "206 description",
RelatedItems: [
341,
472,
649
],
Pictures: {
FrontView: "123",
RearView: "456",
SideView: "789"
}
}
Can I query on RelatedItems[2] or Pictures.RearView attributes?
Yes, you can use a Filter Expression, which is just like Condition Expression. The section that talks about the functions that you can use in these types of expressions mentions the following:
"For a nested attribute, you must provide its full path; for more information, see Document Paths."
The Document Paths reference has examples on how to reference nested attributes in DynamoDB data types like List (what you are calling an array) and Map (what you are calling an object). Check out that reference for examples on how to do so:
MyList[0]
AnotherList[12]
ThisList[5][11]
MyMap.nestedField
MyMap.nestedField.deeplyNestedField
Please note that in DyanomoDB query and scan are quite different (scan is a much costlier operation). So while you can filter on both as pointed out by #coffeeplease; you can only query/index on:
The key schema for the index. Every attribute in the index key schema must be a top-level attribute of type String, Number, or Binary. Other data types, including documents and sets, are not allowed (ref).
Yes, you can by passing list or value.
data = table.scan(FilterExpression=Attr('RelatedItems').contains([1, 2, 3]) & Attr('Pictures.RearView').eq('1'))
Yes, you can query on nested attributes of type array or object using scan or query .
Reference for Python boto3:
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/dynamodb.html#querying-and-scanning
Example: Suppose you want to find out records for which the RearView" > 500 and second item of RelatedItems" > 200, you can do the following:
data = table.scan(
FilterExpression=Attr('RelatedItems[1]').gt('200') & Attr('Pictures.RearView').gt('500'))

How to set the value of one field to another filed in using XQuery

Very new to XQuery and MarkLogic, what is the XQuery version of the following statement?
update all_the_records
set B_field = A_field
where B_field is null and A_field is not null
Something like this might get you started. But remember that you're working with trees, not tables. Things are generally more complicated because of that extra dimension.
for $doc in collection()/doc[not(b)][a]
let $a as element() := $doc/a
return xdmp:node-insert-child($doc, element b { $a/#*, $a/node() })

Partial match fetch

I want to perform partial match like the clause LIKE do in SQL.
In Magical Record, to look for values for a specified field, we use findByAttribute:
NSArray *productsFoundByTitle = [Product MR_findByAttribute:#"product_title" withValue:#"bags"];
The problem is that this will return only exact matches of bags in product_title. I want to return also partial matches so a value like : mail bags would also be returned.
How can I do that in MagicalRecord?
Best solution I came with so far is the following: Grap all data, and find all Partial matches with rangeOfString function:
NSArray *allResults = [Product MR_findAll];
for (id element in allResults) {
if ([[element valueForKey:#"product_title"] rangeOfString:#"bags" options:NSCaseInsensitiveSearch].location != NSNotFound) {
//I got a partial match, save this instance for later use
}
}
I suggest you read up about predicates. There are key words you're looking for such as startswith, endswith, and like. Check out the predicate programming guide

Resources