How to perform mixed computing with multiple data sources
Tags: programming, webdev, json, java
Published: 2024-03-09 12:43:08

Early applications usually only connected to one database, and calculations were also performed in the database, with little or no problem of mixed calculations from multiple data sources. The data sources of modern applications have become very rich, and the same application may also access multiple data sources, such as various SQL and NoSQL databases, text/XLS, WebService/Restful, Kafka, Hadoop…. Mixed computing on multiple data sources is an unavoidable problem that needs to be addressed.

Direct hard coding to implement in applications is very cumbersome, and commonly used application development languages such as Java are not good at doing such things. Compared to SQL, their simplicity is far inferior.

It is also not appropriate to import multi-source data into one database and then calculate. Not to mention that importing takes time and results in a loss of data real-timeness, the importing of certain data (such as data supported by Mongodb and multi-layer document data) into a relational database losslessly is a very difficult and costly task. After all, the existence of these diverse data sources has a reason and cannot be easily replaced by relational databases. Otherwise, there’s no need to invent Mongodb, just use MySQL.

What about a logical data warehouse? It sounds very heavy. Before use, it is necessary to define metadata to map these diverse data sources, which is very cumbersome. Moreover, most logical data warehouses are still SQL based, making it difficult to map these diverse data losslessly.

What about the pile of computing frameworks? Especially the stream computing framework. It is possible to access many data sources, but the computing itself provides very little functionality. Either use SQL, there will be mapping difficulties like a logical data warehouse; To freely access various data sources, you have to write your own calculation code in Java.

Facing mixed computing problems on multiple data sources, esProc SPL is a good approach.

esProc SPL is an open source computing engine developed purely in Java, and it is here https://github.com/SPLWare/esProc .

How can esProc SPL solve this problem? There are mainly two aspects:

1. The abstract access interface for diverse data sources can map a wide range of data into a few data objects.

2. Based on the data objects in 1, independently implement sufficiently rich computing power independent of the data source.

With these two abilities, when encountering new data sources, just encapsulate the interface and constantly supplement it.

esProc SPL provides two basic data objects: table sequence and cursor, corresponding to in-memory data table and streaming data table, respectively.

Almost all data sources, including relational databases, provide interfaces to return these two types of data objects: small data is read at one time, and an in-memory data table (table sequence) is used; Big data needs to be gradually returned using a streaming data table (cursor). With these two types of data objects, almost all data sources can be covered.

In this way, there is no need to define metadata for mapping in advance, and the data can be accessed directly using the methods provided by the data source itself, and then encapsulated into one of these two types of data objects. This can preserve the characteristics of the data source and fully utilize its storage and computing capabilities. Of course, there is no need to perform a “certain” import action on the data first, and real-time access can be implemented. These two types of data objects share the common capabilities of diverse data source access interfaces, while the mapping data table method used in a logical data warehouse does not correctly abstract the common characteristics of diverse data sources, and its applicability is much narrower.

It should be noted that SPL’s table sequence and cursor both support multi-layer structured data as well as text data, which allows for the receiving and processing of JSON data (or its binary variant).

Take a look at some examples:

Relational database, A2 returns a table sequence, A3 returns a cursor

<table> 
 <thead> 
  <tr>
   <th> </th>
   <th> A</th>
  </tr> 
 </thead> 
 <tbody> 
  <tr>
   <td> 1 </td>
   <td> =connect("MyCompany")</td>
  </tr> 
  <tr>
   <td> 2 </td>
   <td> =A1.query("select * from employees order by hire_date asc limit 100")</td>
  </tr> 
  <tr>
   <td> 3 </td>
   <td> =A1.cursor("select * from salaries")</td>
  </tr> 
  <tr>
   <td> 4 </td>
   <td> &gt;A1.close()</td>
  </tr> 
 </tbody> 
</table> 

Local file, A1/A3 returns a table sequence, A2 returns a cursor

<table> 
 <thead> 
  <tr>
   <th> </th>
   <th> A</th>
  </tr> 
 </thead> 
 <tbody> 
  <tr>
   <td> 1 </td>
   <td> =T("Orders.csv")</td>
  </tr> 
  <tr>
   <td> 2 </td>
   <td> =file("sales.txt").cursor@t()</td>
  </tr> 
  <tr>
   <td> 3 </td>
   <td> =file("Orders.xls").xlsimport@t()</td>
  </tr> 
 </tbody> 
</table> 

Restful, A1 returns text in JSON format

<table> 
 <thead> 
  <tr>
   <th> </th>
   <th> A</th>
  </tr> 
 </thead> 
 <tbody> 
  <tr>
   <td> 1 </td>
   <td> =httpfile("http://127.0.0.1:6868/restful/emp_orders").read()</td>
  </tr> 
  <tr>
   <td> 2 </td>
   <td> =json(A1)</td>
  </tr> 
 </tbody> 
</table> 

Elastic Search

<table> 
 <thead> 
  <tr>
   <th> </th>
   <th> A</th>
  </tr> 
 </thead> 
 <tbody> 
  <tr>
   <td> 1 </td>
   <td> &gt;apikey="Authorization:ApiKey a2x6aEF……KZ29rT2hoQQ=="</td>
  </tr> 
  <tr>
   <td> 2 </td>
   <td> '{<br>"counter" : 1,<br>"tags" : ["red"]<br>,"beginTime":"2022-01-03"<br>,"endTime":"2022-02-15"<br>} </td>
  </tr> 
  <tr>
   <td> 3 </td>
   <td> =es_rest("https://localhost:9200/index1/_doc/1", "PUT",A2;"Content-Type: application/x-ndjson",apikey)</td>
  </tr> 
 </tbody> 
</table> 

Mongodb, A2 returns a table sequence, A3 returns a cursor

<table> 
 <thead> 
  <tr>
   <th> </th>
   <th> A</th>
  </tr> 
 </thead> 
 <tbody> 
  <tr>
   <td> 1 </td>
   <td> =mongo_open("mongodb://127.0.0.1:27017/mymongo")</td>
  </tr> 
  <tr>
   <td> 2 </td>
   <td> =mongo_shell(A1,"{'find':'orders',filter:{OrderID: {$gte: 50}},batchSize:100}")</td>
  </tr> 
  <tr>
   <td> 3 </td>
   <td> =mongo_shell@dc(A1,"{'find':'orders',filter:{OrderID: { $gte: 50}},batchSize:10}")</td>
  </tr> 
  <tr>
   <td> 4 </td>
   <td> =mongo_close(A1)</td>
  </tr> 
 </tbody> 
</table> 

Kafka, A2 returns a table sequence containing JSON data, A3 returns a cursor

<table> 
 <thead> 
  <tr>
   <th> </th>
   <th> A</th>
  </tr> 
 </thead> 
 <tbody> 
  <tr>
   <td> 1 </td>
   <td> =kafka_open("/kafka/my.properties", "topic1")</td>
  </tr> 
  <tr>
   <td> 2 </td>
   <td> =kafka_poll(A1)</td>
  </tr> 
  <tr>
   <td> 3 </td>
   <td> =kafka_poll@c(A1)</td>
  </tr> 
  <tr>
   <td> 4 </td>
   <td> =kafka_close(A1)</td>
  </tr> 
 </tbody> 
</table> 

HBase, A2/A3 returns a tables equence, A4 returns a cursor

<table> 
 <thead> 
  <tr>
   <th> </th>
   <th> A</th>
  </tr> 
 </thead> 
 <tbody> 
  <tr>
   <td> 1 </td>
   <td> =hbase_open("hdfs://192.168.0.8", "192.168.0.8")</td>
  </tr> 
  <tr>
   <td> 2 </td>
   <td> =hbase_get(A1,"Orders","row1","datas:Amount":number:amt,"datas:OrderDate":date:od)</td>
  </tr> 
  <tr>
   <td> 3 </td>
   <td> =hbase_scan(A1,"Orders")</td>
  </tr> 
  <tr>
   <td> 4 </td>
   <td> =hbase_scan@c(A1,"Orders")</td>
  </tr> 
  <tr>
   <td> 5 </td>
   <td> =hbase_close(A1)</td>
  </tr> 
 </tbody> 
</table> 

There are already many data sources encapsulated in esProc SPL and they are still increasing:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/533nvpmh5ytix2bch107.png)

esProc SPL provides comprehensive computing power for table sequences, including filtering, grouping, sorting, join, etc. Its richness far exceeds that of SQL, and most operations can be implemented in just one line:

```
Filter：T.select(Amount>1000 && Amount<=3000 && like(Client,"*s*"))
Sort：T.sort(Client,-Amount)
Distinct：T.id(Client)
Group：T.groups(year(OrderDate);sum(Amount))
Join：join(T1:O,SellerId; T2:E,EId)
TopN：T.top(-3;Amount)
TopN in group：T.groups(Client;top(3,Amount))
```

There are similar calculations on cursors, and the syntax is almost identical. We won’t provide detailed examples here. Interested friends can refer to the materials on the esProc SPL official website.

Based on these foundations, mixed computing is very easy to implement:

Two relational databases

<table> 
 <thead> 
  <tr>
   <th> </th>
   <th> A</th>
  </tr> 
 </thead> 
 <tbody> 
  <tr>
   <td> 1 </td>
   <td> =oracle.query("select EId,Name from employees")</td>
  </tr> 
  <tr>
   <td> 2 </td>
   <td> =mysql.query("select SellerId, sum(Amount) subtotal from Orders group by SellerId")</td>
  </tr> 
  <tr>
   <td> 3 </td>
   <td> =join(A1:O,SellerId; A2:E,EId)</td>
  </tr> 
  <tr>
   <td> 4 </td>
   <td> =A3.new(O.Name,E.subtotal)</td>
  </tr> 
 </tbody> 
</table> 

Relational Database and JSON

<table> 
 <thead> 
  <tr>
   <th> </th>
   <th> A</th>
  </tr> 
 </thead> 
 <tbody> 
  <tr>
   <td> 1 </td>
   <td> =json(file("/data/EO.json").read())</td>
  </tr> 
  <tr>
   <td> 2 </td>
   <td> =A1.conj(Orders)</td>
  </tr> 
  <tr>
   <td> 3 </td>
   <td> =A2.select(Amount&gt;1000 &amp;&amp;Amount&lt;=3000 &amp;&amp; like@c(Client,"<em>s</em>"))</td>
  </tr> 
  <tr>
   <td> 4 </td>
   <td> =db.query@x("select ID,Name,Area from Client")</td>
  </tr> 
  <tr>
   <td> 5 </td>
   <td> =join(A3:o,Client;A4:c,ID)</td>
  </tr> 
 </tbody> 
</table> 

Mongodb and relational database

<table> 
 <thead> 
  <tr>
   <th> </th>
   <th> A</th>
  </tr> 
 </thead> 
 <tbody> 
  <tr>
   <td> 1 </td>
   <td> =mongo_open("mongodb://127.0.0.1:27017/mongo")</td>
  </tr> 
  <tr>
   <td> 2 </td>
   <td> =mongo_shell(A1,"test1.find()")</td>
  </tr> 
  <tr>
   <td> 3 </td>
   <td> =A2.new(Orders.OrderID,Orders.Client,Name,Gender,Dept).fetch()</td>
  </tr> 
  <tr>
   <td> 4 </td>
   <td> =mongo_close(A1)</td>
  </tr> 
  <tr>
   <td> 5 </td>
   <td> =db.query@x("select ID,Name,Area from Client")</td>
  </tr> 
  <tr>
   <td> 6 </td>
   <td> =join(A3:o, Orders.Client;A4:c,ID)</td>
  </tr> 
 </tbody> 
</table> 

Restful and local text file

<table> 
 <thead> 
  <tr>
   <th> </th>
   <th> A</th>
  </tr> 
 </thead> 
 <tbody> 
  <tr>
   <td> 1 </td>
   <td> =httpfile("http://127.0.0.1:6868/api/getData").read()</td>
  </tr> 
  <tr>
   <td> 2 </td>
   <td> =json(A1)</td>
  </tr> 
  <tr>
   <td> 3 </td>
   <td> =T("/data/Client.csv")</td>
  </tr> 
  <tr>
   <td> 4 </td>
   <td> =join(A2:o,Client;A3:c,ClientID)</td>
  </tr> 
 </tbody> 
</table> 

With mixed computing capabilities, we can also solve T+0 calculations by the way.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qfzaac40q0tlf452v17n.png)

The single TP database naturally supports T+0 calculation. When there is too much data accumulation, it can affect the performance of the TP database. At this point, a portion of historical data is usually moved to a professional AP database, which is known as hot and cold data separation. The TP database only stores recently generated hot data, while the AP database stores historical cold data. After reducing the pressure on the TP database, it can run smoothly.

But in this way, when doing real-time whole-data statistics, cross database calculations are required, which has always been a hassle, especially when facing heterogeneous databases (TP and AP databases are usually not of the same structure). With esProc SPL’s mixed computing capability of different data sources, this problem can be easily solved.

<table> 
 <thead> 
  <tr>
   <th> </th>
   <th> A </th>
   <th> B</th>
  </tr> 
 </thead> 
 <tbody> 
  <tr>
   <td> 1 </td>
   <td colspan="2"> =[[connect@l("oracle"),"ORACLE"],[connect@l("hive"),"HIVE"]]</td>
  </tr> 
  <tr>
   <td> 2 </td>
   <td colspan="2"> =SQL="select month(orderdate) ordermonth,sellerid,sum(amount) samount,count(amount) camount from sales group by month(orderdate),sellerid"</td>
  </tr> 
  <tr>
   <td> 3 </td>
   <td> fork A1 </td>
   <td> =SQL.sqltranslate(A3(2))</td>
  </tr> 
  <tr>
   <td> 4 </td>
   <td> </td>
   <td> =A3(1).query(B3)</td>
  </tr> 
  <tr>
   <td> 5 </td>
   <td colspan="2"> =A3.conj().groups(ordermonth,sellerid;sum(samount):totalamount,sum(camount):totalcount)</td>
  </tr> 
 </tbody> 
</table> 

For the mixed calculation between TP database Oracle and AP database Hive, SPL can also convert SQL into dialect syntax of different databases.

Then, how can the code written in esProc SPL be integrated into the application?

Very simple, esProc provides a standard JDBC driver, allowing Java programs to call SPL code just like calling database SQL.

```
Class.forName("com.esproc.jdbc.InternalDriver");
Connection conn =DriverManager.getConnection("jdbc:esproc:local://");
Statement statement = conn.createStatement();
ResultSet result = statement.executeQuery("=json(file(\"Orders.csv\")).select(Amount>1000 && like(Client,\"*s*\")
```

More complex SPL scripts can be saved as files, just like calling stored procedures:

```
Class.forName("com.esproc.jdbc.InternalDriver");
Connection conn =DriverManager.getConnection("jdbc:esproc:local://");
CallableStatement statement = conn.prepareCall("call queryOrders()");
statement.execute();
```

This is equivalent to providing a logical database without storage and without SQL.