class NoSE::Backend::MongoBackend

A backend which communicates with MongoDB

Public Class Methods

field_path(index, field) click to toggle source

Find the path to a given field @return [Array<String>]

# File lib/nose/backend/mongo.rb, line 126
def self.field_path(index, field)
  # Find the path from the hash entity to the given key
  field_path = index.graph.path_between index.hash_fields.first.parent,
                                        field.parent
  field_path = field_path.path_for_field(field)

  # Use _id for any primary keys
  field_path[-1] = '_id' if field.is_a? Fields::IDField

  field_path
end
new(model, indexes, plans, update_plans, config) click to toggle source
Calls superclass method NoSE::Backend::Backend::new
# File lib/nose/backend/mongo.rb, line 11
def initialize(model, indexes, plans, update_plans, config)
  super

  @uri = config[:uri]
  @database = config[:database]
  Mongo::Logger.logger.level = ::Logger::FATAL
end
rows_from_mongo(rows, index, fields = nil) click to toggle source

Convert documens returned from MongoDB into the format we understand @return [Array<Hash>]

# File lib/nose/backend/mongo.rb, line 113
def self.rows_from_mongo(rows, index, fields = nil)
  fields = index.all_fields if fields.nil?

  rows.map! do |row|
    Hash[fields.map do |field|
      field_path = MongoBackend.field_path(index, field)
      [field.id, field_path.reduce(row) { |h, p| h[p] }]
    end]
  end
end

Public Instance Methods

by_id_graph() click to toggle source

MongoDB uses ID graphs for column families @return [Boolean]

# File lib/nose/backend/mongo.rb, line 21
def by_id_graph
  true
end
generate_id() click to toggle source

Produce a new ObjectId @return [BSON::ObjectId]

# File lib/nose/backend/mongo.rb, line 27
def generate_id
  BSON::ObjectId.new
end
index_insert_chunk(index, chunk) click to toggle source

Insert a chunk of rows into an index @return [Array<BSON::ObjectId>]

# File lib/nose/backend/mongo.rb, line 76
def index_insert_chunk(index, chunk)
  # We only need to insert into indexes which are ID graphs
  fail unless index == index.to_id_graph

  chunk.map! do |row|
    row_hash = Hash.new { |h, k| h[k] = Hash.new(&h.default_proc) }
    index.all_fields.each do |field|
      field_path = self.class.field_path(index, field)
      entity_hash = field_path[0..-2].reduce(row_hash) { |h, k| h[k] }

      if field_path.last == '_id'
        entity_hash[field_path.last] = BSON::ObjectId.new
      else
        entity_hash[field_path.last] = row[field.id]
      end
    end

    row_hash.default_proc = nil
    row_hash
  end

  client[index.key].insert_many(chunk, ordered: false).inserted_ids
end
index_sample(index, count) click to toggle source

Sample a number of values from the given index

# File lib/nose/backend/mongo.rb, line 101
def index_sample(index, count)
  rows = client[index.to_id_graph.key].aggregate(
    [
      { '$sample' => { 'size' => count } }
    ]
  ).to_a

  MongoBackend.rows_from_mongo rows, index
end
indexes_ddl(execute = false, skip_existing = false, drop_existing = false) click to toggle source

Create new MongoDB collections for each index

# File lib/nose/backend/mongo.rb, line 32
def indexes_ddl(execute = false, skip_existing = false,
                drop_existing = false)
  ddl = []

  # Create the ID graphs for all indexes
  id_graphs = @indexes.map(&:to_id_graph).uniq
  id_graphs.map do |id_graph|
    ddl << "Create #{id_graph.key}"
    next unless execute

    collection = client.collections.find { |c| c.name == id_graph.key }
    collection.drop if drop_existing && !collection.nil?
    client[id_graph.key].create unless skip_existing
  end

  # Create any necessary indexes on the ID graphs
  index_keys = []
  @indexes.sort_by do |index|
    -(index.hash_fields.to_a + index.order_fields).length
  end.each do |index|
    # Check if we already have a prefix of this index created
    keys = index.hash_fields.to_a + index.order_fields
    next if index_keys.any? { |i| i[keys.length - 1] == keys }
    index_keys << keys

    id_graph = index.to_id_graph
    next if id_graph == index

    # Combine the key paths for all fields to create a compound index
    index_spec = Hash[keys.map do |key|
      [self.class.field_path(index, key).join('.'), 1]
    end]

    ddl << "Add index #{index_spec} to #{id_graph.key} (#{index.key})"
    next unless execute

    client[id_graph.key].indexes.create_one index_spec
  end

  ddl
end

Private Instance Methods

client() click to toggle source

Create a Mongo client from the saved config

# File lib/nose/backend/mongo.rb, line 239
def client
  @client ||= Mongo::Client.new @uri, database: @database
end