Вопрос

Let's say we have a MongoDB collection called "images", and a MongoMapper-powered application with a corresponding "Image" model. If we set up a MongoMapper query using this model, we see that it is of type Plucky::Query and returns results of type Image:

>> Image.where(:file_type => 'image/jpeg').class
=> Plucky::Query

>> Image.where(:file_type => 'image/jpeg').first.class
=> Image

We can run the corresponding query directly on the Mongo adapter, mostly bypassing MongoMapper, by accessing the MongoMapper.connection. If we do it this way, the query is of type Mongo::Cursor and returns raw data results of type BSON::OrderedHash:

>> MongoMapper.connection.db(dbname).collection('images').find({ :file_type => 'image/jpeg' }).class
=> Mongo::Cursor

>> MongoMapper.connection.db(dbname).collection('images').find({ :file_type => 'image/jpeg' }).first.class
=> BSON::OrderedHash

The question is, is there a way to take a Plucky::Query like above and convert it to (or retrieve from it) a basic, non-extended Mongo::Cursor object?

At first I thought I found a solution with find_each, which does actually take a Plucky::Query and return a Mongo::Cursor:

>> Image.where(:file_type => 'image/jpeg').find_each.class
=> Mongo::Cursor

But it turns out this Mongo::Cursor is somehow extended or otherwise different from the above one because it still returns Image objects instead of BSON::OrderHash objects:

>> Image.where(:file_type => 'image/jpeg').find_each.first.class
=> Image

Update: I can't simply bypass MongoMapper query magic altogether like I did in the second case because I need to access features of MongoMapper (specifically named scopes) to build up the query, so what I end up with is a Plucky::Query. But then I want the results to be plain data objects, not models, because all I need is data and I don't want the overhead of model instantiation.

Это было полезно?

Решение 2

MongoMapper achieves the conversion by setting a "transformer" lambda on the plucky query. You can see this in the MongoMapper source code:

def query(options={})
    query = Plucky::Query.new(collection, :transformer => transformer)
    ...
end

...

def transformer
    @transformer ||= lambda { |doc| load(doc) }
end

So after each mongo document retrieval, this Plucky::Query runs the transformation that loads the model. Looking at the Plucky source code we see that there is a simple setter method [] we can use to disable this. So this is the solution:

plucky_query = Image.where(:file_type => 'image/jpeg')

plucky_query.first.class
# => Image

plucky_query[:transformer] = nil
plucky_query.first.class
# => BSON::OrderedHash

If you don't mind monkey-patching you can encapsulate like so:

module Plucky
    class Query
        def raw_data
            self[:transformer] = nil
            self
        end
    end
end

Then you could simply write:

Image.where(:file_type => 'image/jpeg').raw_data.first.class
# => BSON::OrderedHash

Другие советы

If you drop to the driver, the transformer is nil by default:

1.9.3p194 :003 > Image.collection.find({ :file_type => 'image/jpeg' }, { :limit => 1 }).first.class
 => BSON::OrderedHash 
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top