Search App with React & Elasticsearch


Aim

The aim of this documentation is to provide working knowledge of the following technologies.

  • Neo4j
  • Elasticsearch
  • Kibana
  • React

What you will need?

  • Elasticsearch Docker container
  • Neo4j Docker container
  • Internet Connection for React.js CDN

Outline

This documentation divided into two parts:

  • Firstly - Synchronization of Neo4j data with Elasticsearch
  • Lastly - Searching data from Elasticsearch using React

Synchronization of Neo4j data with Elasticsearch

Step 01 - Initializing Neo4j Container

You can initialize a docker container through docker-compose file with the following snippet.

docker-compose.yml

Note

you can find docker-compose.yml file in the attached helping-scripts folder.

Step 02 - Download Plugins

Download the jar from the latest release.

and put the jar file to $NEO4J_HOME/plugins folder.

Step 03 - Modify neo4j.conf

Modify $NEO4J_HOME/conf/neo4j.conf accordingly (see the snippet below)

1
2
elasticsearch.host_name=http://elasticsearch:9200
elasticsearch.index_spec=neo-person:Person(name,born), neo-movie:Movie(title,tagline,released)

With that in place, the above snippet track changes of Person and Movie node from the neo4j database to the Elasticsaerch instance running on elasticsearch:9200.

To perform an initial import, you can run the movie example from neo4j

Step 04 - Running an Elasticsearch container

You can initialize a docker container through docker-compose file with the following snippet.

docker-compose.yml

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
version: '2'
services:
  elasticsearch:
    image: docker.elastic.co/elasticsearch/elasticsearch:6.0.0
    restart: always
    container_name: elasticsearch
    hostname: elasticsearch
    networks:
      kong-net:
          aliases:
          - elasticsearch
    environment:
      - bootstrap.memory_lock=true
      - ES_JAVA_OPTS=-Xms512m -Xmx512m
    ports:
      - "9200:9200"
    ulimits:
      memlock:
        soft: -1
        hard: -1
    mem_limit: 1g
    volumes:
      - esdata1:/usr/share/elasticsearch/data
      - eslog:/usr/share/elasticsearch/logs
      - ./config/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
  kibana:
    image: docker.elastic.co/kibana/kibana:6.0.0
    container_name: kibana
    ports:
      - "5601:5601"
    environment:
      ELASTICSEARCH_URL: http://elasticsearch:9200
    depends_on:
    - elasticsearch
    networks:
      kong-net:
          aliases:
          - kibana

volumes:
  esdata1:
    driver: local
  eslog:
    driver: local

networks:
  kong-net:
    external: true

Note

you can find docker-compose.yml file in the attached helping-scripts folder.

Step 05 - Append the following snippet to elasticsaerch.yml file

Note

you can find elasticsearch.yml file in the attached helping-scripts.

Searching data from Elasticsearch using React

This react app provide you an interface to search the data from the elasticsearch.

index.html

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
<!DOCTYPE html>
<html>

<head>
    <title>React Project</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css"
        integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">

    <script src="https://unpkg.com/react@16.7.0/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16.7.0/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/@babel/standalone/babel.min.js"></script>
    <style>
        .heading {
            color: purple;
        }
    </style>

</head>

<body>
    <div id="root"></div>
    <script type="text/babel">


        const Movies = ({ movies }) => {
            console.log("RESULT : " , movies)
            return (
                <div>
                    <div className="card">
                        <div className="card-body">
                            <h1 className="card-title">Title: {movies.title}</h1>
                            <p className="card-text">id: {movies.id}</p>
                            <p className="card-subtitle mb-2 text-muted">released: {movies.released}</p>
                            <p className="card-text">tagline: {movies.tagline}</p>
                        </div>
                    </div>
                </div>
            )
        };

        const MoviesList = ({ movies }) => {
            return (
                <div>
                    {console.log("Going to iterate list from MoviesList")}
                    {movies.map((item, i) => (
                        <Movies movies={item} />
                    ))}
                </div>
            )
        };


        class InputApp extends React.Component {


            constructor(props) {
                super(props);

                this.state = {
                    moviesCollectin: false,
                    username: '',
                    movieName: '',
                    movies: [],
                    moviesData: [],
                }

                this.updateInput = this.updateInput.bind(this);
                this.handleSubmit = this.handleSubmit.bind(this);
                this.updateInputName = this.updateInputName.bind(this);
                this.searchViaName = this.searchViaName.bind(this);

            }


            updateInput(event) {
                this.setState({ username: event.target.value })
            }

            updateInputName(event) {
                this.setState({ movieName: event.target.value })
            }


            handleSubmit() {

                this.setState({ moviesCollectin: false })
                console.log('Your input value is: ' + '"' + this.state.username + '"');
                fetch('http://localhost:9200/neo-movie/Movie/' + this.state.username)
                    .then(res => res.json())
                    .then((data) => {
                        this.setState({ movies: data._source })
                        console.log(data._source)
                    })
                    .catch(console.log)
                //Send state to the server code
            }

            searchViaName() {


                console.log('Your input value is: ' + this.state.movieName);
                fetch('http://localhost:9200/*/_search?q=title:' + this.state.movieName)
                    .then(res => res.json())
                    .then((data) => {

                        console.log(data.hits.hits)
                        let allmovies = data.hits.hits;
                        let tempMovies = [];
                        allmovies.forEach(elem => {
                            tempMovies.push({

                                id: elem['_source'].id,
                                labels: elem['_source'].labels,
                                tagline: elem['_source'].tagline,
                                title: elem['_source'].title,
                                released: elem['_source'].released

                            });
                        });
                        // console.log("before tempMovies " + typeof(tempMovies))
                        this.setState({ moviesData: tempMovies })
                        console.log("Going to iterate list")
                        this.state.moviesData.map(function (item, i) {
                            console.log(item)
                        })
                        // console.log("moviesdata"+ this.state.moviesData + "typeof" + typeof(this.state.moviesData)) 
                        this.setState({ moviesCollectin: true })
                    })
                    .catch(console.log)
                //Send state to the server code


            }



            render() {
                return (

                    <div>

                        <div className="jumbotron"><center><h1>Elasticsearch-React Search Demo Search App</h1></center></div>
                        <div className="container">
                            <div className='input-group'>
                                <input type="text" className="form-control" onChange={this.updateInput} placeholder="Search by ID"></input>
                                <input type="submit" onClick={this.handleSubmit} ></input>

                                <input type="text" className="form-control" onChange={this.updateInputName} placeholder="Search by Title"></input>
                                <input type="submit" onClick={this.searchViaName} ></input>
                            </div>

                            {console.log("Going to iterate list from render()")}
                            {this.state.moviesData.map(function (item, i) {
                                console.log(item)
                            })}


                            {this.state.moviesCollectin
                                ? <MoviesList movies={this.state.moviesData} />
                                : <Movies movies={this.state.movies} />}

                        </div>
                    </div>


                );
            }
        }


        ReactDOM.render(
            <InputApp />,
            document.getElementById("root")
        );
    </script>

</body>

</html>

Note

you can find index.html file in the attached helping-scripts folder.

Conclusion

This documentation presents a working example of making a simple search app for elasticsearch data.

Hope it helps, please let me know if you have any questions or feedback. I might miss something, I need to hear from you. JazakAllahu khaira :)