Plugin Directory

Changeset 3743366


Ignore:
Timestamp:
10/13/2026 09:21:15 AM (3 weeks ago)
Author:
xpil
Message:

Release 1.0.16: align graph articles with current YARPP website results and fix table-cache warm-up

Location:
article-relationship-graph-for-yarpp
Files:
10 edited
1 copied

Legend:

Unmodified
Added
Removed
  • article-relationship-graph-for-yarpp/tags/1.0.16/CHANGELOG.md

    r3684388 r3743366  
    11# Changelog
     2
     3## 1.0.16
     4
     5- Warm YARPP's persistent table cache using its configured limits before rebuilding.
     6- Select graph articles from current YARPP website results, preserving ordering and omitting reverse-only and second-degree matches.
     7- Render focus connections immediately when matching articles are absent from the stored graph.
     8- Allow up to 15 related articles plus the focus node; the node-limit filter continues to cap total nodes.
    29
    310## 1.0.11
  • article-relationship-graph-for-yarpp/tags/1.0.16/article-relationship-graph-for-yarpp.php

    r3684388 r3743366  
    33 * Plugin Name: Article Relationship Graph for YARPP
    44 * Description: Adds an interactive graph of related articles below singular posts using YARPP's local table cache.
    5  * Version: 1.0.15
     5 * Version: 1.0.16
    66 * Author: Ignormatyk
    77 * Requires at least: 6.2
    … …  
    1515defined( 'ABSPATH' ) || exit;
    1616
    17 define( 'ARTICLE_RELATIONSHIP_GRAPH_VERSION', '1.0.15' );
     17define( 'ARTICLE_RELATIONSHIP_GRAPH_VERSION', '1.0.16' );
    1818define( 'ARTICLE_RELATIONSHIP_GRAPH_FILE', __FILE__ );
    1919define( 'ARTICLE_RELATIONSHIP_GRAPH_DIR', plugin_dir_path( __FILE__ ) );
  • article-relationship-graph-for-yarpp/tags/1.0.16/includes/class-article-relationship-graph-builder.php

    r3684388 r3743366  
    287287                }
    288288                if ( function_exists( 'yarpp_get_related' ) ) {
    289                         yarpp_get_related( array( 'limit' => 7 ), $post_id );
     289                        // Use YARPP's defaults so this warms the persistent table cache.
     290                        yarpp_get_related( array(), $post_id );
    290291                }
    291292        }
  • article-relationship-graph-for-yarpp/tags/1.0.16/includes/class-article-relationship-graph-repository.php

    r3684388 r3743366  
    44
    55final class ARTICLE_RELATIONSHIP_GRAPH_Repository {
    6         const MAX_NODES = 15;
     6        const MAX_RELATED = 15;
     7        const MAX_NODES = self::MAX_RELATED + 1;
    78
    89        private $wpdb;
    … …  
    3132                $selected = array( $anchor_id => true );
    3233                $depths = array( $anchor_id => 0 );
    33                 $frontier = array( $anchor_id );
    34                 while ( $frontier && count( $selected ) < $cap && $this->generation ) {
    35                         $candidates = $this->frontier_candidates( $frontier, $selected );
    36                         if ( ! $candidates ) {
    37                                 break;
     34                $related = $this->related_posts( $anchor_id, $cap - 1 );
     35                foreach ( $related as $related_post ) {
     36                        $id = (int) $related_post->ID;
     37                        if ( $id === $anchor_id || ! $this->post_policy->is_public_supported_post( $related_post ) ) {
     38                                continue;
    3839                        }
    39                         usort( $candidates, static function ( $left, $right ) {
    40                                 return $right['weight'] <=> $left['weight'] ?: $left['id'] <=> $right['id'];
    41                         } );
    42                         $next = array();
    43                         $next_depth = $depths[ $frontier[0] ] + 1;
    44                         foreach ( array_slice( $candidates, 0, $cap - count( $selected ) ) as $candidate ) {
    45                                 if ( isset( $selected[ $candidate['id'] ] ) ) {
    46                                         continue;
    47                                 }
    48                                 $selected[ $candidate['id'] ] = true;
    49                                 $depths[ $candidate['id'] ] = $next_depth;
    50                                 $next[] = $candidate['id'];
    51                         }
    52                         $frontier = $next;
     40                        $selected[ $id ] = true;
     41                        $depths[ $id ] = 1;
    5342                }
    5443
    … …  
    6049                }
    6150
    62                 return ARTICLE_RELATIONSHIP_GRAPH_Graph_Response::build( $anchor_id, $nodes, $this->induced_edges( $surviving_ids ), $cap );
     51                $edges = $this->induced_edges( $surviving_ids );
     52                $edges = $this->complete_focus_edges( $anchor_id, $surviving_ids, $related, $edges );
     53                return ARTICLE_RELATIONSHIP_GRAPH_Graph_Response::build( $anchor_id, $nodes, $edges, $cap );
    6354        }
    6455
    … …  
    7364        }
    7465
    75         private function frontier_candidates( array $frontier, array $selected ) {
    76                 $wpdb = $this->wpdb;
    77                 $id_placeholders = implode( ',', array_fill( 0, count( $frontier ), '%d' ) );
    78                 // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- The placeholder list is generated from the bounded integer input immediately above.
    79                 $query = $wpdb->prepare(
    80                         // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- This string inserts only generated %d placeholders; all values are passed separately below.
    81                         "SELECT post_id_a,post_id_b,weight FROM %i WHERE generation = %d AND (post_id_a IN ($id_placeholders) OR post_id_b IN ($id_placeholders))",
    82                         ...array_merge( array( $this->edges_table, $this->generation ), $frontier, $frontier )
    83                 );
    84                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Identifiers and bounded integer lists are prepared immediately above.
    85                 $rows = $wpdb->get_results(
    86                         // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- The query is prepared immediately above.
    87                         $query,
    88                         ARRAY_A
    89                 );
    90                 $frontier_set = array_fill_keys( $frontier, true );
    91                 $weights = array();
    92                 foreach ( $rows ?: array() as $edge ) {
    93                         $a = (int) $edge['post_id_a'];
    94                         $b = (int) $edge['post_id_b'];
    95                         $weight = (int) $edge['weight'];
    96                         if ( isset( $frontier_set[ $a ] ) && ! isset( $selected[ $b ] ) ) {
    97                                 $weights[ $b ] = max( isset( $weights[ $b ] ) ? $weights[ $b ] : 0, $weight );
    98                         }
    99                         if ( isset( $frontier_set[ $b ] ) && ! isset( $selected[ $a ] ) ) {
    100                                 $weights[ $a ] = max( isset( $weights[ $a ] ) ? $weights[ $a ] : 0, $weight );
     66        /**
     67         * Use the website's YARPP settings, without requesting more than its cached
     68         * limit. A larger override switches YARPP to its non-persistent bypass cache.
     69         */
     70        private function related_posts( $anchor_id, $limit ) {
     71                global $yarpp;
     72                if ( $limit < 1 || ! function_exists( 'yarpp_get_related' ) || ! is_object( $yarpp ) ) {
     73                        return array();
     74                }
     75                $limit = min( $limit, max( 1, (int) $yarpp->get_option( 'limit' ) ) );
     76                $posts = yarpp_get_related( array( 'limit' => $limit ), $anchor_id );
     77                return array_slice( is_array( $posts ) ? $posts : array(), 0, $limit );
     78        }
     79
     80        /**
     81         * Newly computed YARPP matches must have links even before the next rebuild.
     82         * Retain stored weights and connections between the selected related posts.
     83         */
     84        private function complete_focus_edges( $anchor_id, array $ids, array $related, array $edges ) {
     85                $selected = array_fill_keys( $ids, true );
     86                $connected = array();
     87                foreach ( $edges as $edge ) {
     88                        if ( (int) $edge['source'] === $anchor_id ) {
     89                                $connected[ (int) $edge['target'] ] = true;
     90                        } elseif ( (int) $edge['target'] === $anchor_id ) {
     91                                $connected[ (int) $edge['source'] ] = true;
    10192                        }
    10293                }
    103                 $result = array();
    104                 foreach ( $weights as $id => $weight ) {
    105                         $result[] = array( 'id' => (int) $id, 'weight' => $weight );
     94                $maximum_score = 0.0;
     95                foreach ( $related as $post ) {
     96                        $maximum_score = max( $maximum_score, isset( $post->score ) ? (float) $post->score : 0.0 );
    10697                }
    107                 return $result;
     98                foreach ( $related as $rank => $post ) {
     99                        $id = (int) $post->ID;
     100                        if ( $id === $anchor_id || ! isset( $selected[ $id ] ) || isset( $connected[ $id ] ) ) {
     101                                continue;
     102                        }
     103                        $strength = ARTICLE_RELATIONSHIP_GRAPH_Math::directional_strength( $rank + 1, isset( $post->score ) ? $post->score : 0, $maximum_score );
     104                        $edges[] = array(
     105                                'source' => min( $anchor_id, $id ),
     106                                'target' => max( $anchor_id, $id ),
     107                                'weight' => ARTICLE_RELATIONSHIP_GRAPH_Math::edge_weight( $strength, 0 ),
     108                                'reciprocal' => 0,
     109                        );
     110                        $connected[ $id ] = true;
     111                }
     112                return $edges;
    108113        }
    109114
  • article-relationship-graph-for-yarpp/tags/1.0.16/readme.txt

    r3684388 r3743366  
    44Requires PHP: 7.4
    55Tested up to: 7.1
    6 Stable tag: 1.0.15
     6Stable tag: 1.0.16
    77License: GPLv2 or later
    88License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    1313== Features ==
    1414
    15 * Displays a symmetrical, non-directional graph of related articles.
     15* Displays a symmetrical, non-directional graph of the website's current YARPP related articles.
    1616* Uses YARPP's table cache and respects the site's publicly viewable post types.
    1717* Provides keyboard-friendly zoom controls and links to each article.
    … …  
    4747wp yarpp-graph status
    4848
    49 The graph is capped at 15 nodes. Developers can lower the server-side limit with the article_relationship_graph_node_limit filter and can change supported post types with article_relationship_graph_post_types.
     49The graph includes up to 15 related articles plus the current article (16 nodes). Membership and ordering follow YARPP's website settings; reverse-only and second-degree matches are not added. Developers can lower the total server-side node limit with the article_relationship_graph_node_limit filter and can change supported post types with article_relationship_graph_post_types.
    5050
    5151== Frequently Asked Questions ==
    … …  
    6161= Can I change the number of nodes? =
    6262
    63 The maximum is 15 nodes to keep public requests bounded. Developers can lower the limit with the `article_relationship_graph_node_limit` filter.
     63The maximum is 16 nodes: the current article and up to 15 YARPP matches. Developers can lower the limit with the `article_relationship_graph_node_limit` filter.
    6464
    6565= Can I change the supported post types? =
    … …  
    7373== Technical notes ==
    7474
    75 Graph data is stored in two local custom database tables and is removed when the plugin is uninstalled. Deactivating the plugin preserves the tables so a later reactivation does not require data loss. The included D3.js library is distributed under its BSD 3-Clause license; see `assets/vendor/D3-LICENSE.txt`.
     75Graph connection weights and metrics are stored in two local custom database tables and are removed when the plugin is uninstalled. Deactivating the plugin preserves the tables so a later reactivation does not require data loss. The included D3.js library is distributed under its BSD 3-Clause license; see `assets/vendor/D3-LICENSE.txt`.
    7676
    7777== Changelog ==
     78
     79= 1.0.16 =
     80
     81* Fix YARPP table-cache warm-up before graph rebuilds.
     82* Match graph articles and ordering to the current YARPP website results.
     83* Display new relationships immediately, without waiting for a graph rebuild.
     84* Allow up to 15 related articles plus the current article; the node-limit filter still caps total nodes.
    7885
    7986= 1.0.15 =
  • article-relationship-graph-for-yarpp/trunk/CHANGELOG.md

    r3684388 r3743366  
    11# Changelog
     2
     3## 1.0.16
     4
     5- Warm YARPP's persistent table cache using its configured limits before rebuilding.
     6- Select graph articles from current YARPP website results, preserving ordering and omitting reverse-only and second-degree matches.
     7- Render focus connections immediately when matching articles are absent from the stored graph.
     8- Allow up to 15 related articles plus the focus node; the node-limit filter continues to cap total nodes.
    29
    310## 1.0.11
  • article-relationship-graph-for-yarpp/trunk/article-relationship-graph-for-yarpp.php

    r3684388 r3743366  
    33 * Plugin Name: Article Relationship Graph for YARPP
    44 * Description: Adds an interactive graph of related articles below singular posts using YARPP's local table cache.
    5  * Version: 1.0.15
     5 * Version: 1.0.16
    66 * Author: Ignormatyk
    77 * Requires at least: 6.2
    … …  
    1515defined( 'ABSPATH' ) || exit;
    1616
    17 define( 'ARTICLE_RELATIONSHIP_GRAPH_VERSION', '1.0.15' );
     17define( 'ARTICLE_RELATIONSHIP_GRAPH_VERSION', '1.0.16' );
    1818define( 'ARTICLE_RELATIONSHIP_GRAPH_FILE', __FILE__ );
    1919define( 'ARTICLE_RELATIONSHIP_GRAPH_DIR', plugin_dir_path( __FILE__ ) );
  • article-relationship-graph-for-yarpp/trunk/includes/class-article-relationship-graph-builder.php

    r3684388 r3743366  
    287287                }
    288288                if ( function_exists( 'yarpp_get_related' ) ) {
    289                         yarpp_get_related( array( 'limit' => 7 ), $post_id );
     289                        // Use YARPP's defaults so this warms the persistent table cache.
     290                        yarpp_get_related( array(), $post_id );
    290291                }
    291292        }
  • article-relationship-graph-for-yarpp/trunk/includes/class-article-relationship-graph-repository.php

    r3684388 r3743366  
    44
    55final class ARTICLE_RELATIONSHIP_GRAPH_Repository {
    6         const MAX_NODES = 15;
     6        const MAX_RELATED = 15;
     7        const MAX_NODES = self::MAX_RELATED + 1;
    78
    89        private $wpdb;
    … …  
    3132                $selected = array( $anchor_id => true );
    3233                $depths = array( $anchor_id => 0 );
    33                 $frontier = array( $anchor_id );
    34                 while ( $frontier && count( $selected ) < $cap && $this->generation ) {
    35                         $candidates = $this->frontier_candidates( $frontier, $selected );
    36                         if ( ! $candidates ) {
    37                                 break;
     34                $related = $this->related_posts( $anchor_id, $cap - 1 );
     35                foreach ( $related as $related_post ) {
     36                        $id = (int) $related_post->ID;
     37                        if ( $id === $anchor_id || ! $this->post_policy->is_public_supported_post( $related_post ) ) {
     38                                continue;
    3839                        }
    39                         usort( $candidates, static function ( $left, $right ) {
    40                                 return $right['weight'] <=> $left['weight'] ?: $left['id'] <=> $right['id'];
    41                         } );
    42                         $next = array();
    43                         $next_depth = $depths[ $frontier[0] ] + 1;
    44                         foreach ( array_slice( $candidates, 0, $cap - count( $selected ) ) as $candidate ) {
    45                                 if ( isset( $selected[ $candidate['id'] ] ) ) {
    46                                         continue;
    47                                 }
    48                                 $selected[ $candidate['id'] ] = true;
    49                                 $depths[ $candidate['id'] ] = $next_depth;
    50                                 $next[] = $candidate['id'];
    51                         }
    52                         $frontier = $next;
     40                        $selected[ $id ] = true;
     41                        $depths[ $id ] = 1;
    5342                }
    5443
    … …  
    6049                }
    6150
    62                 return ARTICLE_RELATIONSHIP_GRAPH_Graph_Response::build( $anchor_id, $nodes, $this->induced_edges( $surviving_ids ), $cap );
     51                $edges = $this->induced_edges( $surviving_ids );
     52                $edges = $this->complete_focus_edges( $anchor_id, $surviving_ids, $related, $edges );
     53                return ARTICLE_RELATIONSHIP_GRAPH_Graph_Response::build( $anchor_id, $nodes, $edges, $cap );
    6354        }
    6455
    … …  
    7364        }
    7465
    75         private function frontier_candidates( array $frontier, array $selected ) {
    76                 $wpdb = $this->wpdb;
    77                 $id_placeholders = implode( ',', array_fill( 0, count( $frontier ), '%d' ) );
    78                 // phpcs:ignore WordPress.DB.PreparedSQLPlaceholders.ReplacementsWrongNumber, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- The placeholder list is generated from the bounded integer input immediately above.
    79                 $query = $wpdb->prepare(
    80                         // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- This string inserts only generated %d placeholders; all values are passed separately below.
    81                         "SELECT post_id_a,post_id_b,weight FROM %i WHERE generation = %d AND (post_id_a IN ($id_placeholders) OR post_id_b IN ($id_placeholders))",
    82                         ...array_merge( array( $this->edges_table, $this->generation ), $frontier, $frontier )
    83                 );
    84                 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery, WordPress.DB.DirectDatabaseQuery.NoCaching, WordPress.DB.PreparedSQL.NotPrepared, WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- Identifiers and bounded integer lists are prepared immediately above.
    85                 $rows = $wpdb->get_results(
    86                         // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared -- The query is prepared immediately above.
    87                         $query,
    88                         ARRAY_A
    89                 );
    90                 $frontier_set = array_fill_keys( $frontier, true );
    91                 $weights = array();
    92                 foreach ( $rows ?: array() as $edge ) {
    93                         $a = (int) $edge['post_id_a'];
    94                         $b = (int) $edge['post_id_b'];
    95                         $weight = (int) $edge['weight'];
    96                         if ( isset( $frontier_set[ $a ] ) && ! isset( $selected[ $b ] ) ) {
    97                                 $weights[ $b ] = max( isset( $weights[ $b ] ) ? $weights[ $b ] : 0, $weight );
    98                         }
    99                         if ( isset( $frontier_set[ $b ] ) && ! isset( $selected[ $a ] ) ) {
    100                                 $weights[ $a ] = max( isset( $weights[ $a ] ) ? $weights[ $a ] : 0, $weight );
     66        /**
     67         * Use the website's YARPP settings, without requesting more than its cached
     68         * limit. A larger override switches YARPP to its non-persistent bypass cache.
     69         */
     70        private function related_posts( $anchor_id, $limit ) {
     71                global $yarpp;
     72                if ( $limit < 1 || ! function_exists( 'yarpp_get_related' ) || ! is_object( $yarpp ) ) {
     73                        return array();
     74                }
     75                $limit = min( $limit, max( 1, (int) $yarpp->get_option( 'limit' ) ) );
     76                $posts = yarpp_get_related( array( 'limit' => $limit ), $anchor_id );
     77                return array_slice( is_array( $posts ) ? $posts : array(), 0, $limit );
     78        }
     79
     80        /**
     81         * Newly computed YARPP matches must have links even before the next rebuild.
     82         * Retain stored weights and connections between the selected related posts.
     83         */
     84        private function complete_focus_edges( $anchor_id, array $ids, array $related, array $edges ) {
     85                $selected = array_fill_keys( $ids, true );
     86                $connected = array();
     87                foreach ( $edges as $edge ) {
     88                        if ( (int) $edge['source'] === $anchor_id ) {
     89                                $connected[ (int) $edge['target'] ] = true;
     90                        } elseif ( (int) $edge['target'] === $anchor_id ) {
     91                                $connected[ (int) $edge['source'] ] = true;
    10192                        }
    10293                }
    103                 $result = array();
    104                 foreach ( $weights as $id => $weight ) {
    105                         $result[] = array( 'id' => (int) $id, 'weight' => $weight );
     94                $maximum_score = 0.0;
     95                foreach ( $related as $post ) {
     96                        $maximum_score = max( $maximum_score, isset( $post->score ) ? (float) $post->score : 0.0 );
    10697                }
    107                 return $result;
     98                foreach ( $related as $rank => $post ) {
     99                        $id = (int) $post->ID;
     100                        if ( $id === $anchor_id || ! isset( $selected[ $id ] ) || isset( $connected[ $id ] ) ) {
     101                                continue;
     102                        }
     103                        $strength = ARTICLE_RELATIONSHIP_GRAPH_Math::directional_strength( $rank + 1, isset( $post->score ) ? $post->score : 0, $maximum_score );
     104                        $edges[] = array(
     105                                'source' => min( $anchor_id, $id ),
     106                                'target' => max( $anchor_id, $id ),
     107                                'weight' => ARTICLE_RELATIONSHIP_GRAPH_Math::edge_weight( $strength, 0 ),
     108                                'reciprocal' => 0,
     109                        );
     110                        $connected[ $id ] = true;
     111                }
     112                return $edges;
    108113        }
    109114
  • article-relationship-graph-for-yarpp/trunk/readme.txt

    r3684388 r3743366  
    44Requires PHP: 7.4
    55Tested up to: 7.1
    6 Stable tag: 1.0.15
     6Stable tag: 1.0.16
    77License: GPLv2 or later
    88License URI: https://www.gnu.org/licenses/gpl-2.0.html
    … …  
    1313== Features ==
    1414
    15 * Displays a symmetrical, non-directional graph of related articles.
     15* Displays a symmetrical, non-directional graph of the website's current YARPP related articles.
    1616* Uses YARPP's table cache and respects the site's publicly viewable post types.
    1717* Provides keyboard-friendly zoom controls and links to each article.
    … …  
    4747wp yarpp-graph status
    4848
    49 The graph is capped at 15 nodes. Developers can lower the server-side limit with the article_relationship_graph_node_limit filter and can change supported post types with article_relationship_graph_post_types.
     49The graph includes up to 15 related articles plus the current article (16 nodes). Membership and ordering follow YARPP's website settings; reverse-only and second-degree matches are not added. Developers can lower the total server-side node limit with the article_relationship_graph_node_limit filter and can change supported post types with article_relationship_graph_post_types.
    5050
    5151== Frequently Asked Questions ==
    … …  
    6161= Can I change the number of nodes? =
    6262
    63 The maximum is 15 nodes to keep public requests bounded. Developers can lower the limit with the `article_relationship_graph_node_limit` filter.
     63The maximum is 16 nodes: the current article and up to 15 YARPP matches. Developers can lower the limit with the `article_relationship_graph_node_limit` filter.
    6464
    6565= Can I change the supported post types? =
    … …  
    7373== Technical notes ==
    7474
    75 Graph data is stored in two local custom database tables and is removed when the plugin is uninstalled. Deactivating the plugin preserves the tables so a later reactivation does not require data loss. The included D3.js library is distributed under its BSD 3-Clause license; see `assets/vendor/D3-LICENSE.txt`.
     75Graph connection weights and metrics are stored in two local custom database tables and are removed when the plugin is uninstalled. Deactivating the plugin preserves the tables so a later reactivation does not require data loss. The included D3.js library is distributed under its BSD 3-Clause license; see `assets/vendor/D3-LICENSE.txt`.
    7676
    7777== Changelog ==
     78
     79= 1.0.16 =
     80
     81* Fix YARPP table-cache warm-up before graph rebuilds.
     82* Match graph articles and ordering to the current YARPP website results.
     83* Display new relationships immediately, without waiting for a graph rebuild.
     84* Allow up to 15 related articles plus the current article; the node-limit filter still caps total nodes.
    7885
    7986= 1.0.15 =
Note: See TracChangeset for help on using the changeset viewer.