How to SORT a SET and GET the full HASH - redis

I'm new to Redis and I have to say I love it till now :)
I'm bumping into an issue I'm not sure how to solve it in the more efficient way.
I have a SET of HASH. Each HASH describe a post.
Here is the code to create and store the HASH:
// Create the HASH
$key = 'post:'.$post->getId();
$this->redis->hSet($key, 'created', $post->getCreated());
$this->redis->hSet($key, 'author', $post->getAuthor());
$this->redis->hSet($key, 'message', $post->getMessage());
// Store the HASH in the SET
$this->redis->sAdd('posts', $post->getId());
Now, previously I was storing all the post's attributes in a data field of the HASH (json_encoded) and I was fetching the information like this:
$key = 'posts';
$data = $this->redis->sort($key, array(
'by' => 'nosort',
'limit' => array($offset, $limit),
'get' => 'post:*->data '
));
if (!is_array($data)) {
return array();
}
foreach ($data as &$post) {
$post = json_decode($post, true);
}
It was working great, I had all the posts information :)
But I had conflicts when updating the post in Redis (concurrent updates), so I've decided to have all the post's attributes in separated fields of the HASH and it fixed my issue of conflicts.
Now the problem I have is to fetch the HASH from my SET. Do I have to specify every single fields like this:
$key = 'posts';
$data = $this->redis->sort($key, array(
'by' => 'nosort',
'limit' => array($offset, $limit),
'get' => array('post:*->created', 'post:*->author', 'post:*->message')
));
Or is there another way to fetch the full HASH directly within the SET?
I heard about pipeline but I'm not sure it's what I'm looking for and if I can use it with phpredis
Cheers, Maxime
UPDATE
I'm not sure I explained myself clearly. I have some elements in a set (post_id).
I want to get the first 10 posts of the SET, which means I want 10 hash (with all their fields and value) in order to build a post object.
I was previously storing all the object information in one field of the hash (data), now I have one field per attribute of the object.
before:
myHash:<id> data
now:
myHash:<id> id "1234" created "2010-01-01" author "John"
Before I was using SORT to fetch the top 10 posts (and paginate easily), like this:
$key = 'posts';
$data = $this->redis->sort($key, array(
'by' => 'nosort',
'limit' => array(0, 10),
'get' => 'post:*->data '
));
Now that I have X members to my hash I'm wondering what is the best solution.
Is it:
$key = 'posts';
$data = $this->redis->sort($key, array(
'by' => 'nosort',
'limit' => array($offset, $limit),
'get' => 'post:*->data '
));
Or maybe:
$key = 'posts';
$data = $this->redis->sort($key, array(
'by' => 'nosort',
'limit' => array($offset, $limit),
'get' => '#'
));
foreach($data as $post_id) {
$posts[] = $this->redis->hGetAll('post:'.$post_id);
}
Or finally:
$key = 'posts';
$data = $this->redis->sort($key, array(
'by' => 'nosort',
'limit' => array($offset, $limit),
'get' => '#'
));
$pipeline = $this->redis->multi();
foreach ($data as $post_id) {
$pipeline->hGetAll('post:'.$post_id);
}
return $pipeline->exec();
Or something else that I don't know yet?
What is the best, faster way to do this?

If you have read redis's source , you'll find that is not possible. There is a workaround that using lua script to combine 'sort' and 'hgetall' commands in a single redis invocation.
The 'get pattern' is processed by function 'lookupKeyByPattern'.
https://github.com/antirez/redis/blob/unstable/src/sort.c#L61

If you start with the redis.io documentation on hashes you'll find there are commands which allow you to get multiple hash members. In particular "HGETALL" for pulling all fields and values, or "HMGET" for pulling a set of fields with their values.
Additionally, for setting them I would recommend setting them in one pass with "HMSET"

Related

Update Post status when post is expire

I want to update post status when a post is expiring.
I have saved expiry date in the WordPress post meta (post_price_plan_expiration_date).
I know how to get an expired post with wp_query,
But I want to use SQL query to update post status.
$todayDate = strtotime(date('m/d/Y h:i:s'));
$args = array(
'post_type' => 'post',
'posts_per_page' => -1,
'meta_query' => array(
array(
'key' => 'featured_post',
'value' => '1',
'compare' => '=='
),
array(
'key' => 'post_price_plan_expiration_date',
'value' => $todayDate,
'compare' => '<='
),
)
);
$wp_query = new WP_Query($args);
print_r($wp_query);
This code returns me correct posts which I need, But I need to write the same query in SQL, And run that with wp_schedule_event
Any help???
You can always do the following out of a WP_Query
$wp_query = new WP_Query( $args );
echo $results->request;
Which should display the generated SQL Query.
Hope this helps.

Problems returning result of CDbCriteria based query

I have a query as follows
$criteria1 = new CDbCriteria();
$criteria1->condition = 'id = 1';
$modelA=Table1::model()->find($criteria1);
I can pass it to a view and return the title and entry
$this->widget('bootstrap.widgets.TbBox', array(
title' => $modelA['title'],
'content' => $modelA['entry'] ));
Now I'd like to return a range of entries
$criteria2 = new CDbCriteria();
$criteria2->condition = 'id > 7';
$modelB=Table1::model()->findAll($criteria2);
(btw : I'm following a form as laid out here). I was expecting to be able to read the resulting array of values out as below, but ['title'] is now being seen as a undefined index (obviously I'm expecting to read this out in a loop but you get the point)
$this->widget('bootstrap.widgets.TbBox', array(
'title' => $modelB['title'][0],
'content' => $modelB['entry'][0]));
Where am I going wrong?
Thanks
No, the indexes should be specified in the different order: the number of a specific element first, then the name of the property. Additionally, it's better (=cleaner) to name the result of findAll so it'll show you (and any other reader) that it's a collection, not a single model:
$models = Table1::model()->findAll($criteria2);
// ...
$this->widget('bootstrap.widgets.TbBox', array(
'title' => $models[0]['title']
//...
));
But even that's not necessary if you use foreach (and you probably will):
foreach ($models as $model):
// ...
$this->widget('some.name', array(
'title' => $model['title']
);
endforeach;

Set specific node id's when importing data into drupal 7

I'm using the feeds module to import my existing data into Drupal 7 and it works great but I have one issue with the nids it generates.
I want these to match my existing site id's then I can have a nice clean transition between old and new keeping even the same urls.
Two approaches here;
1. Somehow assign these nid's as part of the import.
2. Renumber the nid's after import.
I can't find any module or other code in google to do either so looks like I will have to hack something together myself... has anyone done this before?
-
Could it be as simple as updating all these?
SELECT table_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name = 'nid'
comment
history
location_instance
node
node_access
node_comment_statistics
node_counter
node_revision
search_node_links
taxonomy_index
edit: and these...
SELECT table_name
FROM INFORMATION_SCHEMA.COLUMNS
WHERE column_name = 'entity_id'
feeds_item
field_data_body
field_data_comment_body
field_data_field_address
field_data_field_image
field_data_field_state
field_data_field_tags
field_data_field_type
field_data_field_website
field_revision_body
field_revision_comment_body
field_revision_field_address
field_revision_field_image
field_revision_field_state
field_revision_field_tags
field_revision_field_type
field_revision_field_website
Here's what I did in the end...
It seems to all be working correctly, but please be very carefully and make a backup (like I did) before doing anything like this.
header('Content-type: text/plain');
global $database, $tables, $prefix;
$database = // your database
$prefix = 'drupal_';
$tables = array (
'comment' => 'nid',
'history' => 'nid',
'location_instance' => 'nid',
'node' => 'nid',
'node_access' => 'nid',
'node_comment_statistics' => 'nid',
'node_counter' => 'nid',
'node_revision' => 'nid',
'search_node_links' => 'nid',
'taxonomy_index' => 'nid',
'feeds_item' => 'entity_id',
'field_data_body' => 'entity_id',
'field_data_comment_body' => 'entity_id',
'field_data_field_address' => 'entity_id',
'field_data_field_image' => 'entity_id',
'field_data_field_state' => 'entity_id',
'field_data_field_tags' => 'entity_id',
'field_data_field_type' => 'entity_id',
'field_data_field_website' => 'entity_id',
'field_revision_body' => 'entity_id',
'field_revision_comment_body' => 'entity_id',
'field_revision_field_address' => 'entity_id',
'field_revision_field_image' => 'entity_id',
'field_revision_field_state' => 'entity_id',
'field_revision_field_tags' => 'entity_id',
'field_revision_field_type' => 'entity_id',
'field_revision_field_website' => 'entity_id'
);
// Move all nids +10000 (out of the way)
$query = "SELECT nid FROM {$prefix}node WHERE nid < 10000 ORDER BY nid";
echo "$query\n";
$result = $database->query($query);
while($data = $result->fetchRow()) {
echo "Processing nid: {$data['nid']}\n";
changeNodeId($data['nid'], $data['nid'] + 10000);
}
// Move all nids to match guids
// (I originally imported through the feeds module, so used the guids to reorder here, but you can use your own logic as required...)
$query = "SELECT guid, entity_id FROM {$prefix}feeds_item WHERE guid <> entity_id ORDER BY ABS(guid)";
echo "$query\n";
$result = $database->query($query);
while($data = $result->fetchRow()) {
echo "Processing guid: {$data['guid']} (nid: {$data['entity_id']})\n";
changeNodeId($data['entity_id'], $data['guid']);
}
function changeNodeId($old, $new)
{
global $database, $tables, $prefix;
echo "Updating nid: {$old} -> {$new}\n";
// Check new doesn't already exist
$query = "SELECT * FROM {$prefix}node WHERE nid={$new}";
$result = $database->query($query);
if ($result->fetchRow()) {
echo "Error nid: {$new} already exists!\n";
return;
}
foreach ($tables as $table => $column)
{
$query = "UPDATE {$prefix}{$table} SET {$column} = {$new} WHERE {$column} = {$old}";
echo "$query\n";
$database->query($query);
}
}
Notes.
The tables listed above worked for me, it will almost definitely be different for you depending on what modules you have installed.
This will break your menus and any URL aliases you have set, so you have to go through manually afterwards and fix these up, not a major though.
Also good idea to reset your auto increment id on the node table. ALTER TABLE node AUTO_INCREMENT = X where X is 1 greater than the highest nid.
If your source contains the original nids, you can just set a mapping in your feed importer to assign the nid. This way there should be no reason to manipulate the db subsequent to the import process, as each node will simply be assigned the same nid.
Obviously this may break if you have existing nodes in your site with the same nids.

WP_Query() and ordering items

I have to order items from wp_post by their menu_order (pages only).
I've written this line:
$query = new WP_Query();
$all = $query -> query(array('post_type' => 'page', 'posts_per_page' => -1, 'orderby' => 'menu_order', 'order' => 'DESC'));
$children = get_page_children($id, $all);
foreach ($children as $child) {
if ($child -> ID == get_the_id()) {
echo '<li class="active">' . $id . $child -> post_title . '</li>';
I see the items but they are not ordered.
Thanks.
FZ
I would say that the issue relates to the fact you are passing the $all object through get_page_children before using it in a loop.
Why don't you just forget get_page_children and add 'post_parent' to your list of WP_Query arguments?
$all = $query->query(array('post_type' => 'page', 'post_parent' => $id, 'posts_per_page' => -1, 'orderby' => 'menu_order', 'order' => 'DESC'))
A helpful thread on Wordpress Stack Exchange;
https://wordpress.stackexchange.com/questions/35123/wp-get-all-the-sub-pages-of-the-parent-using-wp-query
EDIT.
To expand on comments. Sometimes there's confusion about menu_order - it does not relate to wp_nav_menus but to the Page Attributes Order input box, as shown below;

Accessing magento's checkout/cart using REST API

I'm trying to integrate a cart-synchronisation-solution for my rest-clients.
The goal should be that I can have the same cart wherever I access my store from.
So I'll have to first of all deliver the existing items out to the client using the authenticated api-user.
But I get stuck at the very beginning:
protected function _retrieveCollection()
{
$cart = Mage::getSingleton('checkout/cart')->getQuote();
$cart->setCustomerId($this->getApiUser()->getUserId());
$cart->setStoreId(4);
$cart->load();
return $cart->getAllItems();
}
returns an empty array even though I have products in my cart.
Anyone any hints? Have that feeling I'm totally on the wrong side...
Found a solution. Getting the quote by Customer which is the other way around worked pretty well:
Mage::app()->setCurrentStore(4);
$cart = Mage::getModel('sales/quote')->loadByCustomer($this->getApiUser()->getUserId());
$items = array();
foreach ($cart->getAllVisibleItems() as $key => $item) {
$items[] = array(
'name' => $item->getName(),
'entity_id' => $item->getProductId(),
'description' => $item->getDescription(),
'final_price_with_tax' => $item->getBasePriceInclTax(),
'qty' => $item->getQty()
);
}