Integrate Rails, React and MeiliSearch using Docker

Introduction

In this post, you’ll learn how to integrate MeiliSearch with your Rails application database and how to create a front-end search bar with a search-as-you-type experience using React.

This is a very basic application, my focus …


This content originally appeared on DEV Community and was authored by Rodrigo Odhin

Introduction

In this post, you'll learn how to integrate MeiliSearch with your Rails application database and how to create a front-end search bar with a search-as-you-type experience using React.

This is a very basic application, my focus will be the search, therefore I don't go into much details about Rails or React.

1- Create Docker Containers

To create our application, I will use a docker image (rodrigoodhin/rails:6:0:0) with Rails and Node.js. In addition, I will use the image (getmeili/meilisearch:v0.25.0) to create a MeiliSearch container.

Let's create the docker-compose.yml file and add the following code to create the containers:

version: '3'

services:
  odhin_rails:
    image: rodrigoodhin/rails:6.0.0
    container_name: odhin_rails_app
    restart: always
    volumes:
      - .:/projects
    ports:
      - "14880:22"
      - "14881:3000"
    depends_on:
      - odhin_meilisearch
    extra_hosts:
      - 'host.docker.internal:host-gateway'
    networks:
      odhin_network:
  odhin_meilisearch:
    image: "getmeili/meilisearch:v0.25.0"
    container_name: odhin_meilisearch
    restart: always
    command: ./meilisearch --master-key=A53451A4E5A5C21D265944AB8654984016199CCA362F2CA25B3CCD4DF821993B
    ports:
      - "14883:7700"
    volumes:
      - "/tmp/data.ms:/data.ms"
    networks:
      odhin_network:
networks:
  odhin_network: 

You can generate and set a different master-key for your MeiliSearch or leave it empty. For security reasons, do not leave the master-key empty in production.

Run the command below to create and start the containers:

docker-compose up -d

Now we have an environment ready to start creating your Rails application.

2- Create and set up your Rails app

Now that we've got everything up and running, let's create our RoR app.

Before start, we need to access the Rails container with this command:

ssh app@localhost -p 14880

Type app for the password.

Go to the projects folders with the command:

cd /projects

I have decided to create a very simple collection app named games_collection. Run the following command on the terminal:

rails new games_collection

Go to the games collection project folder:

cd games_collection

Let's generate our model Game, it will have 4 attributes

  • cover
  • title
  • genre
  • platform
rails g model Game cover:string title:string genre:string platform:string

Let's create the database and run the migration with the following commands:

rails db:create
rails db:migrate

Next, we need to generate the controller with its index action:

rails g controller Games index

We are going to use the index view to show our games and search through them with our search bar.

We won't generate the rest of the CRUD actions, that's not the purpose of this post.

Once the controller has been created, we have to modify config/routes.rb file to looks like this:

Rails.application.routes.draw do
  root "games#index"
end

Add the following code to the config/puma.rb file, above the port information:

set_default_host '0.0.0.0'

This will specifies the ip that Puma will listen on to receive requests

Our config/puma.rb file will look like this:

# Puma can serve each request in a thread from an internal thread pool.
# The `threads` method setting takes two numbers: a minimum and maximum.
# Any libraries that use thread pools should be configured to match
# the maximum value specified for Puma. Default is set to 5 threads for minimum
# and maximum; this matches the default thread size of Active Record.
#
max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }
min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count }
threads min_threads_count, max_threads_count

# Specifies the `ip` that Puma will listen on to receive requests.
#
set_default_host '0.0.0.0'

# Specifies the `port` that Puma will listen on to receive requests; default is 3000.
#
port        ENV.fetch("PORT") { 3000 }

# Specifies the `environment` that Puma will run in.
#
environment ENV.fetch("RAILS_ENV") { "development" }

# Specifies the `pidfile` that Puma will use.
pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" }

# Specifies the number of `workers` to boot in clustered mode.
# Workers are forked web server processes. If using threads and workers together
# the concurrency of the application would be max `threads` * `workers`.
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
# workers ENV.fetch("WEB_CONCURRENCY") { 2 }

# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
# before forking the application. This takes advantage of Copy On Write
# process behavior so workers use less memory.
#
# preload_app!

# Allow puma to be restarted by `rails restart` command.
plugin :tmp_restart

3- Integrate MeiliSearch to our Rails app

Now that we have the back-end basics of our application, let's connect it to our running MeiliSearch instance using the meilisearch-rails gem.

Add the following line to your Gemfile:

gem 'meilisearch-rails', '~> 0.5.0'

Create a file named meilisearch.rb inside the config/initializers/ folder to setup your MEILISEARCH_HOST and MEILISEARCH_API_KEY:

To configure the MeiliSearch host, run the following command to get the MeiliSearch container ip:

docker inspect -f \
'{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' \
$(docker container ls -aq --filter "name=odhin_meilisearch" | awk '{print $1;}')

Create the config/initializers/meilisearch.rb file with the following code:

MeiliSearch::Rails.configuration = {
    meilisearch_host: 'http://192.168.80.2:7700',
    meilisearch_api_key: 'A53451A4E5A5C21D265944AB8654984016199CCA362F2CA25B3CCD4DF821993B',
} 

The host must be configured with your MeiliSearch container IP. The API Key must be the same as in docker-compose.yml

Let's open the app/models/game.rb file and add the following line inside the Class declaration:

include MeiliSearch

We also need to add a MeiliSearch block:

class Game < ApplicationRecord
    include MeiliSearch::Rails

    meilisearch force_utf8_encoding: true, primary_key: :id do
        # all attributes will be sent to MeiliSearch if block is left empty
        displayed_attributes ['id', 'cover', 'title', 'genre', 'platform']
        searchable_attributes ['cover', 'title', 'genre', 'platform']
        filterable_attributes ['genre', 'platform']
    end
end

Click here to learn more about displayed and searchable attributes.

4- Seeding the database

To test our application, we need some data in our database. The quickest way is to populate the database using dummy data. For this purpose, we are going to use a gem called faker, very helpful to have real-looking test data.

Add the following line to your Gemfile inside the development group, save and run bundle install:

gem 'faker', :git => 'https://github.com/faker-ruby/faker.git', :branch => 'master'

Add the following code to db/seeds.rb file to populate our database with 1000 games:

# Loads the faker library
require 'faker'

# Deletes existing games, useful if you seed several times
Game.destroy_all

# Creates 1000 fake games
1000.times do
    Game.create!(
        cover: "assets/games/#{rand(1..26)}.jpeg",
        title: Faker::Game.title,
        genre: Faker::Game.genre,
        platform: Faker::Game.platform
    )
end 

# Displays the following message in the console once the seeding is done
puts 'Games created'

To populate the database, we must run the command:

rails db:seed

5- Add React to the Rails app

Start by adding the React gem to your Gemfile:

gem 'react-rails'

Now run the installers:

bundle install
rails webpacker:install
rails webpacker:install:react
rails generate react:install

If you have any errors during the installation, check the react-rails gem common errors and their fixes here

6- Integrate a front-end search bar with a search-as-you-type experience

To integrate a front-end search bar, you need to install two packages:

  • the open-source React InstantSearch library powered by Algolia that provides all the front-end tools you need to highly customize your search bar environment.
  • the MeiliSearch client instant-meilisearch to establish the communication between your MeiliSearch instance and the React InstantSearch library.
npm install react-instantsearch-dom @meilisearch/instant-meilisearch

Let's create our first component Games, which will be added to app/javascript/components/ by default. Run the following command:

rails g react:component Games

We can now open your app/javascript/components/Games.js file and replace with the following code. We only need to modify the searchClientwith our meilisearch host and meilisearch api key, as well as the indexName. It should look like this:

import React from "react";
import {
  InstantSearch,
  Highlight,
  SearchBox,
  Hits,
  RefinementList,
  Pagination,
  ClearRefinements,
} from "react-instantsearch-dom";
import { instantMeiliSearch } from "@meilisearch/instant-meilisearch";

const searchClient = instantMeiliSearch(
  "http://localhost:14883", // Your MeiliSearch host
  "A53451A4E5A5C21D265944AB8654984016199CCA362F2CA25B3CCD4DF821993B" // Your MeiliSearch API key, if you have set one
);

const Games = () => (
  <InstantSearch
    indexName="Game" // Change your index name here
    searchClient={searchClient}
  >
    <div className="left-panel">
      <ClearRefinements />
      <h2>Genre</h2>
      <RefinementList attribute="genre" />
      <h2>Platform</h2>
      <RefinementList attribute="platform" />
    </div>
    <div className="right-panel">
      <SearchBox />
      <Hits hitComponent={Hit} />

      <div className="pagination">
        <Pagination />
      </div>
    </div>
  </InstantSearch>
);

//const Hit = ({ hit }) => <Highlight attribute="title" hit={hit} />

const Hit = (hit) => {
  const { cover, genre, platform } = hit.hit;
  return (
    <div className="hit media">
      <div className="media-left">
        <img className="media-object" src={`http://localhost:14881/${cover}`} />
      </div>
      <div className="media-body">
        <h4 className="media-heading">
          <Highlight attribute="title" hit={hit.hit} />
        </h4>
        <p className="genre">{genre}</p>
        <p className="platform">{platform}</p>
      </div>
    </div>
  );
};

export default Games;

Now, go to your views folder and replace the content of the app/views/games/index.html.erb with the code below:

<%= react_component("Games") %>

InstantSearch provides a CSS theme you can add by inserting the following link into the <head> element of your app/views/layouts/application.html.erb:

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/instantsearch.css@7.4.5/themes/satellite-min.css" integrity="sha256-TehzF/2QvNKhGQrrNpoOb2Ck4iGZ1J/DI4pkd2oUsBc=" crossorigin="anonymous">

You can also customize the widgets or create your own if you want to, check the React InstantSearch documentation.

For this to work we need to add some simple styling in the app/assets/stylesheets/games.scss file:

body {
  font-family: sans-serif;
  padding: 1em;
}

.ais-SearchBox {
  margin: 1em 0;
}

.right-panel {
  margin-left: 210px;
}

.left-panel {
  float: left;
  width: 200px;
}

.hit.media {
  width: 100%;
}

.hit .media-object {
  height: 200px;
  width: auto;
  float: right;
}

.hit .media-heading {
  color: #167ac6;
  font-weight: normal;
  font-size: 18px;
}

.hit .media-left {
  width: calc(20% - 20px);
  max-width: 220px;
  padding: 10px;
  float: left;
}

.hit .media-body {
  width: calc(80% - 20px);
  padding: 10px;
  float: left;
  display: block;
}

Update Yarn packages with the command:

yarn install --check-files

I have compressed games cover images to use in this post. Download this file and unzip all images at folder app/assets/images/games/.

Now starting the application with the following command:

rails server

Check that everything is working properly at http://localhost:14881

example

Yeah... Looks Fine!!

I hope you enjoy! 😉

Get the full code at Rails + React + MeiliSearch gitlab repository.

References:


This content originally appeared on DEV Community and was authored by Rodrigo Odhin


Print Share Comment Cite Upload Translate Updates
APA

Rodrigo Odhin | Sciencx (2022-04-07T06:25:52+00:00) Integrate Rails, React and MeiliSearch using Docker. Retrieved from https://www.scien.cx/2022/04/07/integrate-rails-react-and-meilisearch-using-docker/

MLA
" » Integrate Rails, React and MeiliSearch using Docker." Rodrigo Odhin | Sciencx - Thursday April 7, 2022, https://www.scien.cx/2022/04/07/integrate-rails-react-and-meilisearch-using-docker/
HARVARD
Rodrigo Odhin | Sciencx Thursday April 7, 2022 » Integrate Rails, React and MeiliSearch using Docker., viewed ,<https://www.scien.cx/2022/04/07/integrate-rails-react-and-meilisearch-using-docker/>
VANCOUVER
Rodrigo Odhin | Sciencx - » Integrate Rails, React and MeiliSearch using Docker. [Internet]. [Accessed ]. Available from: https://www.scien.cx/2022/04/07/integrate-rails-react-and-meilisearch-using-docker/
CHICAGO
" » Integrate Rails, React and MeiliSearch using Docker." Rodrigo Odhin | Sciencx - Accessed . https://www.scien.cx/2022/04/07/integrate-rails-react-and-meilisearch-using-docker/
IEEE
" » Integrate Rails, React and MeiliSearch using Docker." Rodrigo Odhin | Sciencx [Online]. Available: https://www.scien.cx/2022/04/07/integrate-rails-react-and-meilisearch-using-docker/. [Accessed: ]
rf:citation
» Integrate Rails, React and MeiliSearch using Docker | Rodrigo Odhin | Sciencx | https://www.scien.cx/2022/04/07/integrate-rails-react-and-meilisearch-using-docker/ |

Please log in to upload a file.




There are no updates yet.
Click the Upload button above to add an update.

You must be logged in to translate posts. Please log in or register.