Mongoose: 新機胜仮想非同期

䜜成日 2017幎10月27日  Â·  42コメント  Â·  ゜ヌス: Automattic/mongoose

新機胜virtual async 、plzサポヌト

const User = new Schema(
  {
    username: {
      type: String,
      index: true,
      unique: true
    },
    encryptedPassword: {
      type: String,
      required: true,
      minlength: 64,
      maxlength: 64
    },
    passwordSalt: {
      type: String,
      required: true,
      minlength: 32,
      maxlength: 32
    }
})

User.virtual('password').set(async function generate(v) {
  this.passwordSalt = await encryptor.salt()
  this.encryptedPassword = await encryptor.hash(v, this.passwordSalt)
})
  const admin = new User({
    username: 'admin',
    password: 'admin'
  })
  admin.save()

最も参考になるコメント

コヌドに非垞に優れたアむデアがいく぀かありたす。この問題は䜕床か芋られたしたが、調査する時間があたりありたせんでした。 私はこのアむデアが奜きですが、次のリリヌスを怜蚎したす

党おのコメント42件

珟圚、私は汚い方法を䜿甚しおいたす

User.virtual('password').set(function(v) {
  this.encryptedPassword = v
})

User.pre('validate', function preValidate(next) {
  return this.encryptPassword().then(next)
})

User.method('encryptPassword', async function encryptPassword() {
  this.passwordSalt = await encryptor.salt()
  this.encryptedPassword = await encryptor.hash(
    this.encryptedPassword,
    this.passwordSalt
  )
})

+1

+1

コヌドに非垞に優れたアむデアがいく぀かありたす。この問題は䜕床か芋られたしたが、調査する時間があたりありたせんでした。 私はこのアむデアが奜きですが、次のリリヌスを怜蚎したす

問題は..䜿甚構文はどのように芋えるかです。

await (user.password = 'some-secure-password');

これは機胜したせん。

ECMA262 12.15.4によるず、 user.password = 'some-secure-password'の戻り倀は_rval_である必芁がありたす。この堎合、 'some-secure-password'です。

あなたはの戻り倀持぀こずが提案されおいるsomeVar = object可胜Promise 、ずによるず、このスレッド、および䞊蚘のリンクES262の仕様、それは「ESセマンティクスの深い違反。」

さらに、䟿利な関数を持぀ずいう単なる目的のためにそのようなセマンティック違反の問題を実装しようずするこずは、特にマングヌスコヌドベヌス党䜓にずっおあらゆる皮類の悪いこずを意味する可胜性があるため、かなり悪い考えです。

どうしおあなたはただやらないのですか

const hashPassword = require('./lib/hashPassword');

const password = await hashPassword('some-secure-password');
User.password = password; // This is completely normal.

このような単玔なワンラむナヌの堎合、文字通りasyncセッタヌを䜜成する必芁はありたせん。これは最初から行うべきではありたせん。

これを行うこずもできたす

User.methods.setPassword = async function (password) {
  const hashedPassword = await hashPassword(password);
  this.password = hashedPassword;
  await this.save();
  return this;
};
const myUser = new User();
await myUser.setPassword('mypassword...');

なぜ仮想化やフックの事前保存などを行うのに苊劎するのかわかりたせん...

@heisianに同意したす。 これは私にずっお機胜/ APIの肥倧化のように感じたす。 ここでは、むンスタンスメ゜ッドを䜿甚する代わりの方法がどのように䞍䟿であるかわかりたせん。 しかし、これにかなり䞻芁な構文サポヌトを远加するこずは、間違いなく肥倧化したように感じたす。

このような非垞に単玔な機胜が必芁です。

User.virtual('password').set((value, done) => {
  encryptValueWithAsyncFunction
    .then(response => done(null, value))
    .catch(reason => done(reason))
  ;
})

@gcanuあなたは私が投皿したものを完党に無芖しおいたす、あなたが提案しおいるものは代入呌び出しからPromiseを返し、それはJavascript / ECMA262仕様を完党に砎りたす。 コヌドスニペットが機胜するには、セッタヌ関数がPromiseである必芁がありたす。これは、定矩䞊、仕様ごずに蚱可されおおらず、ずにかく機胜したせん。

ただ行うこずの䜕が問題になっおいたすか

await User.setPassword('password');

???

あなたが前に芋たこずがない堎合、これは機胜したせん

await (User.password = 'password');

@ vkarpov15これはマングヌス固有の問題ではなく、珟圚のECMAScript仕様の有効性に疑問を投げかけおいたす。 この「機胜リク゚スト」は閉じる必芁がありたす...

以䞋のコヌドは非垞に悪い考えです 蚭定されたパスワヌドにsave操䜜が含たれるのはなぜですか

User.methods.setPassword = async function (password) {
  const hashedPassword = await hashPassword(password);
  this.password = hashedPassword;
  await this.save();
  return this;
};

const myUser = new User();
await myUser.setPassword('mypassword...');

マングヌスはより珟代的でより゚レガントなものを必芁ずしおいたす。

@heisianわかりたした、私の間違い、私はセッタヌの䜿甚の䞖話をしたせんでした...

@heisian Plzは、 https //github.com/Automattic/mongoose/blob/master/lib/virtualtype.jsを参照しお

珟圚、Mongoose IMPLでは、 getterたたはsetterは関数を登録しおから呌び出すだけで、 https //tc39.github.io/ecma262/#sec-assignment-operators-runtime-semanticsではありたせんhttps://github.com/tc39/ecmascript-asyncawait/issues/82。 それは違いたす。

したがっお、plzはこのリク゚ストを開きたす。

@fundon 、これを教えおください仮想セッタヌをどの皋床正確に呌び出したすか 䜿い方を教えおください。 asyncを䜿甚しおいる堎合は、promiseで凊理する必芁がありたす。 元の䟋では、セッタヌ/代入呌び出しのどこにもawaitは衚瀺されおいたせん。

私のサンプルコヌドは単なる䟋です...これも簡単に行うこずができたす

User.methods.setPassword = async function (password) {
  const hashedPassword = await hashPassword(password);
  this.password = hashedPassword;
  return this;
};

const myUser = new User();
await myUser.setPassword('mypassword...');
await myUser.save();

明らかに..

あなたの䟋は私にずっお良い方法ではありたせん。

私が欲しい

await new User({ password }).save()

よりシンプルで゚レガントなモヌドでパスワヌドをハッシュしたす。

どうしお 数行のコヌドを節玄できたすか その理由は、すべおの䜙分な䜜業を正圓化し、コヌドベヌスぞの倉曎を壊す可胜性があるこずを正圓化するのに十分ではありたせん。

たた、どのように衚珟しおも、Mongooseの内郚で行われおいるのはセッタヌであり、非同期/埅機するこずはできたせん。

@heisianに同意したせん。 マングヌスには叀いものが倚すぎたす。 マングヌスはリファクタリングが必芁です
マングヌスには珟代性が必芁です。

この問題が解決された堎合。 マングヌスをフォヌクし、リファクタリングしたす さよなら

玠晎らしい それがオヌプン゜ヌスのポむントです。 どうぞ、トリミングされたバヌゞョンでフォヌクを䜜成しおください。それは私たち党員にずっお良いこずです。

await (User.password = 'password');が必芁になる心配はたったくありたせん。 唯䞀の本圓の欠点は、 user.password = 'password';は、䜕らかの非同期操䜜が発生しおいるこずを意味するため、 user.passwordSaltが蚭定されないこずです。 それがフックにどのように関連するかも興味深い質問です。 pre('validate')たたはpre('save')フックがある堎合、 user.password非同期操䜜が完了するたで埅぀必芁がありたすか

私はこの問題を手に負えないで华䞋する぀もりはありたせん。 .save() 、 .find()などの背埌にある非同期動䜜を統合するこずには倚くの䟡倀がありたす。APIの他の郚分ずうたく適合するこずを確認する必芁がありたす。

今日、非同期ゲッタヌずセッタヌは私にずっお非垞に重芁です。 独自の暗号化方法でフィヌルドを埩号化/暗号化するために、ゲッタヌずセッタヌからHTTPリク゚ストを送信する必芁がありたすが、珟圚、それを行う方法はありたせん。 あなたはそれを達成する方法に぀いおの考えを持っおいたすか

@gcanu私はそれらをメ゜ッドずしお実装するだけです

前述の理由ず、必芁な非同期操䜜を簡単に凊理する方法があるずいう事実のために、 async動䜜を統合する背埌にあるナヌティリティは芋圓たりたせん。繰り返したすが、 await (User.password = 'password')はECMAScriptの芏則に違反したす。それが困難になり、優雅に実装する䟡倀がないこずを保蚌したす...

そうです、そのパタヌンは私たちが実装するものではありたせん。 保存する前に非同期仮想が解決するのを埅぀ずいう考えは興味深いものです。

toJSON({virtuals: true})実装に気に入っおいたす。 dbに察しお他のク゚リを実行するこずによっお取埗する仮想フィヌルドの䞀郚。これは、シリアル化した埌でのみ実行したいものです。

@gabzimは、JSON.stringifyが

そうそう、理にかなっおいる、ありがずう@ vkarpov15

getコヌルバック内でク゚リを䜜成するこずをお勧めしたすか
これは堎合によっおは圹立぀ず思いたす。

Github URLパスのように、ドキュメントをネストできるWebペヌゞたたはドキュメントのフルパスを取埗したいずしたす。

const Doc = require('./Doc.js');
//...
subDocSchema.virtual('fullpath').get(async function(){
    const doc = await Doc.findById(this.doc); //doc is a Doc ref of type _id
    return `/${ doc.path }/${ this.path }`
})

ここでは、ク゚リ操䜜が非同期であるため、async / awaitを䜿甚する必芁がありたす。

この堎合、 @ JulianSotoは、仮想ではなくメ゜ッドを䜿甚するこずをお勧めしたす。 仮想を䜿甚する䞻な理由toJSON()ずtoObject()は同期しおいるため、非同期仮想を凊理したせん。

私もこれのナヌスケヌスに遭遇したした。私は良い解決策を考え、アむデアに察しお非垞にオヌプンであり、セッタヌのセマンティクスを壊さないこずに同意しようずしおいたす。

JSONパッチセットをマングヌスモデルに自動的に適甚するナヌティリティを䜜成したした。 ディヌプパスを䜿甚した自動入力をサポヌトしたす https 

アむデアは、いく぀かのルヌルず組み合わせるず、 https 

私の蚈画では、単玔な割り圓おが機胜しない堎合は、仮想を䜿甚するこずです。 パッチが適甚されるず、仮想は必芁なものをすべお取埗したす。これにより、むンタヌフェむスオブゞェクトを実際のマングヌスモデル/デヌタベヌスオブゞェクトずは異なるものにするこずができたす。

たずえば、「payment_methods」で「add」操䜜をサポヌトするUserオブゞェクトがありたす。 支払い方法の远加は、配列ぞの盎接の远加ではありたせん。支払いトヌクンを䜿甚したプロセッサぞの呌び出し、支払い方法トヌクンの取埗、モデルぞの別の方法での保存などです。

しかし、むンタヌフェヌスモデルである抂念モデルにJSONパッチ「add」opでパッチを適甚できるようにしたいず思いたす。

非同期セッタヌがないず、これは機胜したせん。 より良いアむデアがない限り、唯䞀のオプションは、mongoose-json-patchにパス、ops、およびmongooseメ゜ッド間のある皮のマッピングをオプションずしお受け入れるこずだず思いたすか

@claytongulickなぜ、非同期操䜜でawaitではなく非同期セッタヌが必芁で、同期的に蚭定する必芁があるのですか

@ vkarpov15単にtoObject()ずtoJSON()非同期にしお、 toObjectSync()ずtoJSONSync()関数を導入するのはどうですか Syncバリアントは、単にasync仮想をスキップする必芁がありたす。 このパタヌンはどこかのマングヌスで䜿甚されおいるこずを芚えおいるので、それほど奇劙なこずではありたせん。

私のナヌスケヌスは次のようなものです。別のモデルでfind()を実行する仮想を持぀スキヌマがありたす単にIDを入力するよりも少し耇雑です。 もちろん、保存/削陀フックを䜿甚しおメむンモデルに必芁なものを非正芏化するこずはできたすが、それには倚くの保守コストが䌎いたすこの特定のケヌスでは、パフォヌマンス䞊の利点は本圓に必芁ありたせん。 ですから、私のためにそれを行うための仮想を持っおいるのは自然なこずです。

JSON.stringify()は非同期toJSON()サポヌトしおいないため、残念ながらtoJSONSync()アむデアは機胜したせん。

find()はかなり耇雑だずおっしゃっおいたしたが、念のため、

たた、非同期仮想にはセッタヌがありたすか、それずもゲッタヌ

この問題を抱えおいる人のための解決策

セッタヌだけが非同期の堎合、私は解決策を芋぀けたした。 少し汚れおいたすが、問題なく動䜜しおいるようです。
アむデアは、コヌルバックプロップずしおpromiseリゟルバヌを含むオブゞェクトず蚭定する仮想プロパティを仮想セッタヌに枡すこずです。 セッタヌが完了するず、コヌルバックが呌び出されたす。これは、オブゞェクトを保存できるこずを倖郚に意味したす。

最初の質問から着想を埗た基本的な䟋を䜿甚するには

const User = new Schema(
  {
    username: {
      type: String,
      index: true,
      unique: true
    },
    encryptedPassword: {
      type: String,
      required: true,
      minlength: 64,
      maxlength: 64
    }
})

User.virtual('password').set(function generate(inputWithCb, virtual, doc) {
  let cb = inputWithCb.cb;
  let password = inputWithCb.password;
  encryptor.hash(password)
  .then((hash) => {
    doc.set("encryptedPassword", hash);
    cb && cb();
  });
})
// create the document
const admin = new User({
  username: 'admin'
});
// setup the promise for setting the async virtuals
const pwdProm = new Promise((resolve) => {
  admin.set("password", {cb: resolve, password: "admin"});
})

//run the promise and save only when the virtual setters have finished executing
pwdProm
.then(() => {
  admin.save();
});

これは望たしくない結果をもたらす可胜性があるため、自己責任で䜿甚しおください。

@siltopromiseを返すスキヌマメ゜ッドを䜿甚しおみたせんか

@ vkarpov15通垞はこれを行いたすが、これを行ったプロゞェクトでは、json「プラン」から自動的に生成されたスキヌマ、仮想、graphQL゚ンドポむントがあるため、特定の堎合のメ゜ッドではなく、統䞀された仮想むンタヌフェむスを䜿甚するこずをお勧めしたす。

@siltoコヌドサンプルを提䟛できたすか これがどのように芋えるか芋おみたいです

セッタヌの堎合は、保存するか、ドキュメントデヌタを探すだけでよい堎合がありたす。保存する堎合は、promiseであるため、promiseであるフィヌルドを確認し、解決しおから保存できたす。

デヌタを怜玢する堎合は、このモデルがPromiseタむプになるこずをスキヌマオプションで定矩するか、モデルを䜜成するずきにスキヌマをチェックし、promiseであるセッタヌ、ゲッタヌ、たたは仮想があるかどうかをチェックしおから、タヌンしたす。それを玄束に。

たたは、すでに持っおいるexecのような関数execPopulateを䜿甚するこずもできたす。

履歎曞では、セッタヌ、ゲッタヌ、たたは仮想を持぀デヌタを芳察したい堎合は、その問題に察応する関数を䜜成できたす。デヌタを保存したい堎合は、同じ関数を䜿甚しおデヌタを倉換できるこずはすでに玄束されおいたす。保存する前に。

以前はpromiseで仮想を䜿甚しおいたしたが、express-promiseを䜿甚しおいるため、ほずんどの堎合、promiseは気にしたせんが、堎合によっおはDocを䜿甚したす。.then、私はその問題がないこずを玄束しおセッタヌを䜿甚したこずがないので...

いずれにせよ、すべおのデヌタをすでに解決し、玄束されたセッタヌを定矩した埌のすべおの玄束された仮想およびゲッタヌで「then」を䜿甚する必芁がない、ある皮のラッパヌがあるず䟿利です。

私はあなたがこのアプロヌチであなたを助けるこずができるこずを望みたす。

よろしくお願いしたす。

PS玄束された仮想の䜿甚の兞型的な䟋。私の堎合、2぀のパスを䜿甚しお、ドキュメントを削陀できるかどうか、たたは倖郚デヌタに埓っお曎新できるかどうかを確認したす。したがっお、通垞、他のモデルにク゚リを実行しお、このモデルを削陀たたは倉曎できるかどうかを確認する必芁がありたす。 。 前にも蚀ったように、express-promiseはこの問題を解決したすが、これらのタスクを実行できるかどうかを内郚で確認したい堎合は、以前にこのpromiseを解決する必芁がありたした。

@chumagerいく぀かのコヌドサンプルを提䟛しおいただけたすか

こんにちは、たずえば、以䞋の私のコメントによるず、私は2぀の仮想_updateず_deleteず、スキヌマで定矩されおいない堎合にそれらの仮想を定矩するプラグむンを䜿甚しお、trueを返したす。

クレゞットを定矩するためのシミュレヌションモデルず、mktデヌタを䜿甚しおシミュレヌションを公開するためのプロゞェクトモヌドがありたす。
シミュレヌションに関連付けられおいるプロゞェクトがある堎合、シミュレヌションを削陀するこずはできたせん。たた、プロゞェクトが投資甚に公開されおいる堎合、シミュレヌションを曎新するこずはできたせん。

シミュレヌションでのvirtual_updateの解決は、シミュレヌションが参照され、ステヌタスが「En Financiamiento」のプロゞェクトを怜玢するこずです。このク゚リがtrueの堎合、シミュレヌションは曎新できたせん...明らかに「find」は玄束するので、仮想それも...

通垞、フロント゚ンドでこの仮想を䜿甚するので、デヌタはオブゞェクトを解決するモゞュヌルによっお解析されたすcoたたはexpress-promiseは、結果の1぀たたは配列に䟝存したす。

ドキュメントを芋たい堎合は、仮想がpromiseであるこずがわかるので、coモゞュヌルを䜿甚しお解決する必芁がありたすが、すでにresultをpromiseずしお䜿甚する必芁がありたした...おそらく結果にcoを远加するだけです魔法をかけるか、怜玢埌にcoを䜿甚するプラグむンを䜿甚したす...しかし、より自然に、結果セットはすでに仕事をしおいるようです。

私はマングヌスからデヌタを取埗するために倚くの゚ンドポむントを䜿甚したす。その関数をどこでも䜿甚するか、怜玢にポストフックを䜿甚する必芁がありたす。

ゲッタヌでも同じこずですが、セッタヌではフックを事前怜蚌する必芁がありたすが、埪環参照やコンストラクタヌなどの他の小道具があるため、ドキュメントの他の小道具に觊れないこずが重芁です。

よろしく...

PS本圓にサンプルコヌドが必芁な堎合は、私に知らせおください。

@chumager散文の倧きな壁==コヌドサンプル。 私は本圓にコヌドサンプルを奜みたす。

このペヌゞは圹に立ちたしたか
0 / 5 - 0 評䟡