PluginProbe
404 Solution / trunk
404 Solution vtrunk
4.3.5 4.3.4 4.3.3 4.3.2 4.3.1 4.3.0 4.2.0 4.1.19 4.1.18 4.1.17 4.1.16 4.1.15 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.1.7 4.1.6 4.1.5 4.1.4 4.1.3 trunk 2.30.0 All 109 releases
404-solution / includes / ngram / TermNGramCoveragePolicy.php

TermNGramCoveragePolicy.php in 404 Solution trunk, at includes/ngram/TermNGramCoveragePolicy.php

118 lines 4.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 if (!defined('ABSPATH')) {
4 exit;
5 }
6
7 /**
8 * Decides whether the per-type (category/tag) n-gram prefilter is trustworthy
9 * enough to use on a given 404 request, or whether the caller should fall back
10 * to the full getPublishedCategories()/getPublishedTags() scan.
11 *
12 * Mirrors the post-side gates in SpellNGramPrefilter: a minimum absolute number
13 * of cache entries for the type AND a minimum coverage ratio (cached term
14 * n-grams / published terms of that type). Both must hold. This is intentionally
15 * a separate policy from NGramCoveragePolicy, which measures post coverage
16 * against the permalink cache; the term denominator is the published-taxonomy
17 * count, a different source.
18 *
19 * Unit-testable in isolation: construct with the ngram filter (count source)
20 * and a published-content repository exposing getPublishedCategoryCount() /
21 * getPublishedTagCount(), then call isReady($type).
22 */
23 class ABJ_404_Solution_TermNGramCoveragePolicy {
24
25 /** Minimum cached term n-grams of a type before the prefilter is trusted. */
26 const TERM_MIN_CACHE_ENTRIES = 50;
27
28 /** Minimum (cached / published) coverage ratio for a type. */
29 const TERM_MIN_COVERAGE_RATIO = 0.8;
30
31 /** N-gram cache type for category archives. */
32 const TYPE_CATEGORY = 'category';
33
34 /** N-gram cache type for tag archives. */
35 const TYPE_TAG = 'tag';
36
37 /** @var ABJ_404_Solution_NGramFilter */
38 private $ngramFilter;
39
40 /** @var mixed Object exposing getPublishedCategoryCount() and getPublishedTagCount(). */
41 private $contentRepo;
42
43 /** @var ABJ_404_Solution_Logging */
44 private $logger;
45
46 /**
47 * @param ABJ_404_Solution_NGramFilter $ngramFilter Source of getCacheCountForType().
48 * @param mixed $contentRepo Object exposing getPublishedCategoryCount() / getPublishedTagCount().
49 * @param ABJ_404_Solution_Logging $logger
50 */
51 public function __construct($ngramFilter, $contentRepo, $logger) {
52 $this->ngramFilter = $ngramFilter;
53 $this->contentRepo = $contentRepo;
54 $this->logger = $logger;
55 }
56
57 /**
58 * Is the prefilter trustworthy for this term type right now?
59 *
60 * @param string $type 'category' or 'tag'.
61 * @return bool True when the cache has enough entries AND covers enough of
62 * the published terms; false on cold/insufficient coverage.
63 */
64 public function isReady(string $type): bool {
65 if ($type !== self::TYPE_CATEGORY && $type !== self::TYPE_TAG) {
66 return false;
67 }
68
69 $cacheCount = $this->ngramFilter->getCacheCountForType($type);
70 if ($cacheCount < self::TERM_MIN_CACHE_ENTRIES) {
71 $this->logger->debugMessage(sprintf(
72 "Term n-gram prefilter not ready (min entries): type=%s count=%d (need %d)",
73 $type,
74 $cacheCount,
75 self::TERM_MIN_CACHE_ENTRIES
76 ));
77 return false;
78 }
79
80 $publishedCount = $this->publishedCountFor($type);
81 if ($publishedCount <= 0) {
82 // No published terms of this type: nothing to prefilter against.
83 return false;
84 }
85
86 $ratio = $cacheCount / $publishedCount;
87 if ($ratio < self::TERM_MIN_COVERAGE_RATIO) {
88 $this->logger->debugMessage(sprintf(
89 "Term n-gram prefilter not ready (low coverage): type=%s ratio=%.2f (need %.2f)",
90 $type,
91 $ratio,
92 self::TERM_MIN_COVERAGE_RATIO
93 ));
94 return false;
95 }
96
97 return true;
98 }
99
100 /**
101 * @param string $type
102 * @return int Published term count for the type, or 0 on unknown type.
103 */
104 private function publishedCountFor(string $type): int {
105 $repo = $this->contentRepo;
106 if (!is_object($repo)) {
107 return 0;
108 }
109 if ($type === self::TYPE_CATEGORY && method_exists($repo, 'getPublishedCategoryCount')) {
110 return (int)$repo->getPublishedCategoryCount();
111 }
112 if ($type === self::TYPE_TAG && method_exists($repo, 'getPublishedTagCount')) {
113 return (int)$repo->getPublishedTagCount();
114 }
115 return 0;
116 }
117 }
118