An open API service indexing awesome lists of open source software.

https://github.com/leoc11/elcy

ORM for Typescript and Javascript with Linq-like query syntax.
https://github.com/leoc11/elcy

database elcy entity-framework javascript linq linq-to-entities mssql orm sqlite sqlserver typescript

Last synced: 10 months ago
JSON representation

ORM for Typescript and Javascript with Linq-like query syntax.

Awesome Lists containing this project

README

          

# Elcy
[![codecov](https://codecov.io/gh/leoc11/elcy/branch/master/graph/badge.svg)](https://codecov.io/gh/leoc11/elcy)
[![Build Status](https://travis-ci.org/leoc11/elcy.svg?branch=master)](https://travis-ci.org/leoc11/elcy)
[![CLA assistant](https://cla-assistant.io/readme/badge/leoc11/elcy)](https://cla-assistant.io/leoc11/elcy)
[![Dependency Status](https://david-dm.org/leoc11/elcy.svg)](https://david-dm.org/leoc11/elcy)
[![devDependency Status](https://david-dm.org/leoc11/elcy/dev-status.svg)](https://david-dm.org/leoc11/elcy#info=devDependencies)

Elcy is an [ORM](https://en.wikipedia.org/wiki/Object-relational_mapping)
for typescript and javascript. Elcy is highly influenced by [Entity Framework](https://www.asp.net/entity-framework) and [NHibernate](http://nhibernate.info/).

## Installation
To be updated...

## Supported Database
- Sql Server
- Sqlite (not yet)

## How to use

### Create the Model

Entity on your model is mark with `@Entity` class decorator.

To add database columns, you simply need to add decorator to your entity properties. Elcy has several column decorator for defining your column with specific column type:
- `@StringColumn` : string column type.
- `@BooleanColumn` : boolean column type.
- `@NumberColumn` : integer column type.
- `@DecimalColumn` : decimal column type.
- `@ApproximateNumberColumn` : approximate number column type. ex: float
- `@IdentifierColumn` : uuid column type.
- `@DateColumn` : date column type.
- `@EnumColumn` : *not implemented yet*
- `@EmbeddedColumn` : *not implemented yet*

Here several other decorator for entity property:
- `@CreatedDate`: a date column type used to store entity creation date.
- `@ModifiedDate`: a date column type used to store entity last modified date.
- `@ColumnDescription`: add description to column.
- `@DeletedColumn`: a boolean column type used for soft delete indicator.
- `@NullableColumn`: mark column nullable.
- `@PrimaryKey`: mark column as one of the entity primary key.

Example:
```typescript
import {Entity} from "elcy/Decorator/Entity";
import { PrimaryKey } from "Elcy/Decorator/Column/PrimaryKey";
import { NumberColumn } from "Elcy/Decorator/Column/NumberColumn";
import { DateColumn } from "Elcy/Decorator/Column/DateColumn";
import { IdentifierColumn } from "Elcy/Decorator/Column/IdentifierColumn";
import { UUID } from "Elcy/Data/UUID";

@Entity()
export class Order {
@PrimaryKey()
@IdentifierColumn()
public OrderId: UUID;

@NumberColumn({ columnType: "bigint" })
public Amount: number;

@DateColumn()
public OrderDate: Date;
@CreatedDate()
public CreatedDate: Date;
@ModifiedDate()
public ModifiedDate: Date;
@DeletedColumn()
public isDeleted: boolean;
}
```

### Create a Context

a Context represents a session with the database, allowing us to query and save data. Define a context that derives from `Elcy/Data/DbContext` and exposes a typed DbSet for each class in our model. Elcy has defined DbContext that you could used for each support db under `Elcy/Driver`.

Example:
```typescript
import { MssqlDbContext } from "Elcy/Driver/Mssql";

export class MyDb extends MssqlDbContext {
constructor() {
super(() => new MssqlDriver({
host: "localhost\\SQLSERVER",
database: "mydb",
port: 1433, // example
user: "xxx",
password: "xxx",
}));
}
public entityTypes = [Order]; // all entities that will be loaded using this context.
public orders: DbSet = this.set(Order); // exposed typed DbSet for Order model.
}
```

### Reading Data

Elcy used Linq-like syntax to read data from database. Example:

```typescript
(
async() => {
const db = new MyDb();

// select top 10 order with Amount > 10 order by amount desc.
const orders = await db.orders.take(10).where(o => o.Amount > 10).orderBy([o => o.Amount, "DESC"]).toArray();

// count all orders
const count = await db.orders.count();

// where with parameter
const maxAmount = 10;
const count = await db.orders.parameters({ maxAmount }).where(o => o.Amount < maxAmount).count();
}
)();
```

Below are the supported query expression syntax:
- `where(predicate: (item: T) => boolean): Queryable`
- `distinct(): Queryable`
- `include(...includes: Array<(item: T) => any>): Queryable`
- `orderBy(...selectors: IQueryableOrderDefinition[]): Queryable`
- `skip(skip: number): Queryable`
- `take(take: number): Queryable`
- `select(selector: ((item: T) => TReturn)): Queryable`
- `selectMany(selector: (item: T) => TReturn[]): Queryable`
- `groupBy(keySelector: (item: T) => K): Queryable>`: limitation. groupBy(..).toArray() will not work.
- `union(array2: Queryable, isUnionAll?: boolean): Queryable`
- `intersect(array2: Queryable): Queryable`
- `except(array2: Queryable): Queryable`
- `toArray()`
- `sum()`
- `count()`
- `max()`
- `min()`
- `avg()`
- `all()`
- `any()`
- `first()`
- `innerJoin`: *not yet supported*
- `rightJoin`: *not yet supported*
- `leftJoin`: *not yet supported*
- `fullJoin`: *not yet supported*
- `pivot`: *not yet supported*

### Writing Data

Create:
```typescript
(
async() => {
const db = new MyDb();

// example 1: create and attach
const order = new Order();
order.OrderId = UUID.new();
order.Amount = 10;
db.add(order);

// example 2
const order2 = db.orders.new(UUID.new());
order2.Amount = 10;

db.saveChanges();
}
)();
```

Update
```typescript
(
async() => {
const db = new MyDb();

const order = db.orders.first();
order.Amount += 1;

await db.saveChanges();
}
)();
```

Delete
```typescript
(
async() => {
const db = new MyDb();

const order = db.orders.first();
db.delete(order);
await db.saveChanges();
}
)();
```

### Transaction

```typescript
(
async() => {
const db = new MyDb();

await db.transaction(o => {
// your code goes here
});
}
)();
```