Search code examples
ruby-on-railsrubyjsonsunspot

Sunspot - How to include user data in JSON output?


In my Rails app, I'm using Sunspot, so that users can search "pins" (like a post). All works fine and I'm returning the data as JSON.

However, I'd also like to include in the JSON response, the "user" data associated with each "pin".

I'm not needing "user" to be included in the search, but only as data with each "pin" after the search has been done.

My search works fine, but I can't work out how to add the user data.

module Api

    module V1

        class PinsController < ApplicationController 

            respond_to :json

            def index

                _search = Sunspot.search(Pin) do

                    fulltext params[:q] do

                        highlight :title
                        phrase_fields :title => 2.0
                        phrase_slop 1
                        minimum_match 1

                    end

                    facet(:position_id, :type_id, :instrument_id, :genre_ids, :skill_id)

                    with(:position_id, params[:position]) if params[:position].present?
                    with(:type_id, params[:type]) if params[:type].present?
                    with(:instrument_id, params[:instrument]) if params[:instrument].present?
                    with(:genre_ids).all_of(params[:genre]) if params[:genre].present?
                    with(:skill_id, params[:skill]) if params[:skill].present?

                    paginate(:page => params[:page], :per_page => 25)

                end

                _pins = _search.results

                _pins_positions = []
                _pins_types = []
                _pins_instruments = []
                _pins_genres = []
                _pins_skills = []

                _search.facet(:position_id).rows.each do |row|
                    _facet = Position.find_by_id(row.value)
                    _object = _facet.attributes
                    _object["count"] = row.count
                    _pins_positions << _object
                end

                _search.facet(:type_id).rows.each do |row|
                    _facet = Type.find_by_id(row.value)
                    _object = _facet.attributes
                    _object["count"] = row.count
                    _pins_types << _object
                end

                _search.facet(:instrument_id).rows.each do |row|
                    _facet = Instrument.find_by_id(row.value)
                    _object = _facet.attributes
                    _object["count"] = row.count
                    _pins_instruments << _object
                end

                _search.facet(:genre_ids).rows.each do |row|
                    _facet = Genre.find_by_id(row.value)
                    _object = _facet.attributes
                    _object["count"] = row.count
                    _pins_genres << _object
                end

                _search.facet(:skill_id).rows.each do |row|
                    _facet = Skill.find_by_id(row.value)
                    _object = _facet.attributes
                    _object["count"] = row.count
                    _pins_skills << _object
                end

                _pins_positions = _pins_positions.sort_by { |e| e["title"] }
                _pins_types = _pins_types.sort_by { |e| e["title"] }
                _pins_instruments = _pins_instruments.sort_by { |e| e["title"] }
                _pins_genres = _pins_genres.sort_by { |e| e["title"] }
                _pins_skills = _pins_skills.sort_by { |e| e["title"] }

                _response = {
                    :pins => _pins, 
                    :facets => {
                        :positions => _pins_positions,
                        :types => _pins_types,
                        :instruments => _pins_instruments,
                        :genres => _pins_genres,
                        :skills => _pins_skills
                    }
                }

                respond_with _response

            end

        end

    end

end

A typical response looks like:

{"pins":[{
"id":95,
"user_id":22,
"title":"Ratione sint odio vitae in quis aspernatur.",
"description":"Sunt ut pariatur aut ipsum sit unde alias.",
"latitude":null,
"longitude":null,
"position_id":1,
"type_id":1,
"instrument_id":4,
"skill_id":1,
"soundcloud_url":null,
"created_at":"2012-09-04T06:54:17.000Z",
"updated_at":"2013-10-14T21:58:17.000Z",
"deleted_at":null
}],
"facets":{
"positions":[{
"id":1,
"title":"Non-Professional",
"token":"non-professional","count":2
}],
"types":[{
"id":1,
"title":"Wanted",
"token":"wanted",
"count":2
}],
"instruments":[{
"id":4,
"title":"Synth",
"token":"synth",
"count":2
}],
"genres":[{
"id":12,
"title":"Folk",
"token":"folk",
"count":2
},
{
"id":16,"title":"Hip Hop","token":"hiphop","count":2}
],
"skills":[{
"id":3,
"title":"Advanced",
"token":"advanced",
"count":1},
{
"id":1,
"title":"Novice",
"token":"novice",
"count":1
}]
}}

But, ideally, I'd like to include the associated "user" data for each "pin" in the JSON. As you can see, all I have here is "user_id"...but I want this JSON API to be rather 'flat' and include another hash key called "user" that has things like, "first_name", "age", "gender" etc.

If I was doing a traditional View template, I could just expose "pin" as an instance variable and do something like: @pin.user.profile.first_name - how can I do this when building my data within Sunspot?

Hope I've made this clear. Many thanks!


Solution

  • I figured out a solution by creating an empty array, looping the results from Sunspot, finding the respective user and pushing to the array.

    _results = _search.results
    _pins = []
    
    _results.each do |pin|
        _object = pin.attributes
        _object["user"] = { :profile => pin.user.profile }
        _pins << _object
    end
    

    I now get a structure, where the profile data is nested in a user key within the pin hash, like so:

    enter image description here