Image Uploading and Processing with Amazon S3, Paperclip, and Delayed Jobs

Image uploading has always been a problem for me at my current job. This is because we use paperclip to process our image into many different sizes (thumbnails, small squares, large image, etc). Processing these images take some time and if there’s enough users trying it at the same time, the request will timeout. This was a big issue for us because as our company scales, the timeouts were happening more frequently. So this was a critical issue that needed to be addressed immediately. So our solution was to use Amazon S3 and delayed jobs (https://github.com/collectiveidea/delayed_job).

The idea was to upload the image up to Amazon S3, which will then return an image URL. So this uploading part does not touch our backend at all so even large images should be fine. It will not eat up any web threads. Once we get the URL from the cloud storage, we will send that to our backend. This will create an image with this URL and will use delayed jobs to process that image. Delayed jobs will basically put it in a queue and use workers to process the image in the background. This way, the user can get quick feedback and it will not hold up a web thread. The drawback to this is that the image might take long time to process if there are hundreds of jobs in the queue. The user will have a poor experience if they wonder why their image isn’t showing. But there are ways around that by using the temporary image URL from Amazon S3.

So first things first. We need to create the image model. It only really needs a two fields for now. Just the file attachment and the unprocessed image URL from Amazon S3.

class Image < Asset   
  field :unprocessed_image_url   
  has_mongoid_attached_file :image, {:styles => IMAGE_STYLES, :processors => :thumbnail}

  def process_image
    self.image = open(self.unprocessed_image_url)
    self.save_image
  end
end

The IMAGE_STYLES are just all of the different sizes of the images we need to cut. Then we have a process_image method that will open up the URL. By putting it into the image field, Paperclip will process that image for us.

The next step is to create a signed URLs controller. This controller will be used to create the Amazon S3 upload policy document and signature. This will be called when you upload an image. It will generate a json with these info and pass this along with the image to Amazon S3. It’s basically our credentials to make sure we have the rights to upload an image to our Amazon S3.

class SignedUrlsController < ApplicationController
  def index
    render json: {
        policy: s3_upload_policy_document,
        signature: s3_upload_signature,
        key: "uploads/#{SecureRandom.uuid}/#{params[:doc][:title]}",
        success_action_redirect: "/"
    }
  end

  private

  # generate the policy document that amazon is expecting.
  def s3_upload_policy_document
    Base64.encode64(
        {
            expiration: 30.minutes.from_now.utc.strftime('%Y-%m-%dT%H:%M:%S.000Z'),
            conditions: [
                { bucket: S3['bucket'] },
                { acl: 'public-read' },
                ["starts-with", "$key", "uploads/"],
                { success_action_status: '201' }
            ]
        }.to_json
    ).gsub(/\n|\r/, '')
  end

  # sign our request by Base64 encoding the policy document.
  def s3_upload_signature
    Base64.encode64(
        OpenSSL::HMAC.digest(
            OpenSSL::Digest::Digest.new('sha1'),
            S3['secret_access_key'],
            s3_upload_policy_document
        )
    ).gsub(/\n/, '')
  end
end

Next is a controller to actually upload our image URL we get from Amazon S3. It will create the image model and then use delayed jobs to put the processing into a queue. It’s pretty straight forward the the json_for_image just either checks if the image is there or not. If the image is processed, it will grab the processed image and if not, it will use the unprocessed_image_url. This is just for preview. If you don’t need that, you can just pass a success.

class ImagesController < ApplicationController   
  def create     
    image = Image.create(:unprocessed_image_url => params[:url])
    image.delay.process_image
    render :json => json_for_image(image)
  end
end

Now that our backend is complete, we need to work on the image uploader form. We need a good image uploader and there is a really nice one online (https://github.com/blueimp/jQuery-File-Upload). It has tons of neat features such as progress bar, chunked uploads, client-side image resizing, and the list goes on. But I’m using it mostly for the drag and drop support as well as the multiple file upload at once. So here is the HTML part in HAML:

%form.image-form{:action => "#{S3['protocol'] + '://' + S3['bucket'] + '.' + S3['domain']}", :method => "post"}
  %input{:type => "hidden", :name => "key"}
  %input{:type => "hidden", :name => "AWSAccessKeyId", :value => "#{S3['access_key_id']}"}
  %input{:type => "hidden", :name => "acl", :value => "public-read"}
  %input{:type => "hidden", :name => "policy"}
  %input{:type => "hidden", :name => "signature"}
  %input{:type => "hidden", :name => "success_action_status", :value => "201"}
  %input{:type => "file", :multiple => false, :name => "file", :accept => "image/*", :class => "image-upload"}

You will need to have the Amazon S3 protocal, bucket, domain, and access_key_id key ready. I also have created a drop zone which you can point to any div where you can drag and drop images to. After you set this up, you will need to initialize the plugin in the Javascript (this will be in coffeescript):

initializeUploadField: ->
  jQuery =>
    @$(".image-upload").fileupload
      dropZone: @$(".image-dropzone")

      add: (e, data) =>
        $.ajax
          url: "/signed_urls"
          type: "GET"
          dataType: "json"
          data:
            doc:
              title: data.files[0].name
          async: false
          success: (retdata) =>
            @$("input[name=key]").val retdata.key
            @$("input[name=policy]").val retdata.policy
            @$("input[name=signature]").val retdata.signature
        data.submit()
      success: (data) =>
        image_url = decodeURIComponent($(data).find("Location").text())
        image = new Woahlag.Models.Image
          url: image_url 
        image.save null,
          success: (model, response) =>
            console.log "Success"
          error: ->
            console.log "ERROR SAVING IMAGE"

As you can see in the add block, it will hit our signed_urls controller to fetch that json we set up earlier. If it succeeds, it sets the key, policy, and signature for Amazon S3 and then submit the data. When that’s done, Amazon will pass back a bunch of data but we only want the location of that image. So we grab that and created a Backbone model with that URL to submit. Though I only printed out “Success” in this example, you can use the image json to show the preview.

And that concludes how to upload mass amount of pictures without locking down your site. I will have posts later on about how we had weird critical bugs with this and how we managed cropping. Thanks!

Flux – Day 2 – Movements and reflecting projectiles

I’m not going to cover every little detail of my game since that would be too much work and writing but I will write about interesting things that I stumble upon. Today I would just be explaining some of my code for the core gameplay. The first was adjusting the movements from last time:

stage.on "stagemousemove", (evt) ->
  _my_ship.x = evt.stageX - _ship_width/2

This was a great way to get basic movements in since it would just sit on top of the mouse. So regardless of how fast the mouse was moving, or taken off screen and put back on screen at another spot, the ship would either move really fast or teleport. The game wouldn’t be about dodging bullets well in advance anymore but rather just clicking on empty spots on the screen to teleport your ship there. So I made a minor adjustment.

stage.on "stagemousemove", (evt) ->
  if evt.rawY < stage.canvas.height     
   _my_ship_target_x = evt.stageX - _ship_width/2 
stage.on "mouseleave", (evt) ->
  _my_ship_target_x = null

_my_ship_target_x is a global variable I set to null. So when the mouse is on the canvas, the location is stored. And when the mouse leaves the canvas, that variable is set to null. So then I created a method called myShipMove and I called it in my update method which is being called by the Ticker 60 times a second.
This way, the ship will try to get closer to this target position every cycle.

myShipMove = ->
  if _my_ship_target_x and Math.abs(_my_ship_target_x - _my_ship.x) > _my_ship_speed
    speed = _my_ship_speed
    if _my_ship_target_x < _my_ship.x
      speed *= -1

    _my_ship.x += speed

So I created another variable called _my_ship_speed to adjust the speed. One big thing I added was checking if the distance between the ship and the target was less than the ship speed. Without that, it was impossible to get to that exact location of the mouse because the change in location wasn’t a small enough interval to hit that exact location. So it would cause weird jitteryness which was solved when I told it not to get to that target position if the difference was too small.

The next part of my core game play was the holding and releasing of bullets. Again, my game idea was that our spaceship would not be able to shoot any bullets but rather to hold and release enemy’s bullet. But as you hold the bullets, their directions will change to retarget your ship again but their speed will drastically drop. So if you hold the bullets on for too long, you will still get hit by the bullets. So first step was to create the shield to indicate the hit area this pull.

In my init method, I created the shield:

_my_shield = new createjs.Shape()
_my_shield.graphics.beginFill("#37FDFC").drawCircle 0, 0, 60
stage.addChild _my_shield

stage.on "stagemousedown", (evt) ->
  if !_flux_activate
    _flux_activate = true
    _flux_capture = true

stage.on "stagemouseup", (evt) ->
  _flux_activate = false
  _flux_capture = false
  pushBullets()

Everything is straight forward but my mouse up and mouse down. I created two booleans: _flux_activate and _flux_capture. And I set them both to false when I instantiated them. Flux capture is used to get that exact cycle the bullet entered the shield and the moment the stage is clicked. Any bullets in the shield at that time will be pulled in. Then the idea is to set that boolean to false right after that cycle. This will allow any other bullets to enter the shield and not be pulled in even while you’re holding down the bullets. While flux activate is to know if the mouse is still being held down. So on mouse up, those variables are reset back to false and a pushBullets method is called which I’ll explain in a bit.

So I updated my enemyBulletTravel method to check if the bullets are being pulled in, and then retarget the bullets and move them to my own bullet array. This way, I can keep track of my own bullets since they will be moving with the ship and they have their own attributes, such as not being able to hurt the enemies yet. So I also need to update myShipMove to make my pulled bullets move with me. And the last part of the puzzle is to move the pulled bullets after retargeting it.

enemyBulletTravel = ->
  i = _enemy_bullets.length - 1
  while i >= 0
    bullet = _enemy_bullets[i]
    if bullet.shape.y >= stage.canvas.height
      stage.removeChild bullet.shape
      _enemy_bullets.splice(i, 1)
    else
      if checkHit(bullet, _my_ship)
        stage.removeChild bullet.shape
        _enemy_bullets.splice(i, 1)
        console.log "MY SHIP GOT HIT"
      else if _flux_capture and checkHit(bullet, _my_shield)
        _enemy_bullets[i].shape.graphics.clear().beginFill("#49E20E").drawCircle 0, 0, _bullet_size
        _my_pulled_bullets.push retargetBullet(_enemy_bullets[i])
        _enemy_bullets.splice(i, 1)
      else
        bullet.shape.x += bullet.dir_x
        bullet.shape.y += bullet.dir_y
    i--

retargetBullet = (bullet) ->
  shape = bullet.shape
  target_x = _my_ship.x + _ship_width/2
  target_y = _my_ship.y + _ship_height/2

  dist = Math.sqrt( Math.pow(target_x - shape.x,2) + Math.pow(target_y - shape.y,2))
  bullet.dir_x = (target_x - shape.x)/dist/_bullet_pull_multiplier
  bullet.dir_y = (target_y - shape.y)/dist/_bullet_pull_multiplier
  bullet.charge = 0
  return bullet

myShipMove = ->
  if _my_ship_target_x and Math.abs(_my_ship_target_x - _my_ship.x) > _my_ship_speed
    speed = _my_ship_speed
    if _my_ship_target_x < _my_ship.x       speed *= -1     _my_ship.x += speed     _my_shield.x += speed     i = _my_pulled_bullets.length - 1     while i >= 0
      _my_pulled_bullets[i].shape.x += speed
      i--

myPulledBulletTravel = ->
  i = _my_pulled_bullets.length - 1
  while i >= 0
    bullet = _my_pulled_bullets[i]
    if checkHit(bullet, _my_ship)
      stage.removeChild bullet.shape
      _my_pulled_bullets.splice(i, 1)
      console.log "MY SHIP GOT HIT BY OWN BULLET"
    else
      bullet.shape.x += bullet.dir_x
      bullet.shape.y += bullet.dir_y
      bullet.charge++
    i--

Lots of things going on here but it’s just the same thing for bullets moving. Nothing really interesting to note. So now we go back to the pushBullets method I was referring to earlier. This method will grab all the bullets in the pulled and invert the bullets direction and increase the speed. Then I have another method to move it and check for collisions with the enemy to delete:

pushBullets = ->
  i = _my_pulled_bullets.length - 1
  while i >= 0
    bullet = _my_pulled_bullets[i]
    bullet.dir_x *= -bullet.charge/_bullet_push_multiplier
    bullet.dir_y *= -bullet.charge/_bullet_push_multiplier
    _my_pushed_bullets.push bullet
    i--
  _my_pulled_bullets = []

myPushedBulletTravel = ->
  i = _my_pushed_bullets.length - 1
  while i >= 0
    bullet = _my_pushed_bullets[i]
    if bullet.y < -50       stage.removeChild bullet.shape       _my_pushed_bullets.splice(i, 1)     e = _enemy_ships.length - 1     while e >= 0
      ship = _enemy_ships[e]
      if checkHit(bullet, ship.shape)
        stage.removeChild bullet.shape
        stage.removeChild ship.shape
        _my_pushed_bullets.splice(i, 1)
        _enemy_ships.splice(e, 1)
        bullet = null
        e = 0
      e--

    if bullet
      bullet.shape.x += bullet.dir_x
      bullet.shape.y += bullet.dir_y
    i--

And that’s the basic gameplay for Flux. The next step is the art because I want to make sure the dimensions work well and that the game looks polished. Then it’s off to level design, more enemies, powerups, menu screen, and just cleaning up the game.

Flux – Day 1 – Game Design, Setup and Making Bullets Fly

One of my new years resolution was to create a mobile game this year and I will be using this blog to document my journey. It will be a great learning experience for me and it will allow me to mess with all these new tools. So I have a game idea that has been constantly evolving. I think the process of game design is a very interesting topic and I would like to share the thoughts that lead up to my game design.

So mobile games usually have very simple buttons. Usually just a click on the screen. This is because there’s not much space on a tiny screen for anything else and multiple buttons would just be hard on the user to actually see the screen with their fingers blocking everything. So I thought back on old classic simple games which I played before. And I remembered Jezzball. Jezzball is an extremely old Windows game. The point of the game is to separate the balls by building walls without the balls touching the walls while it’s constructing. The game only required two buttons: one to switch directions and one to build the wall. So in order to limit that to one button click, I decide to make the walls circular. So by holding on to a certain area on the screen, a wall would build around the bouncing balls! One problem with that though is that it’s hard to see the ball you’re trying to trace with your fat fingers blocking it. Would be great for a PC with a mouse but a terrible experience for a mobile user. Also capturing balls all day doesn’t seem that captivating so let’s throw in something that would make it more interesting. Adding in higher stakes.

jezzballclassic

I decide to throw in a space ship and make this one of those 2D shooter airplane game. That way, you can protect your ship from all these bouncing balls. Though that would be hard to explain why there are bouncing balls in the first place. So it was time to add enemy ships who shoot circular bullets where I have to capture. But that idea of your finger covering up the ball still haunted me. I knew it was an interesting concept but the player would be frustrated by this problem. And that’s not something I want my players to feel when they play my game. But I LOVE the idea of not having your own bullets and using what’s around you to win.

The name Flux came from a Heroes of Newerth character. His ultimate power isn’t about doing damage, but about dragging or pushing away the enemies. That idea is so interesting because it leads to insane plays depending on the environment and the cards he is dealt. So with that in mind, I thought about creating a ship where you can either pull in or push out bullets. Combining that with only one button makes this challenging but we’ll just have to wait and see how enjoyable it will be.

Since I don’t have an any iOS, I decide to create it for the Android. But after reading up and setting up my environment, I remembered how painful and ugly Java was. And since I love Javascript, I decide to make an HTML5 game instead.

Setup was a pain. Got VMPlayer to boot up Ubuntu. Installed and setup all my necessary files and programs for this project (node, git, etc). I created a Github repository (https://github.com/jdnguyen/flux). And my IDE of choice has always been something from JetBrains because I’ve been using RubyMine before and it has been amazing. So I got their Javascript IDE, WebStorm. I also installed Coffeescript and hooked it up to WebStorm. Coffeescript just makes it so much easier to code and read. I setup a watcher on the project so it could translate any Coffeescript files immediately to Javascript after each save. I can explain this long tedious task in another post if anyone is interested.

Next up was to actually start an empty project. Quick Googling showed me HTML5 Boilerplate (http://html5boilerplate.com/). It basically starts an empty HTML5 project for you. Which is amazing because it takes into account all browsers, setup all your necessary files, and puts them in an extremely neat format. Made my life so much easier.

Next step, finding a good game engine. I found this great HTML5 Engine called EaselJS (http://createjs.com/#!/EaselJS) which seemed to have what I need: Basic shape drawing, mouse interaction, hit detection, and really easy to update and draw frames. I downloaded the minified Javascript file and loaded that into my project.

Time to start actual coding. First step is to draw my canvas where the game will take place. This should go inside your index.html file in the body block (I took out the brackets because the blog was being weird about showing html):

  canvas id="canvas" width="500" height="500"

You should probably add a black background-color to the stylesheet for this canvas to make the background black (since we’re in space). Everything now will be in Coffeescript because it’s beautiful. I put it all into main.cofeee which would translate to main.js. I initialized the canvas and ship:

stage = new createjs.Stage("canvas")
_my_ship = new createjs.Shape()
_ship_width = 80
_ship_height = 30

init = ->
  _my_ship.graphics.beginFill("blue").drawRect 0, 0, _ship_width, _ship_height
  _my_ship.x = 100
  _my_ship.y = stage.canvas.height - _ship_height
  stage.addChild _my_ship
  stage.update()

  stage.on "stagemousemove", (evt) ->
    _my_ship.x = evt.stageX - _ship_width/2
    stage.update()

init()

This creates a canvas, create my ship, puts it on the bottom of the screen, and it will move along the x-coordinate with my mouse. stage.update() draws the screen so I have to update the stage each time the mouse moves around the campus. Basic controls are done. Next step is drawing enemies and making them shoot bullets.

I created an array that would store all the enemies. Then I created a method to spawn enemies. These ships will spawn on the far top right of the screen and move left. The enemy would also hold a shootDelay which will be how many cycles before it shoots another bullet and shootCounter to keep track of the current cycle:

_enemy_ships = []
spawnEnemy = ->
  shape = new createjs.Shape()
  shape.graphics.beginFill("red").drawRect 0, 0, _ship_width, _ship_height
  shape.x = stage.canvas.width
  shape.y = 100
  _enemy_ship=
    shape: shape
    shootDelay: 40
    shootCounter: 0
  _enemy_ships.push _enemy_ship
  stage.addChild shape

Now that we have a method to spawn enemies, we need to call it. So I created two more global fields. One to keep track of how many cycles til it spawns another enemy and what cycle we’re currently on. Now we need this to run constantly regardless of human’s interaction. So there needs to be a game loop that keeps track of enemy’s movement and spawning. Good thing for EaselJS, this is relatively easy to set up. So I end up adding this this:

_enemy_spawn_counter = 0
_enemy_spawn_rate = 240

init = ->
  _enemy_ships = []
  _enemy_bullets = []

  _my_ship.graphics.beginFill("blue").drawRect 0, 0, _ship_width, _ship_height
  _my_ship.x = 100
  _my_ship.y = stage.canvas.height - _ship_height
  stage.addChild _my_ship

  stage.on "stagemousemove", (evt) ->
    _my_ship.x = evt.stageX - _ship_width/2

  createjs.Ticker.on "tick", update
  createjs.Ticker.setFPS 60
  spawnEnemy()

update = ->
  enemyShipSpawn()
  stage.update()

enemyShipSpawn = ->
  _enemy_spawn_counter++
  if _enemy_spawn_counter >= _enemy_spawn_rate
    _enemy_spawn_counter = 0
    spawnEnemy()

So the Ticker is set to 60 frames per second. That means it will run my update method 60 times a second. I set my spawn counter to 240. That means an enemy will spawn every 6 seconds. But now the enemies are not moving yet. They’re just spawning in the top right corner. We need to get them to move left until they’re off the screen and then unhook them because we don’t want to keep track of things off the screen. There’s no point to it and it will just slow down the game. Also I like to point out that I removed the stage.update() from the mouse movement. The game is already redrawing 60 times a second right now which is more than enough. Redrawing extra when the mouse is moving would only cause unnecessary code execution.

I created another method called enemyShipMovement and called it inside my update method:

enemyShipMovement = ->
  i = _enemy_ships.length - 1
  while i >= 0
    ship = _enemy_ships[i]
    ship.shape.x -= 2
    if ship.shape.x + _ship_width <= 0
      stage.removeChild ship.shape
      _enemy_ships.splice(i, 1)

It goes through my array of enemy ships and move it to the left by 2 pixels each cycle. Since the coordinates of the ship is in the top left corner, I have to make sure that the right corner of the ship is off the screen before deleting the ship. So I add the ship’s width to the x coordinate to make sure that it’s less than 0 before deleting. Deletion comes in two parts. I have to first remove the actual shape from the stage and then remove the ship’s hash info from my array. You might have noticed I traversed through the array from the back as opposed to starting i = 0. I did that at first but it caused me a bug. When I delete ships, it would throw off the i counter and skip through ships moving. Now we have ships moving to the left and deleting themselves once they his the left side of the screen.

Time to create the bullets. I created two different methods. One to spawn bullets and one to move the bullets. Spawning the bullets is a bit harder though. It’s spawning on the enemy with the intention of hitting the player. Once the bullet leaves the enemy, it will travel at a certain speed and direction. So I have to keep these info on the bullet. When something moves, it moves with a velocity in the x and y direction. The bullet should always carry the seem speed regardless of the angle it is shooting so we’re gonna need some basic math. The Pythagorean theorem to be more exact.

createBullet = (start_x, start_y) ->
  shape = new createjs.Shape()
  shape.graphics.beginFill("yellow").drawCircle 0, 0, _bullet_size
  shape.x = start_x + _ship_width/2
  shape.y = start_y + _ship_height
  target_x = _my_ship.x + _ship_width/2
  target_y = _my_ship.y + _ship_height/2

  dist = Math.sqrt( Math.pow(target_x - shape.x,2) + Math.pow(target_y - shape.y,2)) / _bullet_speed
  nx = (target_x - shape.x)/dist
  ny = (target_y - shape.y)/dist

  _bullet=
    shape: shape
    dir_x: nx
    dir_y: ny
  _enemy_bullets.push _bullet
  stage.addChild shape

As you can see, I pass the bullet the starting location of the ship. I spawn it in the center to the enemy ship with it’s target being the center of my ship. The _bullet_speed is a global variable with the speed you want. It just multiples with our directional velocity. With some basic math I’m sure you guys can figure out, I get the x and y velocity and store it into a hash with the shape itself. I then pump that into an array to loop through. I create the second method: the bullet flying.

enemyBulletTravel = ->
  i = _enemy_bullets.length - 1
  while i >= 0
    bullet = _enemy_bullets[i]
    bullet.shape.x += bullet.dir_x
    bullet.shape.y += bullet.dir_y
    if bullet.shape.y >= stage.canvas.height
      stage.removeChild bullet.shape
      _enemy_bullets.splice(i, 1)

It looks the same as the ship moving but it can move along the x and y coordinate and that the bullet gets deleted once it goes to the bottom of the screen. I add this method to my update method so it will move the bullets 60 times a second. Now how do I shoot these bullets? I need to tell the ship when to add create these bullets for it to fly. I add on to the enemy ship movement method:

enemyShipMovement = ->
  i = _enemy_ships.length - 1
  while i >= 0
    ship = _enemy_ships[i]
    ship.shape.x -= 2
    if ship.shape.x + _ship_width = ship.shootDelay
        ship.shootCounter = 0
        createBullet(ship.shape.x, ship.shape.y)
    i--

If my ship isn’t destroyed, they it will add to the shoot counter. Once the shoot counter fills up, it will create a bullet from it’s current location and then reset the counter again. If you run the program, you will see plenty of bullets flying at your space ship with enemies flying over. You can tweek with the global variables to get them to shoot more bullets or fly faster.

Last thing I want to do today is to get basic hit detection with the bullets hitting your ship. And with EaselJS, this is really easy. So I add on to the enemy bullet travel method:

enemyBulletTravel = ->
  i = _enemy_bullets.length - 1
  while i >= 0
    bullet = _enemy_bullets[i]
    bullet.shape.x += bullet.dir_x
    bullet.shape.y += bullet.dir_y
    if bullet.shape.y <= 0
      stage.removeChild bullet.shape
      _enemy_bullets.splice(i, 1)
    else
      pt = bullet.shape.localToLocal(0,0,_my_ship)
      if _my_ship.hitTest(pt.x, pt.y)
        stage.removeChild bullet.shape
        _enemy_bullets.splice(i, 1)
        console.log "MY SHIP GOT HIT"
    i--

If the bullet is not off the screen, it will check the bullets location with my current ships location. EaselJs has a method called hitTest which will return true of the shapes are colliding in any way. If it is true, I destroy the bullet and print out a message. And there we have it for day 1. Flying ships, bullets and hit detection.

Querying embedded documents in Mongoid

So today I wanted to do some quick queries in Mongoid but ran into trouble. Let’s say I had a page with many different items embedded in it.

class Page
  embeds_many :items
end 

class Item
  embedded_in :page
end

I just wanted to get all pages with at least one item. I know that I can query up all empty pages with this query:

Page.where(:items => {"$size" => 0})

So I thought it would be pretty simple to grab all pages where sizes is not zero. Here were some of the examples I tried:

Page.where(:items.ne => {"$size" => 0})
Page.where(:items.not => {"$size" => 0})
Page.where(:items => {"$size" => {"$ne" => 0}})

None of them gave me the results I wanted. I tested each query by printing out the size of every items queried:

Page.where(:items => {"$size" => 0}).collect{|a| a.items.count}

They all had zeros in the array that was returned by the collect. Clearly this was not working so I took a step back and read up on Mongo. I pulled up their operators list (http://docs.mongodb.org/manual/reference/operator/query/) and tried a few different combinations. First one was trying greater than. Maybe $not wasn’t just working for some odd reason.

Page.where(:items => {"$size" => {"$gt" => 0}})

Same result and I’m back to the drawing board. The next one that caught my eye was exists. So I tried it like this:

Page.exists(:items => true)

This returned me a much smaller array and as I inspected the results, I realized most of these pages have at least one item. Success! But not really. I realized it caught nil’s but not empty arrays. Empty arrays were not caught be the exist but that gave me hope. I could easily just query up where all items are nil’s or [].

Page.where(:items.nin => [[], nil])

Gave me all pages with at least one item. Perfect. But that got me thinking: there has to be a way to query by size. What if for some odd reason I wanted to query up all pages with at least two items? This method would not have worked. I decide to venture more into this problem for the sake of learning.

After more researching and asking around, I realized that you can use Javascript for Mongo querying. Mind. Blown. This changes everything. So I first tried this query using Javascript:

Page.where("this.items.length > 0")

This threw back this weird error: “10071 error on invocation of $where function:\nJS Error: TypeError: this.items has no properties nofile_a:0”. It seems like it was calling length of something that did not exist. I had to NULL check first. So then I tried:

Page.where("this.items && this.items.length > 0")

Perfect! Gave me exactly what I wanted. And with this query, I can look up pages with at least 5 items easily. I read up more on this and it turns out that this was only possible as of version 3.1.0 of Mongoid. So make sure your Mongoid version is up to date and begin messing around.

Having fun with dynamic fields in Sunspot

Sunspot is a Solr-powered search for Ruby. I have never used it before working at my current job but it makes searching and pagination so easy. Solr is this awesome search platform that will index models almost immediately after being updated. It has plenty of other cool features but I will be focusing on the indexing portion of it.

We have had a weird problem for the past few months but we ignored it for as long as we could because it was a tough problem to solve. So we have these sale pages:

class Sale
  #fields aren't important for this
end

These sale pages can hold from one to hundreds of items. These sales can range from season themes to fashion style of a certain starlet. So the important thing to take out of this is that an item can be part of many different sales or none at all. Also the ordering of the items on the sale page matters greatly to make them look appealing.

So we have our item model which can be part of many different sales and these items can have different positions on each of these sale. So we created a sale usage model which will be embedded in the item but contain all it’s position for every sale.

class Item
  embeds_many :sale_usages
end 

class SaleUsage
  field :position, :default => 0
  embedded_in :item
  belongs_to :sale
end

Now if I want to grab all items part of the sale, I can do a quick Mongoid query:

Item.where("sale_usages.sale_id" => @sale.id)

The problem with this is that I can’t sort it by the position on the sale usages. There’s no quick and easy way to do this with Mongo. This is because there can be many different sale usages in items and there’s no way to know which of these embedded objects is the one I want to sort by. So we avoided this problem for months by passing the items to backbone and sort it through backbone collection. Sounds like a great solution but there is one problem with this. If there are too many items on the sale, the page timeouts because there are simply too many items to load. This problem is usually very easily avoidable by adding pagination to the page. But wait! We can’t paginate because we’re doing all the sorting on the frontend. So there’s no way to tell what’s on the first page unless we send all the items to the user, which will not work if there’s 200+ items.

Our solution? Sunspot. Sunspot has an interesting concept of creating dynamic fields (https://github.com/sunspot/sunspot/wiki/Dynamic-fields) on the indexed item. So the idea was to create these special fields on the item which will be indexed for fast querying and ordering on Sunspot. We concluded that the best way was to use the sale id as the field name and the position as the value.

I proceeded to read every documentation and thread I can find on dynamic fields but there weren’t much to look at. So I copied and pasted whatever I found online to test. Trial and error is always the best way to learn.

So from what I gathered, this is how you create a dynamic integer field. So I added this to my item searchable block:

searchable do
 dynamic_integer :sale_usages do
   sale_usages.inject({}) do |hash, sale_usage|
     hash.merge(sale_usage.sale_id => sale_usage.position)
   end
 end
end

That will create a field that will look like this:

{"123456789" => "0", "987654321" => "23"}

Looks exactly like what I wanted so I re-indexed all my items and added the search query to my item controller where the pagination take place:

Item.search do
  dynamic :sale_usages do
    with (sale.id.to_s).greater_than(-1)
    order_by(sale.id.to_s), :asc)
  end
  paginate :page => page, :per_page => 50
end

Since the item position is default at 0 and all the positions have to be greater than that, I queried for the sale.id as the field name with a value greater than -1. That did fetch all the items in the sale so I knew the indexing of the dynamic field worked. But the ordering did not work. It threw back a “wrong constant name 123456789Sort” error. It seemed like it was appending the word “Sort” to the end of sale id and it could not find that constant name. Quick Googling came up with nothing so it was back to trial and error.

Tried all sort of things but they all failed. One attempt was to index the dynamic field as sale.id + “Sort” to get around this error but that didn’t help. Another attempt was to convert the dynamic field name from a string to a symbol but that also didn’t work. But as we looked closer at that last attempt, we realized it might be the quotations in the symbol. It looked like this by the way after calling to_sym on the sale id:

{:"123456789" => "0", :"987654321" => "23"}

The colon represents that it is indeed a symbol but it treated the quotations as part of the field name too. I opened up my console and fired up a bunch of different cases to see what made the quotations show up. And here were my results:

"123".to_sym                                 # :"123"
"sale-123".to_sym                            # :"sale-123"
"sale_123".to_sym                            # :sale_123
"sale123".to_sym                             # :sale123

So I found out that starting with a number or using certain characters will cause the quotations to show up in the field name. I adjusted my code to look like:

searchable do
 dynamic_integer :sale_usages do
   sale_usages.inject({}) do |hash, sale_usage|
     hash.merge(("sale_" + sale_usage.sale_id.to_s).to_sym => sale_usage.position)
   end
 end
end
Item.search do
  dynamic :sale_usages do
    with ("sale_" + sale.id.to_s).to_sym ).greater_than(-1)
    order_by(("sale_" + sale.id.to_s).to_sym, :asc)
  end
  paginate :page => page, :per_page => 50
end

And yay! Ordering and querying up possible on the backend due to Sunspot. Learned plenty of interesting things about Ruby and got to try out another feature of Sunspot.