Вопрос

I'm relatively new to MongoDB and Mongoid. My MongoDB query to return only the matching embedded record (using the $ projection) looks like

db.volumes.find({"vol": 1, "chapters.url": "my-chapter"}, {"chapters.$": 1})

where a Volume embeds many Chapters. This query also takes advantage of the index on the chapters.url field that I've specified.

What's the equivalent of the above in Mongoid? I've tried

Volume.where({vol: 1, "chapters.url" => "my-chapter"}).only(:chapters)

but which returns only the parent Volume object, with its more than one embedded Chapters, unlike the original MongoDB query that returns exactly the one Chapter record I'm looking for.

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

Решение

Please find the test below that demonstrates the closest equivalent in Mongoid / Moped. Neither the Mongoid nor the Moped answer is "exactly" the same. For the underlying driver Moped, the query result still has the chapter subdoc wrapped in the parent structure, therefore requiring additional extraction in Ruby on the client side. For the high-level ODM Mongoid, the query result is in terms of the parent model by definition, then the #chapters association helper method traverses to chapters, and the second #where clause locally extracts on the client side. Mongoid has the #pluck method, but it only expects a simple field name, so specifying a dot-path has an unexpected result and returns empty at the model level.

You can play with modifications of the test to satisfy your understanding. Hope that this helps.

test/unit/volume_test.rb

require 'test_helper'
require 'pp'

class VolumeTest < ActiveSupport::TestCase
  def setup
    Volume.delete_all
    puts
  end
  test "project" do
    doc = {vol: 1, chapters: [{url: 'my-chapter'}, {url: 'your-chapter'}]}
    Volume.create(doc)
    assert_equal(1, Volume.count)
    query = {"vol" => 1, "chapters.url" => "my-chapter"}
    puts "Moped:"
    result = Volume.collection.find(query).select("chapters.$" => 1).first['chapters'].first
    assert_equal('my-chapter', result['url'])
    pp result
    puts "Mongoid:"
    result = Volume.where(query).first.chapters.where("url" => "my-chapter").first.attributes
    assert_equal('my-chapter', result['url'])
    pp result
  end
  test "versions" do
    puts "Mongoid version: #{Mongoid::VERSION}\nMoped version: #{Moped::VERSION}"
    puts "MongoDB version: #{Volume.collection.database.command({:buildinfo => 1})['version']}"
  end
end

$ rake test

Run options:

# Running tests:

[1/2] VolumeTest#test_project
Moped:
{"_id"=>"537274767f11ba1977000002", "url"=>"my-chapter"}
Mongoid:
{"_id"=>"537274767f11ba1977000002", "url"=>"my-chapter"}
[2/2] VolumeTest#test_versions
Mongoid version: 3.1.6
Moped version: 1.5.2
MongoDB version: 2.6.1
Finished tests in 0.063019s, 31.7365 tests/s, 47.6047 assertions/s.
2 tests, 3 assertions, 0 failures, 0 errors, 0 skips

app/models/volume.rb

class Volume
  include Mongoid::Document
  field :vol, type: Integer
  embeds_many :chapters
end

app/models/chapter.rb

class Chapter
  include Mongoid::Document
  field :url, type: String
  embedded_in :volume
end
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top