We build a frontend in Vue.js and then send a network request to the node.js server, and the node will query the data to the elastic server. Finally, it will get a response and send the JSON response back to the Vue.js client.
First, we create a Vue.js frontend, then create a Node.js server and index all the data to the elastic server. Then send a request to the node server and fetch the data. So it is pretty simple stuff to understand. You can find complete documentation of elasticsearch here.
Vue Elasticsearch With Node.js
Step 1: Install Vue.js
We install Vue.js using Vue CLI. So if you have not installed Vue CLI, you can install it using the following command.
npm install -g @vue/cli # OR yarn global add @vue/cli
Now create a new Vue.js project using the following command.
vue create vue-elastic
Go into the project folder.
cd vue-elastic
Start the dev server using the following command.
npm run serve
Step 2: Create Bootstrap Form For Search.
Now, install Bootstrap 4 using the following command.
npm install bootstrap --save # or yarn add bootstrap
Import the bootstrap 4 file inside src >> main.js file.
// main.js import Vue from 'vue' import App from './App.vue' import '../node_modules/bootstrap/dist/css/bootstrap.min.css'; Vue.config.productionTip = false new Vue({ render: h => h(App) }).$mount('#app')
Okay, replace the existing code with the following code inside App.vue file.
<!-- App.vue --> <template> <div id="app" class="container"> <div class="input-group input-group-lg bottom"> <div class="input-group-prepend"> <span class="input-group-text">Search</span> </div> <input type="text" class="form-control col-md-6"> </div> </div> </template> <script> export default { } </script> <style> .bottom { margin-top: 50px; margin-left: 200px; } </style>
The next step is to add the @keydown event to the search area. We need to do this because when the user starts typing in the search box, we send a network request to the server with the search query and then get a result and display the result. So write the following code inside App.vue file.
// App.vue <template> <div id="app" class="container"> <div class="input-group input-group-lg bottom"> <div class="input-group-prepend"> <span class="input-group-text">Search</span> </div> <input type="text" class="form-control col-md-6" @keyup.prevent="search" v-model="query" /> </div> </div> </template> <script> export default { data() { return { query: '' } }, methods: { search() { console.log(this.query); return this.query; } } } </script> <style> .bottom { margin-top: 50px; margin-left: 200px; } </style>
Step 3: Install Axios HTTP Promise-based library.
Type the following command to install Axios Promise-based HTTP library.
npm install axios vue-axios --save # or yarn add axios vue-axios
We have not created the node.js server, but we will make it in a minute. But first, we set up axios to send a network request to the server.
Okay, now add the axios and vue-axios inside the main.js file.
// main.js import Vue from 'vue' import App from './App.vue' import axios from 'axios'; import VueAxios from 'vue-axios'; import '../node_modules/bootstrap/dist/css/bootstrap.min.css' Vue.config.productionTip = false Vue.use(axios, VueAxios); new Vue({ render: h => h(App) }).$mount('#app')
Finally, we can use Axios inside App.vue file.
// App.vue export default { data() { return { query: '', data: [] } }, methods: { search() { axios.get("http://localhost:5000/search?q=" + this.query) .then(response => { console.log(response.data); this.data = response.data; }) } } }
So, we will send a network request to the node.js server running at port 5000. It is not running because we have not created it yet.
So let us create in the next step.
Step 4: Create node.js web server.
Inside your vue-elastic project, create one folder called a server; inside that, create one file called server.js.
First, install the following node.js dependencies using the following command.
npm install express body-parser elasticsearch cors --save
Write the following code inside a server.js file.
// server.js const express = require('express'); const app = express(); const PORT = 5000; app.listen(PORT, function() { console.log('Your node.js server is running on PORT: ',PORT); });
You can start this node.js server by typing the following command inside the server folder.
node server
Step 5: Install Elasticsearch on the local machine.
You do not need this step if you have already installed Elasticsearch on the Mac. Otherwise, you can continue with this step. So type the following command to install Elasticsearch via homebrew.
brew install elasticsearch
It will install it and now start the services using the following command.
brew services start elasticsearch
Now, we need to connect our node.js application to the elastic server.
// server.js const express = require('express'); const app = express(); const elasticsearch = require('elasticsearch'); const fs = require('fs'); const PORT = 5000; const client = new elasticsearch.Client({ host: '127.0.0.1:9200', log: 'error' }); client.ping({ requestTimeout: 30000 }, function(error) { if (error) { console.error('elasticsearch cluster is down!'); } else { console.log('Everything is ok'); } }); app.listen(PORT, function() { console.log('Your node.js server is running on PORT:',PORT); });
Now, restart the node.js server and see the console. You can get it like this in the console.
Step 6: Create an index and indexing of data.
We need the data to index. So inside the server folder, create one file called data.json and add the following code inside it. First, we will search for some of the Game of Thrones main characters.
[ { "cast_name": "Tyrion Lannister", "og_name": "Peter Dinklage", "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/5/58/Tyrion_main_s7_e6.jpg/revision/latest?cb=20170818050344" }, { "cast_name": "Daenerys Targaryen", "og_name": "Emilia Clarke", "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/5/5f/Daenerys_Dragonpit.jpg/revision/latest?cb=20171015095128" }, { "cast_name": "Jon Snow", "og_name": "kit Harington", "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/a/a5/Profile-JonSnow-707.png/revision/latest?cb=20170828030553" }, { "cast_name": "Cersei Lannister", "og_name": "Lena Heady", "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/c/c3/Profile-CerseiLannister.png/revision/latest?cb=20170828071355" }, { "cast_name": "Sansa Stark", "og_name": "Sophie Turner", "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/7/7e/Sansastark706.jpg/revision/latest/scale-to-width-down/316?cb=20170828072803" }, { "cast_name": "Arya Stark", "og_name": "Maisie Williams", "url": "https://upload.wikimedia.org/wikipedia/en/3/39/Arya_Stark-Maisie_Williams.jpg" }, { "cast_name": "Jaime Lannister", "og_name": "Nikolaj Coster-Waldau", "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/e/eb/Jaime.jpg/revision/latest?cb=20170818052054" }, { "cast_name": "Ramsay Bolton", "og_name": "Iwan Rheon", "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/d/d2/Ramsay_S06E09_RESZIED_FOR_INFOBOX.jpg/revision/latest?cb=20160622071734" }, { "cast_name": "Petyr Baelish", "og_name": "Aidan Gillen", "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/9/9f/Profile-Littlefinger.png/revision/latest?cb=20170826005231" }, { "cast_name": "Joffrey Baratheon", "og_name": "Jack Gleeson", "url": "https://vignette.wikia.nocookie.net/gameofthrones/images/2/25/Joffrey_Season_4_Episode_2_TLATR.jpg/revision/latest?cb=20171105180252" } ]
Now, we need to index these data. So let us create another file inside the server folder called elasticsearch.js and add the following code.
// elasticsearch.js const elasticsearch = require('elasticsearch'); const fs = require('fs'); const client = new elasticsearch.Client({ hosts: [ 'http://localhost:9200'] }); client.indices.create({ index: 'vue-elastic' }, function(error, response, status) { if (error) { console.log(error); } else { console.log("created a new index", response); } }); const bulkIndex = function bulkIndex(index, type, data) { let bulkBody = []; data.forEach(item => { bulkBody.push({ index: { _index: index, _type: type } }); bulkBody.push(item); }); client.bulk({body: bulkBody}) .then(response => { let errorCount = 0; response.items.forEach(item => { if (item.index && item.index.error) { console.log(++errorCount, item.index.error); } }); console.log( `Successfully indexed ${data.length - errorCount} out of ${data.length} items` ); }) .catch(console.err); }; async function indexData() { const articlesRaw = await fs.readFileSync('./data.json'); const articles = JSON.parse(articlesRaw); console.log(`${articles.length} items parsed from data file`); bulkIndex('vue-elastic', 'characters_list', articles); } indexData();
Save the file, open the terminal inside the server folder, and hit the following command.
node elasticsearch.js
It will index the data. So now our game of thrones characters are ready to query.
Step 7: Create the query.
Inside the server.js file, add the following code.
// server.js const express = require('express'); const app = express(); const elasticsearch = require('elasticsearch'); const cors = require('cors'); const bodyParser = require('body-parser'); app.set('port', process.env.PORT || 5000 ); app.use(cors()); app.use(bodyParser.json()); const client = new elasticsearch.Client({ host: '127.0.0.1:9200', log: 'error' }); client.ping({ requestTimeout: 30000 }, function(error) { if (error) { console.error('elasticsearch cluster is down!'); } else { console.log('Everything is ok'); } }); app.get('/search', function (req, res){ let body = { size: 100, from: 0, query: { match: { cast_name: { query: req.query['q'], fuzziness: 4 } } } } client.search({index:'vue-elastic', body:body, type:'characters_list'}) .then(results => { res.send(results.hits.hits); }) .catch(err=>{ console.log(err) res.send([]); }); }) app.listen(app.get('port'), function() { console.log('Your node.js server is running on PORT: ',app.get('port')); });
So, finally, when the user tries to search any phrase, it will get this get /search route, and we pass the query parameter.
According to the query, it will find the results and send them back to the client.
Step 8: Display the queried data.
Inside App.vue file, add the following code.
<!-- App.vue --> <template> <div id="app" class="container"> <div class="input-group input-group-lg bottom"> <div class="input-group-prepend"> <span class="input-group-text">Search</span> </div> <input type="text" class="form-control col-md-6" @keyup.prevent="search" v-model="query" /> </div> <div class="card-row"> <div v-if="data" v-for="(value, index) in data" :key="index" :ref="`card_${index}`" class="card"> <img class="card-image" :src="value._source.url"> <div class="card-footer"> <h3 class="card-title">{{value._source.cast_name}}</h3> <p class="card-text">by <span class="card-author">{{value._source.og_name}}</span> </p> </div> </div> </div> </div> </template> <script> export default { data() { return { query: '', data: [] } }, methods: { search() { this.axios.get('http://localhost:5000/search?q='+this.query) .then(response => { this.data = response.data; }) } } } </script> <style> body { background-color: #E1E7E7; } .bottom { margin-top: 50px; margin-left: 200px; } .card-row { display: flex; justify-content: center; align-items: center; min-width: 780px; width: 100%; height: 500px; } .card { position: relative; background-color: #FFFFFF; height: 370px; width: 240px; margin: 10px; overflow: hidden; box-shadow: 0px 2px 4px 0px rgba(0,0,0,0.5); } .card-image { position: absolute; left: -9999px; right: -9999px; margin: auto; height: 220px; min-width: 100%; } .card-footer { position: absolute; bottom: 0; height: 130px; padding: 10px 15px; font-family: Helvetica; } .card-text { font-size: 14px; color: rgba(0, 0, 0, 0.7); } .card-title { font-family: Serif; } .card-author { font-size: 14px; color: #BAB096; } </style>
Finally, we can search the data from Vue.js using elasticsearch.
That’s it for this tutorial. Find the GitHub Code here.
I got an error, ” this.axios.get(‘http://localhost:8082/search?q=’+this.query)” –TypeError: Cannot read property ‘get’ of undefined.
How to solve it ?Thanks