2016-04-06 10 views
1

Ich erstelle eine App, wo ein Benutzer verschiedene Arten von benutzerdefinierten Feldern erstellen kann, und ich möchte diese Felder in Mongo speichern. Zu den Typen gehören Zeichenfolge, Nummer und Datum. Mein Schema sieht so aus:Mongoose Mixed type Feld mit Daten

const MetaSchema = new mongoose.Schema({ 
    key: String, 
    value: {type: mongoose.Schema.Types.Mixed}, 
    type: String, 
    created_at: {type: Date, default: Date.now} 
}); 

Das funktioniert gut, und ich kann meine Daten wie erwartet speichern. Das Problem ist, wenn ich ein Datum zum Beispiel gespeichert werden sollen, die über auf dem Server im ISO-Format gesendet wird, eine Nutzlast, die wie folgt aussehen könnte:

{ 
    "key": "Contract Signed", 
    "value": "2016-04-06T22:35:11.540Z", 
    "type": "date" 
} 

Jede Art und Weise ich Mongo/Mungo und zu behandeln bekommen Speichern Sie dies wie ein Datum statt einer Zeichenfolge? Wenn ich dies auf Datum einstelle, denke ich, dass es den Trick machen würde, aber ich muss fast alles speichern, was sie für benutzerdefinierte Felder einbringen können. Vielen Dank!

TLDR: Kann ein gemischter Datentyp in mongoose/mongo basierend auf dem Typ der einzufügenden Daten (IE Date vs String) unterschiedlich behandelt werden.

+1

Der bessere Weg, dies zu handhaben, ist mit [diskriminators] (http://mongoosejs.com/docs/discriminators.html), die ihren eigenen "Typ" implementieren (Standard als '__t'), der jedes Objekt im Wesentlichen bindet zu seinem eigenen Pseudomodell mit seinem eigenen Schema. Dann können Sie für jeden "Typ" eine "strikte" Handhabung haben. Das ist viel besser, als zu versuchen, irgendeine Validierungslogik, in der Sie 'Mixed' verwenden, zu rollen. –

+0

Danke @NeilLunn! Das sieht genau so aus, wie ich es brauche. Möchten Sie eine Antwort einreichen, die ich annehmen kann, damit Sie mehr Internetpunkte erhalten? :) Vielen Dank! – Greg

Antwort

2

Mit Mungo discriminators ist wahrscheinlich der Weg hier zu gehen. Sie arbeiten tatsächlich mit ihrer eigenen "Typ" (Standard __t, aber kann überschrieben werden) Eigenschaft innerhalb der gespeicherten Dokumente, die Mungo erlaubt, tatsächlich eine Art "Modell" auf jedes Objekt mit seinem eigenen angehängten Schema anzuwenden.

Als kurzes Beispiel:

var async = require('async'), 
    util = require('util'), 
    mongoose = require('mongoose'), 
    Schema = mongoose.Schema; 

mongoose.connect('mongodb://localhost/things'); 
mongoose.set("debug",true); 

function BaseSchema() { 

    Schema.apply(this,arguments); 

    this.add({ 
    key: String, 
    created_at: { type: Date, default: Date.now } 
    }); 

} 

util.inherits(BaseSchema,Schema); 

var metaSchema = new BaseSchema(); 

var stringSchema = new BaseSchema({ 
    value: String 
}); 

var numberSchema = new BaseSchema({ 
    value: Number 
}); 

var dateSchema = new BaseSchema({ 
    value: Date 
}); 

var MetaModel = mongoose.model('MetaModel',metaSchema), 
    StringModel = MetaModel.discriminator('StringModel', stringSchema), 
    NumberModel = MetaModel.discriminator('NumberModel', numberSchema), 
    DateModel = MetaModel.discriminator('DateModel', dateSchema); 

async.series(
    [ 
    function(callback) { 
     MetaModel.remove({},callback); 
    }, 
    function(callback) { 
     async.each(
     [ 
      { "model": "StringModel", "value": "Hello" }, 
      { "model": "NumberModel", "value": 12 }, 
      { "model": "DateModel", "value": new Date() } 
     ], 
     function(item,callback) { 
      mongoose.model(item.model).create(item,callback) 
     }, 
     callback 
    ); 
    }, 
    function(callback) { 
     MetaModel.find().exec(function(err,docs) { 
     console.log(docs); 
     callback(err); 
     }); 
    }, 
    function(callback) { 
     DateModel.findOne().exec(function(err,doc) { 
     console.log(doc); 
     callback(err); 
     }); 
    } 
    ], 
    function(err) { 
    if (err) throw err; 
    mongoose.disconnect(); 
    } 
) 

Also, da diese im Grunde „verwandten“ sind ich ein „Base“ Schema mit den gemeinsamen Elementen am definieren. Dann gibt es natürlich separate Schemata für jeden "Typ". Die tatsächliche Zuordnung zu dem Kern „Modell“ geschieht in diesen Linien:

var MetaModel = mongoose.model('MetaModel',metaSchema), 
    StringModel = MetaModel.discriminator('StringModel', stringSchema), 
    NumberModel = MetaModel.discriminator('NumberModel', numberSchema), 
    DateModel = MetaModel.discriminator('DateModel', dateSchema); 

Dies bedeutet, dass MetaModel definiert eigentlich die Erfassung und „default“ schema assingment. Die folgenden Zeilen verwenden .discriminator() aus diesem Modell, um die anderen "Typen" von Dokumenten zu definieren, die in derselben Sammlung gespeichert werden.

Mit der Debugging-Ausgabe auf zeigen, was passiert ist, produziert das Angebot etwas wie folgt aus:

Mongoose: metamodels.remove({}) {} 
Mongoose: metamodels.insert({ value: 'Hello', __t: 'StringModel', created_at: new Date("Thu, 07 Apr 2016 00:24:08 GMT"), _id: ObjectId("5705a8a8443c0f74491bdec0"), __v: 0 }) 
Mongoose: metamodels.insert({ value: 12, __t: 'NumberModel', created_at: new Date("Thu, 07 Apr 2016 00:24:08 GMT"), _id: ObjectId("5705a8a8443c0f74491bdec1"), __v: 0 }) 
Mongoose: metamodels.insert({ value: new Date("Thu, 07 Apr 2016 00:24:08 GMT"), __t: 'DateModel', created_at: new Date("Thu, 07 Apr 2016 00:24:08 GMT"), _id: ObjectId("5705a8a8443c0f74491bdec2"), __v: 0 }) 
Mongoose: metamodels.find({}) { fields: undefined } 
[ { created_at: Thu Apr 07 2016 10:24:08 GMT+1000 (AEST), 
    __t: 'StringModel', 
    __v: 0, 
    value: 'Hello', 
    _id: 5705a8a8443c0f74491bdec0 }, 
    { created_at: Thu Apr 07 2016 10:24:08 GMT+1000 (AEST), 
    __t: 'NumberModel', 
    __v: 0, 
    value: 12, 
    _id: 5705a8a8443c0f74491bdec1 }, 
    { created_at: Thu Apr 07 2016 10:24:08 GMT+1000 (AEST), 
    __t: 'DateModel', 
    __v: 0, 
    value: Thu Apr 07 2016 10:24:08 GMT+1000 (AEST), 
    _id: 5705a8a8443c0f74491bdec2 } ] 
Mongoose: metamodels.findOne({ __t: 'DateModel' }) { fields: undefined } 
{ created_at: Thu Apr 07 2016 10:24:08 GMT+1000 (AEST), 
    __t: 'DateModel', 
    __v: 0, 
    value: Thu Apr 07 2016 10:24:08 GMT+1000 (AEST), 
    _id: 5705a8a8443c0f74491bdec2 } 

Sie, dass alles sehen kann, ist in der metamodels Sammlung dem Hauptmodell zugeordnet erstellt werden, aber wenn Referenzierung Bei jedem "Diskriminatormodell" wird automatisch ein __t Feld erstellt, das den Modellnamen enthält. Dies wird später beim Lesen der Daten verwendet, damit mongoose weiß, welches Modell und angehängtes Schema beim Umsetzen der Objekte angewendet werden soll.

Da diese natürlich alle ein eigenes Schema haben, gelten die Standardvalidierungsregeln. Darüber hinaus gelten alle "Instanzmethoden", die für jeden Typ an das Schema angehängt werden, genauso wie für jedes separate Modell.

Schließlich wird das Feld __t auch mit einem der "Diskriminatormodelle" für alle anderen Operationen wie Abfrage oder Aktualisierung angewendet.Wie in der letzten ausgeführten Anweisung gezeigt:

 DateModel.findOne().exec(function(err,doc) { 
     console.log(doc); 
     callback(err); 
     }); 

Und den eigentlichen Aufruf:

Mongoose: metamodels.findOne({ __t: 'DateModel' }) { fields: undefined } 

Diese Eigenschaft Wert wird automatisch enthält den „Typen“, um anzuzeigen, und eine „virtuelle Sicht“ der Sammlungsdaten geben als ob es nur diesen bestimmten Typ enthielt.

Die tatsächliche Leistung liegt tatsächlich in allen Objekten, die sich in derselben Sammlung befinden, und der Fähigkeit von Mungo, den "Klassentyp" automatisch beim Abrufen von Daten zuzuordnen.

+0

Alter, eine der umfassendsten Antworten, die ich je auf Stackoverflow bekommen habe. Danke dass du dir die Zeit nimmst! Sehr geschätzt! – Greg