Trip
Trip

Reputation: 27114

No idea why MongoMapper can't handle a Has And Belongs to Many Relationship

This is rife with problems and unlike anything I've witnessed with SQL. I'm simply trying to create an HABTM relationship and make the objects match each other.

My Two Models

class Word
  include MongoMapper::Document         

  many :topics

  key :word, String

end

class Topic
  include MongoMapper::Document         

  many :words

  key :name, String

end

That model work alone allows me to create objects and associate them. Part of why I love Mongo.

Then I try to take some sample yaml like so :

Music I Dig:
  reggae:
    bob marley
    isaac hayes
    groundation
  classical:
    philip glass
    bach

And trying to parse it with this Rakefile :

File.open(Rails.root + 'lib/words/disgusting_glass_full_of.yml', 'r') do |file|
  YAML::load(file).each do |topic, word_types|
    puts "Adding #{topic}.."
    @temp_topic = Topic.create name: topic

    @temp_words = word_types.map { |type, words|
      words.split(' ').map{ |word| 
        @word = Word.create type: type, word: word
        @word.topics << @temp_topic
      }
    }
    @temp_topic.words << @temp_words.flatten
  end
end

I kid you not, this is the most random, jangled output I've ever seen. 2 the amount of actual topics are created that are empty and have no data. Some topics have associations, some done. Same with words. Some words will randomly have associations, and other won't. I can't find any connection at all as to how its deriving this outcome.

I believe the problem comes with how I'm setting up my models ( maybe? ). If not, I'm throwing mongo_mapper and trying Mongoid.

Upvotes: 3

Views: 437

Answers (1)

Emily S
Emily S

Reputation: 369

Firstly, you'll need to specify that word_ids and topic_ids are array attributes in your models:

  class Topic
    include MongoMapper::Document         

    many :words, :in => :word_ids
    key :word_ids, Array
    key :name, String
  end

class Word
  include MongoMapper::Document         

  many :topics, :in => :topic_ids
  key :topic_ids, Array
  key :word, String

end

You'll also have to make sure that you are saving your topic and word in your rake task:

task :import => :environment do
  File.open(Rails.root + 'lib/test.yml', 'r') do |file|
    YAML::load(file).each do |topic, word_types|
      puts "Adding #{topic}.."
      temp_topic = Topic.create name: topic
      temp_words = []
      word_types.map do |type, words|
        words.split(' ').map do |word|
          word = Word.create type: type, word: word
          word.topics << temp_topic
          word.save
          temp_words << word
        end
      end
      temp_topic.words << temp_words.flatten
      temp_topic.save
    end
  end  
end

That gives me the following output:

{
    "_id" : ObjectId("502bc54a3005c83a3a000006"),
    "topic_ids" : [
        ObjectId("502bc54a3005c83a3a000001")
    ],
    "word" : "groundation",
    "type" : "reggae"
}
{
    "_id" : ObjectId("502bc54a3005c83a3a000007"),
    "topic_ids" : [
        ObjectId("502bc54a3005c83a3a000001")
    ],
    "word" : "philip",
    "type" : "classical"
}  ....etc

and

{
    "_id" : ObjectId("502bc54a3005c83a3a000001"),
    "word_ids" : [
        ObjectId("502bc54a3005c83a3a000002"),
        ObjectId("502bc54a3005c83a3a000003"),
        ObjectId("502bc54a3005c83a3a000004"),
        ObjectId("502bc54a3005c83a3a000005"),
        ObjectId("502bc54a3005c83a3a000006"),
        ObjectId("502bc54a3005c83a3a000007"),
        ObjectId("502bc54a3005c83a3a000008"),
        ObjectId("502bc54a3005c83a3a000009")
    ],
    "name" : "Music I Dig"
}

Upvotes: 4

Related Questions