wake-up-neo.com

Wie verwende ich Aggregationsoperatoren in einem $ -Match in MongoDB (zum Beispiel $ year oder $ dayOfMonth)?

Ich habe eine Sammlung von Dokumenten mit einem Attribut created_date. Ich möchte diese Dokumente über eine Aggregationspipeline senden, um sie etwas zu bearbeiten. Im Idealfall möchte ich sie mit einem $ -Match filtern, bevor ich andere Arbeiten an ihnen durchführe, sodass ich Indizes ausnutzen kann. Ich kann jedoch nicht herausfinden, wie die neuen $ year/$ month/$ dayOfMonth-Operatoren in meinem verwendet werden $ match Ausdruck. 

Es gibt ein paar Beispiele für die Verwendung der Operatoren in einer $ -Projektoperation. Ich befürchte jedoch, dass ich durch den Einbau eines $ -Projekts als ersten Schritt in meine Pipeline den Zugriff auf meine Indizes verloren habe (die MongoDB-Dokumentation zeigt dies an.) Der erste Ausdruck muss ein $ -Match sein, um die Indizes zu nutzen).

Beispieldaten:

{
    post_body: 'This is the body of test post 1',
    created_date: ISODate('2012-09-29T05:23:41Z')
    comments: 48
}
{
    post_body: 'This is the body of test post 2',
    created_date: ISODate('2012-09-24T12:34:13Z')
    comments: 10
}
{
    post_body: 'This is the body of test post 3',
    created_date: ISODate('2012-08-16T12:34:13Z')
    comments: 10
}

Ich möchte dies durch eine Aggregationspipeline führen, um die gesamten Kommentare zu allen im September gemachten Beiträgen zu erhalten

{
    aggregate: 'posts',
    pipeline: [
         {$match:
             /*Can I use the $year/$month operators here to match Sept 2012?
             $year:created_date : 2012,
             $month:created_date : 9
             */
             /*or does this have to be 
             created_date : 
                  {$gte:{$date:'2012-09-01T04:00:00Z'}, 
                  $lt: {$date:'2012-10-01T04:00:00Z'} }
             */
         },
         {$group:
             {_id: '0',
              totalComments:{$sum:'$comments'}
             }
          }
    ]
 }

Dies funktioniert, aber der Abgleich verliert für kompliziertere Abfragen den Zugriff auf alle Indizes:

{
    aggregate: 'posts',
    pipeline: [
         {$project:
              {
                   month : {$month:'$created_date'},
                   year : {$year:'$created_date'}
              }
         },
         {$match:
              {
                   month:9,
                   year: 2012
               }
         },
         {$group:
             {_id: '0',
              totalComments:{$sum:'$comments'}
             }
          }
    ]
 }
16
Mason

Wie Sie bereits festgestellt haben, können Sie $ nicht auf Felder abstimmen, die nicht im Dokument enthalten sind (es funktioniert genauso wie Suchen, und wenn Sie zuerst $ project verwenden, verlieren Sie die Möglichkeit, Indizes zu verwenden.).

Stattdessen können Sie Ihre Anstrengungen wie folgt kombinieren:

{
    aggregate: 'posts',
    pipeline: [
         {$match: {
             created_date : 
                  {$gte:{$date:'2012-09-01T04:00:00Z'}, 
                  $lt:  {date:'2012-10-01T04:00:00Z'} 
                  }}
             }
         },
         {$group:
             {_id: '0',
              totalComments:{$sum:'$comments'}
             }
          }
    ]
 }

Die obigen Angaben geben Ihnen nur eine Aggregation für September. Wenn Sie mehrere Monate lang aggregieren möchten, können Sie beispielsweise Folgendes tun:

{
    aggregate: 'posts',
    pipeline: [
         {$match: {
             created_date : 
                  { $gte:'2012-07-01T04:00:00Z', 
                    $lt: '2012-10-01T04:00:00Z'
                  }
         },
         {$project: {
              comments: 1,
              new_created: {
                        "yr" : {"$year" : "$created_date"},
                        "mo" : {"$month" : "$created_date"}
                     }
              }
         },
         {$group:
             {_id: "$new_created",
              totalComments:{$sum:'$comments'}
             }
          }
    ]
 }

und du bekommst etwas zurück wie:

{
    "result" : [
        {
            "_id" : {
                "yr" : 2012,
                "mo" : 7
            },
            "totalComments" : 5
        },
        {
            "_id" : {
                "yr" : 2012,
                "mo" : 8
            },
            "totalComments" : 19
        },
        {
            "_id" : {
                "yr" : 2012,
                "mo" : 9
            },
            "totalComments" : 21
        }
    ],
    "ok" : 1
}
17
Asya Kamsky

Lassen Sie uns einige Pipelines bauen, bei denen es sich um Vorgänge handelt, die uns bereits bekannt sind. Wir werden uns also die folgenden Phasen ansehen:

  • match - Dies ist eine Filterstufe, ähnlich der von find.
  • project
  • sort
  • skip
  • limit

Wir fragen uns möglicherweise, warum diese Phasen erforderlich sind, da diese Funktionalität bereits in der Abfragesprache MongoDB bereitgestellt wird. Der Grund dafür ist, dass wir diese Phasen benötigen, um die komplexeren analytikorientierten Funktionen der zu unterstützen Aggregations-Framework. Die folgende Abfrage entspricht einfach einem find:


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, ])

Lassen Sie uns eine Projektphase in dieser Aggregationspipeline vorstellen:


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, {
  $project: {
    _id: 0,
    name: 1,
    founded_year: 1
  }
}])

Wir verwenden die Methode aggregate, um das Aggregationsframework zu implementieren. Die Aggregations-Pipelines sind lediglich eine Reihe von Dokumenten. In jedem Dokument sollte ein bestimmter Bühnenbetreiber angegeben sein. Im obigen Fall haben wir also eine Aggregationspipeline mit zwei Stufen. In der Phase $match Werden die Dokumente einzeln an die Phase $project Übergeben.

Lassen Sie uns auf die Stufe limit erweitern:


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, {
  $limit: 5
}, {
  $project: {
    _id: 0,
    name: 1
  }
}])

Dies erhält die übereinstimmenden Dokumente und beschränkt sich auf fünf, bevor die Felder projiziert werden. Die Projektion funktioniert also nur bei 5 Dokumenten. Angenommen, wir würden so etwas tun:


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, {
  $project: {
    _id: 0,
    name: 1
  }
}, {
  $limit: 5
}])

Dies erhält die passenden Dokumente und projiziert diese große Anzahl von Dokumenten und beschränkt sich schließlich auf fünf. Die Projektion arbeitet also mit einer großen Anzahl von Dokumenten und beschränkt sich schließlich auf 5. Dies gibt uns eine Lehre, die wir beschränken Sie die Dokumente auf diejenigen, die unbedingt erforderlich sind zur Weitergabe an die nächste Stufe. Schauen wir uns nun die Stufe sort an:


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, {
  $sort: {
    name: 1
  }
}, {
  $limit: 5
}, {
  $project: {
    _id: 0,
    name: 1
  }
}])

Dies sortiert alle Dokumente nach Namen und gibt nur 5 von ihnen aus. Angenommen, wir würden so etwas tun:


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, {
  $limit: 5
}, {
  $sort: {
    name: 1
  }
}, {
  $project: {
    _id: 0,
    name: 1
  }
}])

Dies wird zuerst 5 Dokumente nehmen und sie sortieren. Fügen wir die Stufe skip hinzu:


db.companies.aggregate([{
  $match: {
    founded_year: 2004
  }
}, {
  $sort: {
    name: 1
  }
}, {
  $skip: 10
}, {
  $limit: 5
}, {
  $project: {
    _id: 0,
    name: 1
  }
}, ])

Dies sortiert alle die Dokumente und überspringt die anfänglichen 1 Dokumente und kehrt zu uns zurück. Wir sollten versuchen, $match - Stufen so früh wie möglich in die Pipeline aufzunehmen. Um Dokumente mit einer Stufe $match Zu filtern, verwenden wir dieselbe Syntax zum Erstellen von Abfragedokumenten (Filtern) wie für find().

2
student

Versuche dies;

db.createCollection("so");
db.so.remove();
db.so.insert([
{
    post_body: 'This is the body of test post 1',
    created_date: ISODate('2012-09-29T05:23:41Z'),
    comments: 48
},
{
    post_body: 'This is the body of test post 2',
    created_date: ISODate('2012-09-24T12:34:13Z'),
    comments: 10
},
{
    post_body: 'This is the body of test post 3',
    created_date: ISODate('2012-08-16T12:34:13Z'),
    comments: 10
}
]);
//db.so.find();

db.so.ensureIndex({"created_date":1});
db.runCommand({
    aggregate:"so",
    pipeline:[
        {
            $match: { // filter only those posts in september
                created_date: { $gte: ISODate('2012-09-01'), $lt: ISODate('2012-10-01') }
            }
        },
        {
            $group: {
                _id: null, // no shared key
                comments: { $sum: "$comments" } // total comments for all the posts in the pipeline
            }
        },
]
//,explain:true
});

Ergebnis ist;

{ "result" : [ { "_id" : null, "comments" : 58 } ], "ok" : 1 }

Sie können also auch Ihr vorheriges Beispiel ändern, obwohl ich nicht sicher bin, warum Sie dies tun möchten, es sei denn, Sie planen etwas anderes mit Monat und Jahr in der Pipeline.

{
    aggregate: 'posts',
    pipeline: [
     {$match: { created_date: { $gte: ISODate('2012-09-01'), $lt: ISODate('2012-10-01') } } },
     {$project:
          {
               month : {$month:'$created_date'},
               year : {$year:'$created_date'}
          }
     },
     {$match:
          {
               month:9,
               year: 2012
           }
     },
     {$group:
         {_id: '0',
          totalComments:{$sum:'$comments'}
         }
      }
    ]
 }
0
cirrus