0

I have a Collection Report embeds submissions

class Report
  embeds_many :submissions

class Submission
  embedded_in :report
  field :date_submitted, type: TimeWithZone
  field :mistakes, type: Integer

I am trying to create a scope on Report

I want to add a scope query with two parts get the latest submission (given by max date_submitted) that also has zero mistakes

I can create a scope for the mistakes part, but cannot work out how to get the latest submission

scope :my_scope, where("submissions.mistakes" => 0)

So this report would be returned as it's last enter in submissions has zero mistakes

Report
"submissions" : [
        {

            "date_submitted" : ISODate("2014-01-28T13:00:00Z"),
            "mistakes" : 11
        },
        {
            "date_submitted" : ISODate("2014-03-08T13:00:00Z"),
            "mistakes" : 0
        }
    ]

where this one wouldn't be returned

Report
"submissions" : [
        {

            "date_submitted" : ISODate("2014-01-28T13:00:00Z"),
            "mistakes" : 0
        },
        {
            "date_submitted" : ISODate("2014-03-08T13:00:00Z"),
            "mistakes" : 11
        }
    ]
Neil Lunn
  • 148,042
  • 36
  • 346
  • 317
dboyd68
  • 1,104
  • 15
  • 33

1 Answers1

0

This is because you are not filtering the element of the embedded array but the document that contains that element.

There could be an $elemMatch clause here which allows you to combine the conditions on a single element. But find does not have any operation for getting the max value as it were. This is not to be confused with the $max query modifier, which actually clips the index in use to not search beyond those bounds.

So here you use aggregate:

db.collection.aggregate([
   // Optionally query to match and filter your documents.       
   //{ "$match: { /* Same conditions as find */ } },

   // Unwind the array
   { "$unwind": "$submissions" },

   // Filter all but 0 mistakes
   { "$match": { "submissions.mistakes": 0 } },

   // Group the results, taking the max entry and presuming by document `_id`
   { "$group": {
       "_id": "$_id",
       "date_submitted": { "$max": "$submissions.date_submitted" }
   }}

])

That is the general process for filtering the elements of an array. You may look into your driver implementation of aggregate, but the form is always the pipeline represented as an array of documents (hashes) in this form. Possibly using the moped form for getting the collection method. So something like:

Report.collection.aggregate([ /* stages */ ])

For more information on returning the original document form if that is what your requirement is then see here.

Community
  • 1
  • 1
Neil Lunn
  • 148,042
  • 36
  • 346
  • 317