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. fileciteturn0file0turn0file4

[!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. fileciteturn0file0turn0file4

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'. fileciteturn0file0turn0file9

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 devuelve null si no hay valores útiles.
  • applyDnsOverrideIfConfigured(envValue, deps) — Aplica dns.setServers() solo cuando el parseo produce direcciones válidas.
  • summarizeLocationComparison(candidate) — Extrae la evidencia de signals.location ya 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, incluyendo presentableCount y hiddenBlockedCount.
  • 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 para budget_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.
  • isMainModule branch — Ejecuta el modo CLI, imprime JSON y ajusta process.exitCode.

Gotchas & edge cases

Estos puntos están directamente protegidos por el código y por las pruebas del archivo. fileciteturn0file0turn0file3turn0file16

[!IMPORTANT] candidate.status y presentationState no son lo mismo. Un candidato blocked puede aparecer como budget_warning y seguir siendo presentable. El script conserva esa distinción y no la recalcula. fileciteturn0file0turn0file16

[!WARNING] geographyAudit solo 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_SERVERS contiene 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 requirementId no es un ObjectId válido, el CLI también termina con error.
  • Si generateMatchPreview() produce error, el proceso imprime el JSON y marca process.exitCode = 1.
  • includeBlocked siempre se pasa como true en este script.
  • presentableCount deriva de presentationState, no de status directo. fileciteturn0file0turn0file16

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 — Calcula presentationState y 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 — Importa generateMatchPreview, parseDnsServersEnvValue y applyDnsOverrideIfConfigured, y reemplaza métodos estáticos de Listing para 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 presentableCount frente a hiddenBlockedCount.