The $project operation fails to work with Mongoid aggregate method when used after $group

StackOverflow https://stackoverflow.com/questions/19869555

  •  29-07-2022
  •  | 
  •  

Question

I have a simple dataset in a MongoDB (version 2.2.2):

{
  "_id": "527d60865593622ba17643e6",
  "siteid": "100",
  "date": 1383940800,
  "visits": 1
},
{
  "_id": "527d60865593622ba17643e7",
  "siteid": "200",
  "date": 1383940801,
  "visits": 1
},
{
  "_id": "527d60865593622ba17643e8",
  "siteid": "200",
  "date": 1383940802,
  "visits": 1
}

I have a Mongoid (version 3.0.1) collection where I'm trying to use the aggregate method:

query = {
  "$project" => { _id: 0 },
  "$match" => {"$and" => [{:date=>{"$gte"=>1383940800}}, {:date=>{"$lt"=>1384027200}}]},
  "$group" => {"_id" => "$siteid", "visits" => {"$sum" => "$visits"}},
  "$project" => { "siteid" => "$_id" }
}
result = @object.collection.aggregate(query)

The result that I get back is this:

[{"_id"=>200, "visits"=>2}, {"_id"=>100, "visits"=>1}]

What I'd like the result to be is this:

[{"siteid"=>200, "visits"=>2}, {"siteid"=>100, "visits"=>1}]

I thought the last $project operation would do this for me but it seems to be completely ignored. Any suggestions?

Was it helpful?

Solution

The argument to the aggregate method is a pipeline which is an array of hashes where each hash is a stage. Your example query is a hash with values that are hashes; too bad that it reads well, but this obscures the misuse. Unfortunately, Moped somehow allows this misuse even though the mongo shell catches it and returns the server error message "exception: A pipeline stage specification object must contain exactly one field." I hope that you like the following, I think that it is what you want.

test/unit/visit_test.rb

require 'test_helper'
require 'json'

class VisitTest < ActiveSupport::TestCase
  def setup
    Visit.delete_all
    puts
  end
  test "0. mongoid version" do
    puts "Mongoid::VERSION:#{Mongoid::VERSION}\nMoped::VERSION:#{Moped::VERSION}"
  end
  test "project after group" do
    data = JSON.parse(<<-EOD
      [
        {"_id": "527d60865593622ba17643e6", "siteid": "100", "date": 1383940800, "visits": 1},
        {"_id": "527d60865593622ba17643e7", "siteid": "200", "date": 1383940801, "visits": 1},
        {"_id": "527d60865593622ba17643e8", "siteid": "200", "date": 1383940802, "visits": 1}
      ]
    EOD
    )
    Visit.create(data)
    assert_equal 3, Visit.count
    pipeline = [
        {"$match" => {"$and" => [{:date => {"$gte" => 1383940800}}, {:date => {"$lt" => 1384027200}}]}},
        {"$group" => {"_id" => "$siteid", "visits" => {"$sum" => "$visits"}}},
        {"$project" => {"_id" => 0, "siteid" => "$_id", "visits" => 1}}
    ]
    p Visit.collection.aggregate(pipeline)
  end
end

rake test

Run options:

# Running tests:

[1/2] VisitTest#test_0._mongoid_version
Mongoid::VERSION:3.1.5
Moped::VERSION:1.5.1
[2/2] VisitTest#test_project_after_group
[{"visits"=>2, "siteid"=>"200"}, {"visits"=>1, "siteid"=>"100"}]
Finished tests in 0.038172s, 52.3944 tests/s, 26.1972 assertions/s.
2 tests, 1 assertions, 0 failures, 0 errors, 0 skips
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top