mongoid / origin

A Ruby DSL for building MongoDB queries
http://mongoid.org/en/origin/index.html
MIT License
62 stars 29 forks source link

Allow for special case of using only(:a, :b, :c) i.c.w. without(:_id) #50

Closed niels closed 11 years ago

niels commented 11 years ago

While mixing of field in- and exclusion is generally not allowed, one can explicitly exclude the _id field when providing a list of other fields to include. This is desirable when wanting to execute a query that is completely covered by an index which does not include the id field. In these cases, it must explicitly be excluded from the query.

A real-world use case: We have many users, some of which may have registered with their facebook account. We have an index on _id and a sparse index on facebook_id. We do not want to include _id in the latter in order to keep index size down. To quickly gather up all facebook_ids, we want to execute a covered query against the facebook_id index.

Current implementation: User.only(:facebook_id).tap { |q| q.options[:fields][:_id] = 0 }

Desired implementation: User.only(:facebook_id).without(:_id)

rodrigosaito commented 11 years ago

This is already implemented here: https://github.com/mongoid/origin/blob/master/lib/origin/optional.rb#L235, isn't?

niels commented 11 years ago

No, it isn't. Using .without overwrites the field selection previously set by .only, whereas it should append (at least in the special case of wanting to exclude only the id):

1.9.3p286 :043 > User.only(:facebook_id) # Correct
 => #<Mongoid::Criteria
   selector: {},
   options:  {:fields=>{"facebook_id"=>1}},
   class:    User,
   embedded: false>

1.9.3p286 :044 > User.without(:_id) # Correct
 => #<Mongoid::Criteria
   selector: {},
   options:  {:fields=>{"_id"=>0}},
   class:    User,
   embedded: false>

1.9.3p286 :045 > User.only(:facebook_id).without(:_id) # Incorrectly overwritten
 => #<Mongoid::Criteria
   selector: {},
   options:  {:fields=>{"_id"=>0}},
   class:    User,
   embedded: false>

1.9.3p286 :045 > User.only(:facebook_id).without(:_id) # Expected
 => #<Mongoid::Criteria
   selector: {},
   options:  {:fields=>{"facebook_id"=>1, "_id"=>0}},
   class:    User,
   embedded: false>