From 5f59e25b549b40cac419998a80b9332d2c2af466 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 13:46:44 +0000 Subject: [PATCH] refactor(listing-audit): optimize O(N*M) nested loop lookup (STORY-099) Co-authored-by: projectamazonph <286085559+projectamazonph@users.noreply.github.com> --- .jules/bolt.md | 4 + src/engine/listing-audit/engine.test.ts | 97 +++++++++++++++++++++++++ src/engine/listing-audit/engine.ts | 21 +++++- 3 files changed, 120 insertions(+), 2 deletions(-) create mode 100644 src/engine/listing-audit/engine.test.ts diff --git a/.jules/bolt.md b/.jules/bolt.md index ba67a71..05160a9 100644 --- a/.jules/bolt.md +++ b/.jules/bolt.md @@ -3,3 +3,7 @@ ## 2026-07-16 - [O(N*M) Nested Loop Lookups in Grading Engines] **Learning:** In interactive scenarios (such as Bid Elevator and STR Triage), grading engines frequently iterate over user decisions and match them against scenario properties (like keywords or search terms). Performing `array.find()` inside loop bodies or filter predicates results in costly O(N*M) lookups. **Action:** Convert arrays to `Map` lookups before entering loops/nested scans. Mapping keys once in O(M) time enables O(1) lookups during execution, transforming the time complexity of the grading logic to O(N + M). + +## 2026-07-17 - [O(N*M) Nested Loop Lookups in Listing Audit grading] +**Learning:** In the Listing Audit grading engine, both findingsAccuracy and severityCalibration iterated over student findings and performed an `array.find()` lookup against reference findings, leading to O(N*M) operations. +**Action:** Convert the reference findings array into a Map, matching keys once in O(M) time, to enable O(1) lookups inside the loop, achieving O(N+M) time complexity. diff --git a/src/engine/listing-audit/engine.test.ts b/src/engine/listing-audit/engine.test.ts new file mode 100644 index 0000000..25a2706 --- /dev/null +++ b/src/engine/listing-audit/engine.test.ts @@ -0,0 +1,97 @@ +import { describe, it, expect } from 'vitest'; +import { gradeListingAudit, generateAutoFindings } from './engine'; +import type { ListingAuditScenario, ListingAuditFinding } from './types'; + +describe('listing-audit engine', () => { + const mockScenario: ListingAuditScenario = { + id: 'la-test-001', + slug: 'test-cutting-board', + category: 'kitchen', + product: { + asin: 'B09KCB001', + name: 'Bamboo Cutting Board', + category: 'Kitchen', + aov: 1200, + targetAcos: 0.30, + }, + currentListing: { + title: 'Bamboo Cutting Board', + bullets: ['Bamboo material', 'Durable'], + description: 'A cutting board.', + imageCount: 3, + hasAplusContent: false, + pricePhp: 1200, + reviewCount: 18, + averageRating: 4.3, + }, + referenceListing: { + title: 'Bamboo Cutting Board with Deep Juice Groove — Eco-Friendly Kitchen Chopping Board for Vegetables, Meat, Cheese | Large 16x12 inch with Non-Slip Feet | Durable, Dishwasher Safe', + bullets: [ + 'DEEP JUICE GROOVE — Catches liquids from meat, fruit, and vegetables. No more countertop spills.', + 'ECO-FRIENDLY BAMBOO — Sustainably sourced, naturally antibacterial. Lasts longer than plastic.', + 'LARGE 16x12 INCH — Fits a whole meal prep. Reversible design doubles your workspace.', + 'NON-SLIP FEET — Stays put on the counter. No more chasing the board while chopping.', + 'DISHWASHER SAFE — Easy cleanup. Oil monthly with food-grade mineral oil for best results.', + ], + description: 'A premium cutting board made of 100% natural organic bamboo. Perfect for any kitchen task.', + imageCount: 9, + hasAplusContent: true, + pricePhp: 1200, + reviewCount: 200, + averageRating: 4.6, + }, + referenceFindings: [ + { field: 'title', severity: 'critical', message: 'Title is too short.' }, + { field: 'bullets', severity: 'critical', message: 'Only 2 bullets.' }, + { field: 'images', severity: 'critical', message: 'Only 3 images.' }, + { field: 'aplus', severity: 'warning', message: 'No A+ content.' }, + ], + explanation: 'Test explanation', + }; + + it('correctly grades ideal student findings matching reference findings', () => { + const studentFindings: ListingAuditFinding[] = [ + { field: 'title', severity: 'critical', message: 'Title is too short.' }, + { field: 'bullets', severity: 'critical', message: 'Only 2 bullets.' }, + { field: 'images', severity: 'critical', message: 'Only 3 images.' }, + { field: 'aplus', severity: 'warning', message: 'No A+ content.' }, + ]; + + const studentRevision = { + title: 'Bamboo Cutting Board with Deep Juice Groove — Eco-Friendly Kitchen Chopping Board for Vegetables, Meat, Cheese | Large 16x12 inch with Non-Slip Feet | Durable, Dishwasher Safe', + bullets: [ + 'DEEP JUICE GROOVE — Catches liquids from meat, fruit, and vegetables. No more countertop spills.', + 'ECO-FRIENDLY BAMBOO — Sustainably sourced, naturally antibacterial. Lasts longer than plastic.', + 'LARGE 16x12 INCH — Fits a whole meal prep. Reversible design doubles your workspace.', + 'NON-SLIP FEET — Stays put on the counter. No more chasing the board while chopping.', + 'DISHWASHER SAFE — Easy cleanup. Oil monthly with food-grade mineral oil for best results.', + ], + imageCount: 9, + hasAplusContent: true, + }; + + const grade = gradeListingAudit(mockScenario, studentFindings, studentRevision); + expect(grade.passed).toBe(true); + expect(grade.totalScore).toBeGreaterThanOrEqual(70); + }); + + it('correctly grades poor or missing student findings', () => { + const studentFindings: ListingAuditFinding[] = []; // No findings submitted + + const grade = gradeListingAudit(mockScenario, studentFindings); + expect(grade.passed).toBe(false); + expect(grade.totalScore).toBeLessThan(70); + }); + + it('generates auto findings correctly based on guidelines', () => { + const current = mockScenario.currentListing; + const reference = mockScenario.referenceListing; + const autoFindings = generateAutoFindings(current, reference); + + expect(autoFindings.length).toBeGreaterThan(0); + expect(autoFindings.some(f => f.field === 'title' && f.severity === 'critical')).toBe(true); + expect(autoFindings.some(f => f.field === 'bullets' && f.severity === 'critical')).toBe(true); + expect(autoFindings.some(f => f.field === 'images' && f.severity === 'critical')).toBe(true); + expect(autoFindings.some(f => f.field === 'aplus' && f.severity === 'warning')).toBe(true); + }); +}); diff --git a/src/engine/listing-audit/engine.ts b/src/engine/listing-audit/engine.ts index 7d17234..a6b0bf6 100644 --- a/src/engine/listing-audit/engine.ts +++ b/src/engine/listing-audit/engine.ts @@ -83,9 +83,18 @@ function findingsAccuracy( if (student.length === 0) { return binaryCriterion('findings_accuracy', false, PASS, 'No findings submitted.'); } + + // Bolt optimization: Map reference findings by field to replace O(N*M) nested lookups with O(1) lookups + const refMap = new Map(); + for (const rf of ref) { + if (!refMap.has(rf.field)) { + refMap.set(rf.field, rf); + } + } + let truePositives = 0; for (const sf of student) { - const match = ref.find((rf) => rf.field === sf.field); + const match = refMap.get(sf.field); if (!match) continue; // Severity at least matches if (severityAtLeast(sf.severity, match.severity)) truePositives++; @@ -104,10 +113,18 @@ function severityCalibration( student: ListingAuditFinding[], ref: ListingAuditFinding[] ): CriterionResult { + // Bolt optimization: Map reference findings by field to replace O(N*M) nested lookups with O(1) lookups + const refMap = new Map(); + for (const rf of ref) { + if (!refMap.has(rf.field)) { + refMap.set(rf.field, rf); + } + } + let correct = 0; let total = 0; for (const sf of student) { - const match = ref.find((rf) => rf.field === sf.field); + const match = refMap.get(sf.field); if (!match) continue; total++; if (sf.severity === match.severity) correct++;