Aplicación de planificación de turnos de modelado:

Se me ocurrió tal estructura de datos que describe el shift.

{
    "fromHour" : 7,
    "fromMinute" : 30,
    "toHour" : 9,
    "toMinute" : 30,
    "week" : 5,
    "date" : "2015-01-26",
    "user" : {
       // ...
    },
    "_id" : ObjectId("54d0e4a82b9dc26c0c0f36e7")
}

Lo principal que necesito almacenar es la información: 1. cuándo comienza el turno (hora y minuto), 2. cuándo termina el turno (hora y minuto) y 3. qué fecha está sucediendo realmente.

Los campos fromHour, fromMinute, toHour, toMinute y date como cadena ISO me funcionaron bastante bien para almacenar y consultar los turnos por fecha en particular.

El problema ocurrió cuando necesitaba generar informes a partir de él. Digamos, quiero obtener todos los cambios de "2015-01-01" a "2015-02-01" en el rango de 07:00 a 23:00.

Puedo agregar la cláusula $and a mi consulta, como

[ { fromHour: { '$gte': 7 } },
  { fromMinute: { '$gte': 0 } },
  { toHour: { '$lt': 23 } },
  { toMinute: { '$lt': 0 } } ]

Pero eso no funciona bien, ya que para el turno allí toMinute es 30, el $lt será falso.

Estoy tratando de encontrar una estructura de datos eficiente que permita almacenar períodos de tiempo que sean fáciles de consultar.

1
Alexander Beletsky 12 feb. 2015 a las 18:42

2 respuestas

La mejor respuesta

Almacenar horas y minutos en dos campos diferentes separados es demasiado propenso a errores y dificulta su trabajo. Dado que Mongo no tiene un tipo de datos de "Hora" distinto, solo Fecha, y los turnos generalmente comienzan y terminan en momentos "fáciles", recomendaría implementar algo como convertir la hora a un número real en su aplicación de esta manera:

00:00 --> 0
01:00 --> 1
...
08:00 --> 8
08:15 --> 8.25
08:30 --> 8.5
...
16:30 --> 16.5
...

Es un poco de trabajo adicional en la aplicación porque tiene que convertir mientras guarda o muestra, pero aún es mejor que tener un valor de tiempo único en dos campos diferentes.

Entonces sus datos se verían así:

{
    "shiftStart" : 7.5,
    "shiftEnd" : 9.5,
    "week" : 5,
    "date" : "2015-01-26",
    "user" : {
       // ...
    },
    "_id" : ObjectId("54d0e4a82b9dc26c0c0f36e7")
}

Y tu consulta:

[ { shiftStart: { '$gte': 7 } },
  { shiftEnd: { '$lt': 23 } } ]
2
mshthn 12 feb. 2015 a las 16:09

Puede almacenar los datos con el tipo de fecha en formato ISODate(...) y luego usar $project y Operadores de agregación de fechas para consultar los datos.

Por su ejemplo:

db.shifts.aggregate([
  { $match:  //matches the dates first to filter out before $project step
    { datetimeStart:
      { $gte: ISODate("2015-01-01T07:00:00.000Z"),
        $lt: ISODate("2015-02-01T00:00:00.000Z")
      },
      datetimeEnd:
      { $gte: ISODate("2015-01-01T07:00:00.000Z"),
        $lt: ISODate("2015-02-01T00:00:00.000Z")
      }
    }
  },
  { $project:  // $project step extracts the hours
    { otherNeededFields: 1,  // any other fields you want to see
      datetimeStart: 1,
      datetimeEnd: 1,
      hourStart: { $hour: "$datetimeStart" },
      hourEnd: { $hour: "$datetimeEnd" }
    }
  },
  { $match: // match the shift hours
    { hourStart: { $gte: 7 },
      hourEnd: { $lte: 23 }
    }
  }
])

Con este sistema sería posible , pero complicado encontrar algo más como turnos entre las 7:30 a. M. Y las 10:30 p. M.:

db.shifts.aggregate([
  { $match:  //matches the dates first to filter out before $project step
    { datetimeStart:
      { $gte: ISODate("2015-01-01T07:30:00.000Z"),
        $lt: ISODate("2015-02-01T00:00:00.000Z")
      },
      datetimeEnd:
      { $gte: ISODate("2015-01-01T07:30:00.000Z"),
        $lt: ISODate("2015-02-01T00:00:00.000Z")
      }
    }
  },
  { $project:  // $project step extracts the hours
    { otherNeededFields: 1,  // any other fields you want to see
      datetimeStart: 1,
      datetimeEnd: 1,
      hourStart: { $hour: "$datetimeStart" },
      minStart: { $minute: "$datetimeStart" },
      hourEnd: { $hour: "$datetimeEnd" },
      minEnd: { $minute: "$date
    }
  },
  { $match: // match the shift hours
    { $or:
      [
        {hourStart: 7, minStart: {$gte: 30}}, // hour is 7, minute >= 30
        {hourStart: { $gte: 8 }}  // hour is >= 8
      ],
      $or:
      [
        {hourEnd: 22, minEnd: {$lte: 30}}, // hour is 22, minute <= 30
        {hourEnd: { $lte: 21 }} // hour is <= 21
      ]
    }
  }
])
1
NoOutlet 12 feb. 2015 a las 17:26