Search code examples
rubymongodbbson

Ruby pretty print a BSON document?


I am using Ruby to print out a BSON document in MongoDB. This is done using a Ruby wrapper for MongoDB.

  client[:test].find().each do |doc|
    puts doc.to_json()
  end

The find method returns a BSON document. This can be converted to a string using the to_json method.

The output which looks like this:

{"_id":{"$oid":"5a64d2ce36ab1f1ea4b06228"},"admin":"1234","users":"12345","house":{"a":0,"b":0,"c":0},"room":{"a":0,"b":2,"c":1}}

The output is not very human readable. While I could possibly use some regex magic to format the above string to something more comprehensible, is there an easier way to format a BSON document nicely in a similar format as below:

_id: #
"admin": "1234"
"users": "12345"
"house":
  "a": 1
  "b": 2

I have tried the following StackOverflow link. However, the answer yields a no method error for attributes and following the replacing that with keys following the comment only prints keys, not values.


Solution

  • While I haven't used mongodb or BSON before, if we approach this from a JSON perspective you have a couple options. First, instead of calling doc.to_json, it looks like that uses a method as_json to get a hash representation, that it then turns into a string, so we'll use doc.as_json as a starting point instead.

    doc = {"_id":{"$oid":"5a64d2ce36ab1f1ea4b06228"},"admin":"1234","users":"12345","house":{"a":0,"b":0,"c":0},"room":{"a":0,"b":2,"c":1}}
    

    The first way is to use the PP library which is a pretty printer for ruby:

    require 'pp'
    pp doc
    # Outputs:
    # {:_id=>{:$oid=>"5a64d2ce36ab1f1ea4b06228"},
    #  :admin=>"1234",
    #  :users=>"12345",
    #  :house=>{:a=>0, :b=>0, :c=>0},
    #  :room=>{:a=>0, :b=>2, :c=>1}}
    

    which is close to what you want, the pp library has a way to define a customized output by defining a #pretty_print(pp) function in your class, that you can use to help get it the rest of the way.

    The second option to look into is JSON.pretty_generate

    require 'json'
    puts JSON.pretty_generate(doc)
    # {
    #   "_id": {
    #     "$oid": "5a64d2ce36ab1f1ea4b06228"
    #   },
    #   "admin": "1234",
    #   "users": "12345",
    #   "house": {
    #     "a": 0,
    #     "b": 0,
    #     "c": 0
    #   },
    #   "room": {
    #     "a": 0,
    #     "b": 2,
    #     "c": 1
    #   }
    # }
    

    Which is a bit closer to what you're wanting. With some post-processing you can get rid of those brackets and commas, if you want:

    puts JSON.pretty_generate(doc).delete('{},').gsub(/\n\s*\n/, "\n")
    

    As for why switching from attributes to keys starts outputting only the keys, well attributes looks and sounds like it would be a hash, thus looping through you have 2 elements being passed to the block each time, and keys sounds like an array, so you only get the name of the attribute. As mentioned, I don't know mongo, but you would then need to somehow look up the attribute with the key named on your model to get it's value. For instance in ActiveRecord, you might use a public_send(key) if you weren't able to use its attributes method.