ListingEvent
Overview
services/api/src/models/ListingEvent.js define un modelo Mongoose para registrar eventos de actividad sobre una Listing. El esquema persiste quién recibió el evento, qué acción ocurrió, desde qué origen llegó y si ya fue leído. También fija la colección listing_events y activa timestamps automáticos.
Este modelo sirve como historial conductual de una publicación. Sus valores de type y source muestran que cubre interacciones como like, comment, qr_scan, pdf_download, contact_click, detail_open y profile_open.
[!NOTE] El archivo no contiene lógica de negocio adicional. Solo define el esquema, sus índices y la exportación del modelo Mongoose.
How it works
El archivo construye un Schema de Mongoose, define los campos del evento y después registra el modelo con mongoose.model("ListingEvent", ListingEventSchema). Mongoose guarda los documentos en la colección listing_events y añade automáticamente createdAt y updatedAt por timestamps: true.
El esquema también declara dos índices. Uno organiza la consulta por ownerId y createdAt descendente. El otro combina listingId, type y createdAt descendente para soportar la agregación del digest descrita en el comentario del código.
Key components
{
"title": "ListingEvent",
"content": "Modelo Mongoose que persiste eventos de actividad en la colecci\u00f3n listing_events y expone el historial conductual de una publicaci\u00f3n.",
"type": "class",
"filePath": "services/api/src/models/ListingEvent.js",
"badges": [
"public"
]
}
Data & fields
[
{
"name": "listingId",
"type": "Schema.Types.ObjectId",
"required": true,
"description": "Referencia obligatoria a \"Listing\". Identifica la publicación relacionada con el evento."
},
{
"name": "ownerId",
"type": "Schema.Types.ObjectId",
"required": true,
"description": "Referencia obligatoria a \"User\". Representa al propietario del evento."
},
{
"name": "actorId",
"type": "Schema.Types.ObjectId",
"description": "Referencia opcional a \"User\" que ejecutó la acción. Por defecto es null."
},
{
"name": "actorName",
"type": "String",
"description": "Nombre visible del actor. Usa trim y por defecto es una cadena vacía."
},
{
"name": "type",
"type": "String",
"required": true,
"description": "Tipo de evento. Valores permitidos: like, unlike, comment, qr_scan, pdf_download, contact_click, detail_open y profile_open."
},
{
"name": "source",
"type": "String",
"description": "Origen del evento. Valores permitidos: feed, drawer, profile, pdf y qr. Por defecto es feed."
},
{
"name": "metadata",
"type": "Schema.Types.Mixed",
"description": "Carga útil libre para información adicional. Por defecto es un objeto vacío."
},
{
"name": "read",
"type": "Boolean",
"description": "Marca de lectura del evento. Por defecto es false."
}
]
Campos obligatorios
listingId[badge:required]Schema.Types.ObjectId— referencia a laListingasociada al evento.ownerId[badge:required]Schema.Types.ObjectId— referencia alUserpropietario del registro.type[badge:required]String— clasifica el evento dentro del conjunto permitido por el esquema.
Campos opcionales
actorId[badge:optional]Schema.Types.ObjectId— identifica alUserque realizó la acción, si existe.actorName[badge:optional]String— guarda el nombre visible del actor, contrim: true.source[badge:optional]String— registra el contexto de origen del evento.metadata[badge:optional]Schema.Types.Mixed— almacena datos auxiliares sin estructura fija.read[badge:optional]Boolean— indica si el evento ya fue marcado como leído.
Valores permitidos
type
likeunlikecommentqr_scanpdf_downloadcontact_clickdetail_openprofile_open
source
feeddrawerprofilepdfqr
Behaviors
mongoose.Schema(...)— define la forma del documento y sus validaciones de enum, requerido y valores por defecto.ListingEventSchema.index({ ownerId: 1, createdAt: -1 })— acelera lecturas recientes por propietario.ListingEventSchema.index({ listingId: 1, type: 1, createdAt: -1 })— soporta la agregación del digest por publicación, tipo y ventana temporal.mongoose.model("ListingEvent", ListingEventSchema)— registra y exporta el modeloListingEvent.
Persistence details
El esquema persiste en la colección explícita listing_events. La opción timestamps: true añade createdAt y updatedAt automáticamente a cada documento.
[!IMPORTANT] El índice compuesto
{ listingId: 1, type: 1, createdAt: -1 }no es decorativo. El comentario del archivo explica que evita escanear todos los eventos de una publicación cuando el digest agrupa por tipo dentro de una ventana temporal.
Relationships
ListingEvent se relaciona directamente con Listing mediante listingId. También se relaciona con User por ownerId y actorId, lo que permite distinguir al propietario del evento y al actor que produjo la interacción.
El diseño del esquema muestra que el modelo está orientado a registrar actividad contextual sobre una publicación. La presencia de source, type y read confirma que no solo almacena el evento, sino también su procedencia y su estado de lectura.
Gotchas & edge cases
[!WARNING]
listingId,ownerIdytypeson obligatorios. Un documento incompleto no cumple el esquema.
[!WARNING]
metadatausaSchema.Types.Mixed. El esquema no restringe su forma interna.
[!CAUTION]
actorIdpuede sernull. El campo existe aunque no haya un actor asociado.
[!NOTE]
readnace enfalse. Los eventos nuevos se crean como no leídos.
Related
services/api/src/models/Listing.jsaparece como referencia enlistingId.services/api/src/models/User.jsaparece como referencia enownerIdyactorId.services/api/src/models/ListingEvent.jses el único archivo documentado aquí y define toda la persistencia de este modelo.
[!NOTE] En el código revisado no aparecen endpoints HTTP dentro de este archivo. Por eso no se incluye una sección de API.