मूल समस्या
निकट भविष्य में वर्तमान में एकत्रीकरण ढांचे में इसे आजमाने और करने के लिए यह सबसे बुद्धिमान विचार नहीं है। निश्चित रूप से मुख्य समस्या इस लाइन से आपके पास पहले से मौजूद कोड में आती है:
"items" : { "$push": "$$ROOT" }
और इसका मतलब यह है कि, इसमें मूल रूप से जो होना चाहिए वह यह है कि किसी भी बाद के कोड में "शीर्ष एन" परिणाम प्राप्त करने के लिए समूहीकरण कुंजी के भीतर सभी वस्तुओं को एक सरणी में धकेलने की आवश्यकता होती है।
यह स्पष्ट रूप से स्केल नहीं करता है क्योंकि अंततः उस सरणी का आकार 16 एमबी की बीएसओएन सीमा से अधिक हो सकता है, और समूहबद्ध दस्तावेज़ में शेष डेटा की परवाह किए बिना। यहां मुख्य पकड़ यह है कि केवल एक निश्चित संख्या में "पुश को सीमित करना" संभव नहीं है। ऐसी ही एक चीज़ पर लंबे समय से JIRA का मुद्दा है।
केवल इसी कारण से, इसका सबसे व्यावहारिक तरीका प्रत्येक समूह कुंजी के लिए "शीर्ष एन" आइटम के लिए अलग-अलग प्रश्नों को चलाना है। इन्हें .aggregate()
. होने की भी आवश्यकता नहीं है स्टेटमेंट (डेटा के आधार पर) और वास्तव में कुछ भी हो सकता है जो आपके इच्छित "टॉप एन" मानों को सीमित करता है।
सर्वश्रेष्ठ तरीका
आपका आर्किटेक्चर node.js
पर प्रतीत होता है mongoose
. के साथ , लेकिन कुछ भी जो एसिंक्स आईओ का समर्थन करता है और प्रश्नों के समानांतर निष्पादन सबसे अच्छा विकल्प होने जा रहा है। आदर्श रूप से इसकी अपनी एपीआई लाइब्रेरी के साथ कुछ ऐसा है जो उन प्रश्नों के परिणामों को एक ही प्रतिक्रिया में संयोजित करने का समर्थन करता है।
उदाहरण के लिए आपके आर्किटेक्चर और उपलब्ध पुस्तकालयों (विशेष रूप से async
. का उपयोग करके यह सरलीकृत उदाहरण सूची है ) जो यह समानांतर और संयुक्त परिणाम बिल्कुल ठीक करता है:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var data = [
{ "merchant": 1, "rating": 1 },
{ "merchant": 1, "rating": 2 },
{ "merchant": 1, "rating": 3 },
{ "merchant": 2, "rating": 1 },
{ "merchant": 2, "rating": 2 },
{ "merchant": 2, "rating": 3 }
];
var testSchema = new Schema({
merchant: Number,
rating: Number
});
var Test = mongoose.model( 'Test', testSchema, 'test' );
async.series(
[
function(callback) {
Test.remove({},callback);
},
function(callback) {
async.each(data,function(item,callback) {
Test.create(item,callback);
},callback);
},
function(callback) {
async.waterfall(
[
function(callback) {
Test.distinct("merchant",callback);
},
function(merchants,callback) {
async.concat(
merchants,
function(merchant,callback) {
Test.find({ "merchant": merchant })
.sort({ "rating": -1 })
.limit(2)
.exec(callback);
},
function(err,results) {
console.log(JSON.stringify(results,undefined,2));
callback(err);
}
);
}
],
callback
);
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
यह परिणाम में प्रत्येक व्यापारी के लिए केवल शीर्ष 2 परिणाम देता है:
[
{
"_id": "560d153669fab495071553ce",
"merchant": 1,
"rating": 3,
"__v": 0
},
{
"_id": "560d153669fab495071553cd",
"merchant": 1,
"rating": 2,
"__v": 0
},
{
"_id": "560d153669fab495071553d1",
"merchant": 2,
"rating": 3,
"__v": 0
},
{
"_id": "560d153669fab495071553d0",
"merchant": 2,
"rating": 2,
"__v": 0
}
]
यह वास्तव में इसे संसाधित करने का सबसे कुशल तरीका है, हालांकि यह संसाधन लेने जा रहा है क्योंकि यह अभी भी कई प्रश्न हैं। लेकिन यदि आप सभी दस्तावेज़ों को एक सरणी में संग्रहीत करने और इसे संसाधित करने का प्रयास करते हैं, तो एकत्रीकरण पाइपलाइन में खाए गए संसाधनों के पास कहीं भी नहीं है।
समग्र समस्या, अभी और निकट भविष्य
उस पंक्ति तक, यह विचार करना संभव है कि दस्तावेजों की संख्या बीएसओएन सीमा में उल्लंघन का कारण नहीं बनती है कि ऐसा किया जा सकता है। MongoDB की वर्तमान रिलीज़ के तरीके इसके लिए बहुत अच्छे नहीं हैं, लेकिन आगामी रिलीज़ (लेखन के रूप में, 3.1.8 dev शाखा ऐसा करती है) कम से कम एक $slice
का परिचय देती है एकत्रीकरण पाइपलाइन के लिए ऑपरेटर। इसलिए अगर आप एग्रीगेशन ऑपरेशन के बारे में होशियार हैं और $sort
. का इस्तेमाल करते हैं पहले, फिर सरणी में पहले से सॉर्ट किए गए आइटम आसानी से निकाले जा सकते हैं:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var data = [
{ "merchant": 1, "rating": 1 },
{ "merchant": 1, "rating": 2 },
{ "merchant": 1, "rating": 3 },
{ "merchant": 2, "rating": 1 },
{ "merchant": 2, "rating": 2 },
{ "merchant": 2, "rating": 3 }
];
var testSchema = new Schema({
merchant: Number,
rating: Number
});
var Test = mongoose.model( 'Test', testSchema, 'test' );
async.series(
[
function(callback) {
Test.remove({},callback);
},
function(callback) {
async.each(data,function(item,callback) {
Test.create(item,callback);
},callback);
},
function(callback) {
Test.aggregate(
[
{ "$sort": { "merchant": 1, "rating": -1 } },
{ "$group": {
"_id": "$merchant",
"items": { "$push": "$$ROOT" }
}},
{ "$project": {
"items": { "$slice": [ "$items", 2 ] }
}}
],
function(err,results) {
console.log(JSON.stringify(results,undefined,2));
callback(err);
}
);
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
जो एक ही मूल परिणाम देता है क्योंकि शीर्ष 2 आइटम पहले क्रमबद्ध किए जाने के बाद सरणी से "कटा हुआ" होते हैं।
यह वर्तमान रिलीज़ में वास्तव में "संभव" भी है, लेकिन इसमें समान बुनियादी बाधाओं के साथ इसमें पहले सामग्री को सॉर्ट करने के बाद सभी सामग्री को एक सरणी में धकेलना शामिल है। यह सिर्फ एक "पुनरावृत्ति" दृष्टिकोण लेता है। आप अधिक से अधिक प्रविष्टियों के लिए एकत्रीकरण पाइपलाइन का निर्माण करने के लिए इसे कोड कर सकते हैं, लेकिन केवल "दो" दिखाना चाहिए कि यह कोशिश करने के लिए वास्तव में एक अच्छा विचार नहीं है:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var data = [
{ "merchant": 1, "rating": 1 },
{ "merchant": 1, "rating": 2 },
{ "merchant": 1, "rating": 3 },
{ "merchant": 2, "rating": 1 },
{ "merchant": 2, "rating": 2 },
{ "merchant": 2, "rating": 3 }
];
var testSchema = new Schema({
merchant: Number,
rating: Number
});
var Test = mongoose.model( 'Test', testSchema, 'test' );
async.series(
[
function(callback) {
Test.remove({},callback);
},
function(callback) {
async.each(data,function(item,callback) {
Test.create(item,callback);
},callback);
},
function(callback) {
Test.aggregate(
[
{ "$sort": { "merchant": 1, "rating": -1 } },
{ "$group": {
"_id": "$merchant",
"items": { "$push": "$$ROOT" }
}},
{ "$unwind": "$items" },
{ "$group": {
"_id": "$_id",
"first": { "$first": "$items" },
"items": { "$push": "$items" }
}},
{ "$unwind": "$items" },
{ "$redact": {
"$cond": [
{ "$eq": [ "$items", "$first" ] },
"$$PRUNE",
"$$KEEP"
]
}},
{ "$group": {
"_id": "$_id",
"first": { "$first": "$first" },
"second": { "$first": "$items" }
}},
{ "$project": {
"items": {
"$map": {
"input": ["A","B"],
"as": "el",
"in": {
"$cond": [
{ "$eq": [ "$$el", "A" ] },
"$first",
"$second"
]
}
}
}
}}
],
function(err,results) {
console.log(JSON.stringify(results,undefined,2));
callback(err);
}
);
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
और फिर से पिछले संस्करणों में "संभव" होने पर (यह छोटा करने के लिए 2.6 पेश की गई सुविधाओं का उपयोग कर रहा है क्योंकि आप पहले ही $$ROOT
टैग कर चुके हैं। ), मूल चरण सरणी को संग्रहीत कर रहे हैं और फिर $first
का उपयोग करके प्रत्येक आइटम को "ऑफ द स्टैक" प्राप्त कर रहे हैं और उस (और संभावित रूप से अन्य) को हटाने के लिए सरणी के भीतर की वस्तुओं की तुलना करना और फिर उस स्टैक से "अगला पहला" आइटम प्राप्त करना जब तक कि आपका "टॉप एन" अंततः पूरा नहीं हो जाता।
निष्कर्ष
जब तक वह दिन नहीं आता जब तक एक ऐसा ऑपरेशन नहीं होता है जो आइटम को $push
. में अनुमति देता है एकत्रीकरण संचायक को एक निश्चित संख्या तक सीमित किया जाए, तो यह वास्तव में कुल के लिए एक व्यावहारिक ऑपरेशन नहीं है।
आप इसे कर सकते हैं, यदि आपके पास इन परिणामों में डेटा काफी छोटा है, और यह क्लाइंट साइड प्रोसेसिंग से भी अधिक कुशल हो सकता है यदि डेटाबेस सर्वर वास्तविक लाभ प्रदान करने के लिए पर्याप्त कल्पना के हैं। लेकिन संभावना है कि उचित उपयोग के अधिकांश वास्तविक अनुप्रयोगों में ऐसा नहीं होगा।
सबसे अच्छा दांव पहले प्रदर्शित "समानांतर क्वेरी" विकल्प का उपयोग करना है। यह हमेशा अच्छी तरह से स्केल करने जा रहा है, और इस तरह के तर्क को "चारों ओर कोड" करने की कोई आवश्यकता नहीं है कि एक विशेष समूह कम से कम कुल "शीर्ष एन" आइटम वापस नहीं लौटा सकता है और उन्हें कैसे बनाए रखना है (उस छोड़े गए का बहुत लंबा उदाहरण ) क्योंकि यह केवल प्रत्येक क्वेरी को निष्पादित करता है और परिणामों को जोड़ता है।
समानांतर प्रश्नों का प्रयोग करें। यह आपके पास कोडित दृष्टिकोण से बेहतर होगा, और यह लंबे समय से प्रदर्शित एकत्रीकरण दृष्टिकोण से बेहतर प्रदर्शन करने वाला है। जब तक कम से कम कोई बेहतर विकल्प न हो।