previewMatchForRequirement.js
Overview
Este archivo implementa una superficie interna de QA para revisar Match desde la consola. No es un producto, no expone una ruta HTTP y no tiene consumidores en producción. Además, es de solo lectura: solo consulta datos con Mongoose y formatea un documento JSON seguro para inspección.
Su función principal es orquestar generateCandidatesForRequirement() y las utilidades de presentación de candidate-presentation.js. Luego agrega un resumen del Requirement, un audit de geografía, los candidatos formateados y metadatos de retrieval. fileciteturn0file0turn0file4
[!NOTE] El script también puede ejecutarse como CLI. Cuando se invoca directamente, valida
requirementId, carga.env, aplica un override opcional de DNS y escribe el preview en JSON por stdout.
How it works
sequenceDiagram
participant CLI as CLI
participant Script as previewMatchForRequirement
participant Dotenv as dotenv
participant DNS as dns
participant Mongo as mongoose
participant Listing as Listing model
participant Retrieval as candidate retrieval
participant Presentation as candidate presentation
CLI->>Script: requirementId
Script->>Dotenv: load env files
Script->>DNS: applyDnsOverrideIfConfigured
Script->>Mongo: connect
Script->>Listing: findById requirement
Script->>Retrieval: generateCandidatesForRequirement
Script->>Listing: find offer geography rows
Script->>Presentation: orderCandidatesForPresentation
Script->>Mongo: disconnect
El flujo empieza con la validación del requirementId y la lectura del Requirement con Listing.findById().select(...).lean(). Después llama al motor real de Match con includeBlocked: true y un objetivo fijo de 20 candidatos. Finalmente, consulta solo la geografía necesaria de los Offers evaluados y ensambla la salida final. fileciteturn0file0turn0file4
Entrada de datos
El script recibe un único identificador de Requirement. Si falta o no es un ObjectId válido, el proceso marca error y no continúa. Si el Requirement no existe, generateMatchPreview() devuelve un objeto con error: 'REQUIREMENT_NOT_FOUND'. fileciteturn0file0turn0file9
Dependencias de dominio
La lógica de elegibilidad, compatibilidad de pares, comparación de campos y geografía de radio vive fuera de este archivo. Aquí solo se consumen los resultados ya calculados por generateCandidatesForRequirement() y por candidate-presentation.js. El archivo no reimplementa reglas de Haversine ni recalcula comparaciones geográficas.
Key components
Estos son los elementos exportados que estructuran la superficie reutilizable del archivo.
{
"title": "generateMatchPreview",
"content": "Funci\u00f3n principal exportada. Carga el Requirement, ejecuta el motor de Match, a\u00f1ade presentaci\u00f3n y geograf\u00eda, y devuelve un documento JSON seguro para QA o un objeto de error.",
"type": "function",
"filePath": "services/api/src/scripts/previewMatchForRequirement.js",
"badges": [
"public"
]
}
{
"title": "parseDnsServersEnvValue",
"content": "Parses `PLANO_TRANSPORT_DNS_SERVERS` as a comma-separated list of IPv4 addresses. Returns a valid array or `null` without throwing.",
"type": "function",
"filePath": "services/api/src/scripts/previewMatchForRequirement.js",
"badges": [
"public"
]
}
{
"title": "applyDnsOverrideIfConfigured",
"content": "Aplica `dns.setServers(...)` solo cuando el valor de entorno contiene IPv4 v\u00e1lidas. Tambi\u00e9n registra qu\u00e9 IPs se aplicaron.",
"type": "function",
"filePath": "services/api/src/scripts/previewMatchForRequirement.js",
"badges": [
"public"
]
}
Componentes internos relevantes
summarizeRequirementForPreview()construye el resumen seguro del Requirement.summarizeMatchResults()agrega totales del resultado.summarizeGeographyAvailability()resume la disponibilidad geográfica de los Offers ya evaluados.summarizeCandidateForPreview()crea la vista compacta por candidato.summarizeRetrievalDiagnostics()reexpone metadatos de retrieval.getBudgetWarningReason()extrae la razón de advertencia de precio cuando aplica.summarizeLocationComparison()lee la evidencia geográfica ya calculada.summarizeLocationOutcomeTotals()agrega los resultados geográficos por compatibilidad.
Data & fields
La salida de generateMatchPreview() es un documento JSON compuesto por cuatro bloques principales. El archivo solo expone campos seguros y observacionales. No devuelve título, descripción, contacto, medios ni PDF del Requirement.
[
{
"name": "requirement",
"type": "object",
"required": true,
"description": "Resumen seguro del Requirement y de su elegibilidad.",
"children": [
{
"name": "requirementId",
"type": "string|null",
"required": true,
"description": "Identificador del Requirement convertido a string."
},
{
"name": "entityType",
"type": "string|null",
"required": true,
"description": "Valor de meta.entityType."
},
{
"name": "operationType",
"type": "string|null",
"required": true,
"description": "Valor de meta.operationType."
},
{
"name": "propertyType",
"type": "string|null",
"required": true,
"description": "Valor de meta.propertyType."
},
{
"name": "eligibility",
"type": "object",
"required": true,
"description": "Resultado de elegibilidad entregado por el motor de Match.",
"children": [
{
"name": "eligible",
"type": "boolean|null",
"required": true,
"description": "Bandera de elegibilidad del Requirement."
},
{
"name": "reasons",
"type": "string[]",
"required": true,
"description": "Razones de elegibilidad devueltas por el motor."
},
{
"name": "validationReasons",
"type": "string[]",
"required": true,
"description": "Razones de validación tomadas desde meta.requirementValidationReasons."
}
]
},
{
"name": "criteria",
"type": "object",
"required": true,
"description": "Resumen de criterios declarados por el Requirement.",
"children": [
{
"name": "hasBudgetCriterion",
"type": "boolean",
"required": true,
"description": "Indica si existe presupuesto mínimo o máximo."
},
{
"name": "hasTermCriterion",
"type": "boolean",
"required": true,
"description": "Indica si existe maximumAcceptedTermValue."
},
{
"name": "physicalPreferencesDeclared",
"type": "string[]",
"required": true,
"description": "Campos físicos presentes en preferences."
},
{
"name": "infrastructurePreferencesDeclared",
"type": "string[]",
"required": true,
"description": "Campos de infraestructura presentes en preferences."
},
{
"name": "desiredAmenitiesCount",
"type": "number",
"required": true,
"description": "Cantidad de amenidades deseadas."
}
]
},
{
"name": "geography",
"type": "object",
"required": true,
"description": "Resumen de disponibilidad geográfica del lado Requirement.",
"children": [
{
"name": "searchZoneCount",
"type": "number",
"required": true,
"description": "Cantidad total de searchZones."
},
{
"name": "zonesWithCoordinates",
"type": "number",
"required": true,
"description": "Zonas con centro geográfico completo."
},
{
"name": "zonesWithRadius",
"type": "number",
"required": true,
"description": "Zonas con radiusKm."
},
{
"name": "zonesWithCountry",
"type": "number",
"required": true,
"description": "Zonas con country."
},
{
"name": "zonesWithState",
"type": "number",
"required": true,
"description": "Zonas con state."
},
{
"name": "zonesWithCity",
"type": "number",
"required": true,
"description": "Zonas con city."
},
{
"name": "zonesWithNeighborhood",
"type": "number",
"required": true,
"description": "Zonas con neighborhood."
}
]
}
]
},
{
"name": "summary",
"type": "object",
"required": true,
"description": "Resumen agregado del resultado de Match.",
"children": [
{
"name": "candidatesReturned",
"type": "number",
"required": true,
"description": "Cantidad de candidatos devueltos."
},
{
"name": "byStatus",
"type": "object",
"required": true,
"description": "Conteo por status de dominio.",
"children": [
{
"name": "eligible",
"type": "number",
"required": true,
"description": "Candidatos con status eligible."
},
{
"name": "pending",
"type": "number",
"required": true,
"description": "Candidatos con status pending."
},
{
"name": "blocked",
"type": "number",
"required": true,
"description": "Candidatos con status blocked."
}
]
},
{
"name": "presentableCount",
"type": "number",
"required": true,
"description": "Cantidad de candidatos no ocultos según presentationState."
},
{
"name": "hiddenBlockedCount",
"type": "number",
"required": true,
"description": "Cantidad de candidatos ocultos según presentationState."
},
{
"name": "presentationStateTotals",
"type": "object",
"required": true,
"description": "Conteo por estado de presentación.",
"children": [
{
"name": "eligible",
"type": "number",
"required": true,
"description": "Candidatos presentables como eligible."
},
{
"name": "pending",
"type": "number",
"required": true,
"description": "Candidatos presentables como pending."
},
{
"name": "budgetWarning",
"type": "number",
"required": true,
"description": "Candidatos presentables como budget_warning."
},
{
"name": "alternative",
"type": "number",
"required": true,
"description": "Candidatos presentables como alternative."
},
{
"name": "hidden",
"type": "number",
"required": true,
"description": "Candidatos ocultos."
}
]
},
{
"name": "signalBucketTotals",
"type": "object",
"required": true,
"description": "Totales de buckets de señales.",
"children": [
{
"name": "blockers",
"type": "number",
"required": true,
"description": "Total de señales en blockers."
},
{
"name": "pendingRequired",
"type": "number",
"required": true,
"description": "Total de señales en pendingRequired."
},
{
"name": "pendingGates",
"type": "number",
"required": true,
"description": "Total de señales en pendingGates."
},
{
"name": "unknowns",
"type": "number",
"required": true,
"description": "Total de señales en unknowns."
},
{
"name": "notApplicable",
"type": "number",
"required": true,
"description": "Total de señales en notApplicable."
}
]
},
{
"name": "locationOutcomeTotals",
"type": "object",
"required": true,
"description": "Totales de compatibilidad geográfica derivados de signals.location.",
"children": [
{
"name": "compatible",
"type": "number",
"required": true,
"description": "Candidatos compatibles geográficamente."
},
{
"name": "incompatible",
"type": "number",
"required": true,
"description": "Candidatos incompatibles geográficamente."
},
{
"name": "pending",
"type": "number",
"required": true,
"description": "Candidatos con geografía pendiente."
},
{
"name": "notApplicable",
"type": "number",
"required": true,
"description": "Candidatos con geografía no aplicable."
}
]
},
{
"name": "includeBlockedCandidates",
"type": "boolean",
"required": true,
"description": "Refleja si la invocación pidió incluir blocked."
}
]
},
{
"name": "geographyAudit",
"type": "object",
"required": true,
"description": "Audit observacional de geografía del lado Offer para los candidatos evaluados.",
"children": [
{
"name": "scope",
"type": "string",
"required": true,
"description": "Siempre EVALUATED_CANDIDATES_ONLY."
},
{
"name": "scopeNote",
"type": "string",
"required": true,
"description": "Explica que el audit no cubre toda la colección."
},
{
"name": "offersEvaluated",
"type": "number",
"required": true,
"description": "Cantidad de Offers presentes en los candidatos evaluados."
},
{
"name": "offersLocationDeclared",
"type": "number",
"required": true,
"description": "Offers con location.declaration igual a declared."
},
{
"name": "offersLocationNotDeclared",
"type": "number",
"required": true,
"description": "Offers con location.declaration igual a not_declared."
},
{
"name": "offersLocationUnspecified",
"type": "number",
"required": true,
"description": "Offers con declaration ausente o no reconocida."
},
{
"name": "offersWithCoordinates",
"type": "number",
"required": true,
"description": "Offers con latitude y longitude válidos."
},
{
"name": "offersWithoutCoordinates",
"type": "number",
"required": true,
"description": "Offers sin coordenadas completas."
},
{
"name": "offersWithCity",
"type": "number",
"required": true,
"description": "Offers con city."
},
{
"name": "offersWithState",
"type": "number",
"required": true,
"description": "Offers con state."
},
{
"name": "offersWithNeighborhood",
"type": "number",
"required": true,
"description": "Offers con neighborhood."
},
{
"name": "declaredOffersWithCoordinates",
"type": "number",
"required": true,
"description": "Offers declarados con coordenadas completas."
},
{
"name": "declaredOffersWithoutCoordinates",
"type": "number",
"required": true,
"description": "Offers declarados sin coordenadas completas."
}
]
},
{
"name": "candidates",
"type": "object[]",
"required": true,
"description": "Lista compacta de candidatos formateados para inspección.",
"children": [
{
"name": "offerId",
"type": "string",
"required": true,
"description": "Identificador del Offer."
},
{
"name": "status",
"type": "string",
"required": true,
"description": "Status de dominio del candidato."
},
{
"name": "presentable",
"type": "boolean",
"required": true,
"description": "True cuando el candidato no está hidden."
},
{
"name": "presentationState",
"type": "string",
"required": true,
"description": "Estado calculado por candidate-presentation.js."
},
{
"name": "presentationPosition",
"type": "number|null",
"required": true,
"description": "Posición 1-based entre candidatos presentables."
},
{
"name": "budgetWarningReason",
"type": "string|null",
"required": true,
"description": "Razón de advertencia de presupuesto cuando corresponde."
},
{
"name": "entityType",
"type": "string|null",
"required": true,
"description": "meta.entityType del Offer."
},
{
"name": "operationType",
"type": "string|null",
"required": true,
"description": "meta.operationType del Offer."
},
{
"name": "propertyType",
"type": "string|null",
"required": true,
"description": "meta.propertyType del Offer."
},
{
"name": "signals",
"type": "object",
"required": true,
"description": "Resumen de buckets de señales.",
"children": [
{
"name": "blockerFields",
"type": "string[]",
"required": true,
"description": "Nombres de campos en blockers."
},
{
"name": "pendingRequiredFields",
"type": "string[]",
"required": true,
"description": "Nombres de campos en pendingRequired."
},
{
"name": "pendingGateFields",
"type": "string[]",
"required": true,
"description": "Nombres de campos en pendingGates."
},
{
"name": "unknownFields",
"type": "string[]",
"required": true,
"description": "Nombres de campos en unknowns."
},
{
"name": "notApplicableFields",
"type": "string[]",
"required": true,
"description": "Nombres de campos en notApplicable."
}
]
},
{
"name": "geography",
"type": "object",
"required": true,
"description": "Resumen geográfico del Offer y su comparación.",
"children": [
{
"name": "locationDeclaration",
"type": "string",
"required": true,
"description": "Declaración de geografía del Offer."
},
{
"name": "hasCoordinates",
"type": "boolean",
"required": true,
"description": "Indica si latitude y longitude existen."
},
{
"name": "hasCity",
"type": "boolean",
"required": true,
"description": "Indica si hay city."
},
{
"name": "hasState",
"type": "boolean",
"required": true,
"description": "Indica si hay state."
},
{
"name": "hasNeighborhood",
"type": "boolean",
"required": true,
"description": "Indica si hay neighborhood."
},
{
"name": "comparison",
"type": "object|null",
"required": true,
"description": "Evidencia de la comparación de geografía ya calculada.",
"children": [
{
"name": "outcome",
"type": "string|null",
"required": true,
"description": "Resultado de la comparación de location."
},
{
"name": "reason",
"type": "string|null",
"required": true,
"description": "Razón textual del resultado."
},
{
"name": "matchedZoneIndex",
"type": "number|null",
"required": true,
"description": "Índice de la zona que coincidió."
},
{
"name": "distanceKm",
"type": "number|null",
"required": true,
"description": "Distancia ya calculada en kilómetros."
},
{
"name": "radiusKm",
"type": "number|null",
"required": true,
"description": "Radio usado por la zona coincidente."
},
{
"name": "nearestZoneIndex",
"type": "number|null",
"required": true,
"description": "Índice de la zona más cercana."
},
{
"name": "nearestDistanceKm",
"type": "number|null",
"required": true,
"description": "Distancia a la zona más cercana."
}
]
}
]
}
]
},
{
"name": "retrieval",
"type": "object",
"required": true,
"description": "Metadatos de retrieval expuestos tal como los devuelve el motor.",
"children": [
{
"name": "evaluatedCount",
"type": "number|null",
"required": true,
"description": "Cantidad de Offers evaluados."
},
{
"name": "returnedCount",
"type": "number|null",
"required": true,
"description": "Cantidad de candidatos devueltos por retrieval."
},
{
"name": "queriedOfferCount",
"type": "number|null",
"required": true,
"description": "Cantidad de Offers consultados."
},
{
"name": "batchesRead",
"type": "number|null",
"required": true,
"description": "Cantidad de lotes leídos."
},
{
"name": "exhausted",
"type": "boolean|null",
"required": true,
"description": "Indica si el retrieval se agotó."
},
{
"name": "hasMoreCoarseCandidates",
"type": "boolean|null",
"required": true,
"description": "Indica si quedan candidatos gruesos por leer."
},
{
"name": "safetyCapReached",
"type": "boolean|null",
"required": true,
"description": "Indica si se alcanzó el safety cap."
},
{
"name": "targetCandidateCount",
"type": "number|null",
"required": true,
"description": "Objetivo de candidatos usado por el retrieval."
},
{
"name": "queryBatchSize",
"type": "number|null",
"required": true,
"description": "Tamaño de lote de consulta del retrieval."
},
{
"name": "maxEvaluatedOffers",
"type": "number|null",
"required": true,
"description": "Máximo de Offers evaluados."
},
{
"name": "includeBlockedRequested",
"type": "boolean",
"required": true,
"description": "Refleja que el preview siempre pide incluir blocked."
}
]
}
]
Constantes del módulo
Estas constantes controlan el alcance del preview y los campos consultados. Son valores de solo lectura dentro del archivo.
PREVIEW_TARGET_CANDIDATE_COUNT[badge:read-only]20— Objetivo fijo de candidatos para el preview.REQUIREMENT_SELECT_PROJECTION[badge:read-only]'_id createdBy intent publication.status lifecycle.status meta requirement'— Proyección mínima del Requirement.OFFER_GEOGRAPHY_SELECT_PROJECTION[badge:read-only]'meta location'— Proyección mínima para el audit geográfico de Offers.PHYSICAL_PREFERENCE_FIELDS[badge:read-only]string[]— Campos físicos que el resumen reconoce.INFRASTRUCTURE_PREFERENCE_FIELDS[badge:read-only]string[]— Campos de infraestructura que el resumen reconoce.
Behaviors
El archivo aplica una serie de comportamientos bien delimitados. Cada uno transforma datos ya existentes, sin escribir en la base de datos.
parseDnsServersEnvValue(value)— Convierte una lista separada por comas en IPv4 válidas, o devuelvenullsi no hay valores útiles.applyDnsOverrideIfConfigured(envValue, deps)— Aplicadns.setServers()solo cuando el parseo produce direcciones válidas.summarizeLocationComparison(candidate)— Extrae la evidencia designals.locationya calculada por el motor.summarizeLocationOutcomeTotals(candidates)— Cuenta compatibilidades geográficas por estado.summarizeRequirementForPreview(requirement, requirementEligibility, requirementValidationReasons)— Construye el resumen seguro del Requirement.summarizePresentationStateTotals(candidates)— Cuenta candidatos por estado de presentación.summarizeMatchResults(result, matchOptions)— Construye el resumen global, incluyendopresentableCountyhiddenBlockedCount.summarizeGeographyAvailability(candidates, offerFieldsById)— Genera el audit geográfico del lado Offer para los candidatos evaluados.getBudgetWarningReason(candidate, presentationState)— Devuelve la razón de precio solo parabudget_warning.summarizeCandidateForPreview(candidate, offerFieldsById, presentationPositionByOfferId)— Formatea cada candidato para el preview.summarizeRetrievalDiagnostics(meta, matchOptions)— Expone metadatos de retrieval sin inventar campos.generateMatchPreview(requirementId)— Coordina todo el proceso y devuelve la estructura final.isMainModulebranch — Ejecuta el modo CLI, imprime JSON y ajustaprocess.exitCode.
Gotchas & edge cases
Estos puntos están directamente protegidos por el código y por las pruebas del archivo. fileciteturn0file0turn0file3turn0file16
[!IMPORTANT]
candidate.statusypresentationStateno son lo mismo. Un candidatoblockedpuede aparecer comobudget_warningy seguir siendo presentable. El script conserva esa distinción y no la recalcula. fileciteturn0file0turn0file16
[!WARNING]
geographyAuditsolo describe los Offers ya evaluados en esta corrida. No hace un censo de toda la colección y no representa disponibilidad global.
[!NOTE] El override de DNS solo aplica si
PLANO_TRANSPORT_DNS_SERVERScontiene IPv4 válidas. Los valores ausentes, vacíos o malformados no tocan el DNS de Node.
[!CAUTION] El preview no expone direcciones exactas, latitud, longitud,
googleMapsUrl, owner id ni datos de contacto. Solo muestra disponibilidad y evidencia ya calculada.
- Si falta
requirementId, el CLI escribe un mensaje de uso y termina con error. - Si el
requirementIdno es unObjectIdválido, el CLI también termina con error. - Si
generateMatchPreview()produceerror, el proceso imprime el JSON y marcaprocess.exitCode = 1. includeBlockedsiempre se pasa comotrueen este script.presentableCountderiva depresentationState, no destatusdirecto. fileciteturn0file0turn0file16
Related
Este archivo se conecta con pocas piezas, pero todas son centrales para Match y para la lectura segura del preview.
services/api/src/modules/match/candidate-retrieval.js— Fuente autoritativa de generación de candidatos. El script consume su salida y no recalcula reglas de negocio.services/api/src/modules/match/candidate-presentation.js— CalculapresentationStatey el orden determinista de presentación.services/api/src/models/Listing.js— Se usa para cargar el Requirement y para leer la geografía mínima de los Offers.services/api/test/preview-match-for-requirement.test.js— ImportagenerateMatchPreview,parseDnsServersEnvValueyapplyDnsOverrideIfConfigured, y reemplaza métodos estáticos deListingpara aislar el comportamiento.
[!TIP] Las pruebas del archivo confirman dos aspectos clave del diseño. Verifican el override de DNS y el cálculo de
presentableCountfrente ahiddenBlockedCount.