How Does 2 Problems Live Together In Peace

گاهی دو مشکل کنار هم در سورس کد می توانند به نحو جالبی همدیگر را بپوشانند و باعث نتایج تقریبا درست شوند. چیزی شبیه اتفاقی که برای for-in در جاوااسکریپت و find در mongodb در کنار هم می افتد.


مقدمه اول
در جاواسکریپت حلقه ای به نام for-in داریم که کارش Enumeration در یک آرایه است. مثلا:

var list = [1,2,3];
for( var item in list){
   ...
}

تا اینجا چیزی شبیه foreach است که در خیلی زبانها داریم. اما فرقی که وجود دارد این است که for-in روی کلیدها Enumeration انجام می دهد. برای نمونه در مثال بالا مقادیر item عبارت خواهد بود از 0, 1, 2. چرا که برای یک آرایه کلیدها همان اندیس های آرایه هستند. یا به عنوان نمونه ای دیگر:

var product = { name:'MXD56', price:56, weight:32.4 }
for( var key in product){
   console.log(key)
}

Output: 
name
price
weight
البته استفاده از for-in زیاد توصیه نمی شود و در نکوهش استفاده از آن مطالبی زیادی نوشته شده است.


مقدمه دوم
اگر بخواهید در Collection های mongoDB به دنبال row هایی با یک مقدار خاص بگردید، احتمالا از دستوری شبیه این استفاده می کنید:

find( {field:value} )

اما وقتی value از نوع undefined باشد، شرط در نظر گرفته نمی شود و تمام row ها برگردانده می شود.
حالا یک نمونه پیچیده تر را بررسی می کنیم. اگر یک آرایه از مقادیر داشته باشید (مثلا به اسم list ) و بخواهید تمام row هایی را استخراج کنید که مقدار فیلد مشخصی از آنها یکی از مقادیر این آرایه باشد، از چنین دستوری استفاده می کنید:

find( { field : { $in : list }}

هنگام استفاده از mongoDB در دو حالت عجیب ولی منطقی زیر تمام row ها به عنوان نتیجه جست و جو برگردانده می شوند:
1- وقتی به عنوان مقدار فیلد مورد جست و جو undefined را وارد کنید؛ مثل این:

find({email:undefined)


2- و حالت کمی پیچیده تر، وقتی بخواهید مقدار فیلدی برابر یکی از مقادیر یک آرایه باشد و در آن آرایه مقادیر undefined داشته باشید و همچنین اسم فیلد را هم اشتباه زده باشید؛ مثلا _email به جای email:

find({_email:{$in:[undefined]}​})

مقدمه سوم
حالا فرض کنید در nodejs می خواهید از یک collection آن row هایی که یک فیلدشان مقدار خاصی دارند را پیدا کنید و در این بین با تصوری که از ForEach در سایر زبان ها دارید ( که خود object ها را بر می گرداند ) از for-in استفاده کنید(مشکل اول) و از طرفی اسم فیلد مورد نظر را هم اشتباه بزنید(مشکل دوم):

var items = [ {ID:123} , {ID:456} , {ID:789} ];
var IDList = [];
for( var item in items){
   IDList.push( item.ID );
}
mycollection.find( { ID : {$in : IDList } } ). ...


تمام row های collection به عنوان نتیجه برگردانده می شود. علت این است که در هر اجرای for-in مقدار item برابر اندیس های آرایه items خواهد بود؛ یعنی 0, 1 , 2. و این مقادیر فیلدی به نام ID ندارند. پس 3 تا مقدار undefined در آرایه IDList وارد می شود. در نهایت find هم در یک آرایه از undefined ها برای وجود مقدار فیلد ID می گردد ( که نام فیلد را هم اشتباها ID است و در collection چنین فیلدی وجود ندارد) و این یعنی شرطی که نادیده گرفته می شود ( یا به عبارتی دیگر شرطی همیشه درست! ).



نتیجه
گاهی ممکن است دو مشکل بنیادین در کنار هم ( 1- استفاده اشتباه از for-in و 2- استفاده از مقدار undefined برای جست و جو در mongoDB ) تقریبا درست کار کنند!