---
title: "SQL on Mongo (legacy)"
canonical: "https://onesaitplatform.refined.site/space/DOCT/2220830489/SQL%20on%20Mongo%20(legacy)"
format: markdown
---
> ℹ️ **IMPORTANT**
> ℹ️ 
> ℹ️ This guide applies to the Quasar-based SQL query engine, which worked for Mongo versions up to 3.4. 
> ℹ️ 
> ℹ️ If you are using the new SQL engine or a Mongo 4.X version you should read this guide: [New SQL Engine on MongoDB](https://onesaitplatform.atlassian.net/wiki/spaces/DOCT/pages/2220830733)


The Semantic Data Hub has among other objectives to **decouple the ontology from the underlying database**. 

For that, we propose to use SQL query language, so the platform offers a [SQL Query Engine](https://quasar-analytics-documentation.readthedocs.io/en/latest/sql-squared-reference/) for each of the supported databases.

Here you can find Reference Guides for this language:

> Macro (toc)

# SELECT Operations

## Data Types

| **Type** | **Description** | **Examples** | **MongoDB native-supported** |
| --- | --- | --- | --- |
| Null | Indicates missing information. | `null` | Yes |
| Boolean | true or false. | `true`, `false` | Yes |
| Integer | Whole numbers (no fractional component). | `1`, `-2` | Yes |
| Decimal | Decimal numbers (optional fractional components). | `1.0`, `-2.19743` | Yes |
| String | Text. | `'221B Baker Street'` | Yes |
| DateTime | Date and time, in ISO8601 format. | `TIMESTAMP '2004-10-19 10:23:54'` | Yes |
| Time | Time in the format HH:MM:SS. | `TIME '10:23:54'` | No |
| Date | Date in the format YYYY-MM-DD. | `DATE '2004-10-19'` | No |
| Interval | Time interval, in ISO8601 format. | `INTERVAL 'P3DT4H5M6S'` | No |
| Object ID | Unique object identifier. | `OID '507f1f77bcf86cd799439011'` | Yes |
| Ordered Set | Ordered list with no duplicates allowed. | `(1, 2, 3)` | No |
| Array | Ordered list with duplicates allowed. | `[1, 2, 2]` | Yes |

## Clauses

The following clauses are supported:

| **Type** | **Clauses** |
| --- | --- |
| Basic | `SELECT`, `AS`, `FROM` |
| Joins | `LEFT OUTER JOIN`, `RIGHT OUTER JOIN`, `INNER JOIN`, `FULL JOIN`, `CROSS` |
| Filtering | `WHERE` |
| Grouping | `GROUP BY`, `HAVING` |
| Conditional | `CASE` , `WHEN`, `DEFAULT` |
| Paging | `LIMIT`, `OFFSET` |
| Sorting | `ORDER BY` , `DESC`, `ASC` |

The following operators are supported:

| **Type** | **Operators** |
| --- | --- |
| Numeric | `+`, `-`, `*`, `/`, `%` |
| String | `~` , `~*`, `!~`, `!~*`, `LIKE`, `||` |
| Array | `||`, `[ ... ]` |
| Relational | `=`, `>=`, `<=`, `<>`, `BETWEEN`, `IN`, `NOT IN` |
| Boolean | `AND`, `OR`, `NOT` |
| Projection | `foo.bar`, `foo[2]`, `foo{*}`, `foo[*]` |
| Date/Time | `TIMESTAMP`, `DATE`, `INTERVAL`, `TIME` |
| Identity | `OID` |

**Note:** `~` , `~*`, `!~`, and `!~*` are regular expression operators. `~*`, `!~`, and `!~*` are preliminary and may not work in the current release.

**Note:** The `||` operator for strings will concatenate two strings; for example, you can create a full name from a first and last name property: `c.firstName || ' ' || c.lastName`. The `||` operator for arrays will concatenate two arrays; for example, if `xy` is an array with two values, then `c.xy || [0]` will create an array with three values, where the third value is zero.

The following functions are supported:

| **Type** | **Functions** |
| --- | --- |
| String | `CONCAT`, `LOWER`, `UPPER`, `SUBSTRING`, `LENGTH`, `SEARCH` |
| DateTime | `DATE_PART`, `TO_TIMESTAMP` |
| Nulls | `COALESCE` |
| Arrays | `ARRAY_LENGTH`, `FLATTEN_ARRAY` |
| Objects | `FLATTEN_OBJECT` |
| Set-Level | `DISTINCT`, `DISTINCT_BY` |
| Aggregation | `COUNT`, `SUM`, `MIN`, `MAX`, `AVG` |
| Identity | `SQUASH` |

## Basic Queries

<span style="color: #0000ff">select * from ISO3166_1 as c </span><span style="color: #000000">returns info and ContextData:</span>

![image](media://38a02939-7376-4024-8845-b66ff359ea02)

<span style="color: #0000ff">select c.ISO3166 as ISO3166 from ISO3166_1 as c</span> returns only Data:

![image](media://b5c71bd7-7879-42ca-b8a3-0b6814165c6e)

<span style="color: #0000ff">select _id,c from ISO3166_1 </span> returns * including id from Registry:

![image](media://822e8528-6ca3-4113-9183-2cc73a940c46)


## Filtering

You can filter a result set using the WHERE clause. The following operators are supported:

- Relational: `-`, `=`, `>=`, `<=`, `<>`, `BETWEEN`, `IN`, `NOT IN`
- Boolean: `AND`, `OR`, `NOT`

**Examples:**

<span style="color: #0000ff">select c.ISO3166 as ISO3166 from ISO3166_1 as c where </span>[<span style="color: #0000ff">c.ISO3166.name</span>](http://c.ISO3166.name)<span style="color: #0000ff">="Zambia"</span>

![image](media://4a3d1b47-dc38-462a-8ea1-7cfe3030537b)

## <span style="color: #000000">Numeric and String Operations</span>

You can use any of the operators or functions listed in the [Clauses, Operators, and Functions](https://quasar-analytics-documentation.readthedocs.io/en/latest/sql-squared-reference/#clauses-operators-functions) section on numbers and strings. Some common string operators and functions include:

| **Operator or Function** | **Description** |
| --- | --- |
| `||` | Concatenates. |
| `LOWER` | Converts to lowercase. |
| `UPPER` | Converts to uppercase. |
| `SUBSTRING` | Returns a substring. |
| `LENGTH` | Returns length of string. |

**Examples:**

<span style="color: #0000ff">select c.ISO3166.number/100+1000 as calculated from ISO3166_1 as c where </span>[<span style="color: #0000ff">c.ISO3166.name</span>](http://c.ISO3166.name)<span style="color: #0000ff">="Zambia" and c.ISO3166.language="EN"</span>

![image](media://b6404431-0e60-47b9-bb89-6b6a86ac2739)

<span style="color: #0000ff">select </span>[<span style="color: #0000ff">c.ISO3166.name</span>](http://c.ISO3166.name)<span style="color: #0000ff"> as name from ISO3166_1 as c where </span>[<span style="color: #0000ff">c.ISO3166.name</span>](http://c.ISO3166.name)<span style="color: #0000ff"> like "S%" and c.ISO3166.language="EN"</span>

![image](media://3ffa5ec2-577a-4004-9fb1-27648189033d)


## Dates and Times

Filter by dates and times using the `TIMESTAMP`, `TIME`, and `DATE` operators. Also, you can also use the `DATEPART` operator for selection to select part of a date, such as the day.

**Note:** Some databases will automatically convert strings into dates or date/times. SlamData does not perform this conversion, since the underlying database has no schema and no fixed type for any field. As a result, an expression like `WHERE ts > '2015-02-10'` compares string-valued `ts` fields with the string `'2015-02-10'` instead of a date comparison. If you want to embed literal dates, timestamps, etc. into your SQL queries, you should use the time conversion operators, which accept a string and return value of the appropriate type. For example, the above snippet could be converted to `WHERE ts > DATE '2015-02-10'`, which looks for date-valued `ts` fields and compares them with the date `2015-02-10`.

**Note:** If your database data does not use a native date/time type, and instead, you store your timestamps as epoch milliseconds in a numeric value, then you should either compare numbers or use the TO_TIMESTAMP function.

**Filter based on a timestamp (date and time).**

Use the TIMESTAMP operator to convert a string into a date and time. The string should have the format `YYYY-MM-DDTHH:MM:SS`.

<span style="color: #0000ff">select c.type,</span>[<span style="color: #0000ff">c.name</span>](http://c.name)<span style="color: #0000ff">,c.dateLastUpdate,DATE_PART("year",c.dateLastUpdate) as year from CLM_BusStop as c</span>

<span style="color: #0000ff">select c from CLM_BusStop as c WHERE c.CLM_BusStop.lastUpdateMillis > TO_TIMESTAMP(1446335999)</span>

<span style="color: #0000ff">select c from CLM_BusStop as c WHERE c.CLM_BusStop.lastUpdate > TIMESTAMP('2018-11-06T12:00:00.000Z')</span>


### Function NOW()

This function **returns the current system date**, you can use it in the WHERE of your queries to bring data.

**<span style="color: #0000ff">NOW(“format“,'unitTime', amount)</span>**

- “**format**“: we will give the format that we need to date, for example "yyyy-MM-dd'T'HH: mm: ss'Z '".
- '**unitTime**': to date we can increase or decrease a number of hours, days, ... the possible values for 'unitTime' are: 'year', 'month', 'date', 'hour', 'minute', 'second', 'millisecond'.
- "**amount**": is a positive or negative integer value that is added or subtracted depending on the unitTime selected.

Examples:

<span style="color: #0000ff">select * from MyOntology as c where c.date.timestamp>NOW()</span>

## Grouping

SQL on Mongo allows you to group data by fields and by date parts.

**Group based on a single field.**

Use GROUP BY to group results by a field.

Example:

`SELECT c.age, COUNT(*) AS cnt
  FROM Users as c GROUP BY c.age`  


`SELECT c.age, c.gender, COUNT(*) AS cnt
  FROM Users as c GROUP BY c.age, c.gender`  


**Group based on date part.**

Use the DATE_PART function to group by a part of a date, such as the month.

Example:

`SELECT DATE_PART('day', c.ts) AS day, COUNT(*) AS cnt
  FROM Events as c
  GROUP BY DATE_PART('day', c.ts)`  


**Filter within a group.**

Filter results within a group by adding a HAVING clause followed by a Boolean predicate.

Example:

`SELECT DATE_PART('day', c.ts) AS day, COUNT(*) AS cnt
  FROM Events as c
  GROUP BY DATE_PART('day', c.ts)
  HAVING c.gender = 'female'`  


**Double grouping**

Perform double-grouping operations by putting operators inside other operators. The inside operator will be performed on each group created by the GROUP BY clause, and the outside operator will be performed on the results of the inside operator.

Example:

This query returns the average population of states. The outer aggregation function (AVG) operates on the results of the inner aggregation (SUM) and GROUP BY clause.

`SELECT AVG(SUM(p.pop)) FROM Population as p
  GROUP BY p.state`

## Nested Data and arrays

Unlike a relational database, many NoSQL databases allows data to be nested (that is, data can be objects) and to contain arrays.

**Nesting**

Nesting is represented by levels separated by a period (`.`).

**Arrays**

Array elements are represented by the array index in square brackets (`[n]`).

Example:

`SELECT c.profile.allAddress[0].street.number
  FROM Users as c`  


**Flattening**

You can extract all elements of an array or all field values simultaneously, essentially removing levels and flattening the data. Use the asterisk in square brackets (`[*]`) to extract all array elements.

Example:

`SELECT c.profile.allAddresses[*]
  FROM Users as c`  


Use the asterisk in curly brackets (`{*}`) to extract all field values.

Example:

`SELECT c.profile.{*} FROM Users as c`  


**Filtering using arrays**

You can filter using data in all array elements by using the asterisk in square brackets (`[*]`) in a WHERE clause.

Example:

`SELECT DISTINCT * FROM Users as c
  WHERE c.profile.allAddresses[*].street.number = '221B'`

## Pagination and Sorting

**Pagination**

Pagination is used to break large return results into smaller chunks. Use the LIMIT operator to set the number of results to be returned and the OFFSET operator to set the index at which the results should start.

Example (Limit results to 20 entries):

`SELECT u FROM Users as u LIMIT 20`  


Example (Limit results to 20 entries empezando en el 10):

`SELECT u FROM Users as u OFFSET 10 LIMIT 20`  


**Sorting**

Use the ORDER BY clause to sort the results. You can specify one or more fields for sorting, and you can use operators in the ORDER BY arguments. Use ASC for ascending sorting and DESC for decending sorting.

Example (Sort users by ascending age):

`SELECT u 
  FROM Users as u
  ORDER BY u.age ASC`

## Joins

Use the JOIN operator to join different collections.

Examples:

This example returns the names of employees and the names of the departments they belong to by matching up the employee department ID with the department’s ID, where both IDs are ObjectID types.

`SELECT emp.name, dept.name
  FROM Employee as emp
  JOIN Department as dept
  ON dept._id = emp.departmentId`

## Conditional and Nulls

**Conditionals**

Use the CASE expression to provide if-then-else logic to SQL². The CASE sytax is:

`SELECT (CASE <field>
  WHEN <value1> THEN <result1>
  WHEN <value2> THEN <result2>
  ...
  [ELSE <elseResult>
  END)
FROM "<path>"`

Example:

The following example translates string number values into actual numbers.

<span style="color: #0000ff">select </span>

<span style="color: #0000ff">    (CASE c.Ticket.number </span>

<span style="color: #0000ff">      WHEN 'one' THEN 1 </span>

<span style="color: #0000ff">      WHEN 'two' THEN 2 </span>

<span style="color: #0000ff">      END) </span>

<span style="color: #0000ff">as nameParsed </span>

<span style="color: #0000ff">from Ticket as c</span>

## Considerations on ID

MongoDB has special rules about fields called `_id`. For example, they must remain unique, which means that some queries (such as `SELECT myarray[*] FROM foo`) will introduce duplicates that MongoDB won’t allow. In addition, other queries change the value of `_id` (such as grouping).


**To filter on `_id`,** you must first convert a string to an object ID, by using the `OID` function. For example:

<span style="color: #0000ff">SELECT * FROM Ticket WHERE _id = OID( 'abc123')</span>


By default, the `_id` field will not appear in a result set. However, you can specify it by selecting the `_id` field. For example:

<span style="color: #0000ff">SELECT _id, u AS email FROM users as u</span>

## Considerations on wildcard *

For performance reasons, we encourage you to avoid the use of the wildcard '*' on SELECTs, instead replace it with either projections or explicit fields:

<span style="color: #0000ff">SELECT * FROM Ticket  -> </span><span style="color: #0000ff">SELECT t FROM Ticket as t</span>

<span style="color: #0000ff">SELECT * FROM Ticket  -> </span><span style="color: #0000ff">SELECT t.Ticket.name as name, t.Ticket.number as number FROM Ticket as t</span>


## Complex examples

Here we show some examples of queries that we've used in different projects:

`select `  
` t.tempo, e.lotto, count(1) `  
` from TempoDesc as t `  
` inner join `  
`   (select `  
`    c.lotto, c.epochEnd - d.minStart as durataTotale `  
`    from `  
`      (select `  
`        min(epochStart) as minStart, lotto `  
`        from Eventi1 `  
`        group by lotto) as d `  
`      inner join Eventi1 as c `  
`      on c.lotto=d.lotto ) as e `  
` on e.durataTotale <= t.tempo `  
` group by t.tempo, e.lotto`

`select `  
`      re.countrysrc,re.countrydest,re.count, iso.ISO3166.latitude , iso.ISO3166.longitude `  
`     from`  
`       ( select rx.routesexten.countrysrc as countrysrc, rx.routesexten.countrydest as countrydest, count(re.routesexten.countrysrc) as count `  
`from routesexten as rx `  
`group by rx.routesexten.countrysrc, rx.routesexten.countrydest `  
`order by count desc) as re `  
`     inner join `  
`        ISO3166_1 As iso on re.countrydest = iso.ISO3166.name`  
`     order by `  
`     re.count desc `  
`     limit 10`

# UPDATE Operations

  
You can also perform UPDATE operations in SQL. Here we will explain the peculiarities of this type of SQL directives.

**Simple syntax**

As in standard SQL, the update directive is built as follows:

<span style="color: #0000ff">UPDATE column SET attributes WHERE attributesFilter</span>


Here are some examples of basic Update operations:

Let the Restaurants ontology be our working example with the following structure:

![image](media://c2c00a87-9063-4818-b254-9b53dab6e364)

<span style="color: #0000ff">update Restaurants set Restaurant.borough="Queens" where Restaurant.borough="Manhattan"</span>

![image](media://fd3d487b-af20-4cb8-a664-02e47027fe08)

<span style="color: #0000ff">update Restaurants set Restaurant.borough="Queens", Restaurant.restaurant_id=5400 where Restaurant.borough="Manhattan" and Restaurant.borough="Dj Reynolds Pub And Restaurant"</span>

## ObjectId

If you want to filter by Mongo ObjectId, you don't need to use any special function, you just need to indicate the quoted value of the ObjectId, for example:

![image](media://4d981ca6-fe84-4620-ac8e-66961a848add)

<span style="color: #0000ff">update Restaurants set Restaurant.borough="Manhattan", Restaurant.restaurant_id=5400 where _id="5d14883e29a91330a9625d0c"</span>

![image](media://bad68ee8-3b5e-41e3-8d74-0873fc138779)

## ISODates

As for the ObjectId, if you want work with Mongo dates, you just need to put the quoted value of the date in ISODate Format (yyyy-MM-dd'T'HH:mm:ss.SSS'Z'):

![image](media://ea9f8210-8ee5-4905-ae50-3c17ea45bcb9)

<span style="color: #0000ff">update ThermometerData set ThermometerData.value=60 where ThermometerData.timestamp="2019-06-26T13:57:01.000Z"</span>

The compiled query:

![image](media://51a3dc19-8c5a-43e3-bdb9-19537352e9c2)

 Result:

![image](media://8fff914f-1aaf-438e-ad47-8b3f3ac82cb9)

## Booleans

If you want to set or filter boolean values, you have to use the SQL function Boolean() as follows:

<span style="color: #0000ff">update ThermometerData set ThermometerData.bool=Boolean(true) where ThermometerData.timestamp="2019-06-26T13:57:01.000Z"</span>

![image](media://7cf8f05b-b6de-4f57-b137-dcb5474109f1)

## Arrays

If you want to add elements to an array field, you have to use the APPEND() function.

APPEND(i, value) accepts two parameters:

- i: This is optional (default is last position in array). It indicates the position where the value is going to be pushed.
- value: This is the value being pushed to the array, it can be an object, string, number... This parameter can be also an array of values.

For example:

<span style="color: #0000ff">update ThermometerData set ThermometerData.array=APPEND("{\"Property\":\"value\"}")</span>

<span style="color: #0000ff">update ThermometerData set ThermometerData.array=APPEND([true, false])</span>

<span style="color: #0000ff">update ThermometerData set ThermometerData.array=APPEND([true, false, 45, "somestring", "2019-06-26T13:57:01.000Z"])</span>

The result is:

![image](media://6bc94d85-c2f4-490a-9376-17a8ec268396)


**Important note:** If you want to push an ISODate (Mongo date) to the array, you want to surround the isodate value by ISODate function:

<span style="color: #0000ff">update ThermometerData set ThermometerData.array=APPEND([true, false, 45, "somestring", ISODate("2019-06-26T13:57:01.000Z")])</span>

![image](media://f2527224-7083-4ea0-8275-332f6746eef4)


# DELETE operations

Delete directive works as in standard SQL.

<span style="color: #0000ff">DELETE FROM column WHERE attributes</span>

ObjectId, ISODate, and Boolean operations works as with the updates. You can't use any type of array operations yet in this directive.

Examples:

<span style="color: #0000ff">DELETE FROM Restaurants WHERE _id="5d14883e29a91330a9625d0c"</span>

<span style="color: #0000ff">DELETE FROM Restaurants WHERE timestamp="2019-06-26T13:57:01.000Z"</span>

<span style="color: #0000ff">DELETE FROM Restaurants WHERE isClosed=Boolean(true)</span>




> Macro (__confluenceADFMigrationUnsupportedContentInternalExtension__)