Examples

Example (many-to-many)

This example presents a very simple “student” database where students are enrolled in classes, so that the students and classes are in a many-to-many relationship. The example has tables and fields that are intentionally poorly named so as to demonstrate the aliasing features of the database modeling language.

Get PostgreSQL running in a docker container:

docker pull postgres
docker run --rm --name pg-docker -e POSTGRES_PASSWORD=docker -d -p 5432:5432 postgres

Run the PostgreSQL terminal to create a database (with password “docker”):

psql -h localhost -U postgres -d postgres

which can be installed if necessary with the command

sudo apt-get install postgresql-client

Create a simple database by copy-pasting the following (yes, all in one shot) at the psql prompt:

CREATE DATABASE student;

CREATE TABLE students (
  id SERIAL PRIMARY KEY,
  stu_name TEXT
);

CREATE TABLE class (
  id SERIAL PRIMARY KEY,
  name TEXT
);

CREATE TABLE student_class (
  studentid INTEGER REFERENCES students (id),
  classid INTEGER REFERENCES class (id),
  year INTEGER,
  semester TEXT,
  grade TEXT
);

INSERT INTO students (id, stu_name) VALUES
  (1, 'John'),
  (2, 'Debbie');

INSERT INTO class (id, name) VALUES
  (1, 'English'),
  (2, 'Maths'),
  (3, 'Spanish'),
  (4, 'Biology'),
  (5, 'Science'),
  (6, 'Programming'),
  (7, 'Law'),
  (8, 'Commerce'),
  (9, 'Physical Education');

INSERT INTO student_class (studentid, classid, year, semester, grade) VALUES
  (1, 3, 2019, 'fall', 'B+'),
  (1, 5, 2018, 'winter', 'A'),
  (1, 9, 2019, 'summer', 'F'),
  (2, 1, 2018, 'fall', 'A+'),
  (2, 4, 2019, 'winter', 'B-'),
  (2, 5, 2018, 'summer', 'A-'),
  (2, 9, 2019, 'fall', 'B+');

The above database can be modelled using the following diagram:

Student ER Diagram

Create a file called student-data-model and copy-paste the following text into it:

entity class {
 *id: integer
  name: text
  students: [student] (enrollment)
}

entity student (students) {
 *id: integer
  name (stu_name): text
  classes: [class] (enrollment)
}

entity enrollment (student_class) {
  student (studentid): student
  class (classid): class
  year: integer
  semester: text
  grade: text
}

Run the following TypeScript program:

import { OQL } from '@vinctus/oql'
import fs from 'fs'

const oql = new OQL(
  fs.readFileSync('student-data-model').toString(),
  'localhost',
  5432,
  'postgres',
  'postgres',
  'docker',
  false,
  0,
  10
)

oql
  .queryMany(
    `
    student {
      * classes { * students <name> } <name>
    } [name = 'John']
    `
  )
  .then((res: any) => console.log(JSON.stringify(res, null, 2)))

Output:

[
  {
    "id": 1,
    "name": "John",
    "classes": [
      {
        "id": 9,
        "name": "Physical Education",
        "students": [
          {
            "id": 2,
            "name": "Debbie"
          },
          {
            "id": 1,
            "name": "John"
          }
        ]
      },
      {
        "id": 5,
        "name": "Science",
        "students": [
          {
            "id": 2,
            "name": "Debbie"
          },
          {
            "id": 1,
            "name": "John"
          }
        ]
      },
      {
        "id": 3,
        "name": "Spanish",
        "students": [
          {
            "id": 1,
            "name": "John"
          }
        ]
      }
    ]
  }
]

The query

student {
  * classes { * students <name> } <name>
} [name = 'John']

in the above example is asking for the names of the students enrolled only in the classes in which John is enrolled. Also, the query is asking for the classes and the students in each class to be ordered by class name and student name, respectively. The * operator is a wildcard that stands for all attributes that do not result in an array value.

With entities that are in a many-to-many relationship, it is possible to query the junction entity that is between them.

Run the following TypeScript program:

import { OQL } from '@vinctus/oql'
import fs from 'fs'

const oql = new OQL(
  fs.readFileSync('student-data-model').toString(),
  'localhost',
  5432,
  'postgres',
  'postgres',
  'docker',
  false,
  0,
  10
)

oql
  .queryMany(
    `
    enrollment {
      name: student.name
      classes: count(*)
      GPA: avg(
        case
          when grade = 'A-' then 3.7
          when grade = 'A' then 4
          when grade = 'A+' then 4.3
          when grade = 'B-' then 2.7
          when grade = 'B' then 3
          when grade = 'B+' then 3.3
          when grade = 'F' then 0
        end
        )
    } /student.id/
    `
  )
  .then((res: any) => console.log(JSON.stringify(res, null, 2)))

Output:

[
  {
    "name": "Debbie",
    "classes": 4,
    "GPA": 3.5
  },
  {
    "name": "John",
    "classes": 3,
    "GPA": 2.433333333333333
  }
]

The query

enrollment {
  name: student.name
  classes: count(*)
  GPA: avg(
    case
      when grade = 'A-' then 3.7
      when grade = 'A' then 4
      when grade = 'A+' then 4.3
      when grade = 'B-' then 2.7
      when grade = 'B' then 3
      when grade = 'B+' then 3.3
      when grade = 'F' then 0
    end
    )
} /student.id/

says, “group all the students who are enrolled and show the name of each enrolled student, how many classes they are enrolled in, and their grade point average”.