একাধিক ক্ষেত্র দ্বারা mongodb গ্রুপ মান


111

উদাহরণস্বরূপ, আমার কাছে এই নথিগুলি রয়েছে:

{
  "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"
}

ইত্যাদি।


আমি কীভাবে একটি অনুরোধ করতে পারি, যা শীর্ষে এন ঠিকানাগুলি এবং ঠিকানার উপরে শীর্ষ এম বইগুলি বর্ণনা করবে?

প্রত্যাশিত ফলাফলের উদাহরণ:

ঠিকানা 1 | book_1: 5
| book_2: 10
| book_3: 50
| মোট: 65
______________________ ঠিকানা
2 | book_1: 10
| book_2: 10
| ...
| | বই_এম: 10
| মোট: এম * 10
...
______________________
ঠিকানাএন | book_1: 20
| book_2: 20
| ...
| | book_M: 20
| মোট: এম * 20

উত্তর:


214

টিএলডিআর সংক্ষিপ্তসার

আধুনিক মঙ্গোডিবি রিলিজগুলিতে আপনি $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]);

    // Clear and load books
    await books.deleteMany({});

    await books.insertMany(
      (await fs.readFile('books.json'))
        .toString()
        .replace(/\n$/,"")
        .split("\n")
        .map(JSON.parse)
    );

    if ( version >= 3.6 ) {

    // Non-correlated pipeline with limits
      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 });
    }

    // Serial result procesing with parallel fetch

    // First get top addr items
    let topaddr = await books.aggregate([
      { "$group": {
        "_id": "$addr",
        "count": { "$sum": 1 }
      }},
      { "$sort": { "count": -1 } },
      { "$limit": 2 }
    ]).toArray();

    // Run parallel top books for each addr
    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()
      )
    );

    // Merge output
    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"  }

47

নীচের মত সামগ্রিক ফাংশন ব্যবহার করে:

[
{$group: {_id : {book : '$book',address:'$addr'}, total:{$sum :1}}},
{$project : {book : '$_id.book', address : '$_id.address', total : '$total', _id : 0}}
]

এটি আপনাকে নীচের মত ফলাফল দেবে:

        {
            "total" : 1,
            "book" : "book33",
            "address" : "address90"
        }, 
        {
            "total" : 1,
            "book" : "book5",
            "address" : "address1"
        }, 
        {
            "total" : 1,
            "book" : "book99",
            "address" : "address9"
        }, 
        {
            "total" : 1,
            "book" : "book1",
            "address" : "address5"
        }, 
        {
            "total" : 1,
            "book" : "book5",
            "address" : "address2"
        }, 
        {
            "total" : 1,
            "book" : "book3",
            "address" : "address4"
        }, 
        {
            "total" : 1,
            "book" : "book11",
            "address" : "address77"
        }, 
        {
            "total" : 1,
            "book" : "book9",
            "address" : "address3"
        }, 
        {
            "total" : 1,
            "book" : "book1",
            "address" : "address15"
        }, 
        {
            "total" : 2,
            "book" : "book1",
            "address" : "address2"
        }, 
        {
            "total" : 3,
            "book" : "book1",
            "address" : "address1"
        }

আমি আপনার প্রত্যাশিত ফলাফলের ফর্ম্যাটটি পুরোপুরি পাইনি, সুতরাং আপনার প্রয়োজনের সাথে এটি নির্দ্বিধায় নির্দ্বিধায়।


4
এটি কেবল সমস্যার অংশটি সমাধান করে এবং দুটি গ্রুপিংয়ের জন্য "শীর্ষ" করে না।
ওয়্যার্ডপ্রাইরি

এছাড়াও @ ওয়্যার্ডপ্রাইয়ের মন্তব্য সম্পর্কে আমি দেখতে পাচ্ছি না যে এটি কীভাবে উপস্থাপিত প্রশ্নের কোনও অংশ সমাধান করেছিল। "শীর্ষে এন ঠিকানা এবং প্রতি ঠিকানা শীর্ষ এন বই"।
নীল লুন

অনুগ্রহ যদি আপনি MongoDB সম্পর্কিত প্রশ্নের জন্য যা করতে পারেন হেল্প এবং - stackoverflow.com/questions/61067856/...
newdeveloper

5

কোয়েরির নীচে কাঙ্ক্ষিত প্রতিক্রিয়া হিসাবে দেওয়া ঠিক একই ফলাফল প্রদান করবে:

db.books.aggregate([
    {
        $group: {
            _id: { addresses: "$addr", books: "$book" },
            num: { $sum :1 }
        }
    },
    {
        $group: {
            _id: "$_id.addresses",
            bookCounts: { $push: { bookName: "$_id.books",count: "$num" } }
        }
    },
    {
        $project: {
            _id: 1,
            bookCounts:1,
            "totalBookAtAddress": {
                "$sum": "$bookCounts.count"
            }
        }
    }

]) 

প্রতিক্রিয়া নীচের মত দেখতে হবে:

/* 1 */
{
    "_id" : "address4",
    "bookCounts" : [
        {
            "bookName" : "book3",
            "count" : 1
        }
    ],
    "totalBookAtAddress" : 1
},

/* 2 */
{
    "_id" : "address90",
    "bookCounts" : [
        {
            "bookName" : "book33",
            "count" : 1
        }
    ],
    "totalBookAtAddress" : 1
},

/* 3 */
{
    "_id" : "address15",
    "bookCounts" : [
        {
            "bookName" : "book1",
            "count" : 1
        }
    ],
    "totalBookAtAddress" : 1
},

/* 4 */
{
    "_id" : "address3",
    "bookCounts" : [
        {
            "bookName" : "book9",
            "count" : 1
        }
    ],
    "totalBookAtAddress" : 1
},

/* 5 */
{
    "_id" : "address5",
    "bookCounts" : [
        {
            "bookName" : "book1",
            "count" : 1
        }
    ],
    "totalBookAtAddress" : 1
},

/* 6 */
{
    "_id" : "address1",
    "bookCounts" : [
        {
            "bookName" : "book1",
            "count" : 3
        },
        {
            "bookName" : "book5",
            "count" : 1
        }
    ],
    "totalBookAtAddress" : 4
},

/* 7 */
{
    "_id" : "address2",
    "bookCounts" : [
        {
            "bookName" : "book1",
            "count" : 2
        },
        {
            "bookName" : "book5",
            "count" : 1
        }
    ],
    "totalBookAtAddress" : 3
},

/* 8 */
{
    "_id" : "address77",
    "bookCounts" : [
        {
            "bookName" : "book11",
            "count" : 1
        }
    ],
    "totalBookAtAddress" : 1
},

/* 9 */
{
    "_id" : "address9",
    "bookCounts" : [
        {
            "bookName" : "book99",
            "count" : 1
        }
    ],
    "totalBookAtAddress" : 1
}

অনেক অনেক ধন্যবাদ ভাই! এই আমি খুঁজছিলাম ছিল। তোমাকে অনেক ধন্যবাদ!!
অভিজিৎ রাজ
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.