MDL-24355 Greatly improved performance of the tag correlation function and fixed up bugs with it
This commit is contained in:
+89
-44
@@ -758,59 +758,104 @@ function tag_cleanup() {
|
||||
* It works as a cache for a potentially heavy load query done at the 'tag_instance' table.
|
||||
* So, the 'tag_correlation' table stores redundant information derived from the 'tag_instance' table.
|
||||
*
|
||||
* @param number $min_correlation cutoff percentage (optional, default is 2)
|
||||
* @param number $mincorrelation cutoff percentage (optional, default is 2)
|
||||
*/
|
||||
function tag_compute_correlations($min_correlation=2) {
|
||||
|
||||
function tag_compute_correlations($mincorrelation = 2) {
|
||||
global $CFG;
|
||||
|
||||
if (!$all_tags = get_records_list('tag')) {
|
||||
return;
|
||||
}
|
||||
$mincorrelation = (int)$mincorrelation;
|
||||
// This mighty one line query fetches a row from the database for every
|
||||
// individual tag correlation. We then need to process the rows collecting
|
||||
// the correlations for each tag id.
|
||||
// The fields used by this query are as follows:
|
||||
// tagid : This is the tag id, there should be at least $mincorrelation
|
||||
// rows for each tag id.
|
||||
// correlation : This is the tag id that correlates to the above tagid field.
|
||||
// correlationid : This is the id of the row in the tag_correlation table that
|
||||
// relates to the tagid field and will be NULL if there are no
|
||||
// existing correlations
|
||||
$sql = "SELECT pairs.tagid, pairs.correlation, pairs.ocurrences, co.id AS correlationid
|
||||
FROM (
|
||||
SELECT ta.tagid, tb.tagid AS correlation, COUNT(*) AS ocurrences
|
||||
FROM {$CFG->prefix}tag_instance ta
|
||||
JOIN {$CFG->prefix}tag_instance tb ON (ta.itemtype = tb.itemtype AND ta.itemid = tb.itemid AND ta.tagid <> tb.tagid)
|
||||
GROUP BY ta.tagid, tb.tagid
|
||||
HAVING COUNT(*) > $mincorrelation
|
||||
) pairs
|
||||
LEFT JOIN {$CFG->prefix}tag_correlation co ON co.tagid = pairs.tagid
|
||||
ORDER BY pairs.tagid ASC, pairs.ocurrences DESC, pairs.correlation ASC";
|
||||
$rs = get_recordset_sql($sql);
|
||||
|
||||
$tag_correlation_obj = new object();
|
||||
foreach($all_tags as $tag) {
|
||||
// Set up an empty tag correlation object
|
||||
$tagcorrelation = new stdClass;
|
||||
$tagcorrelation->id = null;
|
||||
$tagcorrelation->tagid = null;
|
||||
$tagcorrelation->correlatedtags = array();
|
||||
|
||||
// query that counts how many times any tag appears together in items
|
||||
// with the tag passed as argument ($tag_id)
|
||||
$query = "SELECT tb.tagid ".
|
||||
"FROM {$CFG->prefix}tag_instance ta INNER JOIN {$CFG->prefix}tag_instance tb ON ta.itemid = tb.itemid ".
|
||||
"WHERE ta.tagid = {$tag->id} AND tb.tagid != {$tag->id} ".
|
||||
"GROUP BY tb.tagid ".
|
||||
"HAVING COUNT(*) > $min_correlation ".
|
||||
"ORDER BY COUNT(*) DESC";
|
||||
// We store each correction id in this array so we can remove any correlations
|
||||
// that no longer exist.
|
||||
$correlations = array();
|
||||
|
||||
$correlated = array();
|
||||
|
||||
// Correlated tags happen when they appear together in more occasions
|
||||
// than $min_correlation.
|
||||
if ($tag_correlations = get_records_sql($query)) {
|
||||
foreach($tag_correlations as $correlation) {
|
||||
// commented out - now done in query. kept here in case it breaks on some db
|
||||
// if($correlation->nr >= $min_correlation){
|
||||
$correlated[] = $correlation->tagid;
|
||||
// }
|
||||
// Iterate each row of the result set and build them into tag correlations.
|
||||
while ($row = rs_fetch_next_record($rs)) {
|
||||
if ($row->tagid != $tagcorrelation->tagid) {
|
||||
// The tag id has changed so its now time to process the tag
|
||||
// correlation information we have.
|
||||
$tagcorrelationid = tag_process_computed_correlation($tagcorrelation);
|
||||
if ($tagcorrelationid) {
|
||||
$correlations[] = $tagcorrelationid;
|
||||
}
|
||||
// Now we reset the tag correlation object so we can reuse it and set it
|
||||
// up for the current record.
|
||||
$tagcorrelation = new stdClass;
|
||||
$tagcorrelation->id = $row->correlationid;
|
||||
$tagcorrelation->tagid = $row->tagid;
|
||||
$tagcorrelation->correlatedtags = array();
|
||||
}
|
||||
|
||||
if (empty($correlated)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$correlated = implode(',', $correlated);
|
||||
//var_dump($correlated);
|
||||
|
||||
//saves correlation info in the caching table
|
||||
if ($tag_correlation_obj = get_record('tag_correlation', 'tagid', $tag->id, '', '', '', '', 'id')) {
|
||||
$tag_correlation_obj->correlatedtags = $correlated;
|
||||
update_record('tag_correlation', $tag_correlation_obj);
|
||||
} else {
|
||||
$tag_correlation_obj = new stdClass();
|
||||
$tag_correlation_obj->tagid = $tag->id;
|
||||
$tag_correlation_obj->correlatedtags = $correlated;
|
||||
insert_record('tag_correlation', $tag_correlation_obj);
|
||||
}
|
||||
$tagcorrelation->correlatedtags[] = $row->correlation;
|
||||
}
|
||||
// Update the current correlation after the last record.
|
||||
$tagcorrelationid = tag_process_computed_correlation($tagcorrelation);
|
||||
if ($tagcorrelationid) {
|
||||
$correlations[] = $tagcorrelationid;
|
||||
}
|
||||
|
||||
// Close the recordset
|
||||
rs_close($rs);
|
||||
|
||||
// Remove any correlations that weren't just identified
|
||||
delete_records_select('tag_correlation', 'id NOT IN ('.join(',', $correlations).')');
|
||||
}
|
||||
|
||||
/**
|
||||
* This function processes a tag correlation and makes changes in the database
|
||||
* as required.
|
||||
*
|
||||
* The tag correlation object needs have both a tagid property and a correlatedtags
|
||||
* property that is an array.
|
||||
*
|
||||
* @param stdClass $tagcorrelation
|
||||
* @return int The id of the tag correlation that was just processed.
|
||||
*/
|
||||
function tag_process_computed_correlation(stdClass $tagcorrelation) {
|
||||
|
||||
// You must provide a tagid and correlatedtags must be set and be an array
|
||||
if (empty($tagcorrelation->tagid) || !isset($tagcorrelation->correlatedtags) || !is_array($tagcorrelation->correlatedtags)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// The row tagid doesn't match the current tag id which means we are onto
|
||||
// the next tag. Before we switch over we need to either insert or update
|
||||
// the correlation.
|
||||
$tagcorrelation->correlatedtags = join(',', $tagcorrelation->correlatedtags);
|
||||
if (!empty($tagcorrelation->id)) {
|
||||
// The tag correlation already exists so update it
|
||||
update_record('tag_correlation', $tagcorrelation);
|
||||
} else {
|
||||
// This is a new correlation to insert
|
||||
$tagcorrelation->id = insert_record('tag_correlation', $tagcorrelation, true);
|
||||
}
|
||||
return $tagcorrelation->id;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user