Appending value to DynamoDB number set using aws-php-sdk - php

I am using aws-php-sdk with dynamoDB to store a set of unique numerical values for a key.
I want to be able to append a new value to the set - this is easy to achieve with a list however this also results in duplicate values so I would rather use a set.
Because of the way dynamoDB is charged I don’t want to have to read all the values, insert unique value and write the whole set back because that would be very costly when scaled out. Docs seem to suggest appending to a number set is possible but doesn’t give much help in making it work.
$UpdateExpression = "
SET #subscribers = list_append(if_not_exists(#subscribers, :empty_list), :subscriber)
";
// This condition should match if the value already exists.
$ConditionExpression = "not contains (#subscribers,:subscriberStr)";
$ExpressionAttributeNames = [
"#subscribers" => "subscribers"
];
$ExpressionAttributeValues = $marshaler->marshalJson('
{
":subscriber": ['.$subscriber.'],
":empty_list": [],
":subscriberStr": "'.$subscriber.'"
}
');
$tableName = 'publisherRelationships';
$key = $marshaler->marshalJson('
{
"publisher": '.$publisher.'
}
');
$params = [
'TableName' => $tableName,
'Key' => $key,
'UpdateExpression' => $UpdateExpression,
'ConditionExpression' => $ConditionExpression,
'ExpressionAttributeNames' => $ExpressionAttributeNames,
'ExpressionAttributeValues' => $ExpressionAttributeValues
];
$result = $client->updateItem($params);

Related

A csv file has a state, county, and data on each line. How can I use PHP associative arrays to convert to states=>counties=>county=>data

My data looks like:
countyFIPS,County Name,State,stateFIPS,1/22/20,1/23/20,1/24/20,1/25/20,....
1001,Autauga County,AL,1,0,0,0,0,0,0,0,0,....
...
I've been able to retrieve it using an Ajax call and collect it into a simple PHP array, then convert it to json to use in my javascript application. While it appears that the data is all counties of a state, followed by the same configuration for the next state, there is no guarantee that it won't be mixed up in some later set of data.
I'm an old Fortran programmer, and would tend to build a hash table for the "states", then check if the state exists in the hash table. If not create a new hash table and add this empty hash table as the value for the key with the name of the state to the "state" hash table. Then check the state hash table to see if it has a key for the county. Again, if it doesn't, then add an empty array as the value for the key with the county name and add that to the state hash table, then proceed to put the values for that row into the county array. I know this will work, but thought maybe there was some clever way to use associative arrays in PHP to accomplish the same thing.
I look at array_filter, but can't seem to figure out how to adapt it to this case. Are there other functions that might work here?
Then, once I have this structure of
$nested_object = { state1=>{county1,county2,county3...},state2=>{counties}},
and those counties have:
county=>[values],
how can I easily convert this to a json structure? Should it have other keys like "states", and within a state "counties". From looking at Haroldo's question "Convert a PHP object to an associative array" of Dec 3, 2010, it appears like I would use:
$array = json_decode(json_encode($nested_object), true);
Will this give me the structure I am looking for?
I want to end up with a structure that I can ask for the states as a set of keys, then for a selected state ask for the counties in that state as a set of keys, and upon selecting one, get the array of values for that state/county. This has to run on a server with potentially a large amount of data and a moderate amount of hits per unit time so I wanted as reasonably efficient way as possible.
I want to end up with a structure that I can ask for the states as a set of keys, then for a selected state ask for the counties in that state as a set of keys, and upon selecting one, get the array of values for that state/county
Okay, so you need something like:
$structure = [
'AL' => [
'counties' => [
'FIPS1' => 'County1',
'FIPS2' => 'County2',
],
'data' => [
'FIPS1' => [
[ 'date1' => value1, 'date2' => value2, 'date3' => value3... ]
],
],
],
'AK' => [ ... ]
];
You can do that using array_map() and a lambda function writing to $structure, but... in my experience, it is not worth it.
Best to do like you said:
while ($row = get_another_row()) {
$countyFIPS = array_unshift($row);
$countyName = array_unshift($row);
$stateName = array_unshift($row);
$stateFIPS = array_unshift($row);
if (!array_key_exists($stateName, $structure)) {
$structure[$stateName] = [
'counties' => [ ],
'data' => [ ],
];
}
if (!array_key_exists($countyFIPS, $structure[$stateName]['counties'])) {
$structure[$stateName]['counties'][$countyFIPS] = $countyName;
$structure[$stateName]['data'][$countyFIPS] = [ ];
}
// Now here you will have $headers, obtained from the header row unshifting
// the first four fields.
foreach ($headers as $i => $key) {
$structure[$stateName]['data'][$countyFIPS][$key] = $row[$i];
}
}
This way if you add two CSVs with different dates, the code will still work properly. Dates will not be sorted though, but you can do that with a nested array_map and the aksort function.
To output this in JSON, just use json_encode on $structure.

Insert dynamic Object property names into Database

Alright so I have an insert query that I would like to run but the issue I am having is with getting object properties/values that I need to insert.
Say I have a query that looks like the one below.
$this->db->insert('tblitems_in', array(
'platform' => $item['Platform'],
'ram' => $item['RAM'],
'qty' => $item['qty'],
'rate' => number_format($item['rate'], 2, '.', ''),
'rel_id' => $insert_id,
'rel_type' => 'estimate',
'item_order' => $item['order'],
'unit' => $item['unit']
));
This works fine when the person chooses RAM on the webpage which sets the $item Objects property 'RAM' to the value that was picked. Now if they choose HardDrive, that properties name is now sent as 'HardDrive' with the value they chose. Is there a way that i Could replace the 'ram' and 'RAM' from the below example with a variable so I could change what the property name is that I would like to insert and insert into the corresponding db column?
EDIT:
I should have added that the options on the webpage are also dynamically created from a database so I do not know at the time of coding what the property names are. They could be RAM, HardDrive, Processor, maybe even Elephant. I was hoping I could use variables so that I could look at the DB used to create the webpage so that I know the property names and then dynamically add those names into the query.
EDIT:
Right now I am using the following code in order to get all the possible options that can be received from the webpage from a DB the webpages uses to create itself.
$plat_options = $this->db->get('tblplatform_options')->row()->name;
In the database right now it is only populated with names RAM and HardDrive to make things known for testing purposes. So this returns $plat_options = {RAM, HardDrive}. I now have to figure out how to test is $item has these(RAM and HardDrive) as properties and if $item does have them then add them into the query previously shown.
You can set an array of key => variable names, then loop over those values to see if they exist in the $item variable and, if so, add that value to the data to be inserted into the db:
//default array of data to insert
$data = [
'platform' => $item['Platform'],
'qty' => $item['qty'],
'rate' => number_format($item['rate'], 2, '.', ''),
'rel_id' => $insert_id,
'rel_type' => 'estimate',
'item_order' => $item['order'],
'unit' => $item['unit']
];
//Get column names from db
$plat_options = $this->db->get('tblplatform_options')->row()->name;
// $plat_options = [RAM, HardDrive]
//Check if $item[$name] exists. If it does, add that to the
// array of data to be inserted
foreach($plat_options as $key) {
if(array_key_exists($key, $item)) {
$data[$key] = $item[$key];
}
}
$this->db->insert('tblitems_in', $data);
edit
I'm not sure this will work (I don't understand the use case).
It is possible, using array_diff_key to get a list of array keys that exist in $item but not in $data. With this array of keys, you can add the missing keys.
I have altered my previous code to demonstrate this.
You could create the array one element at a time based on whatever field data you received. I used a switch statement, but it could be a simple if/then/else as well.
$data_array = array();
$data_array['platform'] = $item['Platform']
switch($item['Object'] {
case 'HardDrive':
$data_array['harddrive'] = $item['HardDrive'];
break;
case 'RAM':
$data_array['ram'] = $item['RAM'];
break;
}
$data_array['qty'] = $item['qty'];
$data_array['rate' = number_format($item['rate'], 2, '.', '');
$data_array['rel_id'] = $insert_id;
$data_array['rel_type' = 'estimate';
$data_array['item_order'] = $item['order'];
$data_array['unit'] = $item['unit'];
$this->db->insert('tblitems_in', $data_array);

How to merge data in elasticsearch

i want to merge some data in Elasticsearch, but every time it is replacing my previous data and not merging it.
Suppose when i new is created it should add with the previous data, not replacing previous data. So Suppose there is a user exists in the "update_field" named "Christofer" so when i array_merge($usernames) where $usernames contains one or couple of usernames it is always replacing previous data.
I am working on PHP.
$usernames= array ("Johanna", "Maria");
$doc = array();
$doc['update_field'] = array_merge($usernames);
$u_params = array();
$u_params['id'] = 'my_id';
$u_params['index'] = 'my_index';
$u_params['type'] = 'my_type';
$u_params['body'] = array('doc' => $doc);
$client->update($u_params);
For being more clear, as a example let's say in the usernames field there are couple of username exists- like - "Christofer", "Henrik", "Eric".
So now i want to add more user like - "Johanna", "Maria", ...
Now every time i merge and update documents it is replacing the data, like ("Christofer", "Henrik", "Eric") is getting replace by ("Johanna", "Maria").
I want them to be added not replaced.
Do any body knows how can i merge the new data, or just the new data in other process. Thanks in advanced.
You need to use partial update. Try this instead, i.e. you need to send a doc hash in the body with the fields to marge (i.e. update_fields):
$params = [
'index' => 'my_index',
'type' => 'my_type',
'id' => 'my_id',
'body' => [
'doc' => [
'update_field' => array_merge($usernames)
]
]
];
$client->update($params);
UPDATE
That's right, core values and arrays are getting replaced.
You may want to try scripted partial update then
$usernames= array ("Johanna", "Maria");
$script = array();
$script['script'] = 'ctx._source.update_field += new_value';
$script['params'] = array('new_value' => array_merge($usernames));
$u_params = array();
$u_params['id'] = 'my_id';
$u_params['index'] = 'my_index';
$u_params['type'] = 'my_type';
$u_params['body'] = $script;
$client->update($u_params);
And make sure that scripting is enabled in your elasticsearch.yml config file:
script.disable_dynamic: false

QuickBase foreach insert

I'm having an issue using the Quickbase API to perform the following:
SELECT 1, 2, 3 FROM table AA that has column BB = 1
foreach record {
Insert 1, 2, 3 into table ZZ.
}
function add_children($opportunity_id) {
global $config;
$qbc = new QuickBase($_SESSION['qb_username'] ,
$_SESSION['qb_password'],
true,
$config['AA'],
$config['debug'],
$config['app_token']);
$xml = $qbc->do_query("{'" . $config['AA'] . "'.EX.''}", 0, 0, 'a', 0, '', '');
$records = array();
foreach($xml->record as $record) {
$r = array();
$r['record_id'] = $record->record_id_;
$r['account_number'] = $record->account_number;
$records[] = $r;
$xml = $qbc->add_record($records[]);
}
}
First, I'm assuming that you're using this PHP SDK by QuickbaseAdmirer https://github.com/QuickbaseAdmirer/QuickBase-PHP-SDK. There are a few potential problems with your code.
Double check that your constructor is correct. Unless you've modified it, the Quickbase constructor in the SDK (again that I'm assuming you're using) takes user name, password, xml, database id, and then token in that order. Whatever value is in $config['debug'] may be taken as the token and the value of $config['app_token'] may be taken as your realm. Also, $config['AA'] as used in the constructor should be a string of random seeming characters like "bbqn1y5qv". Here's the constructor in the SDK for reference:
public function __construct($un, $pw, $usexml = true, $db = '', $token
= '', $realm = '', $hours = '')
Your query $xml = $qbc->do_query("{'" . $config['AA'] . "'.EX.''}", 0, 0, 'a', 0, '', ''); is not returning any records because $config['AA'] is both being used as your DBID (in the constructor) and your field ID in the query. The DBID must be a string and the field ID must be an integer that corresponds to the field you're making the query for. For example, if you wanted to return records created today your query would be '{1.IR.today}' because 1 is always the field ID for date created. It's also not returning any records because the SDK requires queries be passed as an array of arrays. So, my records created today query needs to be rewritten as:
$query= array(
array(
'fid' => '1',
'ev' => 'IR'),
'cri' => 'today'),
);
You'll also need to pass a string of period separated values to the clist parameter of the method or leave it blank for the table defaults. For example, if I wanted to get the date created and record ID for all records in this table sorted by date ascending, I would use this:
$query= array(
array(
'fid' => '3',
'ev' => 'GT'),
'cri' => '0'),
);
$xml = $qbc->do_query($query, '', '', '1.3', '1', '', 'sortorder-A');
You can read up more on the Quickbase API, and do_query specifically, here http://www.quickbase.com/api-guide/index.html#do_query.html
The add record API call takes pairs of field IDs and values. The SDK handles that by taking an array of arrays with 'fid' and 'value' pairs. Assuming you want to put the value of $record->record_id_ in field #37 and $record->account_number in field #30 your code should look like this:
foreach($xml->record as $record) {
$records= array(
array(
'fid' => '37', //Whatever field you want to store the value to
'value' => $record->record_id_),
array(
'fid' => '30',
'value' => $record->account_number),
);
$xml = $qbc->add_record($records);
}
Throw in a print_r($xml); at the end and you can see any response from Quickbase for debugging. You should get something like this for a success:
SimpleXMLElement Object ( [action] => API_AddRecord [errcode] => 0 [errtext] => No error [rid] => 81 [update_id] => 1436476140453 )
The way your code is presented, you may not get the results you expect. Your do query and add record method calls are performed on the same table and that isn't normally what someone would want. Usually, the goal is to perform a do query on one table and then use that data to add records in a different table. If that's the case, you'll need to change the database ID in your $qbc object before you preform the add record call. This is easy enough to do with $qbc->set_database_table('DBID'); where DBID is the target table ID (which should be a string of random seeming characters like "bbqn1y5qv").
Best of luck!

CakePHP: How to use Find method + AJAX request with possibly empty search parameters

I'm working with CakePHP v2.3.x and on an edit page I need to dynamically update the page with search results...
I'm making an AJAX call from one of my Views/Tests/admin_edit.php view page to a specific action in my QuestionsController.php.
Here's the action (so far) that handles the request:
public function admin_search() {
if ($this->request->is('post')) {
$searchdata = $this->request->data;
$r = $this->Question->find('all', array('conditions' => array('Question.id' => $searchdata['id'])));
echo json_encode($r);
exit;
}
}
It currently only returns questions whose IDs match the one entered by the user, but the finished version will search several different fields. I know how to do this by adding additional key/value pairs to the conditions array. However, I don't know how to make those fields optional. What if the user enters the question name, but NOT the id, or visa versa? Is there a configuration so that CakePHP will ignore any empty field conditions?
Similarly, is there a way to set the operator so that, for example, I could match substrings or integer ranges? Update: I found this in the docs.
I would just remove any empty entries yourself first.
So let's say you have a $searchdata array with three optional fields, one of which is blank. First build your conditions array:
$searchdata = array("id" => 1, "name" => "", "type" => "foo");
$conditions = array('Question.id' => $searchdata['id'], 'Question.name' => $searchdata['name'], "Question.type" => $searchdata['type']);
(Or if you want to get fancy)
foreach($searchdata AS $key => $value) $conditions['Question.' . $key] = $value;
Now clean up $conditions, get rid of empty values:
$conditions = array_filter($conditions);
Tada:
$r = $this->Question->find('all', array('conditions' => $conditions));
See http://3v4l.org/JN6PA

Categories