Changeset 3743366
- Timestamp:
- 10/13/2026 09:21:15 AM (3 weeks ago)
- Location:
- article-relationship-graph-for-yarpp
- Files:
-
- 10 edited
- 1 copied
-
tags/1.0.16 (copied) (copied from article-relationship-graph-for-yarpp/trunk )
-
tags/1.0.16/CHANGELOG.md (modified) (1 diff)
-
tags/1.0.16/article-relationship-graph-for-yarpp.php (modified) (2 diffs)
-
tags/1.0.16/includes/class-article-relationship-graph-builder.php (modified) (1 diff)
-
tags/1.0.16/includes/class-article-relationship-graph-repository.php (modified) (4 diffs)
-
tags/1.0.16/readme.txt (modified) (5 diffs)
-
trunk/CHANGELOG.md (modified) (1 diff)
-
trunk/article-relationship-graph-for-yarpp.php (modified) (2 diffs)
-
trunk/includes/class-article-relationship-graph-builder.php (modified) (1 diff)
-
trunk/includes/class-article-relationship-graph-repository.php (modified) (4 diffs)
-
trunk/readme.txt (modified) (5 diffs)
Legend:
- Unmodified
- Added
- Removed
-
article-relationship-graph-for-yarpp/tags/1.0.16/CHANGELOG.md
r3684388 r3743366 1 1 # 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. 2 9 3 10 ## 1.0.11 -
article-relationship-graph-for-yarpp/tags/1.0.16/article-relationship-graph-for-yarpp.php
r3684388 r3743366 3 3 * Plugin Name: Article Relationship Graph for YARPP 4 4 * Description: Adds an interactive graph of related articles below singular posts using YARPP's local table cache. 5 * Version: 1.0.1 55 * Version: 1.0.16 6 6 * Author: Ignormatyk 7 7 * Requires at least: 6.2 … … 15 15 defined( 'ABSPATH' ) || exit; 16 16 17 define( 'ARTICLE_RELATIONSHIP_GRAPH_VERSION', '1.0.1 5' );17 define( 'ARTICLE_RELATIONSHIP_GRAPH_VERSION', '1.0.16' ); 18 18 define( 'ARTICLE_RELATIONSHIP_GRAPH_FILE', __FILE__ ); 19 19 define( '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 287 287 } 288 288 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 ); 290 291 } 291 292 } -
article-relationship-graph-for-yarpp/tags/1.0.16/includes/class-article-relationship-graph-repository.php
r3684388 r3743366 4 4 5 5 final class ARTICLE_RELATIONSHIP_GRAPH_Repository { 6 const MAX_NODES = 15; 6 const MAX_RELATED = 15; 7 const MAX_NODES = self::MAX_RELATED + 1; 7 8 8 9 private $wpdb; … … 31 32 $selected = array( $anchor_id => true ); 32 33 $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; 38 39 } 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; 53 42 } 54 43 … … 60 49 } 61 50 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 ); 63 54 } 64 55 … … 73 64 } 74 65 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; 101 92 } 102 93 } 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 ); 106 97 } 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; 108 113 } 109 114 -
article-relationship-graph-for-yarpp/tags/1.0.16/readme.txt
r3684388 r3743366 4 4 Requires PHP: 7.4 5 5 Tested up to: 7.1 6 Stable tag: 1.0.1 56 Stable tag: 1.0.16 7 7 License: GPLv2 or later 8 8 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 13 13 == Features == 14 14 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. 16 16 * Uses YARPP's table cache and respects the site's publicly viewable post types. 17 17 * Provides keyboard-friendly zoom controls and links to each article. … … 47 47 wp yarpp-graph status 48 48 49 The graph i s 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.49 The 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. 50 50 51 51 == Frequently Asked Questions == … … 61 61 = Can I change the number of nodes? = 62 62 63 The maximum is 1 5 nodes to keep public requests bounded. Developers can lower the limit with the `article_relationship_graph_node_limit` filter.63 The 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. 64 64 65 65 = Can I change the supported post types? = … … 73 73 == Technical notes == 74 74 75 Graph data is stored in two local custom database tables and isremoved 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`.75 Graph 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`. 76 76 77 77 == 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. 78 85 79 86 = 1.0.15 = -
article-relationship-graph-for-yarpp/trunk/CHANGELOG.md
r3684388 r3743366 1 1 # 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. 2 9 3 10 ## 1.0.11 -
article-relationship-graph-for-yarpp/trunk/article-relationship-graph-for-yarpp.php
r3684388 r3743366 3 3 * Plugin Name: Article Relationship Graph for YARPP 4 4 * Description: Adds an interactive graph of related articles below singular posts using YARPP's local table cache. 5 * Version: 1.0.1 55 * Version: 1.0.16 6 6 * Author: Ignormatyk 7 7 * Requires at least: 6.2 … … 15 15 defined( 'ABSPATH' ) || exit; 16 16 17 define( 'ARTICLE_RELATIONSHIP_GRAPH_VERSION', '1.0.1 5' );17 define( 'ARTICLE_RELATIONSHIP_GRAPH_VERSION', '1.0.16' ); 18 18 define( 'ARTICLE_RELATIONSHIP_GRAPH_FILE', __FILE__ ); 19 19 define( 'ARTICLE_RELATIONSHIP_GRAPH_DIR', plugin_dir_path( __FILE__ ) ); -
article-relationship-graph-for-yarpp/trunk/includes/class-article-relationship-graph-builder.php
r3684388 r3743366 287 287 } 288 288 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 ); 290 291 } 291 292 } -
article-relationship-graph-for-yarpp/trunk/includes/class-article-relationship-graph-repository.php
r3684388 r3743366 4 4 5 5 final class ARTICLE_RELATIONSHIP_GRAPH_Repository { 6 const MAX_NODES = 15; 6 const MAX_RELATED = 15; 7 const MAX_NODES = self::MAX_RELATED + 1; 7 8 8 9 private $wpdb; … … 31 32 $selected = array( $anchor_id => true ); 32 33 $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; 38 39 } 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; 53 42 } 54 43 … … 60 49 } 61 50 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 ); 63 54 } 64 55 … … 73 64 } 74 65 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; 101 92 } 102 93 } 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 ); 106 97 } 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; 108 113 } 109 114 -
article-relationship-graph-for-yarpp/trunk/readme.txt
r3684388 r3743366 4 4 Requires PHP: 7.4 5 5 Tested up to: 7.1 6 Stable tag: 1.0.1 56 Stable tag: 1.0.16 7 7 License: GPLv2 or later 8 8 License URI: https://www.gnu.org/licenses/gpl-2.0.html … … 13 13 == Features == 14 14 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. 16 16 * Uses YARPP's table cache and respects the site's publicly viewable post types. 17 17 * Provides keyboard-friendly zoom controls and links to each article. … … 47 47 wp yarpp-graph status 48 48 49 The graph i s 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.49 The 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. 50 50 51 51 == Frequently Asked Questions == … … 61 61 = Can I change the number of nodes? = 62 62 63 The maximum is 1 5 nodes to keep public requests bounded. Developers can lower the limit with the `article_relationship_graph_node_limit` filter.63 The 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. 64 64 65 65 = Can I change the supported post types? = … … 73 73 == Technical notes == 74 74 75 Graph data is stored in two local custom database tables and isremoved 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`.75 Graph 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`. 76 76 77 77 == 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. 78 85 79 86 = 1.0.15 =
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)