2017-10-12 213 views
0

我正在使用MongoDB + Express。遞歸函數生成一個沒有重複的變量

我有一個發佈端點,名稱字段是隨機生成的。除了當我找到重複的名字時,我的一切都正常工作。

我想檢查名稱字段是否已經存在於該數據庫中。如果是這樣,那麼我想重新生成名稱字段,如果沒有,請將它保存到數據庫中。這是我在爲控制器創建方法的地方。

此外,如果名稱重複,我想隨機字符串,來一個,這樣的隨機生成函數的長度使用8變爲上去 - 9

create: (req, res, next) => { 
    const defaultRandomNameLength = 8; 
    const { title, desc, img } = req.body; 
    let randomlength = 0; 
    const randomNameLength = (randomlength) ? randomlength : defaultRandomNameLength; 
    let name = randomstring.generate({ length: randomNameLength, charset: "alphabetic" }); 

    SpoofItem.findOne({name}) 
    .then((result) => { 

     if (result) { 

     res.send(`dup found: ${name}`) 
     //create recursive function here 

     } else { 
     const spoofer = new SpoofItem({ title, desc, img, name }); 
     spoofer.save().then((spoofItem) => { 
      res.json(spoofItem); 
     }).catch((err) => { 
      return res.status(500).json({ 
      error: err 
      }) 
     }) 
     } 
    }).catch((err) => { 
     return res.status(500).json({ 
     error: err 
     }) 
    }) 
    }, 
+0

爲什麼你需要從8到9?底線是在8個位置上,當然只有可能產生的有限數量的組合。查詢數據庫的現有值並不一定意味着在找到匹配項時已經使用了這些可能的組合。如果你想要一個隨機標記,那麼你應該選擇一個具有足夠可能值的長度並堅持下去。或者甚至考慮你的用例,並研究出已經保證唯一性的東西就足夠了(即'ObjectId')。哪種方法比遞歸查詢更好。 –

+0

我完全明白你的意思,並深深地考慮了這一點。名稱var將用於URL的末尾,否則將使用objectID。雖然它發生的可能性可能不會,但我想調整可擴展性以防萬一。 – brandenbuilds

回答

0

不,我真的很同意它的最好的處理問題,但作爲一個學術練習,它實際上只是一個將您的名字生成包裹在遞歸函數中的問題,您可以在找到匹配項時提供遞增的長度參數。

這裏唯一真正的約束是你的函數本身應該返回一個Promise,無論是結果還是對遞歸調用本身。

對於懶惰,你的執行將是這樣的:使用async/await關鍵字

create: (req, res, next) => { 
    const { title, desc, img } = req.body; 

    // Recursive function definition 
    function generateName(title, desc, img, randomLength) { 
    const defaultLength = 8; 

    randomLength = randomLength || defaultLength; 

    let name = randomstring.generate({ 
     length: randomLength, charset: "alphabetic" }); 

    return SpoofItem.count({ name }).then(count => { 
     if (count > 0) { 
     randomLength++; 
     return generateName(title, desc, img, randomLength); 
     } else { 
     return SpoofItem.create({ title, desc, img, name }); 
     } 
    }); 

    } 

    generateName(title, desc, img).then(spoofItem => res.json(spoofItem)) 
    .catch(error => res.status(500).json({ error })); 

} 

或許有點更現代:

create: async (req, res, next) => {   // marked as async 
    const { title, desc, img } = req.body; 

    // Recursive function definition also marked as async 
    async function generateName(title, desc, img, randomLength) { 
    const defaultLength = 8; 

    randomLength = randomLength || defaultLength; 

    let name = randomstring.generate({ 
     length: randomLength, charset: "alphabetic" }); 

    let count = await SpoofItem.count({ name }); 
    if (count > 0) { 
     randomLength++; 
     return generateName(title, desc, img, randomLength); 
    } else { 
     return SpoofItem.create({ title, desc, img, name }); 
    } 

    } 

    // try/catch instead of .catch() 
    try { 
    let spoofItem = await generateName(title, desc, img); 
    res.json(spoofItem); 
    } catch(error) { 
    res.status(500).json({ error }); 
    } 
} 

最大的變化在這裏是把重複的邏輯會以遞歸方式存在於實際進行遞歸調用的函數中。請注意0​​的「可選」參數,因爲當它沒有提供時,您將使用「默認」。此外,實際的數據庫寫入會在此函數內移動,因此其他將寫入的值也會傳遞到該函數中。

基本思想是給定參數名稱生成,然後在數據庫中查找該名稱。使用.count()是最有效的方法,因爲值在遊標狀態中可用,而不實際檢索除統計本身之外的任何數據。

該操作返回Promise,因此您要麼繼續使用.then(),要麼使用await。然後它就是基於返回的「count」的簡單分支。

要不就是價值發現和randomLength量就會增,並通過回遞歸調用,或.create()方法被調用(基本上new SpoofItem().save()一)實際編寫了「獨一無二」的價值。無論哪種方式,返回Promise

在相同的情況下,還可以根據需要通過.then()await等待解析「包裝」功能。 .catch()catch涵蓋了可能的錯誤。

同樣,這些「錯誤」可能「應該」被委託給next處理程序,但這不是真正的問題。


作爲更完整的演示,如下自包含在任一種形式應該表現出字符串長度列表遞增,主要是由於初始短的長度,其是某些吸引重複:

要麼標誌着與async功能和內部呼叫使用await

const randomstring = require('randomstring'), 
     mongoose = require('mongoose'), 
     Schema = mongoose.Schema; 

mongoose.Promise = global.Promise; 
mongoose.set('debug',true); 

const uri = 'mongodb://localhost/test', 
     options = { useMongoClient: true }; 

const testSchema = new Schema({ 
    name: String 
}); 

const Test = mongoose.model('Test', testSchema); 

async function generateName(randomLength) { 
    const defaultLength = 1; 

    randomLength = randomLength || defaultLength; 

    let name = randomstring.generate({ 
    length: randomLength, charset: "alphabetic" }); 

    let count = await Test.count({ name }); 

    if (count > 0) { 
    randomLength++; 
    return generateName(randomLength); 
    } else { 
    return Test.create({ name }); 
    } 

} 

(async function() { 

    try { 

    const conn = await mongoose.connect(uri,options); 

    // Clear existing data 
    await Test.remove(); 

    // Loop random generation 
    for (let x = 1; x <= 1000; x++) { 
     await generateName(); 
    } 

    } catch(e) { 
    console.error(e); 
    } finally { 
    mongoose.disconnect(); 
    } 

})(); 

,或以傳統.then()

const randomstring = require('randomstring'), 
     mongoose = require('mongoose'), 
     Schema = mongoose.Schema; 

mongoose.Promise = global.Promise; 
mongoose.set('debug',true); 

const uri = 'mongodb://localhost/test', 
     options = { useMongoClient: true }; 

const testSchema = new Schema({ 
    name: String 
}); 

const Test = mongoose.model('Test', testSchema); 

function generateName(randomLength) { 
    const defaultLength = 1; 

    randomLength = randomLength || defaultLength; 

    let name = randomstring.generate({ 
    length: randomLength, charset: "alphabetic" }); 

    return Test.count({ name }).then(count => { 

    if (count > 0) { 
     randomLength++; 
     return generateName(randomLength); 
    } else { 
     return Test.create({ name }); 
    } 

    }); 

} 

(async function() { 

    try { 

    const conn = await mongoose.connect(uri,options); 

    // Clear existing data 
    await Test.remove(); 

    // Loop random generation 
    for (let x = 1; x <= 1000; x++) { 
     await generateName(); 
    } 

    } catch(e) { 
    console.error(e); 
    } finally { 
    mongoose.disconnect(); 
    } 

})(); 

任何一種情況下都會生成1000個「唯一」隨機字符串,其中在給定長度的碰撞可能性的情況下,元素的數量將推送到3

可選地在架構上添加一個「唯一」約束來「證明」這些值實際上是「唯一」的,但這基本上是.count()操作的要點,所以它只是額外的「強制」當由其他現有的邏輯來管理時是必需的。

+0

謝謝你。對於我來說,圍繞遞歸函數是很難的。我很感激你用與我正在努力解決的問題相關的代碼示例來解釋這一點。 – brandenbuilds