Not long ago Lucia posted about using SAP HANA express edition (HXE) on the Google Cloud Platform. So after a little, well basically begging she let me use her existing install to do some experimenting.

So experimenting I did….

Google App Engine meets SAP HANA express edition

The end result I am pleased to say was a nice robust Node.js application that contained both a Node.js HDB library connection as well as an OData source using Bootstrap, JQuery, Google Maps and and and and…

The first challenge was just figuring out how the Google App Engine worked in particular the part related to Node.js. With the use of their quick start guide I was up and running in a short while. Then it was a matter of repeating the process but with my own code and Github repository.

Google App Engine meets SAP HANA express edition

 

Once my repo was connected to the Development section I was able to launch the Google Cloud Shell and clone my repo into a new folder.

Google App Engine meets SAP HANA express edition

 

Google App Engine meets SAP HANA express edition

Then I was able to (after testing of course) run the “gcloud deploy” option and deploy it. I know I might have skipped over a few steps there, maybe more than a few but the point was – it was literally that easy to have my app up and running.

Speaking of the app though, since I was copying the quickstart guide it was rather quick which meant the yaml file was there already. I’m not a fan of those…

# [START runtime] runtime: nodejs env: flex # [END runtime] # Temporary setting to keep gcloud from uploading node_modules skip_files: - ^node_modules$

For the rest since I was working with Node.js I just needed to follow the basic tutorials, like how to generate the “package.json” file. There are several tutorials out there actually so there is no limit to finding something to help you get started. Some of the ones I started with were around Node.js and Express.

  • https://nodeguide.com/beginner.html
  • https://expressjs.com/en/starter/hello-world.html
  • https://expressjs.com/en/starter/generator.html

I decided to use Express for mine as it meant I could stick with pure HTML for my templates (through the view engine, “ejs“) and there were a ton of examples already out there.

Google App Engine meets SAP HANA express edition

Using the templates, I was able to define my first route access my HXE system and pass variables into my template.

app.get('/', function (req, res) { sql_command = "SELECT TO_VARCHAR(ROUND((FREE_PHYSICAL_MEMORY) /1024/1024/1024, 2)) AS FREEMEM FROM PUBLIC.M_HOST_RESOURCE_UTILIZATION"; async.waterfall([connect, executeSQL, disconnect], function (err, rows) { client.end(); if (err) { return console.error(err); } lv_freemem = rows[0].FREEMEM; res.render('index', { version: lv_version, app_version: lv_appversion, freemem: lv_freemem, diskvol: lv_disk, cpu: lv_cpu, alerts: lv_alerts, users: lv_users }); }); })

The template, as I mentioned since I chose “ejs” I was able to use pure HTML.

                            

Google App Engine meets SAP HANA express edition SAP HANA is a data platform that provides everything you need to build real-time data-driven applications. Learn about the different features and components, how to use them and how to program your applications.

SAP Developers

The following is a Node JS demo connecting to SAP HANA, express edition running within the Google Cloud Platform.

Google App Engine meets SAP HANA express edition

Components

  • Node Library: express
  • Node Library: ejs
  • Node Library: util
  • Node Library: async
  • Node Library: hdb
  • JS Library: JQuery
  • JS Library: JQuery UI
  • Layout Library: Bootstrap
  • SAP HANA, express edition

Server Information

HANA v<%= version %>

Application v<%= app_version %>

Active Users <%= users %>

Free Memory <%= freemem %> GB

CPUs <%= cpu %>

Free Disk Space <%= diskvol %> GB

Alerts <%= alerts %>

Here I passed in several variables such as the following,

HANA v<%= version %>

Application v<%= app_version %>

Those variables came from the “app.js” file where I defined the route

res.render('index', { version: lv_version, app_version: lv_appversion, ... }

Since I put the SQL call to access the server free memory into the route itself every time the “index” is clicked the SQL will run and return the current value. However, one thing to remember in order to make this work when I deployed I also needed to add a Firewall rule under the Networking options for the HDB library to be able to find the server.

Google App Engine meets SAP HANA express edition

Then whether I was working on my local system for testing on on the Cloud system then it would connect, in my case it was port 30041 but that was something I had to check on the system directly to see which port was allocated (usually 30015).

Google App Engine meets SAP HANA express edition

Having my “index” working it was now time to go a bit more complex, I decided to use an existing example from a few months ago my “Fuzzy Search”  which meant I needed to get onto the HXE system and create a basic XS application with the data, structures, and sample data. Then I moved the UI layer to my Node.js application.

Google App Engine meets SAP HANA express edition

This meant I needed to be able to send data from my webpage back to my main Node.js application. This actually was a lot easier than I originally thought it would be. I just needed to set the form action to a new route that I needed to add to my main Node.js application.

<%= search_sql %>

Therefore in my “app.js” I added a new route, “/fuzzy_get

app.get('/fuzzy_get', function (req, res) { var lv_res = { txtFieldSearch: req.query.txtFieldSearch, txtfuzzyweight: req.query.txtfuzzyweight }; sql_command = "SELECT SCORE() AS score, RTEXT FROM "FSM"."sap.devs.demo.fuzzymovie.data::movies.Review" WHERE CONTAINS(RTEXT,'" + req.query.txtFieldSearch + "', FUZZY(0." + req.query.txtfuzzyweight + ") ) ORDER BY score DESC limit 15"; async.waterfall([connect, executeSQL, disconnect], function (err, rows) { client.end(); if (err) { return console.error(err); } res.render('fuzzy', { data: rows, version: lv_version, app_version: lv_appversion, search_field: req.query.txtFieldSearch, search_sql: sql_command, sql_count: lv_count }); }); })

There I was able to access the form fields and then execute my SQL and return the “rows” back to my templated page. Here I do a little “old school” JavaScript and created the table of results thanks the to the “ejs” engine.

 <% for (var i = 0; i < data.length; i++) { %>  <% } %> 
ScoreReview
<%= data[i].SCORE %>    <%= data[i].RTEXT %>

I was on a roll and moved on from this point to a second route and the addition of a Google Map example. I based this off another blog from a few months ago. However, this time instead of my own data from Openpaths I decided to put in the locations of all the SAP offices worldwide along with locations of several folks who helped me test it real quick.

Google App Engine meets SAP HANA express edition

In this part I decided to mix it up a bit and use an OData service to submit my current geo location to the server and add it to the collection for displaying later.

Google App Engine meets SAP HANA express edition

 

// Insert to HANA var timeStamp = new Date().getTime(); var data = { "ID": 1, "PDATE": "/Date(" + timeStamp + ")/", "PLAT": "" + position.coords.latitude + "", "PLON": "" + position.coords.longitude + "", "PDESC": " " }; console.log("Data: " + JSON.stringify(data)); var aUrl = '/sap/devs/demo/mylocation/services/location_input.xsodata/locs'; jQuery.ajax({ url: aHost + aUrl, method: 'POST', crossDomain: true, contentType: "application/json", data: JSON.stringify(data), success: function (data) { console.log('Posted to SAP HANA'); console.log(data); }, failure: function (errMsg) { console.log(errMsg); }, error: function (errMsg) { console.log(errMsg); }, xhrFields: { withCredentials: true }, crossDomain: true });

Otherwise I used the HDB library to connected directly to the server and execute SQL commands. I pulled the results of two different tables and passed those rows to my template.

app.get('/map', function (req, res) { sql_command = "SELECT * FROM "HGL"."sap.devs.demo.mylocation.data::hanalocations_details.locations""; async.waterfall([connect, executeSQL, disconnect], function (err, rows) { client.end(); if (err) { return console.error(err); } var result = JSON.stringify({ Items: rows }); sql_command = "SELECT * FROM "HGL"."sap.devs.demo.mylocation.data::hanalocations_details.offices""; async.waterfall([connect, executeSQL, disconnect], function (err, rows) { client.end(); if (err) { return console.error(err); } var office_result = JSON.stringify({ Items: rows }); res.render('map', { api_key: API_KEY, version: lv_version, app_version: lv_appversion, data: result, offices: office_result }); }); }); })

Once in the template, it was a bit different to get the data in place. Turned out to be a bit easier as well.

// Marker Locations var result = "<%= data %>" var map_data = replaceAll(result); map_data = JSON.parse(map_data); // Offce locations var office_result = "<%= offices %>" var map_officedata = replaceAll(office_result); map_officedata = JSON.parse(map_officedata);

For some reason though I also needed a little helper function as the data was being passed over looking a bit strange, whether this is a standard issue or something specific to what I did I am not yet sure.

function replaceAll(str) { return str.replace(new RegExp('"', 'g'), '"'); }

Otherwise that was the only “special” things I had to to make this work beyond the standard basic examples you can find all over the internet for working with Google Maps.

That was my first experiences with the Google App Engine and I have to say, it was an extreme pleasure to work with! My testers agreed.

Google App Engine meets SAP HANA express edition

New NetWeaver Information at SAP.com

Very Helpfull

User Rating: Be the first one !