2012-05-30 2 views
0

У меня есть большая коллекция, где я хочу изменить все документы, заполнив поле.Можете ли вы использовать карту MongoDB/уменьшить для переноса данных?

Простым примером может быть кэширование комментариев кол на каждую должность:

class Post 
    field :comment_count, type: Integer 
    has_many :comments 
end 
class Comment 
    belongs_to :post 
end 

Я могу запустить его в серийное что-то вроде:

Post.all.each do |p| 
    p.udpate_attribute :comment_count, p.comments.count 
end 

Но это занимает 24 часа, чтобы бежать (большой коллекция). Мне было интересно, можно ли использовать карту/сокращение монго для этого? Но я еще не видел замечательного примера.

Я предполагаю, что вы отобразите коллекцию комментариев, а затем сохраните уменьшенные результаты в коллекции сообщений. Я на правильном пути?

ответ

0

Вы можете использовать карту MongoDB/уменьшить, чтобы «помочь» перенести данные, , к сожалению, вы не можете использовать ее для полной миграции на сервер. Вы находитесь на правильном пути, основная идея будет заключаться в следующем:

  1. карте каждый комментарий испускать (POST_ID, {COMMENT_COUNT: 1}) ---> {_id: post_id, значение: {COMMENT_COUNT: 1 }}
  2. сводятся к значению {COMMENT_COUNT: N} N является счетчиком суммой ---> {_id: post_id, значение: {COMMENT_COUNT: N}}
  3. указать опцию вывода {уменьшить: 'сообщения'}, чтобы уменьшить результаты map/reduce comment_counts обратно в подборку сообщений

После нескольких обстоятельных исследований я обнаружил, что вы можете приходите близко, , но есть проблема, которая блокирует вас от полностью миграции на стороне сервера. Результат сокращения имеет форму {_id: KEY, значение: MAP_REDUCE_VALUE}. На данный момент мы застряли с этой фигурой, похоже, вокруг не существует способа. Таким образом, вы не можете получить полный исходный документ за пределами этой формы в качестве входных данных для уменьшения (фактически, вы потеряете данные вне этой формы), и не обновите документ за пределами этой формы в результате уменьшения. Поэтому «окончательное» обновление вашей коллекции сообщений должно выполняться программным путем через клиента. Похоже, что это будет хороший запрос на модификацию.

Ниже приведен рабочий пример, демонстрирующий использование карты MongoDB/сокращение в Ruby для расчета всех comment_counts. Затем я программно использую коллекцию map_reduce_results для обновления comment_count в коллекции сообщений. Функция уменьшения раздели вниз от попытки использовать из: {уменьшить: «сообщения»}

Вы можете проверить мой ответ с небольшим количеством экспериментов, или я могу отправить нерабочего полностью на стороне сервера покушение на запрос если вы хотите, в комплекте с фиксированными моделями. Надеюсь, что это поможет понять карту MongoDB/уменьшить в Ruby.

тест/блок/comment_test.rb

require 'test_helper' 

class CommentTest < ActiveSupport::TestCase 
    def setup 
    @map_reduce_results_name = 'map_reduce_results' 
    delete_all 
    end 

    def delete_all 
    Post.delete_all 
    Comment.delete_all 
    Mongoid.database.drop_collection(@map_reduce_results_name) 
    end 

    def dump(title = nil) 
    yield 
    puts title 
    Post.all.to_a.each do |post| 
     puts "#{post.to_json} #{post.comments.collect(&:text).to_json}" 
    end 
    end 

    def generate 
    (2+rand(2)).times do |p| 
     post = Post.create(text: 'post_' + p.to_s) 
     comments = (2+rand(3)).times.collect do |c| 
     Comment.create(text: "post_#{p} comment_#{c}") 
     end 
     post.comments = comments 
    end 
    end 

    def generate_and_migrate(title = nil) 
    dump(title + ' generate:') { generate } 
    dump(title + ' migrate:') { yield } 
    end 

    test "map reduce migration" do 
    generate_and_migrate('programmatic') do 
     Post.all.each do |p| 
     p.update_attribute :comment_count, p.comments.count 
     end 
    end 
    delete_all 
    generate_and_migrate('map/reduce') do 
     map = "function() { emit(this.post_id, {comment_count: 1}); }" 
     reduce = <<-EOF 
     function(key, values) { 
      var result = {comment_count: 0}; 
      values.forEach(function(value) { result.comment_count += value.comment_count; }); 
      return result; 
     } 
     EOF 
     out = @map_reduce_results_name #{reduce: 'posts'} 
     result_coll = Comment.collection.map_reduce(map, reduce, out: out) 
     puts "#{@map_reduce_results_name}:" 
     result_coll.find.each do |doc| 
     p doc 
     Post.find(doc['_id']).update_attribute :comment_count, doc['value']['comment_count'].to_i 
     end 
    end 
    end 
end 

выход теста (жаль о смеси JSON и Ruby, осмотр)

Run options: --name=test_map_reduce_migration 

# Running tests: 

programmatic generate: 
{"_id":"4fcae3bde4d30b21e2000001","comment_count":null,"text":"post_0"} ["post_0 comment_0","post_0 comment_1","post_0 comment_2"] 
{"_id":"4fcae3bde4d30b21e2000005","comment_count":null,"text":"post_1"} ["post_1 comment_1","post_1 comment_0","post_1 comment_2","post_1 comment_3"] 
{"_id":"4fcae3bde4d30b21e200000a","comment_count":null,"text":"post_2"} ["post_2 comment_1","post_2 comment_3","post_2 comment_0","post_2 comment_2"] 
programmatic migrate: 
{"_id":"4fcae3bde4d30b21e2000001","comment_count":3,"text":"post_0"} ["post_0 comment_0","post_0 comment_1","post_0 comment_2"] 
{"_id":"4fcae3bde4d30b21e2000005","comment_count":4,"text":"post_1"} ["post_1 comment_1","post_1 comment_0","post_1 comment_2","post_1 comment_3"] 
{"_id":"4fcae3bde4d30b21e200000a","comment_count":4,"text":"post_2"} ["post_2 comment_1","post_2 comment_3","post_2 comment_0","post_2 comment_2"] 
map/reduce generate: 
{"_id":"4fcae3bee4d30b21e200000f","comment_count":null,"text":"post_0"} ["post_0 comment_0","post_0 comment_1"] 
{"_id":"4fcae3bee4d30b21e2000012","comment_count":null,"text":"post_1"} ["post_1 comment_2","post_1 comment_0","post_1 comment_1"] 
{"_id":"4fcae3bee4d30b21e2000016","comment_count":null,"text":"post_2"} ["post_2 comment_0","post_2 comment_1","post_2 comment_2","post_2 comment_3"] 
map_reduce_results: 
{"_id"=>BSON::ObjectId('4fcae3bee4d30b21e200000f'), "value"=>{"comment_count"=>2.0}} 
{"_id"=>BSON::ObjectId('4fcae3bee4d30b21e2000012'), "value"=>{"comment_count"=>3.0}} 
{"_id"=>BSON::ObjectId('4fcae3bee4d30b21e2000016'), "value"=>{"comment_count"=>4.0}} 
map/reduce migrate: 
{"_id":"4fcae3bee4d30b21e200000f","comment_count":2,"text":"post_0"} ["post_0 comment_0","post_0 comment_1"] 
{"_id":"4fcae3bee4d30b21e2000012","comment_count":3,"text":"post_1"} ["post_1 comment_2","post_1 comment_0","post_1 comment_1"] 
{"_id":"4fcae3bee4d30b21e2000016","comment_count":4,"text":"post_2"} ["post_2 comment_0","post_2 comment_1","post_2 comment_2","post_2 comment_3"] 
. 

Finished tests in 0.072870s, 13.7231 tests/s, 0.0000 assertions/s. 

1 tests, 0 assertions, 0 failures, 0 errors, 0 skips 
Смежные вопросы