Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
skylight / lib / skylight / normalizers / moped / query.rb
Size: Mime:
module Skylight
  module Normalizers
    module Moped
      class Query < Normalizer
        register "query.moped"

        CAT = "db.mongo.query".freeze

        def normalize(trace, name, payload)
          # payload: { prefix: "  MOPED: #{address.resolved}", ops: operations }

          # We can sometimes have multiple operations. However, it seems like this only happens when doing things
          # like an insert, followed by a get last error, so we can probably ignore all but the first.
          operation = payload[:ops] ? payload[:ops].first : nil
          type = operation && operation.class.to_s =~ /^Moped::Protocol::(.+)$/ ? $1 : nil

          case type
          when "Query"       then normalize_query(operation)
          when "GetMore"     then normalize_get_more(operation)
          when "Insert"      then normalize_insert(operation)
          when "Update"      then normalize_update(operation)
          when "Delete"      then normalize_delete(operation)
          else :skip
          end
        end

      private

        def normalize_query(operation)
          title = normalize_title("QUERY", operation)

          hash, binds = extract_binds(operation.selector)
          description = hash.to_json

          annotations = build_annotations(operation)
          annotations[:skip] = operation.skip
          if operation.fields
            annotations[:fields] = operation.fields.select{|k,v| v == 1 }.keys.map(&:to_s)
          end
          annotations[:binds] = binds unless binds.empty?

          [CAT, title, description, annotations]
        end

        def normalize_get_more(operation)
          title = normalize_title("GET_MORE", operation)

          annotations = build_annotations(operation)
          annotations[:limit] = operation.limit

          [CAT, title, nil, annotations]
        end

        def normalize_insert(operation)
          title = normalize_title("INSERT", operation)

          annotations = build_annotations(operation)
          annotations[:count] = operation.documents.count

          [CAT, title, nil, annotations]
        end

        def normalize_update(operation)
          title = normalize_title("UPDATE", operation)

          selector_hash, selector_binds = extract_binds(operation.selector)
          update_hash, update_binds = extract_binds(operation.update)

          description = { selector: selector_hash, update: update_hash }.to_json

          annotations = build_annotations(operation)

          binds = {}
          binds[:selector] = selector_binds unless selector_binds.empty?
          binds[:update]   = update_binds   unless update_binds.empty?
          annotations[:binds] = binds unless binds.empty?

          [CAT, title, description, annotations]
        end

        def normalize_delete(operation)
          title = normalize_title("DELETE", operation)

          hash, binds = extract_binds(operation.selector)
          description = hash.to_json

          annotations = build_annotations(operation)
          annotations[:binds] = binds unless binds.empty?

          [CAT, title, description, annotations]
        end

        def normalize_title(type, operation)
          "#{type} #{operation.collection}"
        end

        def build_annotations(operation)
          annotations = {}

          if operation.respond_to?(:flags)
            flags = operation.flags.map{|f| flag_name(f) }
            annotations[:flags] = flags unless flags.empty?
          end

          annotations
        end

        # Some flags used by Moped don't map directly to the Mongo docs
        # See http://docs.mongodb.org/meta-driver/latest/legacy/mongodb-wire-protocol/
        FLAG_MAP = {
          tailable: "TailableCursor",
          multi:    "MultiUpdate"
        }

        def flag_name(flag)
          FLAG_MAP[flag] || flag.to_s.sub(/^[a-z\d]*/) { $&.capitalize }.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }
        end

        def extract_binds(hash, binds=[])
          ret = {}

          hash.each do |k,v|
            if v.is_a?(Hash)
              ret[k] = extract_binds(v, binds)[0]
            else
              binds << stringify(hash[k])
              ret[k] = '?'
            end
          end

          [ret, binds]
        end

        def stringify(value)
          value.is_a?(Regexp) ? value.inspect : value.to_s
        end

      end
    end
  end
end