DynamoDB query using search key conditions throwing an odd exception (using PHP) - php

I'm querying a DynamoDB table using the method described here in the AWS PHP developer guide.
The array that I pass to the Dynamo connection looks like the following:
Array(
[ConsistentRead] => false
[TableName] => bbq_lol_test
[KeyConditions] => Array(
[stinky_cheese] => Array(
[ComparisonOperator] => EQ
[AttributeValueList] => Array(
[S] => camembert)
)
)
)
As you can see, it's in the exact format that the example uses, with the exception of the Type enum (which is just a string).
When executing $connection->query with the above array as an argument, I get the following exception:
Guzzle\Service\Exception\ValidationException:
Validation errors: [KeyConditions][stinky_cheese][AttributeValueList][S][AttributeValue] must be of type object
Which is odd, because the array in the stack trace appears to be fine:
Aws/Common/Client/AbstractClient.php(103): Guzzle\Service\Client->__call("Query", array(array("ConsistentRead" => false, "TableName" => "bbq_lol_test", "KeyConditions" => array("stinky_cheese" => array("ComparisonOperator" => "EQ", "AttributeValueList" => array("S" => "camembert"))))))
I DO have an index on the stinky_cheese field (since I'm using query). I'm probably doing something daft, but I can't seem to figure it out now. Any help would be appreciated - thanks!

There were two problems.
First, AttributeValueList should be an array of arrays, not a single array, going from this:
[AttributeValueList] => Array([S] => camembert)
To this:
[AttributeValueList] => Array(Array([S] => camembert))
Since it's possible to add more stuff in there, like so:
[AttributeValueList] => Array(Array([S] => camembert), Array([S] => bleu))
The second problem was that I always have to query using the primary hash key, which I was not doing.

Related

Get value from an array PHP - illegal offset

This is the print_r from the $form_data variable / array in php log.
[28-Sep-2018 18:04:03 UTC] Array
(
[cfdb7_status] => unread
[meno] => data
[email] => data
[telefon] => phone
[meno-ucastnika__1] => data
[email-ucastnika__1] => data
[meno-komory__1] =>
[registracne-cislo__1] =>
[_wpcf7_groups_count] => Array
(
[emails] => 1
)
[obchodne-meno] => obchod
[obchodne-sidlo] => fs
[ico] => 50426508
[dic] => dic
[icdph] => icdicko
)
How can I get the value of _wpcf7_groups_count key?
If I want email I simply wrote $form_data['email']. Everything goes like this except _wpcf7_groups_count.
$form_data['_wpcf7_groups_count']
$form_data['_wpcf7_groups_count'][0]['emails']
$form_data['_wpcf7_groups_count']['emails']
Anything from above doesn't work. The first is giving me an illegal offset.
From the data you posted(*),
$form_data['_wpcf7_groups_count']['emails']
should work, and yield 1.
Note that the parent key value is an array, so if the form data goes through some sort of templating engine, the problem might lie there.
I find it strange that you get an error for the first method, and not for the others: in PHP, if you can't reference an array key, you cannot reference any of the descendants, and you still get the error from the parent key. This is what makes me suspect that something else is afoot.
==========
(*) I assumed that you have deleted some information while keeping the formatting. Otherwise writing
[meno-komory__1] =>
[registracne-cislo__1] =>
could possibly be interpreted as a nested key. I saw no 'Array', so I assumed there was just some data missing. But next time write it explicitly to avoid any ambiguity:
[meno-komory__1] => "(redacted)",
[registracne-cislo__1] => "(array, redacted)",

Renaming array keys that differ on each run

This is driving me mad.. I have a PHP script that returns an array in the form $key => $value and I want to rename the key so that I can display it in a table header. I saw there are several ways of doing this but I'm not sure they are what I need... Either that or I haven't understood the examples correctly which is the likely problem.
Basically my array keys differ each time I iterate over a foreach loop and also some can be blank. How can I get round this?
The first output might look like this:
'_can_chaccess' => false,
'_can_chown' => false,
'_can_delete' => false,
'_can_modify' => false,
'_can_read' => true,
'assigned_to_name_879' => 'Unassigned',
'id' => 1,
'type' => 'Private::Reporting::DataViewModel::DataView_223_42858',
'type_877' => 'Email',
The next run through, I might get this:
'_can_chaccess' => false,
'_can_chown' => false,
'_can_delete' => false,
'_can_modify' => false,
'_can_read' => true,
'assigned_to_name_793' => 'Consultants',
'id' => 1,
'object_reference_794' => 'CASE-1004',
'summary_795' => 'Deployment of New System for HQ (Project)',
'type' => 'Private::Reporting::DataViewModel::DataView_200_42858',
),
As you can see, some keys rename the same e.g. id, type. But the most important ones that I am interested in change each time e.g. Assigned To Name.
Any ideas?
Where do you receive your data from?
You can either somehow modify the source of your data, so if it were a query (what I do not assume here), you have the SELECT ... AS ... statement.
First you do need to know how to interpret the changing keys. If e.g. "assigned_to_name_879" and "assigned_to_name_793" is the same field, you can define a canonical function, which mapps both inputs to a unique output.
The output of the cannonical function and as well the other array keys can serve as keys for an additional array, which contains the table headers of your output.
So your current array is the value's array, and by hand you define a header's array:
array(
'assigned_to_name_879' => 'Name assignment'
);
This dynamic way of storing the table headers in an array only makes sense if you are using the array twice. Otherwise you could simply write the header in the html-code which you do output.
I've managed to figure it out using the below:
$mappings_array = array();
foreach ($report['data'][0] as $key => $value) {
$workbooks->log('Old Key', $key);
preg_match_all('([^_\d]+)', $key, $new_key);
$workbooks->log('New Key', $new_key);
$str = implode(" ", $new_key[0]);
$capitalised = ucwords($str);
array_push($mappings_array,$capitalised);
}
Maybe it's not the best solution but it works :) I get the following output:
> New array: «array (
0 => 'Can Chaccess',
1 => 'Can Chown',
2 => 'Can Delete',
3 => 'Can Modify',
4 => 'Can Read',
5 => 'Id',
6 => 'Total Type',
7 => 'Type',
8 => 'Type',
)

How is it possible to correctly nest custom fields in a CakePHP find query?

we're often dealing with a code that looks like the following:
return $this->find(
'all', [
'fields' => ['DISTINCT Tag.tag', 'COUNT(Tag.tag) as count'],
'group' => ['Tag.tag'],
'order' => ['count' => 'DESC']
]);
This query leads us to the following output:
[0] => Array
(
[Tag] => Array
(
[tag] => walls.io
)
[0] => Array
(
[count] => 15
)
)
As you can see, the query returns the results in a "somehow wrong" nesting. The "count" field is unfortunately put into a pseudo [0]-array.
IIRC, CakePHP uses internally a syntax like Tag__field for correctly nesting virtual fields.
When changing the code to the Model__-syntax, the problem stays the same:
return $this->find(
'all', [
'fields' => ['DISTINCT Tag.tag', 'COUNT(Tag.tag) as Tag__count'],
'group' => ['Tag.tag'],
'order' => ['COUNT(Tag.tag)' => 'DESC']
]);
Output:
[0] => Array
(
[Tag] => Array
(
[tag] => walls.io
)
[0] => Array
(
[Tag__count] => 15
)
)
Workaround 1: array_map
CakePHP pros: Is there a better/more elegant solution than manually mapping the array after the select statement?
$tags = array_map(function($tag) {
$tag['Tag']['count'] = $tag[0]['count'];
unset($tag[0]);
return $tag;
}, $tags);
Workaround 2: virtual field
As described above, the usage of a virtual field might solve this problem:
$this->virtualFields = ['count' => 'COUNT(Tag.Tag)'];
return $this->find(
'all', [
'group' => ['Tag.tag'],
'order' => [$this->getVirtualField('count') => 'DESC']
]);
Unfortunately, with this solution, it's not possible to specify ANY fields at all. only by completely leaving the "fields"-key, the nesting of the array works as expected. when selecting $fields = ['Tag.tag', $this->getVirtualField('count')] the nesting is wrong again.
CakePHP pros: Do you know a method, where the nesting is done right, even if you specify your own fields?
Looking at the CakePHP code, such a method does not exist.
Have a look at the file lib/Cake/Model/Datasource/Database/Mysql.php.
Find the method called: Mysql::resultSet( $results ); (around line 240).
That method maps the result to an array. To determine if a column is part of a table or not it uses PDOStatement::getColumnMeta(). For your "virtual column" that method will return an empty table and so the CakePHP code will put it separately, see the else branch
$this->map[$index++] = array(0, $column['name'], $type);
In order to avoid that else branch you would have to use Virtual fields, but then you run into the other problems that you have noticed.
So you are left with the array_map solution or you could try to overload that Mysql class and add your custom logic at how to identify where a column fits.

DyanmoDB: not getting the answer I'm expecting using batch_get_item

I am trying to do a batch_get_item to request multiple items from a table. I am following the PHP example in the DynamoDB documentation, but I am not getting the results I'm expecting.
Following is the code:
$batch_array = array ();
$batch_array[]= array ('HashKeyElement' =>
array( AmazonDynamoDB::TYPE_STRING => 'V1L3M5O5L1W8R5B6D2Q1S8V0B3R8M7A6R0X0'));
$options = array (
'RequestItems' => array(
'profile_dev' => array (
'Keys' => $batch_array
)
)
);
$result = $this->db->batch_get_item($options);
Instead of getting the data, I am getting a very long response, and I'm including the relevant information from the tail end of it:
[x-aws-body] => {"RequestItems":{"profile_dev":{"Keys":[{"HashKeyElement":{"S":"V1L3M5O5L1W8R5B6D2Q1S8V0B3R8M7A6R0X0"}}]}}} ) [body] => CFSimpleXML Object ( [__type] => com.amazon.coral.validate#ValidationException [message] => One or more parameter values were invalid: The provided key size does not match with that of the schema ) [status] => 400 ) )
The hashKey for this table is a string. It has a rangeKey, but I am using the hashKey so I can get all the rows matching the hashKey. What am I missing?
The DynamoDB documentation (and SDK samples) have colossal bugs in them. The documentation, and actual SDK code, make use only of the hashKeyElement, but in fact if a table has both a hashKey AND a rangeKey, both must be used.
When I used both the hashKey and the rangeKey, the call worked.
Get (or batch get) requires you to completely define the key of all items you are getting. If you want to retrieve all rows with the same hashKey using a single call, it seems like you're looking for Query.
You don't need to use BatchGet, you should be using Query. Here is an example using the PHP SDK to get all items with the HASH key 'YourHashKey' on table 'YourTable'
// Instantiate the class
$dynamodb = new AmazonDynamoDB();
$response = $dynamodb->query(array(
'TableName' => 'YourTable',
'HashKeyValue' => array( AmazonDynamoDB::TYPE_STRING => 'YourHashKey' ),
));
Reference: http://docs.amazonwebservices.com/amazondynamodb/latest/developerguide/LowLevelPHPQuerying.html

Mongodb and PHP: how to query nested arrays without using the key name

I'm probably missing something simple here but I can't seem to find a way to build a query that will allow me to update a match in a group of nested values.
I have a document like this for a blog app I've been working on (currently uses MySQL):
array (
'_id' => new MongoId("4bc8dcee8ba936a8101a0000"),
'created' => '20100418-201312 +0000',
'post-title' => 'Some Post Title',
'post-body' => 'Blah Blah Blah Blah.',
'post-blog-name' => 'default',
'post-comments' =>
array (
0 =>
array (
'comment-title' => 'Test1',
'comment-body' => 'asdf1',
'created' => '20100418-214512 +0000',
'owner' => 'User1',
),
1 =>
array (
'comment-title' => 'Test2',
'comment-body' => 'asdf2',
'created' => '20100418-214512 +0000',
'owner' => 'User2',
),
),
'owner' => 'zach',
'updated' => '20100418-201312 +0000',
)
I'd like to be able to build a query that can search 'comment-title' for a match and then allow me to update/change/delete data as needed.
Obviously I can perform an update using a query which includes the key value. Something like this works:
$collection->update(
array("post-comments.0.comment-title" => $_POST['comment-title']),
array('$set' => array('entries.0' => array('comment-title' => $_POST['comment-title'], 'comment-body' => $_POST['comment-body'], 'owner' => $_SESSION['username'], 'updated' => gmdate('Ymd\-His O')))));
But I expect I'm missing something that would allow me to leave out the key and still be able to match one of the nested arrays based on a value (in this example the 'comment-title').
Anyway, sorry, this probably isn't the best example and I probably will end up using the keys in comments to identify them (comment #) but since nesting and creating rather complex objects seem to be a few of Mongodbs strong points I'm just hoping someone can point out what it is I might be missing.
A query to remove or update all comments by a specific user (say a user the blog author just black-listed) might be a better example. I'm not sure how I'd do this short of pulling out the entire document and then iterating through the nested arrays using PHP.
try ... notice I removed the "key"
$collection->update(array("post-comments.comment-title" ...
Cheers!

Categories