Quincy Larson
Quincy Larson

Reputation: 621

Querying mongoid for value in attribute array

I need to search within Mongoid objects that have array attributes. Here are the relevant objects:

class Author
  include Mongoid::Document
  field :name, type: String

class Book
  include Mongoid::Document
  field :name, type: String
  field :authors, type: Array

I can see that at least one book has a given author:

Book.all.sample.authors
=> [BSON::ObjectId('5363c73a4d61635257805e00'),
 BSON::ObjectId('5363c73a4d61635257835e00'),
 BSON::ObjectId('5363c73a4d61635257c75e00'),
 BSON::ObjectId('5363c73b4d616352574a5f00')]

But I'm unable to find books that have that author.

Book.where(authors: '5363c73a4d61635257805e00').first
=> nil

I've tried the solution listed here: https://groups.google.com/forum/#!topic/mongoid/csNOcugYH0U but it didn't work for me:

Book.any_in(:author => ["5363c73b4d616352574a5f00"]).first
=> nil

I'm not sure what I'm doing wrong. Any ideas? I'd prefer to use Mongoid Origin commands.

Upvotes: 0

Views: 110

Answers (1)

mu is too short
mu is too short

Reputation: 434585

This output:

Book.all.sample.authors
=> [BSON::ObjectId('5363c73a4d61635257805e00'),
 BSON::ObjectId('5363c73a4d61635257835e00'),
 BSON::ObjectId('5363c73a4d61635257c75e00'),
 BSON::ObjectId('5363c73b4d616352574a5f00')]

tells us that authors contains BSON::ObjectIds. ObjectIds are often presented as Strings and sometimes you can use a String instead of a full blown ObjectId (such as with Model.find) but they're still not Strings. You are searching the array for a String:

Book.where(authors: '5363c73a4d61635257805e00')

but '5363c73a4d61635257805e00' and ObjectId('5363c73a4d61635257805e00') are not the same thing inside MongoDB. You need to search for the right thing:

Book.where(authors: BSON::ObjectId('5363c73a4d61635257805e00'))

You might want to monkey patch a to_bson_id method into various places. Something like this:

class String
  def to_bson_id
    BSON::ObjectId.from_string(self)
  end
end

module Mongoid
  module Document
    def to_bson_id
      id
    end
  end
end

module BSON
  class ObjectId
    def to_bson_id
      self
    end
  end
end

class NilClass
  def to_bson_id
    self
  end
end

Should do the trick. Then you can say things like:

Book.where(authors: '5363c73a4d61635257805e00'.to_bson_id)
Book.where(authors: some_string_or_object_id.to_bson_id)

and The Right Thing happens.

You might want to rename authors to author_ids to make its nature a little clearer.

Upvotes: 1

Related Questions