diff --git a/app/controllers/v1/search_controller.rb b/app/controllers/v1/search_controller.rb
new file mode 100644
index 0000000000000000000000000000000000000000..7ed1ead41c4a1bc42bbfbb30dcbe2f3b585ffa78
--- /dev/null
+++ b/app/controllers/v1/search_controller.rb
@@ -0,0 +1,21 @@
+class V1::SearchController < ApplicationController
+
+  # GET v1/searches
+  # GET v1/searches.json
+  def index
+    render json: search_service.search(params)
+  end
+
+  # GET v1/searches/autocomplete
+  # GET v1/searches/autocomplete.json
+  def autocomplete
+    render json: search_service.autocomplete(params)
+  end
+
+  private
+
+  def search_service
+    @search_service ||= SearchService.new
+  end
+
+end
\ No newline at end of file
diff --git a/app/services/search_service.rb b/app/services/search_service.rb
new file mode 100644
index 0000000000000000000000000000000000000000..8ee8fdd9ecf52e7bab929ef049eee41aa52bd51e
--- /dev/null
+++ b/app/services/search_service.rb
@@ -0,0 +1,119 @@
+class SearchService
+
+  def search(params)
+    params[:query] = "*" if params[:query].blank?
+
+    case params[:search_class]
+    when "LearningObject"
+      result = LearningObject.search params[:query], where: where_hash(params), order: lo_order_hash(params[:order]), page: params[:page] || 1, per_page: 10
+    when "Collection"
+      result = Collection.search params[:query], where: {privacy: "public"}, order: col_order_hash(params[:order]), page: params[:page] || 1, per_page: 10
+    when "User"
+      result = User.search params[:query], order: user_order_hash(params[:order]), page: params[:page] || 1, per_page: 10
+    else
+      raise "Wrong search class parameter"
+    end
+    result
+  end
+
+  def autocomplete(params)
+    params_hash = {}
+    get_thumbnail = nil
+    case params[:search_class]
+    when "LearningObject"
+      params_hash = { fields: ['name^10', 'description', 'author'] }
+      get_thumbnail = Proc.new { |obj| image_tag(learning_object_thumbnail(obj)) }
+    when "Collection"
+      params_hash = { where: {privacy: "public"},
+        fields: ['name^10', 'description', 'owner'] }
+      get_thumbnail = Proc.new { |obj| image_tag("/assets/icons/collection") }
+    when "User"
+      params_hash = { fields: ['name'] }
+      get_thumbnail = Proc.new { |obj| image_tag(obj.avatar.url(:thumb), 32) }
+    else
+      raise "Wrong search class parameter"
+    end
+    autocomplete_search(Object.const_get(params[:search_class]), params[:query], params_hash, get_thumbnail)
+  end
+
+  private
+
+  def autocomplete_search(search_class, query, params_hash={}, get_thumbnail)
+    response = []
+    search_params = { limit: 10, misspellings: { below: 5 }, where: {state: validate_object} }
+    objs = search_class.search(query, search_params.merge(params_hash))
+    objs.each do |obj|
+      hash = {}
+      hash["name"] = obj.name
+      hash["thumbnail"] = get_thumbnail.call(obj)
+      hash["url"] = url_for([obj, :only_path => false])
+      response <<  hash
+    end
+    response
+  end
+
+  def image_tag(image, width=56, height=32)
+    ActionController::Base.helpers.image_tag image, width: width, height: height, class: "autocomplete"
+  end
+
+  def where_hash(params)
+    hash = {}
+
+    topics = params[:topic] unless params[:topic].blank?
+    topics += ", " unless topics.nil?
+    topics = topics.to_s + params[:school_level] unless params[:school_level].blank?
+
+    # build hash
+    hash[:topics_name] = topics.split(', ') unless topics.nil?
+    hash[:object_type] = params[:type].split(', ') unless params[:type].blank?
+    hash[:publisher] = params[:source].split("-s- ") unless params[:source].blank?
+    hash[:state] = validate_object
+    # year = params[:year].blank? ? nil : params[:year].split('-').take(2)
+
+    hash.blank? ? nil : hash
+  end
+
+  def lo_order_hash(order)
+    case order
+    when 'author'
+      { author: {order: :asc, unmapped_type: :string} }
+    when 'publicationasc'
+      { published_at: {order: :asc, unmapped_type: :timestamp} }
+    when 'publicationdesc'
+      { published_at: {order: :desc, unmapped_type: :timestamp} }
+    when 'title'
+      { name: {order: :asc, unmapped_type: :string} }
+    else
+      { score: {order: :desc, unmapped_type: :integer} }
+    end
+  end
+
+  def col_order_hash(order)
+    case order
+    when 'author'
+      { owner: {order: :asc, unmapped_type: :string} }
+    when 'publicationasc'
+      { created_at: {order: :asc, unmapped_type: :timestamp} }
+    when 'publicationdesc'
+      { created_at: {order: :desc, unmapped_type: :timestamp} }
+    when 'title'
+      { name: {order: :asc, unmapped_type: :string} }
+    else
+      { score: {order: :desc, unmapped_type: :integer} }
+    end
+  end
+
+  def user_order_hash(order)
+    if order == 'title'
+      { name: {order: :asc, unmapped_type: :string} }
+    else
+      { score: {order: :desc, unmapped_type: :integer} }
+    end
+  end
+
+  def validate_object
+    return 'published' if current_user.nil? || !current_user.is_admin?
+    return ['published', 'suspended', 'draft']
+  end
+
+end
\ No newline at end of file