Kühlen & Lebensmittelprozesse
Kühlen, Gefrieren, Wiedererhitzen und Transport — vier Collections, die einen Temperaturwechsel aufzeichnen, und wie ein unvollendeter aussieht.
Ein heißes Gericht, das langsam abkühlt, verbringt Stunden in dem Bereich, in dem sich Bakterien vermehren; die Regel lautet also, es schnell herunterzubringen und das auch belegen zu können. Vier Collections zeichnen einen solchen Wechsel auf. Drei davon sind ein Wechsel in der Zeit — herunter, weiter herunter, wieder hinauf — und die vierte ist derselbe Gedanke im Raum.
| Collection | Berechtigung | Was ein Datensatz ist |
|---|---|---|
cooling | cooling:read | Ein heißes Produkt, das auf Kühllagertemperatur heruntergebracht wurde. |
freezing | freezing:read | Ein Produkt, das bis zum Gefrieren heruntergebracht wurde. |
reheating | reheating:read | Ein kaltes Produkt, das wieder auf Ausgabetemperatur gebracht wurde. |
transport | transport:read | Ein Produkt, das von einem Ort zum anderen bewegt wurde, mit der Temperatur an beiden Enden. |
Alle vier teilen sich den Snapshot-Umschlag: id,
deleted, capturedAt, receivedAt, sequence und der Rest stehen neben dem
data, das unten beschrieben ist.
cooling
Ein Zyklus: ein Produkt, wann er begann und wie heiß es war, wann er endete und wie kalt es war.
| Feld | Typ | Bedeutung |
|---|---|---|
timestamp | string, Pflicht | Der Zeitstempel des Datensatzes selbst, als Zeichenkette mit Millisekunden seit der Epoche. Nicht die Uhr des Zyklus — das sind die beiden Daten darunter. |
product | string, Pflicht | Was gekühlt wurde, als Text. |
beginDate | string, Pflicht | Wann der Zyklus begann, als Zeichenkette mit Millisekunden seit der Epoche. |
beginTemperature | number, Pflicht | Die Temperatur zu Beginn. |
endDate | string | null | Wann er endete. null, solange er das nicht hat. |
endTemperature | number | null | Die Temperatur am Ende. null zusammen mit endDate. |
reminderId | string | null | Die Erinnerung, die die App an den Zyklus gehängt hat, sofern es eine gab. |
userId | string | null | Der Mitarbeiter, der ihn erfasst hat, sofern die App einen erfasst hat. |
{
"id": "0e5d1a7c-9f24-4b13-8d60-c1a7f0b25e94",
"collection": "cooling",
"deleted": false,
"capturedAt": "1789779612441",
"receivedAt": "1789779910228",
"sequence": "1",
"data": {
"timestamp": "1789772400000",
"product": "Blanquette de veau",
"beginDate": "1789772400000",
"beginTemperature": 63.5,
"endDate": "1789779600000",
"endTemperature": 8.2,
"userId": "2774953d-8d9b-4a68-8ec4-1209edd90777"
}
}
product ist ein Name, keine Referenz. Auf diesen Datensätzen gibt es kein
productId, Sie können sie also nicht mit products verbinden, ohne
Zeichenketten abzugleichen — und die Zeichenketten werden während des Betriebs
getippt, in der Sprache des Restaurants.
reminderId ist undurchsichtig. Keine Collection dieser API löst es auf.
Führen Sie es mit, wenn es Ihnen beim Gruppieren von Datensätzen hilft; erwarten
Sie nicht, es nachschlagen zu können.
freezing
Dieselbe Struktur, für ein Produkt, das bis zum Gefrieren heruntergebracht wurde
statt auf Kühlschranktemperatur. Feld für Feld ist es cooling.
| Feld | Typ | Bedeutung |
|---|---|---|
timestamp | string, Pflicht | Der Zeitstempel des Datensatzes selbst. |
product | string, Pflicht | Was gefroren wurde. |
beginDate | string, Pflicht | Wann der Vorgang begann. |
beginTemperature | number, Pflicht | Die Temperatur zu Beginn. |
endDate | string | null | Wann er endete, oder null. |
endTemperature | number | null | Die Temperatur am Ende, oder null. |
reminderId | string | null | Wie oben. |
userId | string | null | Wer ihn erfasst hat. |
Nichts im Datensatz sagt, welcher der drei Fälle vorliegt: Das ist die
Collection, aus der er stammt, und das Feld collection im Umschlag spiegelt
sie. Eine Pipeline, die alle drei in eine Tabelle zusammenführt, muss diese
Spalte behalten.
reheating
Ein kaltes Produkt, das wieder hinaufgebracht wird. Wieder dieselbe Struktur, in
der anderen Richtung gelesen: beginTemperature ist der niedrige Wert und
endTemperature der hohe.
| Feld | Typ | Bedeutung |
|---|---|---|
timestamp | string, Pflicht | Der Zeitstempel des Datensatzes selbst. |
product | string, Pflicht | Was wiedererhitzt wurde. |
beginDate | string, Pflicht | Wann es hineinkam. |
beginTemperature | number, Pflicht | Die Temperatur zu Beginn. |
endDate | string | null | Wann es herauskam, oder null. |
endTemperature | number | null | Die Temperatur, die es erreicht hat, oder null. |
reminderId | string | null | Wie oben. |
userId | string | null | Wer es erfasst hat. |
Ein Vorgang ohne Ende sieht so aus:
{
"id": "b4c8e21d-3a76-4f58-9c02-77ad5e1b8f30",
"collection": "reheating",
"deleted": false,
"capturedAt": "1789783221907",
"receivedAt": "1789783402115",
"sequence": "2",
"data": {
"timestamp": "1789783200000",
"product": "Gratin dauphinois",
"beginDate": "1789783200000",
"beginTemperature": 4.1,
"endDate": null,
"endTemperature": null,
"userId": "2774953d-8d9b-4a68-8ec4-1209edd90777"
}
}
transport
Derselbe Wechsel, im Raum statt in der Zeit: wo das Produkt losgefahren ist und wo es angekommen ist, mit einer Temperatur an jedem Ende.
| Feld | Typ | Bedeutung |
|---|---|---|
timestamp | string, Pflicht | Der Zeitstempel des Datensatzes selbst. |
product | string, Pflicht | Was transportiert wurde, als Text. |
departureLocation | string, Pflicht | Wo es losging, als Text. |
departureTime | string, Pflicht | Wann es losging, als Zeichenkette mit Millisekunden seit der Epoche. |
departureTemperature | number, Pflicht | Die Temperatur beim Abgang. |
arrivalLocation | string | null | Wo es ankam. null, solange es das nicht ist. |
arrivalTime | string | null | Wann es ankam, oder null. |
arrivalTemperature | number | null | Die Temperatur bei der Ankunft, oder null. |
reminderId | string | null | Wie oben. |
userId | string | null | Wer es erfasst hat. |
{
"id": "f31b6a08-5c47-4de9-b1a5-6e0c9d247b82",
"collection": "transport",
"deleted": false,
"capturedAt": "1789786944610",
"receivedAt": "1789787101883",
"sequence": "3",
"data": {
"timestamp": "1789785000000",
"product": "Plats préparés — service traiteur",
"departureLocation": "Cuisine centrale",
"departureTime": "1789785000000",
"departureTemperature": 3.4,
"arrivalLocation": "Salle Montparnasse",
"arrivalTime": "1789786800000",
"arrivalTemperature": 5.9,
"userId": "2774953d-8d9b-4a68-8ec4-1209edd90777"
}
}
Die beiden Orte sind Freitext, keine Bereiche und keine Adressen. Sie sind, was die Person getippt hat, und die Ankunftsseite ist genau so optional wie die Ankunftstemperatur: Ein Datensatz kann einen Abgang tragen und sonst nichts.
Damit arbeiten
Ein leeres Ende ist zweierlei. endDate und endTemperature — und
arrivalTime, arrivalTemperature und arrivalLocation bei transport —
dürfen null sein, und der Datensatz sagt Ihnen nicht, warum. Es kann ein
Vorgang sein, der noch läuft, während Sie ihn lesen, oder einer, den nie jemand
abgeschlossen hat. Beide sehen gleich aus.
Das zählt in dem Moment, in dem Sie eine Dauer berechnen. Die Daten sind
Zeichenketten, endDate - beginDate subtrahiert bei einem abgeschlossenen
Datensatz also sauber und wandelt bei einem offenen null in Null um und gibt
Ihnen eine große negative Zahl statt eines Fehlers. Nichts wirft eine Ausnahme;
Ihr Mittelwert ist einfach falsch. Filtern Sie zuerst auf abgeschlossene
Datensätze, zählen Sie die offenen getrennt und entscheiden Sie selbst, ab
welchem Alter ein offener Datensatz aufhört, in Arbeit zu sein, und anfängt,
liegen geblieben zu sein — nichts in dieser API trifft diese Entscheidung für
Sie.
Rechnen Sie mit beginDate und endDate, nicht mit timestamp. Die Daten
sind die Uhr des Vorgangs. timestamp ist die des Datensatzes selbst, und
daraus wird keine Dauer gemacht.
Ordnen Sie nach capturedAt, nicht nach receivedAt. Ein Tablet in einem
Kühlraum lädt hoch, sobald es Empfang findet; ein Zyklus, der um 09:00 Uhr
begonnen hat, kann also um 14:00 Uhr ankommen.
Die Seite zum Umschlag erzählt die ganze Geschichte.
Abwesenheit beweist nichts. Ein Produkt ohne Kühldatensatz wurde vielleicht nie gekühlt oder liegt auf einem Gerät, das noch nicht hochgeladen hat. Machen Sie aus einer Anzahl davon keine Compliance-Kennzahl — siehe die Anmerkung zur Vollständigkeit.