2017-09-21 3 views
0

Ich habe ein Sails.js-Modell namens "products", das eine MongoDB-Sammlung ist. Im Modell habe ich einen beforeCreate() Hook, der eine eindeutige productId wie "170921-00001" im Format "YYMMDD-count" generiert, wobei count die für den Tag erstellte Datensatznummer ist. Dies ist, was mein Modell wie folgt aussieht:Datenbankaufruf in Sails.js beforeCreate() - Model-Hook verursacht Validierungsfehler

module.exports = { 

    attributes: { 
     name: { type: 'string', required: true }, 
     productId: { type: 'string', unique: true } 
    }, 


    beforeCreate: function(values, next) { 

     var moment = require('moment'); 

     // Generate the productId 
     Products.count({ 
      createdAt: { 
       '>=': moment().format('YYYY-MM-DD'), 
       '<': moment(moment().format('YYYY-MM-DD')).add(1, 'days').format('YYYY-MM-DD') 
      } 
     }).exec(function(error, productCount) { 

      if (error) { 
       throw error; 
      } 

      var count = '', 
       totalPlaces = 5 - productCount.toString().length; 
      while (totalPlaces > 0) { 
       count = count + '0'; 
       totalPlaces--; 
      } 

      values.productId = moment().format('YYMMDD') + '-' + (count + productCount); 

      next(); 
     }); 
    } 
}; 

Das Problem ist, diese Pause, wenn ich mehrere Produkte in die Sammlung in einem einzigen Datenbank-Aufruf einfügen versuchen. Ich erhalte die folgende Fehlermeldung:

debug: Error (E_VALIDATION) :: 1 attribute is invalid 
MongoError: E11000 duplicate key error index: my-app.products.$productId_1 dup key: { : "170921-00002" } 
    at Function.MongoError.create (/Users/Nag/Code/my-app/web-service/node_modules/sails-mongo/node_modules/mongodb-core/lib/error.js:31:11) 
    at toError (/Users/Nag/Code/my-app/web-service/node_modules/sails-mongo/node_modules/mongodb/lib/utils.js:114:22) 
    at /Users/Nag/Code/my-app/web-service/node_modules/sails-mongo/node_modules/mongodb/lib/collection.js:658:23 
    at handleCallback (/Users/Nag/Code/my-app/web-service/node_modules/sails-mongo/node_modules/mongodb/lib/utils.js:95:56) 
    at resultHandler (/Users/Nag/Code/my-app/web-service/node_modules/sails-mongo/node_modules/mongodb/lib/bulk/ordered.js:421:14) 
    at /Users/Nag/Code/my-app/web-service/node_modules/sails-mongo/node_modules/mongodb-core/lib/connection/pool.js:455:18 
    at /Users/Nag/Code/my-app/web-service/node_modules/async-listener/glue.js:188:31 
    at _combinedTickCallback (internal/process/next_tick.js:73:7) 
    at process._tickDomainCallback (internal/process/next_tick.js:128:9) 
    at process.fallback (/Users/Nag/Code/my-app/web-service/node_modules/async-listener/index.js:529:15) 

Invalid attributes sent to undefined: 
• productId 
    • A record with that `productId` already exists (`170921-00002`). 

Wenn ich einen einzelnen Datensatz einfügen, es funktioniert gut, aber wenn ich mehrere Datensätze, der erste Datensatz einfügen wird eingefügt, aber jede nachfolgende Datensatz erzeugt diesen Fehler. Liegt es daran, dass das Dokument eingefügt wird, bevor der Hook den Wert productId berechnet, bevor er den vorherigen Datensatz eingefügt hat? Wie löse ich das?

+0

Welche Version von Sails verwenden Sie? Und können Sie den Code posten, den Sie zum Erstellen der Datensätze verwenden? – sgress454

Antwort

0

Dies ist eine ziemlich schwierige Lösung mit Waterline. Wenn diese Konvention für die productId noch nicht in Ihrem Code eingebettet ist, ist es am klügsten, sie zu ändern, bevor zu viel darauf aufgebaut wird.

Ich habe ein ähnliches Problem in meinem Code, aber mit einem Modell, das ich nie ein Vielfach von in einem einzigen Aufruf erstellen - zufällig „Kollisionen“ zum Schutz vor benutze ich nur ein trycatch in die zuvor erstellen und erneut ausführen, das schafft in der catch Block.

Aber hier ist eine andere Lösung ... vielleicht wäre es möglich, eine Live-Tag-count in Ihrem Product.js Datei zu speichern:

var day = new Date(); 
var daycount = 0; 

module.exports = { 

    attributes: { 
     name: { type: 'string', required: true }, 
     productId: { type: 'string', unique: true } 
    }, 
    beforeCreate: function(values, next) { 
     var moment = require('moment'); 
     // Generate the productId 
     Products.count({ 
      createdAt: { 
       '>=': moment().format('YYYY-MM-DD'), 
       '<': moment(moment().format('YYYY-MM-DD')).add(1, 'days').format('YYYY-MM-DD') 
      } 
     }).exec(function(error, productCount) { 
      if (error) { 
       throw error; 
      } 

      // stop here to check your day and count variables... 
      var today = new Date(); 
      if (productCount === 0) { // none created yet today 
       day = today; 
       daycount = 0; 
      } else if (daycount === 0) { // must have started the app with records in db 
       daycount = productCount; 
      } 
      if (day < today && day.getDate() < today.getDate()) { // new day 
       day = today; 
       daycount = 0; 
      } 
      productCount = Math.max(productCount, daycount++); // notice the increment 
      // now proceed as before... 

      var count = '', 
       totalPlaces = 5 - productCount.toString().length; 
      while (totalPlaces > 0) { 
       count = count + '0'; 
       totalPlaces--; 
      } 
      values.productId = moment().format('YYMMDD') + '-' + (count + productCount); 
      next(); 
     }); 
    } 
}; 

Auch wenn viele Produkt Anrufe alle zuvor haben ihre beforeCreate Lauf schaffen hinzugefügt zu die Datenbank (und damit die gleichen productCount erhalten), die daycount und day Variablen sollten live im Speicher gehalten und einzeln aktualisiert werden.

Auch wenn das funktioniert, fühlt es sich prekär an - ich würde ernsthaft darüber nachdenken, Ihre Daten zu restrukturieren, anstatt diesen Ansatz zu wählen!

0

Wenn Sie Daten an Ihre API senden, legen Sie den Namen Ihres Produkts fest?

name: { type: 'string', required: true } <= Here, the name is required from client to api. 
+0

Ihre Frage sollte in den Kommentaren sein. Keine Antwort auf die Frage. –

0

Sie sollten eindeutige Werte vor Update-Methode machen:

values.productId = moment().format('YYMMDD') + '-' + (count + productCount);

Diese Zeile des Codes sein könnte:

values.productId = moment().format('YYMMDD') + '-' + (count + productCount)+ Math.random();

es immer eindeutige ID erzeugen.

Verwandte Themen