项目作者: echebbi

项目描述 :
Heterogeneous and type-safe table structure
高级语言: Java
项目地址: git://github.com/echebbi/datatable.git
创建时间: 2017-11-11T12:40:41Z
项目社区:https://github.com/echebbi/datatable

开源协议:Apache License 2.0

下载


Heterogeneous and type-safe tables

build status codecov

Motivation

This project is aimed to experiment with the implementation of heterogeneous yet type-safe table structure.

As of now, the API makes able to:

  • create heterogeneous tables,
  • modify them and their content,
  • query their content in a type-safe way.

Full example

Because code is better than words, here is a quick overview of the API:

  1. ColumnId<Integer> AGE = id("age", Integer.class);
  2. ColumnId<String> NAME = id("name", String.class);
  3. Table people = new DataTable();
  4. people.columns()
  5. .create(NAME, "Luc", "Baptiste", "Marie")
  6. .create(AGE, 23, 32, 42);
  7. Table adultsWhoseNameEndsWithLetterE = people.filter(row ->
  8. row.get(AGE) > 18 &&
  9. row.get(NAME).startsWith("E")
  10. );

Quick start

Understanding ids

Ids can be seen as references, as keys used to identify specific columns within a table.
They are tricks used to provide a type-safe way to access the content of a table.

Basically, they are simple data structure containing:

  • a type, that is the Java class of the column’s elements
  • a header, that is the name of the column.

An id is represented by the ColumnId class and can be created with the id static method :

  1. // name is an id that matches any column containing Strings and which header is "col1"
  2. ColumnId<String> name = ColumnId.id("col1", String.class);

In the following document, ids are used everywhere.
However, although they are the advised way to deal with a table, they can always be replaced by indexes or column names.

Populating a Table

Creation of a new Table consists of an easy one-liner :

  1. Table people = new DataTable();

A table can be filled either by adding new columns :

  1. people.columns()
  2. .create("Name", String.class, "Luc", "Baptiste", "Marie")
  3. .create("Age", Integer.class, 23, 32, 42);

or by adding new rows :

  1. people.rows()
  2. .create("Julie", 12)
  3. .create("Mathieu", 67);

The table resulting of the above statements is the following :

  1. +----------+-----+
  2. | Name | Age |
  3. +----------+-----|
  4. | Luc | 23 |
  5. | Baptiste | 32 |
  6. | Marie | 42 |
  7. | Julie | 12 |
  8. | Mathieu | 67 |
  9. +----------+-----+

Depopulating a Table

A table can be emptied either by removing columns :

  1. people.columns()
  2. .remove("age");

or by removing rows :

  1. people.rows()
  2. .remove(4)
  3. .remove(2);

Notice how the last row has been removed first in order to avoid bugs related to changes of ids. The table now looks like :

  1. +----------+
  2. | Name |
  3. +----------+
  4. | Luc |
  5. | Baptiste |
  6. | Julie |
  7. +----------+

Querying a Table

The filter method makes easy to retrieve the rows of a table that match a specific criterion:

  1. import static fr.kazejiyu.generic.datatable.core.impl.ColumnId.*;
  2. public class Main {
  3. // Reference table's columns
  4. private static final ColumnId<Integer> AGE = id("age", Integer.class);
  5. private static final ColumnId<String> NAME = id("name", String.class);
  6. Table adultsWhoseNameEndsWithLetterE(Table people) {
  7. return people.filter(row ->
  8. row.get(AGE) > 18 &&
  9. row.get(NAME).startsWith("E")
  10. );
  11. }
  12. }

SQL-like DSL

For more complex cases, the API also brings a SQL-like DSL that makes possible to apply a same filter to multiple columns. It can be used as follows :

  1. import static fr.kazejiyu.generic.datatable.core.impl.ColumnId.*;
  2. public class Main {
  3. // Reference table's columns
  4. private static final ColumnId<Integer> AGE = id("age", Integer.class);
  5. private static final ColumnId<String> NAME = id("name", String.class);
  6. private static final ColumnId<String> SURENAME = id("surename", String.class);
  7. // Retrieve all european adults whose both name and surename ends with the letter "e"
  8. Table adultsWhoseNameEndsWithLetterE(Table people) {
  9. return Query
  10. .from(people)
  11. .where(s(NAME, SURENAME)).endsWith("e")
  12. .and(AGE).gt(18)
  13. .and(COUNTRY).match(Country::isInEurope)
  14. .select();
  15. }
  16. }

Notice how the s method is used to specify that the columns concerning by the WHERE clause contain String instances.
That makes possible to use tailored methods (such as endsWith in the above example) ; because of Java’s type erasure, this trick is mandatory.
This static method is defined in the ColumnId class, as well as the methods:

  • n: used to apply a same filter to several columns of numbers,
  • b: used to apply a same filter to several columns of booleans.