openvibers/openvibe.reviews Commits

Official records count toward the index gate (their own lines and their public body as a source): 1,155 NYC restaurant pages with their inspections were in no sitemap and no network search; bare counts (a CFPB tally, an HPD building) stay under the word floor (#86)

OP OpenVibers committed 26 days ago · ef1f7aa92c · 2 files, +40 −2

Changes

ddiff --git a/server/reviews/service.js b/server/reviews/service.js
iindex a311928..cc2daf1 100644
---- a/server/reviews/service.js
++++ b/server/reviews/service.js
@@@ -624,12 +624,14 @@ function createReviewsService({ db, stores, outbox, config, now = () => Date.now
const rec = rev && rev.meta && rev.meta.authorship;
// Members' reviews count as substance: their words (from three reviews up) and each review as a citation.
const mem = await memberStats(members);
++ // So do official records: their own lines and their public body as the source (recordSubstance).
++ const recs = recordSubstance(await recordsView(e.id));
return {
state: e.state === 'deleted' ? 'deleted' : 'published',
visibility: 'public',
canonicalUrl: entityUrl(e),
-- wordCount: (rev ? ssr.wordCount(summaryText(rev)) : 0) + (mem.count >= 3 ? mem.words : 0),
-- citationCount: active.length + mem.count,
++ wordCount: (rev ? ssr.wordCount(summaryText(rev)) : 0) + (mem.count >= 3 ? mem.words : 0) + recs.words,
++ citationCount: active.length + mem.count + recs.sources,
unsupportedClaims: rev ? await unsupportedCount(e.id, rev) : 0,
noindex: !!e.noindex,
...(rec ? authorship.gateFacts(rec, await reviews.latest(summary.id, rev.number)) : {}),
@@@ -1045,6 +1047,21 @@ function createReviewsService({ db, stores, outbox, config, now = () => Date.now
}
/** An entity's official records, for its page: the CFPB count, HPD's open violations, NYC inspections by day (newest first). */
++ /**
++ * What an entity's official records add to the index gate: the words of their specific lines (each inspection's
++ * date, grade, score and violations; the complaint count; the housing counts by class), never the page's fixed
++ * captions and caveats, and one source per public body. Only summaries and members' reviews counted before, so
++ * 1,155 NYC restaurant pages with their inspections were in no sitemap and no network search (2026-10-18). A page
++ * whose records are a bare count (a CFPB tally, an HPD building) stays under the gate's word floor.
++ */
++ function recordSubstance(rec) {
++ const lines = [];
++ if (rec.complaints) lines.push(`CFPB complaints ${rec.complaints.count}`);
++ if (rec.housing) lines.push(`HPD open violations ${rec.housing.open}: class C ${rec.housing.c}, class B ${rec.housing.b}, class A ${rec.housing.a}${rec.housing.last ? `; inspected ${rec.housing.last}` : ''}`);
++ for (const i of rec.inspections) lines.push([i.date, i.grade && `grade ${i.grade}`, i.score != null && `score ${i.score}`, ...i.violations.map((v) => v.text)].filter(Boolean).join(' '));
++ return { words: lines.length ? ssr.wordCount(lines.join('\n')) : 0, sources: (rec.complaints ? 1 : 0) + (rec.housing ? 1 : 0) + (rec.inspections.length ? 1 : 0) };
++ }
++
async function recordsView(entityId) {
const rows = await q.recordsOf.all([...await closure(entityId)]);
const out = { complaints: null, housing: null, inspections: [], restaurant: null };
ddiff --git a/test/official-records.test.js b/test/official-records.test.js
iindex 5f53c5c..352ab01 100644
---- a/test/official-records.test.js
++++ b/test/official-records.test.js
@@@ -51,6 +51,27 @@ t('NYC HPD: a building with open hazardous violations is a landlord page with th
} finally { await h.stop(); }
});
++t('a restaurant page with its inspections is indexable on its own; a bare count is not', async () => {
++ const h = await boot();
++ try {
++ // NYC Health's violation texts are the page's own substance (60 words is the gate's floor).
++ const cited = [
++ ['06D', 'Food contact surface not properly washed, rinsed and sanitized after each use and following any activity when contamination may have occurred.'],
++ ['08A', 'Establishment is not free of harborage or conditions conducive to rodents, insects or other pests, including holes, gaps and standing water.'],
++ ['02G', 'Cold food item held above 41 degrees Fahrenheit (smoked fish and reduced oxygen packaged foods above 38 degrees) except during necessary preparation.'],
++ ];
++ await cited.reduce((p, [code, violation]) => p.then(() => importItem(h, nyc({ inspection_date: '2026-10-01T00:00:00.000', violation_code: code, violation, critical_flag: 'Critical' }))), Promise.resolve());
++ await importItem(h, cfpb('CAPITAL ONE FINANCIAL CORPORATION', 34100));
++ await importItem(h, hpd({ buildingid: '288857', housenumber: '760', streetname: 'ELDERT LANE', open: '1794', c: '423', b: '1118', a: '252' }));
++ const index = (await req(h, 'GET', '/sitemap.xml')).text;
++ assert.match(index, /\/sitemaps\/entities-1\.xml/);
++ const urls = (await req(h, 'GET', '/sitemaps/entities-1.xml')).text;
++ assert.match(urls, /\/e\/the-coffee-club-9526-4-avenue-brooklyn-ny</, 'the restaurant, by its inspection');
++ assert.ok(!/capital-one/.test(urls) && !/eldert/.test(urls), 'a complaint count or a housing tally alone stays out (thin)');
++ assert.ok(!/noindex/.test((await req(h, 'GET', '/e/the-coffee-club-9526-4-avenue-brooklyn-ny')).text), 'and its page says so');
++ } finally { await h.stop(); }
++});
++
t('company names read as the companies write them', () => {
const { recordName } = require('../server/reviews/service');
assert.strictEqual(recordName('JPMORGAN CHASE & CO.'), 'JPMorgan Chase & Co.');