টিএলডিআর সংক্ষিপ্তসার
আধুনিক মঙ্গোডিবি রিলিজগুলিতে আপনি $slice
কেবলমাত্র মৌলিক সমষ্টিগত ফলাফল বন্ধ করে জোর করে ফেলতে পারেন । "বড়" ফলাফলের জন্য, প্রতিটি গোষ্ঠীবদ্ধ করার জন্য পরিবর্তে সমান্তরাল প্রশ্নের চালানো (ক বিক্ষোভের তালিকা উত্তর শেষে), বা জন্য অপেক্ষা সার্ভার 9377 সমাধান করতে, যা আইটেম সংখ্যা একটি "সীমা" সম্ভব হবে $push
একটি থেকে অ্যারে।
db.books.aggregate([
{ "$group": {
"_id": {
"addr": "$addr",
"book": "$book"
},
"bookCount": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.addr",
"books": {
"$push": {
"book": "$_id.book",
"count": "$bookCount"
},
},
"count": { "$sum": "$bookCount" }
}},
{ "$sort": { "count": -1 } },
{ "$limit": 2 },
{ "$project": {
"books": { "$slice": [ "$books", 2 ] },
"count": 1
}}
])
মঙ্গোডিবি 3.6 পূর্বরূপ
তবুও SERVER-9377 সমাধান করছেন না , তবে এই রিলিজটিতে $lookup
একটি নতুন "অ-সম্পর্কযুক্ত" বিকল্পের অনুমতি দেয় যা বিকল্প এবং বিকল্পগুলির "pipeline"
পরিবর্তে একটি যুক্তি হিসাবে প্রকাশ করে । এটি তখন অন্য একটি পাইপলাইন অভিব্যক্তির সাথে "স্ব-যোগদান" করতে অনুমতি দেয়, যাতে আমরা "শীর্ষ-এন" ফলাফলগুলি ফেরত দেওয়ার জন্য আবেদন করতে পারি ।"localFields"
"foreignFields"
$limit
db.books.aggregate([
{ "$group": {
"_id": "$addr",
"count": { "$sum": 1 }
}},
{ "$sort": { "count": -1 } },
{ "$limit": 2 },
{ "$lookup": {
"from": "books",
"let": {
"addr": "$_id"
},
"pipeline": [
{ "$match": {
"$expr": { "$eq": [ "$addr", "$$addr"] }
}},
{ "$group": {
"_id": "$book",
"count": { "$sum": 1 }
}},
{ "$sort": { "count": -1 } },
{ "$limit": 2 }
],
"as": "books"
}}
])
অন্যান্য উপরন্তু এখানে অবশ্যই মাধ্যমে পরিবর্তনশীল ঢুকান করার দক্ষতা $expr
ব্যবহার $match
মধ্যে "যোগদান করুন" ম্যাচিং আইটেম নির্বাচন করতে, কিন্তু সাধারণ প্রতিজ্ঞা যেখানে ভেতরের কন্টেন্ট পিতা বা মাতা থেকে মিলগুলি দ্বারা ফিল্টার করা যেতে পারে একটি "একটি পাইপলাইন মধ্যে পাইপলাইন" হয় । যেহেতু তারা উভয়ই "পাইপলাইন" তাই আমরা $limit
প্রত্যেকে পৃথকভাবে ফলাফল করতে পারি ।
সমান্তরাল ক্যোয়ারী চালানোর জন্য এটি পরবর্তী সেরা বিকল্প হবে $match
এবং "সাব-পাইপলাইন" প্রসেসিংয়ে কোনও সূচক ব্যবহারের অনুমতি দেওয়া এবং সক্ষম হতে পারলে এটি আরও ভাল । যা যা $push
উল্লেখযোগ্য ইস্যুটি হিসাবে "সীমাবদ্ধতা" ব্যবহার করে না , এটি আসলে এমন কিছু সরবরাহ করে যা আরও ভাল কাজ করা উচিত।
আসল বিষয়বস্তু
আপনি শীর্ষ "এন" সমস্যার উপর হোঁচট খেয়েছেন বলে মনে হচ্ছে। আপনার কাছে যে সমস্যাটির সীমাবদ্ধতাটি চাওয়া হয়েছে তার সাথে না থাকলেও একভাবে আপনার সমস্যার সমাধান করা মোটামুটি সহজ:
db.books.aggregate([
{ "$group": {
"_id": {
"addr": "$addr",
"book": "$book"
},
"bookCount": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.addr",
"books": {
"$push": {
"book": "$_id.book",
"count": "$bookCount"
},
},
"count": { "$sum": "$bookCount" }
}},
{ "$sort": { "count": -1 } },
{ "$limit": 2 }
])
এখন এটি আপনাকে এর মতো ফলাফল দেবে:
{
"result" : [
{
"_id" : "address1",
"books" : [
{
"book" : "book4",
"count" : 1
},
{
"book" : "book5",
"count" : 1
},
{
"book" : "book1",
"count" : 3
}
],
"count" : 5
},
{
"_id" : "address2",
"books" : [
{
"book" : "book5",
"count" : 1
},
{
"book" : "book1",
"count" : 2
}
],
"count" : 3
}
],
"ok" : 1
}
সুতরাং এটির মধ্যে আপনি যা জিজ্ঞাসা করছেন তার থেকে এটি আলাদা হয়, যখন আমরা ঠিকানার মানগুলির জন্য শীর্ষ ফলাফল পাই তবে অন্তর্নিহিত "বই" নির্বাচন কেবল ফলাফলের প্রয়োজনীয় পরিমাণে সীমাবদ্ধ নয়।
এটি করা খুব কঠিন হতে প্রমাণিত হয়, তবে এটি আপনার মেলানো আইটেমের সংখ্যার সাথে জটিলতা বাড়ালেও এটি করা যায়। এটি সহজ রাখতে আমরা এটিকে সর্বোচ্চ 2 টি ম্যাচে রাখতে পারি:
db.books.aggregate([
{ "$group": {
"_id": {
"addr": "$addr",
"book": "$book"
},
"bookCount": { "$sum": 1 }
}},
{ "$group": {
"_id": "$_id.addr",
"books": {
"$push": {
"book": "$_id.book",
"count": "$bookCount"
},
},
"count": { "$sum": "$bookCount" }
}},
{ "$sort": { "count": -1 } },
{ "$limit": 2 },
{ "$unwind": "$books" },
{ "$sort": { "count": 1, "books.count": -1 } },
{ "$group": {
"_id": "$_id",
"books": { "$push": "$books" },
"count": { "$first": "$count" }
}},
{ "$project": {
"_id": {
"_id": "$_id",
"books": "$books",
"count": "$count"
},
"newBooks": "$books"
}},
{ "$unwind": "$newBooks" },
{ "$group": {
"_id": "$_id",
"num1": { "$first": "$newBooks" }
}},
{ "$project": {
"_id": "$_id",
"newBooks": "$_id.books",
"num1": 1
}},
{ "$unwind": "$newBooks" },
{ "$project": {
"_id": "$_id",
"num1": 1,
"newBooks": 1,
"seen": { "$eq": [
"$num1",
"$newBooks"
]}
}},
{ "$match": { "seen": false } },
{ "$group":{
"_id": "$_id._id",
"num1": { "$first": "$num1" },
"num2": { "$first": "$newBooks" },
"count": { "$first": "$_id.count" }
}},
{ "$project": {
"num1": 1,
"num2": 1,
"count": 1,
"type": { "$cond": [ 1, [true,false],0 ] }
}},
{ "$unwind": "$type" },
{ "$project": {
"books": { "$cond": [
"$type",
"$num1",
"$num2"
]},
"count": 1
}},
{ "$group": {
"_id": "$_id",
"count": { "$first": "$count" },
"books": { "$push": "$books" }
}},
{ "$sort": { "count": -1 } }
])
সুতরাং এটি আপনাকে প্রথম দুটি "ঠিকানা" শীর্ষস্থানীয় দুটি "ঠিকানা" এন্ট্রি দেবে।
তবে আমার অর্থের জন্য, প্রথম ফর্মের সাথে থাকুন এবং তারপরে অ্যারের উপাদানগুলিকে কেবল "টুকরা" করুন যা প্রথম "এন" উপাদানগুলি নেওয়ার জন্য ফিরে আসে।
বিক্ষোভ কোড
V8.x এবং v10.x প্রকাশের নোডজেএসের বর্তমান এলটিএস সংস্করণগুলির সাথে ব্যবহারের জন্য বিক্ষোভ কোডটি উপযুক্ত। এটি বেশিরভাগ async/await
বাক্য গঠনের জন্য , তবে সাধারণ প্রবাহের অভ্যন্তরে এমন কিছু নেই যা এরকম কোনও বিধিনিষেধ নেই এবং প্লেইন প্রতিশ্রুতিতে এমনকি সামান্য কলব্যাক প্রয়োগের ক্ষেত্রেও সামান্য পরিবর্তনের সাথে মানিয়ে যায়।
index.js
const { MongoClient } = require('mongodb');
const fs = require('mz/fs');
const uri = 'mongodb://localhost:27017';
const log = data => console.log(JSON.stringify(data, undefined, 2));
(async function() {
try {
const client = await MongoClient.connect(uri);
const db = client.db('bookDemo');
const books = db.collection('books');
let { version } = await db.command({ buildInfo: 1 });
version = parseFloat(version.match(new RegExp(/(?:(?!-).)*/))[0]);
await books.deleteMany({});
await books.insertMany(
(await fs.readFile('books.json'))
.toString()
.replace(/\n$/,"")
.split("\n")
.map(JSON.parse)
);
if ( version >= 3.6 ) {
let result = await books.aggregate([
{ "$group": {
"_id": "$addr",
"count": { "$sum": 1 }
}},
{ "$sort": { "count": -1 } },
{ "$limit": 2 },
{ "$lookup": {
"from": "books",
"as": "books",
"let": { "addr": "$_id" },
"pipeline": [
{ "$match": {
"$expr": { "$eq": [ "$addr", "$$addr" ] }
}},
{ "$group": {
"_id": "$book",
"count": { "$sum": 1 },
}},
{ "$sort": { "count": -1 } },
{ "$limit": 2 }
]
}}
]).toArray();
log({ result });
}
let topaddr = await books.aggregate([
{ "$group": {
"_id": "$addr",
"count": { "$sum": 1 }
}},
{ "$sort": { "count": -1 } },
{ "$limit": 2 }
]).toArray();
let topbooks = await Promise.all(
topaddr.map(({ _id: addr }) =>
books.aggregate([
{ "$match": { addr } },
{ "$group": {
"_id": "$book",
"count": { "$sum": 1 }
}},
{ "$sort": { "count": -1 } },
{ "$limit": 2 }
]).toArray()
)
);
topaddr = topaddr.map((d,i) => ({ ...d, books: topbooks[i] }));
log({ topaddr });
client.close();
} catch(e) {
console.error(e)
} finally {
process.exit()
}
})()
books.json
{ "addr": "address1", "book": "book1" }
{ "addr": "address2", "book": "book1" }
{ "addr": "address1", "book": "book5" }
{ "addr": "address3", "book": "book9" }
{ "addr": "address2", "book": "book5" }
{ "addr": "address2", "book": "book1" }
{ "addr": "address1", "book": "book1" }
{ "addr": "address15", "book": "book1" }
{ "addr": "address9", "book": "book99" }
{ "addr": "address90", "book": "book33" }
{ "addr": "address4", "book": "book3" }
{ "addr": "address5", "book": "book1" }
{ "addr": "address77", "book": "book11" }
{ "addr": "address1", "book": "book1" }