Socket
Socket
Sign inDemoInstall

redisk

Package Overview
Dependencies
6
Maintainers
1
Versions
15
Alerts
File Explorer

Advanced tools

Install Socket

Detect and block malicious and high-risk dependencies

Install

Comparing version 1.2.0 to 2.0.0

dist/interfaces/where-condition.d.ts

2

dist/client/client.d.ts

@@ -22,2 +22,3 @@ export interface Client {

zrange(key: string, start: number, stop: number): Promise<string[]>;
zrangebyscore(key: string, min: string, max: string, offset: number, count: number): Promise<string[]>;
zrevrange(key: string, start: number, stop: number): Promise<string[]>;

@@ -29,2 +30,3 @@ lrange(key: string, start: number, stop: number): Promise<string[]>;

hmget(key: string, properties: string[]): Promise<string[]>;
eval(...args: any[]): Promise<any[]>;
}

@@ -25,2 +25,3 @@ import { Client } from './client';

sunion(keys: string[]): Promise<string[]>;
zrangebyscore(key: string, min: string, max: string, offset: number, count: number): Promise<string[]>;
zrange(key: string, start: number, stop: number): Promise<string[]>;

@@ -33,2 +34,3 @@ zrevrange(key: string, start: number, stop: number): Promise<string[]>;

hmget(key: string, properties: string[]): Promise<string[]>;
eval(...args: any[]): Promise<any[]>;
}

@@ -103,2 +103,7 @@ "use strict";

}
zrangebyscore(key, min, max, offset, count) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.client.zrangebyscoreAsync(key, min, max, 'LIMIT', offset, count);
});
}
zrange(key, start, stop) {

@@ -139,3 +144,8 @@ return __awaiter(this, void 0, void 0, function* () {

}
eval(...args) {
return __awaiter(this, void 0, void 0, function* () {
return yield this.client.evalAsync(args);
});
}
}
exports.RedisClient = RedisClient;

1

dist/decorators/index.d.ts
export * from './entity.decorator';
export * from './index.decorator';
export * from './primary.decorator';

@@ -4,0 +3,0 @@ export * from './property.decorator';

@@ -7,3 +7,2 @@ "use strict";

__export(require("./entity.decorator"));
__export(require("./index.decorator"));
__export(require("./primary.decorator"));

@@ -10,0 +9,0 @@ __export(require("./property.decorator"));

import 'reflect-metadata';
export declare function Property(options?: {
sortable: boolean;
searchable: boolean;
searchable?: boolean;
indexed?: boolean;
}): Function;

@@ -6,21 +6,18 @@ "use strict";

function Property(options = {
sortable: false,
searchable: false,
indexed: false,
}) {
return (object, propertyName) => {
const type = Reflect.getMetadata('design:type', object, propertyName).name;
if (options.sortable && (type !== 'Date' && type !== 'Number')) {
throw new Error('You can only make Dates and numbers sortables');
}
if (metadata_storage_1.MetadataStorage.getGlobal().properties[object.constructor.name] === undefined) {
metadata_storage_1.MetadataStorage.getGlobal().properties[object.constructor.name] = [];
metadata_storage_1.MetadataStorage.getGlobal().properties[object.constructor.name] = {};
}
metadata_storage_1.MetadataStorage.getGlobal().properties[object.constructor.name].push({
metadata_storage_1.MetadataStorage.getGlobal().properties[object.constructor.name][propertyName] = {
name: propertyName,
sortable: options.sortable,
searchable: options.searchable,
indexed: options.indexed,
type,
});
};
};
}
exports.Property = Property;

@@ -6,5 +6,6 @@ import { PropertyMetadata } from './property.metadata';

primary: string;
indexes: string[];
uniques: string[];
properties: PropertyMetadata[];
properties: {
[key: string]: PropertyMetadata;
};
canBeListed: boolean;

@@ -11,0 +12,0 @@ hasOneRelations: {

@@ -15,3 +15,3 @@ "use strict";

getEntityMetadata(entityName) {
const { names, indexes, primary, properties, canBeListed, uniques, hasOneRelations } = metadata_storage_1.MetadataStorage.getGlobal();
const { names, primary, properties, canBeListed, uniques, hasOneRelations } = metadata_storage_1.MetadataStorage.getGlobal();
if (names[entityName] === undefined) {

@@ -26,3 +26,2 @@ throw new Error(entityName + ' is not an entity!');

primary: primary[entityName],
indexes: indexes[entityName],
uniques: uniques[entityName],

@@ -29,0 +28,0 @@ properties: properties[entityName],

@@ -12,3 +12,2 @@ "use strict";

canBeListed: {},
indexes: {},
uniques: {},

@@ -15,0 +14,0 @@ properties: {},

export interface PropertyMetadata {
name: string;
type: string;
sortable: boolean;
searchable: boolean;
indexed: boolean;
}

@@ -10,5 +10,2 @@ import { PropertyMetadata } from './property.metadata';

};
indexes: {
[key: string]: string[];
};
uniques: {

@@ -18,3 +15,5 @@ [key: string]: string[];

properties: {
[key: string]: PropertyMetadata[];
[key: string]: {
[key: string]: PropertyMetadata;
};
};

@@ -21,0 +20,0 @@ primary: {

@@ -6,2 +6,3 @@ import { Type } from './metadata/type';

import { ClientOptions, Client } from './client';
import { WhereCondition } from './interfaces/where-condition';
export declare class Redisk {

@@ -16,8 +17,12 @@ private readonly metadata;

count<T>(entityType: Type<T>): Promise<number>;
list<T>(entityType: Type<T>, limit?: number, offset?: number, orderBy?: OrderBy): Promise<T[]>;
find<T>(entityType: Type<T>, conditions: Condition[], limit?: number, offset?: number, type?: 'AND' | 'OR'): Promise<T[]>;
list<T>(entityType: Type<T>, where?: {
conditions: WhereCondition[];
type: 'AND' | 'OR';
}, limit?: number, offset?: number, orderBy?: OrderBy): Promise<T[]>;
search<T>(entityType: Type<T>, condition: Condition, limit: number): Promise<T[]>;
searchIds<T>(entityType: Type<T>, condition: Condition, limit: number): Promise<string[]>;
findIds<T>(entityType: Type<T>, conditions: Condition[], type?: 'AND' | 'OR'): Promise<string[]>;
listIds<T>(entityType: Type<T>, limit?: number, offset?: number, orderBy?: OrderBy): Promise<string[]>;
listIds<T>(entityType: Type<T>, where?: {
conditions: WhereCondition[];
type: 'AND' | 'OR';
}, limit?: number, offset?: number, orderBy?: OrderBy): Promise<string[]>;
delete<T>(entityType: Type<T>, id: string): Promise<void>;

@@ -29,10 +34,11 @@ getOne<T>(entityType: Type<T>, value: any, key?: string): Promise<T>;

private dropIndexes;
private dropIndex;
private dropSearchables;
private dropSortables;
private getIndexNumberKeyName;
private getIndexKeyName;
private getIndexPrefix;
private getListKeyName;
private getUniqueKeyName;
private getSortableKeyName;
private getSearchableKeyName;
private getSearchableValuePrefix;
}

@@ -14,2 +14,4 @@ "use strict";

const client_1 = require("./client");
const fs = require("fs");
const path = require("path");
class Redisk {

@@ -33,3 +35,3 @@ constructor(metadata, client) {

return __awaiter(this, void 0, void 0, function* () {
const { name, uniques, primary, canBeListed, indexes, properties, hasOneRelations } = this.metadata.getEntityMetadataFromInstance(entity);
const { name, uniques, primary, canBeListed, properties, hasOneRelations } = this.metadata.getEntityMetadataFromInstance(entity);
const hashKey = name + ':' + entity[primary];

@@ -39,3 +41,3 @@ const persistedEntity = yield this.getOne(entity.constructor, entity[primary]);

const changedFields = [];
for (const property of properties) {
for (const property of Object.keys(properties).map(key => properties[key])) {
if (entity[property.name] !== persistedEntity[property.name]) {

@@ -52,13 +54,7 @@ changedFields.push(property.name);

}
if (property.sortable) {
yield this.client.zrem(this.getSortableKeyName(name, property.name), persistedEntity[property.name]);
if (property.indexed) {
yield this.dropIndex(persistedEntity, property, persistedEntity[primary]);
}
}
}
if (indexes) {
const indexesChanged = changedFields.some(value => indexes.indexOf(value) >= 0);
if (indexesChanged) {
yield this.dropIndexes(persistedEntity, entity[primary]);
}
}
if (uniques) {

@@ -83,3 +79,3 @@ const uniquesChanged = changedFields.some(value => uniques.indexOf(value) >= 0);

const valuesToStore = [];
for (const property of properties) {
for (const property of Object.keys(properties).map(key => properties[key])) {
if (entity[property.name] !== null) {

@@ -96,23 +92,23 @@ let valueToStore = this.convertPropertyTypeToPrimitive(property, entity[property.name]);

valuesToStore.push(valueToStore);
if (property.sortable === true) {
yield this.client.zadd(this.getSortableKeyName(name, property.name), this.convertPropertyTypeToPrimitive(property, entity[property.name]), entity[primary]);
}
if (property.searchable === true) {
if (property.searchable) {
yield this.client.sadd(this.getSearchableKeyName(name, property.name), this.getSearchableValuePrefix(entity[primary]) + entity[property.name].toLowerCase());
}
if (property.indexed) {
let value = entity[property.name];
if (hasOneRelations !== undefined && hasOneRelations[property.name] && entity[property.name] !== null) {
const relatedEntity = this.metadata.getEntityMetadataFromName(hasOneRelations[property.name].entity);
value = entity[property.name][relatedEntity.primary];
}
if (value !== null) {
if (property.type === 'Date' || property.type === 'Number') {
yield this.client.zadd(this.getIndexNumberKeyName(name, property.name), this.convertPropertyTypeToPrimitive(property, entity[property.name]), entity[primary]);
}
else {
yield this.client.zadd(this.getIndexKeyName(name, property.name, value), '0', entity[primary]);
}
}
}
}
}
yield this.client.hmset(hashKey, valuesToStore);
if (indexes) {
for (const indexName of indexes) {
let value = entity[indexName];
if (hasOneRelations !== undefined && hasOneRelations[indexName] && entity[indexName] !== null) {
const relatedEntity = this.metadata.getEntityMetadataFromName(hasOneRelations[indexName].entity);
value = entity[indexName][relatedEntity.primary];
}
if (value !== null) {
yield this.client.sadd(this.getIndexKeyName(name, indexName, value), entity[primary]);
}
}
}
if (canBeListed && persistedEntity === null) {

@@ -131,5 +127,5 @@ yield this.client.rpush(this.getListKeyName(name), entity[primary]);

}
list(entityType, limit, offset, orderBy) {
list(entityType, where, limit, offset, orderBy) {
return __awaiter(this, void 0, void 0, function* () {
const ids = yield this.listIds(entityType, limit, offset, orderBy);
const ids = yield this.listIds(entityType, where, limit, offset, orderBy);
const response = [];

@@ -142,22 +138,2 @@ for (const id of ids) {

}
find(entityType, conditions, limit, offset, type = 'AND') {
return __awaiter(this, void 0, void 0, function* () {
const ids = yield this.findIds(entityType, conditions, type);
const response = [];
if (limit !== undefined || offset !== undefined) {
if (limit === undefined || offset === undefined) {
throw new Error('You must specify limit and offset, not just one arg.');
}
for (let index = offset; index < ids.length && index < (limit + offset); index++) {
response.push(yield this.getOne(entityType, ids[index]));
}
}
else {
for (const id of ids) {
response.push(yield this.getOne(entityType, id));
}
}
return response;
});
}
search(entityType, condition, limit) {

@@ -193,23 +169,5 @@ return __awaiter(this, void 0, void 0, function* () {

}
findIds(entityType, conditions, type = 'AND') {
listIds(entityType, where, limit, offset, orderBy) {
return __awaiter(this, void 0, void 0, function* () {
if (conditions.length === 0) {
throw new Error('You should at least specify one key to search');
}
const { name } = this.metadata.getEntityMetadataFromType(entityType);
const keyNames = [];
for (const condition of conditions) {
keyNames.push(this.getIndexKeyName(name, condition.key, String(condition.value)));
}
if (type === 'AND') {
return yield this.client.sinter(keyNames);
}
else {
return yield this.client.sunion(keyNames);
}
});
}
listIds(entityType, limit, offset, orderBy) {
return __awaiter(this, void 0, void 0, function* () {
const { name, canBeListed } = this.metadata.getEntityMetadataFromType(entityType);
const { name, canBeListed, properties } = this.metadata.getEntityMetadataFromType(entityType);
if (!canBeListed) {

@@ -227,4 +185,4 @@ throw new Error(entityType.name + ' can\'t be listed!');

}
if (orderBy !== undefined) {
const sortableKey = this.getSortableKeyName(name, orderBy.field);
if (orderBy !== undefined && where === undefined) {
const sortableKey = this.getIndexNumberKeyName(name, orderBy.field);
if (orderBy.strategy === 'ASC') {

@@ -237,2 +195,74 @@ return yield this.client.zrange(sortableKey, start, stop);

}
if (where !== undefined) {
if (where.conditions.length === 0) {
throw new Error('Conditions can\'t be empty');
}
const scores = {};
const equals = [];
for (const condition of where.conditions) {
if (condition.comparator === '>') {
if (!scores[condition.key]) {
scores[condition.key] = { min: '-inf', max: '+inf' };
}
scores[condition.key].min = this.convertPropertyTypeToPrimitive(properties[condition.key], condition.value);
}
if (condition.comparator === '<') {
if (!scores[condition.key]) {
scores[condition.key] = { min: '-inf', max: '+inf' };
}
scores[condition.key].max = this.convertPropertyTypeToPrimitive(properties[condition.key], condition.value);
}
if (condition.comparator === '!=' || condition.comparator === '=') {
if (properties[condition.key] === undefined || !properties[condition.key].indexed) {
throw new Error('Property ' + condition.key + ' not found or not indexed');
}
equals.push(condition);
}
}
if (Object.keys(scores).length === 1 && equals.length === 0 && orderBy === undefined) {
const scoreKey = Object.keys(scores)[0];
return yield this.client.zrangebyscore(this.getIndexNumberKeyName(name, scoreKey), scores[scoreKey].min, scores[scoreKey].max, offset ? offset : 0, limit ? limit : -1);
}
if (equals.length === 1 && Object.keys(scores).length === 0 && orderBy === undefined && equals[0].comparator != "!=") {
const condition = equals[0];
return yield this.client.zrange(this.getIndexKeyName(name, condition.key, this.convertPropertyTypeToPrimitive(properties[condition.key], condition.value)), start, stop);
}
let luaOrderBy = undefined;
if (orderBy !== undefined) {
for (const scoreKey in scores) {
if (scoreKey === orderBy.field) {
luaOrderBy = {
name: scoreKey,
min: String(scores[scoreKey].min),
max: String(scores[scoreKey].max),
strategy: orderBy.strategy,
};
}
}
if (luaOrderBy === undefined) {
luaOrderBy = {
name: orderBy.field,
strategy: orderBy.strategy,
min: "-inf",
max: "+inf",
};
}
}
let luaArgs = {
prefix: this.getIndexPrefix(name),
listKey: this.getListKeyName(name),
tempPrefix: 'temp:' + name + ':',
orderBy: luaOrderBy,
scores: Object.keys(scores).map((key) => ({
min: scores[key].min,
max: scores[key].max,
key,
})),
equals,
limit: limit ? limit : -1,
offset: offset ? offset : 0,
type: where.type,
};
return yield this.client.eval(fs.readFileSync(path.join(__dirname, './lua/complex.query.lua')), 0, JSON.stringify(luaArgs));
}
return yield this.client.lrange(keyName, start, stop);

@@ -243,3 +273,3 @@ });

return __awaiter(this, void 0, void 0, function* () {
const { name, uniques, indexes, canBeListed } = this.metadata.getEntityMetadataFromType(entityType);
const { name, uniques, canBeListed } = this.metadata.getEntityMetadataFromType(entityType);
const hashKey = name + ':' + id;

@@ -250,9 +280,6 @@ const persistedEntity = yield this.getOne(entityType, id);

}
if (indexes) {
yield this.dropIndexes(persistedEntity, id);
}
if (canBeListed) {
yield this.client.lrem(this.getListKeyName(name), 1, id);
}
yield this.dropSortables(persistedEntity);
yield this.dropIndexes(persistedEntity, id);
yield this.dropSearchables(persistedEntity);

@@ -284,10 +311,11 @@ yield this.client.del(hashKey);

const hashKey = name + ':' + id;
const result = yield this.client.hmget(hashKey, properties.map((property) => property.name));
const result = yield this.client.hmget(hashKey, Object.keys(properties).map(key => properties[key].name));
const propertiesArray = Object.keys(properties).map(key => properties[key]);
let index = 0;
for (const resultKey of result) {
if (hasOneRelations !== undefined && hasOneRelations[properties[index].name] && resultKey !== null) {
entity[properties[index].name] = yield this.getOne(hasOneRelations[properties[index].name].entityType, resultKey);
if (hasOneRelations !== undefined && hasOneRelations[propertiesArray[index].name] && resultKey !== null) {
entity[propertiesArray[index].name] = yield this.getOne(hasOneRelations[propertiesArray[index].name].entityType, resultKey);
}
else {
entity[properties[index].name] = this.convertStringToPropertyType(properties[index], resultKey);
entity[propertiesArray[index].name] = this.convertStringToPropertyType(propertiesArray[index], resultKey);
}

@@ -333,31 +361,30 @@ index++;

return __awaiter(this, void 0, void 0, function* () {
const { name, indexes, hasOneRelations } = this.metadata.getEntityMetadataFromInstance(entity);
if (indexes) {
for (const indexName of indexes) {
let value = entity[indexName];
if (hasOneRelations !== undefined && hasOneRelations[indexName]) {
const relatedEntity = this.metadata.getEntityMetadataFromName(hasOneRelations[indexName].entity);
value = entity[indexName][relatedEntity.primary];
}
yield this.client.srem(this.getIndexKeyName(name, indexName, value), id);
}
const { properties } = this.metadata.getEntityMetadataFromInstance(entity);
for (const property of Object.keys(properties).filter(key => properties[key].indexed).map(key => properties[key])) {
yield this.dropIndex(entity, property, id);
}
});
}
dropSearchables(entity) {
dropIndex(entity, property, id) {
return __awaiter(this, void 0, void 0, function* () {
const { name, properties, primary } = this.metadata.getEntityMetadataFromInstance(entity);
for (const property of properties) {
if (property.searchable === true) {
yield this.client.srem(this.getSearchableKeyName(name, property.name), this.getSearchableValuePrefix(entity[primary]) + entity[property.name].toLowerCase());
}
const { name, hasOneRelations } = this.metadata.getEntityMetadataFromInstance(entity);
let value = entity[property.name];
if (hasOneRelations !== undefined && hasOneRelations[property.name]) {
const relatedEntity = this.metadata.getEntityMetadataFromName(hasOneRelations[property.name].entity);
value = entity[property.name][relatedEntity.primary];
}
if (property.type === 'Date' || property.type === 'Number') {
yield this.client.zrem(this.getIndexNumberKeyName(name, property.name), id);
}
else {
yield this.client.zrem(this.getIndexKeyName(name, property.name, value), id);
}
});
}
dropSortables(entity) {
dropSearchables(entity) {
return __awaiter(this, void 0, void 0, function* () {
const { name, properties, primary } = this.metadata.getEntityMetadataFromInstance(entity);
for (const property of properties) {
if (property.sortable === true) {
yield this.client.zrem(this.getSortableKeyName(name, property.name), entity[primary]);
for (const property of Object.keys(properties).map(key => properties[key])) {
if (property.searchable) {
yield this.client.srem(this.getSearchableKeyName(name, property.name), this.getSearchableValuePrefix(entity[primary]) + entity[property.name].toLowerCase());
}

@@ -367,5 +394,11 @@ }

}
getIndexNumberKeyName(entityName, indexName) {
return this.getIndexPrefix(entityName) + indexName;
}
getIndexKeyName(entityName, indexName, indexValue) {
return entityName + ':index:' + indexName + ':' + indexValue;
return this.getIndexPrefix(entityName) + indexName + ':' + indexValue;
}
getIndexPrefix(entityName) {
return entityName + ':index:';
}
getListKeyName(entityName) {

@@ -377,5 +410,2 @@ return entityName + ':list';

}
getSortableKeyName(entityName, fieldName) {
return entityName + ':sort:' + fieldName;
}
getSearchableKeyName(entityName, fieldName) {

@@ -382,0 +412,0 @@ return entityName + ':search:' + fieldName;

{
"name": "redisk",
"version": "1.2.0",
"version": "2.0.0",
"description": "TypeScript ORM for Redis.",

@@ -11,3 +11,3 @@ "main": "index.js",

"prebuild": "rm -rf dist",
"build": "tsc -p tsconfig.json",
"build": "tsc -p tsconfig.json && cpx 'src/lua/*.lua' dist/lua",
"prepublish:npm": "npm run build",

@@ -31,2 +31,4 @@ "publish:npm": "npm publish --access public",

"redis",
"redisk",
"ts",
"orm"

@@ -42,2 +44,4 @@ ],

"@types/jest": "^24.9.1",
"@types/node": "^13.9.0",
"cpx": "^1.5.0",
"jest": "^25.1.0",

@@ -44,0 +48,0 @@ "ts-jest": "^25.0.0",

@@ -1,3 +0,5 @@

Redisk
=====
<h1 align="center">
<img src="https://raw.githubusercontent.com/arkerlabs/redisk/master/docs/images/logo.png" alt="Redisk">
</h1>
[![npm version](https://badge.fury.io/js/redisk.svg)](https://badge.fury.io/js/redisk)

@@ -16,3 +18,3 @@

* List entities with common filters, like limit, count and sort by.
* Find entities with multiple conditions.
* Find entities with multiple conditions ('>', '<', '=', '!=').
* Search (Similar to LIKE in SQL)

@@ -34,3 +36,3 @@ * And much more.

@Property()
public readonly name: string;
public name: string;

@@ -61,23 +63,23 @@ constructor(

- [Connection](#connection)
- - [Options](#options)
- [Options](#options)
- [Models](#models)
- - [Model definition](#model-definition)
- - [Entity](#entity)
- - [Property](#property)
- - - [Supported types](#supported-types)
- - [Primary](#primary)
- - [Unique](#unique)
- - [Index](#index)
- - [Embedding other entities](#embedding-other-entities)
- [Model definition](#model-definition)
- [Entity](#entity)
- [Property](#property)
- [Supported types](#supported-types)
- [Primary](#primary)
- [Unique](#unique)
- [Embedding other entities](#embedding-other-entities)
- [Queries](#queries)
- - [Save and Update](#save-and-update)
- - [Get by primary key](#get-by-primary-key)
- - [Get by unique key](#get-by-unique-key)
- - [Count](#count)
- - [List all](#list-all)
- - [Find all by index](#find-all-by-index)
- - - [Simple](#simple)
- - - [Multiple conditions](#multiple-conditions)
- - [Pattern matching](#pattern-matching)
- - [Delete](#delete)
- [Save](#save)
- [Update](#update)
- [Get by primary key](#get-by-primary-key)
- [Get by unique key](#get-by-unique-key)
- [Count](#count)
- [List all](#list-all)
- [List all with conditions](#find-all-by-index)
- [Simple](#simple)
- [Multiple conditions](#multiple-conditions)
- [Pattern matching](#pattern-matching)
- [Delete](#delete)

@@ -91,3 +93,3 @@

### `options`
### Options
| Property | Description |

@@ -112,3 +114,3 @@ |----------|-----------------------------------------------------------------------------------------------------------------------------------------|

```ts
@Entity('user', { canBeListed: true })
@Entity('user')
export class User {

@@ -120,19 +122,18 @@

@Property({sortable: false, searchable: true})
public readonly name: string;
@Property({searchable: true})
public name: string;
@Unique()
@Property()
public readonly email: string;
public email: string;
@Index()
@Property()
public readonly color: string;
@Property({indexed: true})
public color: string;
@HasOne(Group, {cascadeInsert: true, cascadeUpdate: true})
@Property()
public readonly group: Group;
public group: Group;
@Property({sortable: true, searchable: false})
public readonly created: Date;
@Property({indexed: true})
public created: Date;

@@ -160,3 +161,4 @@ constructor(

You can pass the option canBeListed to 'false' (Default is true) to save some space.
You can pass the option canBeListed to 'false' (Default is true) to save some space, but you will not be able to list user entities.
```ts

@@ -170,4 +172,5 @@ @Entity('user', { canBeListed: true })

The decorator `Property` is used to save the fields into redis.
Optionally, you can pass the options `sortable` if you want to use the field to sort in the 'list' method or `searchable` if you want to use pattern matching in this field.
Optionally, you can pass the options `indexed` if you want to use the field to sort or to use as a condition in the 'list' method or `searchable` if you want to use pattern matching in this field.
Both options are false by default.

@@ -179,3 +182,3 @@

@Property({sortable: true, searchable: false})
@Property({indexed: true, searchable: false})
public readonly created: Date;

@@ -220,18 +223,6 @@

### Index
Use the decorator `Index` on the fields that you want to query later with the find() method.
```ts
@Entity('user')
export class User {
@Index()
@Property()
public readonly color: string;
}
```
### Embedding other entities
You can make one to one relations with the `HasOne` decorator.
Cascade inserts and updates are supported.
Cascade inserts and updates are supported. (These options are false by default)

@@ -250,3 +241,3 @@ ```ts

### Save and update
### Save

@@ -257,2 +248,10 @@ ```ts

### Update
```ts
const user = await redisk.getOne(User, id);
user.name = 'Bar';
await redisk.save(user);
```
### Get by primary key

@@ -279,54 +278,97 @@

### List all
Returns an array of all user entities.
```ts
await redisk.list(User);
```
Returns the first 10 user entities
```ts
await redisk.list(User); // Returns an array of entities
const limit = 10;
const offset = 0;
await redis.list(User, limit, offset); // Returns 10 user entities
await redis.list(User, limit, offset);
```
await redisk.list(User, undefined, undefined, {
Return an array of user entities sorted by his creation date in descending order
```ts
await redisk.list(User, undefined, undefined, undefined, {
field: 'created',
strategy: 'DESC',
}); // Returns an array of entities sorted by his creation date in descending order
});
```
### Find all by index
### List all with conditions
#### Simple
Returns an array of users where his color is red
```ts
const conditions = [
{
key: 'color',
value: 'red',
},
];
await redisk.find(User, conditions, limit, offset); // Returns an array of entities that match the conditions
const where =
conditions: [
{
key: 'color',
value: 'red',
comparator: '=',
},
],
type: 'AND',
};
await redisk.find(User, where, limit, offset);
```
Returns an array of users where his creation date is greater than the day 23
```ts
const where =
conditions: [
{
key: 'created',
value: new Date('2020-02-23 00:00:00'),
comparator: '>',
},
],
type: 'AND',
};
await redisk.find(User, where, limit, offset);
```
#### Multiple conditions
Returns an array of entities that his color field is 'red' or 'blue'.
Warning: Using multiple conditions leads to multiple queries with table intersections, to achieve high performance queries try to reduce the results with more concise conditional.
```ts
const conditions = [
{
key: 'color',
value: 'red',
},
{
key: 'color',
value: 'blue',
},
];
await redisk.find(User, conditions, limit, offset, 'OR'); // Returns an array of entities that his color field is 'red' or 'blue'
const where =
conditions: [
{
key: 'color',
value: 'red',
comparator: '=',
},
{
key: 'color',
value: 'blue',
comparator: '=',
},
],
type: 'OR',
};
await redisk.find(User, where, limit, offset);
```
Returns an array of entities that his color field is 'red' and his food field is 'avocado'
```ts
const conditions = [
{
key: 'color',
value: 'red',
},
{
key: 'food',
value: 'avocado',
},
];
await redisk.find(User, conditions, limit, offset, 'AND'); // Returns an array of entities that his color field is 'red' and his food field is 'avocado'
const where =
conditions: [
{
key: 'color',
value: 'red',
comparator: '=',
},
{
key: 'color',
value: 'blue',
comparator: '=',
},
],
type: 'AND',
};
await redisk.find(User, where, limit, offset);
```

@@ -333,0 +375,0 @@

@@ -19,2 +19,3 @@ export interface Client {

zrange(key: string, start: number, stop: number): Promise<string[]>;
zrangebyscore(key: string, min: string, max: string, offset: number, count: number): Promise<string[]>;
zrevrange(key: string, start: number, stop: number): Promise<string[]>;

@@ -26,2 +27,3 @@ lrange(key: string, start: number, stop: number): Promise<string[]>;

hmget(key: string, properties: string[]): Promise<string[]>;
eval(...args: any[]): Promise<any[]>;
}

@@ -84,2 +84,6 @@ import { Client } from './client';

async zrangebyscore(key: string, min: string, max: string, offset: number, count: number): Promise<string[]> {
return await this.client.zrangebyscoreAsync(key, min, max, 'LIMIT', offset, count);
}
async zrange(key: string, start: number, stop: number): Promise<string[]> {

@@ -112,2 +116,6 @@ return await this.client.zrangeAsync(key, start, stop);

}
async eval(...args: any[]): Promise<any[]> {
return await this.client.evalAsync(args);
}
}
import { MetadataStorage } from '../metadata/metadata.storage';
import 'reflect-metadata';
export function Property(options: {sortable: boolean, searchable: boolean} = {
sortable: false,
export function Property(options: {searchable?: boolean, indexed?: boolean} = {
searchable: false,
indexed: false,
// tslint:disable-next-line: ban-types

@@ -12,16 +12,12 @@ }): Function {

if (options.sortable && (type !== 'Date' && type !== 'Number')) {
throw new Error('You can only make Dates and numbers sortables');
}
if (MetadataStorage.getGlobal().properties[object.constructor.name] === undefined) {
MetadataStorage.getGlobal().properties[object.constructor.name] = [];
MetadataStorage.getGlobal().properties[object.constructor.name] = {};
}
MetadataStorage.getGlobal().properties[object.constructor.name].push({
MetadataStorage.getGlobal().properties[object.constructor.name][propertyName] = {
name: propertyName,
sortable: options.sortable,
searchable: options.searchable,
indexed: options.indexed,
type,
});
};
};
}

@@ -7,7 +7,6 @@ import { PropertyMetadata } from './property.metadata';

primary: string;
indexes: string[];
uniques: string[];
properties: PropertyMetadata[];
properties: {[key: string] : PropertyMetadata};
canBeListed: boolean;
hasOneRelations: {[key: string]: HasOneOptions };
}

@@ -8,3 +8,2 @@ import { StateMetadata } from './state.metadata';

canBeListed: {},
indexes: {},
uniques: {},

@@ -11,0 +10,0 @@ properties: {},

@@ -20,3 +20,3 @@ import { MetadataStorage } from './metadata.storage';

private getEntityMetadata<T>(entityName: string): EntityMetadata {
const { names, indexes, primary, properties, canBeListed, uniques, hasOneRelations } = MetadataStorage.getGlobal();
const { names, primary, properties, canBeListed, uniques, hasOneRelations } = MetadataStorage.getGlobal();

@@ -34,3 +34,2 @@ if (names[entityName] === undefined) {

primary: primary[entityName],
indexes: indexes[entityName],
uniques: uniques[entityName],

@@ -37,0 +36,0 @@ properties: properties[entityName],

export interface PropertyMetadata {
name: string;
type: string;
sortable: boolean;
searchable: boolean;
indexed: boolean;
}

@@ -7,7 +7,6 @@ import { PropertyMetadata } from './property.metadata';

canBeListed: { [key: string]: boolean; };
indexes: { [key: string]: string[]; };
uniques: { [key: string]: string[]; };
properties: { [key: string]: PropertyMetadata[]; };
properties: { [key: string]: { [key: string]: PropertyMetadata} };
primary: { [key: string]: string; };
hasOneRelations: { [key: string]: {[key: string]: HasOneOptions } }
}

@@ -7,2 +7,5 @@ import { Type } from './metadata/type';

import { ClientOptions, Client, RedisClient } from './client';
import { WhereCondition } from './interfaces/where-condition';
import * as fs from 'fs';
import * as path from 'path';

@@ -31,3 +34,3 @@ export class Redisk {

const {name, uniques, primary, canBeListed, indexes, properties, hasOneRelations} = this.metadata.getEntityMetadataFromInstance(entity);
const {name, uniques, primary, canBeListed, properties, hasOneRelations} = this.metadata.getEntityMetadataFromInstance(entity);

@@ -40,3 +43,3 @@ const hashKey = name + ':' + entity[primary];

for (const property of properties) {
for (const property of Object.keys(properties).map(key => properties[key])) {
if (entity[property.name] !== persistedEntity[property.name]) {

@@ -59,4 +62,5 @@ changedFields.push(property.name);

}
if (property.sortable) {
await this.client.zrem(this.getSortableKeyName(name, property.name), persistedEntity[property.name]);
if (property.indexed) {
await this.dropIndex(persistedEntity, property, persistedEntity[primary]);
}

@@ -66,9 +70,2 @@ }

if (indexes) {
const indexesChanged = changedFields.some(value => indexes.indexOf(value) >= 0);
if (indexesChanged) {
await this.dropIndexes(persistedEntity, entity[primary]);
}
}
if (uniques) {

@@ -99,3 +96,3 @@ const uniquesChanged = changedFields.some(value => uniques.indexOf(value) >= 0);

const valuesToStore = [];
for (const property of properties) {
for (const property of Object.keys(properties).map(key => properties[key])) {
if (entity[property.name] !== null) {

@@ -116,12 +113,4 @@

valuesToStore.push(valueToStore);
if (property.sortable === true) {
await this.client.zadd(
this.getSortableKeyName(name, property.name),
this.convertPropertyTypeToPrimitive(property, entity[property.name]),
entity[primary],
);
}
if (property.searchable === true) {
if (property.searchable) {
await this.client.sadd(

@@ -132,18 +121,29 @@ this.getSearchableKeyName(name, property.name),

}
}
}
await this.client.hmset(hashKey, valuesToStore);
if (indexes) {
for (const indexName of indexes) {
let value = entity[indexName];
if (hasOneRelations !== undefined && hasOneRelations[indexName] && entity[indexName] !== null) {
const relatedEntity = this.metadata.getEntityMetadataFromName(hasOneRelations[indexName].entity);
value = entity[indexName][relatedEntity.primary];
if (property.indexed) {
let value = entity[property.name];
if (hasOneRelations !== undefined && hasOneRelations[property.name] && entity[property.name] !== null) {
const relatedEntity = this.metadata.getEntityMetadataFromName(hasOneRelations[property.name].entity);
value = entity[property.name][relatedEntity.primary];
}
if (value !== null) {
if (property.type === 'Date' || property.type === 'Number') {
await this.client.zadd(
this.getIndexNumberKeyName(name, property.name),
this.convertPropertyTypeToPrimitive(property, entity[property.name]),
entity[primary],
);
} else {
await this.client.zadd(
this.getIndexKeyName(name, property.name, value),
'0',
entity[primary],
);
}
}
}
if (value !== null) {
await this.client.sadd(this.getIndexKeyName(name, indexName, value), entity[primary]);
}
}
}
await this.client.hmset(hashKey, valuesToStore);

@@ -164,4 +164,13 @@ if (canBeListed && persistedEntity === null) {

async list<T>(entityType: Type<T>, limit?: number, offset?: number, orderBy?: OrderBy): Promise<T[]> {
const ids = await this.listIds(entityType, limit, offset, orderBy);
async list<T>(
entityType: Type<T>,
where?: {
conditions: WhereCondition[],
type: 'AND' | 'OR',
},
limit?: number,
offset?: number,
orderBy?: OrderBy,
): Promise<T[]> {
const ids = await this.listIds(entityType, where, limit, offset, orderBy);
const response = [];

@@ -176,28 +185,2 @@

async find<T>(
entityType: Type<T>,
conditions: Condition[],
limit?: number,
offset?: number,
type: 'AND' | 'OR' = 'AND',
): Promise<T[]> {
const ids = await this.findIds(entityType, conditions, type);
const response = [];
if (limit !== undefined || offset !== undefined) {
if (limit === undefined || offset === undefined) {
throw new Error('You must specify limit and offset, not just one arg.');
}
for (let index = offset; index < ids.length && index < (limit + offset); index++) {
response.push(await this.getOne(entityType, ids[index]));
}
} else {
for (const id of ids) {
response.push(await this.getOne(entityType, id));
}
}
return response;
}
async search<T>(entityType: Type<T>, condition: Condition, limit: number): Promise<T[]> {

@@ -241,26 +224,13 @@ const ids = await this.searchIds(entityType, condition, limit);

async findIds<T>(entityType: Type<T>, conditions: Condition[], type: 'AND' | 'OR' = 'AND'): Promise<string[]> {
if (conditions.length === 0) {
throw new Error('You should at least specify one key to search');
}
const { name } = this.metadata.getEntityMetadataFromType(entityType);
const keyNames: string[] = [];
for (const condition of conditions) {
keyNames.push(this.getIndexKeyName(name, condition.key, String(condition.value)));
}
if (type === 'AND') {
return await this.client.sinter(keyNames);
} else {
return await this.client.sunion(keyNames);
}
}
async listIds<T>(entityType: Type<T>, limit?: number, offset?: number, orderBy?: OrderBy): Promise<string[]> {
const { name, canBeListed } = this.metadata.getEntityMetadataFromType(entityType);
async listIds<T>(
entityType: Type<T>,
where?: {
conditions: WhereCondition[],
type: 'AND' | 'OR',
},
limit?: number,
offset?: number,
orderBy?: OrderBy,
): Promise<string[]> {
const { name, canBeListed, properties } = this.metadata.getEntityMetadataFromType(entityType);
if (!canBeListed) {

@@ -283,4 +253,4 @@ throw new Error(entityType.name + ' can\'t be listed!');

if (orderBy !== undefined) {
const sortableKey = this.getSortableKeyName(name, orderBy.field);
if (orderBy !== undefined && where === undefined) {
const sortableKey = this.getIndexNumberKeyName(name, orderBy.field);

@@ -295,2 +265,107 @@

if (where !== undefined) {
if (where.conditions.length === 0) {
throw new Error('Conditions can\'t be empty');
}
const scores: {[key: string]: {min: any, max: any}} = {};
const equals: WhereCondition[] = [];
for (const condition of where.conditions) {
if (condition.comparator === '>') {
if (!scores[condition.key]) {
scores[condition.key] = {min: '-inf', max: '+inf'};
}
scores[condition.key].min = this.convertPropertyTypeToPrimitive(properties[condition.key], condition.value);
}
if (condition.comparator === '<') {
if (!scores[condition.key]) {
scores[condition.key] = {min: '-inf', max: '+inf'};
}
scores[condition.key].max = this.convertPropertyTypeToPrimitive(properties[condition.key], condition.value);
}
if (condition.comparator === '!=' || condition.comparator === '=') {
if (properties[condition.key] === undefined || !properties[condition.key].indexed) {
throw new Error('Property ' + condition.key + ' not found or not indexed');
}
equals.push(condition);
}
}
if (Object.keys(scores).length === 1 && equals.length === 0 && orderBy === undefined) {
const scoreKey = Object.keys(scores)[0];
return await this.client.zrangebyscore(
this.getIndexNumberKeyName(name, scoreKey),
scores[scoreKey].min,
scores[scoreKey].max,
offset ? offset : 0,
limit ? limit : -1,
);
}
if (equals.length === 1 && Object.keys(scores).length === 0 && orderBy === undefined && equals[0].comparator != "!=") {
const condition = equals[0];
return await this.client.zrange(
this.getIndexKeyName(name, condition.key, this.convertPropertyTypeToPrimitive(properties[condition.key], condition.value)),
start,
stop,
);
}
let luaOrderBy: {
name: string,
min: string,
max: string,
strategy: 'ASC' | 'DESC'
} = undefined;
if (orderBy !== undefined) {
for (const scoreKey in scores) {
if (scoreKey === orderBy.field) {
luaOrderBy = {
name: scoreKey,
min: String(scores[scoreKey].min),
max: String(scores[scoreKey].max),
strategy: orderBy.strategy,
}
}
}
if (luaOrderBy === undefined) {
luaOrderBy = {
name: orderBy.field,
strategy: orderBy.strategy,
min: "-inf",
max: "+inf",
};
}
}
let luaArgs = {
prefix: this.getIndexPrefix(name),
listKey: this.getListKeyName(name),
tempPrefix: 'temp:' + name + ':',
orderBy: luaOrderBy,
scores: Object.keys(scores).map((key: string) => ({
min: scores[key].min,
max: scores[key].max,
key,
})),
equals,
limit: limit ? limit : -1,
offset: offset ? offset : 0,
type: where.type,
};
return await this.client.eval(
fs.readFileSync(path.join(__dirname, './lua/complex.query.lua')),
0,
JSON.stringify(luaArgs),
);
}
return await this.client.lrange(keyName, start, stop);

@@ -300,3 +375,3 @@ }

async delete<T>(entityType: Type<T>, id: string): Promise<void> {
const { name, uniques, indexes, canBeListed } = this.metadata.getEntityMetadataFromType(entityType);
const { name, uniques, canBeListed } = this.metadata.getEntityMetadataFromType(entityType);
const hashKey = name + ':' + id;

@@ -308,5 +383,2 @@

}
if (indexes) {
await this.dropIndexes(persistedEntity, id);
}

@@ -317,3 +389,4 @@ if (canBeListed) {

await this.dropSortables(persistedEntity);
await this.dropIndexes(persistedEntity, id);
await this.dropSearchables(persistedEntity);

@@ -348,9 +421,10 @@

const result = await this.client.hmget(hashKey, properties.map((property: PropertyMetadata) => property.name));
const result = await this.client.hmget(hashKey, Object.keys(properties).map(key => properties[key].name));
const propertiesArray = Object.keys(properties).map(key => properties[key]);
let index = 0;
for (const resultKey of result) {
if (hasOneRelations !== undefined && hasOneRelations[properties[index].name] && resultKey !== null) {
entity[properties[index].name] = await this.getOne(hasOneRelations[properties[index].name].entityType as any, resultKey);
if (hasOneRelations !== undefined && hasOneRelations[propertiesArray[index].name] && resultKey !== null) {
entity[propertiesArray[index].name] = await this.getOne(hasOneRelations[propertiesArray[index].name].entityType as any, resultKey);
} else {
entity[properties[index].name] = this.convertStringToPropertyType(properties[index], resultKey);
entity[propertiesArray[index].name] = this.convertStringToPropertyType(propertiesArray[index], resultKey);
}

@@ -398,19 +472,27 @@ index++;

private async dropIndexes<T>(entity: T, id: string): Promise<void> {
const { name, indexes, hasOneRelations } = this.metadata.getEntityMetadataFromInstance(entity);
if (indexes) {
for (const indexName of indexes) {
let value = entity[indexName];
if (hasOneRelations !== undefined && hasOneRelations[indexName]) {
const relatedEntity = this.metadata.getEntityMetadataFromName(hasOneRelations[indexName].entity);
value = entity[indexName][relatedEntity.primary];
}
await this.client.srem(this.getIndexKeyName(name, indexName, value), id);
}
const { properties } = this.metadata.getEntityMetadataFromInstance(entity);
for (const property of Object.keys(properties).filter(key => properties[key].indexed).map(key => properties[key])) {
await this.dropIndex(entity, property, id);
}
}
private async dropIndex<T>(entity: T, property: PropertyMetadata, id: string): Promise<void> {
const { name, hasOneRelations } = this.metadata.getEntityMetadataFromInstance(entity);
let value = entity[property.name];
if (hasOneRelations !== undefined && hasOneRelations[property.name]) {
const relatedEntity = this.metadata.getEntityMetadataFromName(hasOneRelations[property.name].entity);
value = entity[property.name][relatedEntity.primary];
}
if (property.type === 'Date' || property.type === 'Number') {
await this.client.zrem(this.getIndexNumberKeyName(name, property.name), id);
} else {
await this.client.zrem(this.getIndexKeyName(name, property.name, value), id);
}
}
private async dropSearchables<T>(entity: T): Promise<void> {
const { name, properties, primary } = this.metadata.getEntityMetadataFromInstance(entity);
for (const property of properties) {
if (property.searchable === true) {
for (const property of Object.keys(properties).map(key => properties[key])) {
if (property.searchable) {
await this.client.srem(

@@ -424,18 +506,14 @@ this.getSearchableKeyName(name, property.name),

private async dropSortables<T>(entity: T): Promise<void> {
const { name, properties, primary } = this.metadata.getEntityMetadataFromInstance(entity);
for (const property of properties) {
if (property.sortable === true) {
await this.client.zrem(
this.getSortableKeyName(name, property.name),
entity[primary],
);
}
}
private getIndexNumberKeyName(entityName: string, indexName: string): string {
return this.getIndexPrefix(entityName) + indexName;
}
private getIndexKeyName(entityName: string, indexName: string, indexValue: string): string {
return entityName + ':index:' + indexName + ':' + indexValue;
return this.getIndexPrefix(entityName) + indexName + ':' + indexValue;
}
private getIndexPrefix(entityName: string): string {
return entityName + ':index:';
}
private getListKeyName(entityName: string): string {

@@ -449,6 +527,2 @@ return entityName + ':list';

private getSortableKeyName(entityName: string, fieldName: string): string {
return entityName + ':sort:' + fieldName;
}
private getSearchableKeyName(entityName: string, fieldName: string): string {

@@ -455,0 +529,0 @@ return entityName + ':search:' + fieldName;

import { RediskTestUtils } from './utils/redisk-test-utils';
import { users } from './fixtures/users';
import { User } from './models/user.model';
import { User } from './entities/user.entity';

@@ -5,0 +5,0 @@ let utils: RediskTestUtils;

import { RediskTestUtils } from './utils/redisk-test-utils';
import { users } from './fixtures/users';
import { User } from './models/user.model';
import { User } from './entities/user.entity';
import { groups } from './fixtures/groups';

@@ -36,7 +36,7 @@

expect(await utils.redisk.getClient().sinter('user:index:color:' + users[0].color)).toEqual([]);
expect(await utils.redisk.getClient().sinter('user:index:food:' + users[0].food)).toEqual([ users[2].id ]);
expect(await utils.redisk.getClient().sinter('user:index:group:' + groups[0].id)).toEqual([ users[1].id ]);
expect(await utils.redisk.getClient().zrange('user:index:color:' + users[0].color, 0, -1)).toEqual([]);
expect(await utils.redisk.getClient().zrange('user:index:food:' + users[0].food, 0, -1)).toEqual([ users[2].id ]);
expect(await utils.redisk.getClient().zrange('user:index:group:' + groups[0].id, 0, -1)).toEqual([ users[1].id ]);
expect(await utils.redisk.getClient().zrange('user:sort:created', 0, -1)).toEqual([ users[1].id, users[2].id ]);
expect(await utils.redisk.getClient().zrange('user:index:created', 0, -1)).toEqual([ users[1].id, users[2].id ]);

@@ -43,0 +43,0 @@ expect(await utils.redisk.getClient().smembers('user:search:name')).toEqual(

@@ -1,2 +0,2 @@

import { Group } from '../models/group.model';
import { Group } from '../entities/group.entity';

@@ -3,0 +3,0 @@ export const groups = [

@@ -1,2 +0,2 @@

import { User } from '../models/user.model';
import { User } from '../entities/user.entity';
import { groups } from './groups';

@@ -3,0 +3,0 @@

import { RediskTestUtils } from './utils/redisk-test-utils';
import { users } from './fixtures/users';
import { User } from './models/user.model';
import { User } from './entities/user.entity';

@@ -5,0 +5,0 @@ let utils: RediskTestUtils;

import { RediskTestUtils } from './utils/redisk-test-utils';
import { users } from './fixtures/users';
import { User } from './models/user.model';
import { User } from './entities/user.entity';
import { groups } from './fixtures/groups';

@@ -30,3 +31,3 @@ let utils: RediskTestUtils;

it('should return all persisted entities', async () => {
expect((await utils.redisk.list(User)).sort()).toEqual(users.sort());
expect((await utils.redisk.list(User))).toEqual(users);
});

@@ -40,3 +41,3 @@ });

const offset = 1;
expect((await utils.redisk.list(User, limit, offset)).sort()).toEqual([users[1], users[2]].sort());
expect((await utils.redisk.list(User, undefined, limit, offset)).sort()).toEqual([users[1], users[2]].sort());
});

@@ -47,3 +48,3 @@ });

it('should return persisted entities sorted', async () => {
const response = await utils.redisk.list(User, undefined, undefined, { field: 'created', strategy: 'ASC' });
const response = await utils.redisk.list(User, undefined, undefined, undefined, { field: 'created', strategy: 'ASC' });
expect(response).toEqual(users);

@@ -55,3 +56,3 @@ });

it('should return persisted entities sorted', async () => {
const response = await utils.redisk.list(User, 2, 1, { field: 'created', strategy: 'ASC' });
const response = await utils.redisk.list(User, undefined, 2, 1, { field: 'created', strategy: 'ASC' });
expect(response).toEqual([users[1], users[2]]);

@@ -63,5 +64,304 @@ });

it('should return persisted entities sorted', async () => {
const response = await utils.redisk.list(User, undefined, undefined, { field: 'created', strategy: 'DESC' });
expect(response).toEqual(users.reverse());
const response = await utils.redisk.list(User, undefined, undefined, undefined, { field: 'created', strategy: 'DESC' });
expect(response).toEqual([users[4], users[3], users[2], users[1], users[0]]);
});
});
describe('List with one condition', () => {
it('should return filtered persisted entities', async () => {
expect((await utils.redisk.list(
User,
{
conditions: [
{
key: 'color',
value: 'red',
comparator: '=',
},
],
type: 'AND',
},
))).toEqual([users[0], users[4]]);
});
});
describe('List with relation index', () => {
it('should return filtered persisted entities', async () => {
expect((await utils.redisk.list(
User,
{
conditions: [
{
key: 'group',
value: groups[0].id,
comparator: '=',
}
],
type: 'AND',
},
))).toEqual([users[0], users[1]]);
});
});
describe('List with one condition with limit and offset', () => {
it('should return filtered persisted entities', async () => {
expect(await utils.redisk.list(
User,
{
conditions: [
{
key: 'color',
value: 'blue',
comparator: '=',
}
],
type: 'AND',
},
1,
2,
)).toEqual([users[1]]);
});
});
describe('List with two AND condition and order by', () => {
it('should return filtered persisted entities', async () => {
expect((await utils.redisk.list(
User,
{
conditions: [
{
key: 'color',
value: 'blue',
comparator: '=',
},
{
key: 'food',
value: 'tofu',
comparator: '=',
},
],
type: 'AND'
},
undefined,
undefined,
{
field: 'created',
strategy: 'DESC',
},
))).toEqual([users[3], users[2]]);
});
});
describe('List with two AND condition', () => {
it('should return filtered persisted entities', async () => {
expect((await utils.redisk.list(
User,
{
conditions: [
{
key: 'color',
value: 'blue',
comparator: '=',
},
{
key: 'food',
value: 'tofu',
comparator: '=',
},
],
type: 'AND'
},
))).toEqual([users[2], users[3]]);
});
});
describe('List with two OR condition', () => {
it('should return filtered persisted entities', async () => {
expect((await utils.redisk.list(
User,
{
conditions: [
{
key: 'color',
value: 'red',
comparator: '=',
},
{
key: 'food',
value: 'avocado',
comparator: '='
},
],
type: 'OR',
},
))).toEqual([users[4], users[0], users[1]]);
});
});
describe('List with greater than condition', () => {
it('should return filteres entities', async () => {
expect(await utils.redisk.list(
User,
{
conditions: [
{
key: 'created',
value: new Date('2020-02-23 20:35:00'),
comparator: '>'
},
],
type: 'AND'
}
)).toEqual([users[3], users[4]]);
});
});
describe('List with greater than and equal condition', () => {
it('should return filteres entities', async () => {
expect(await utils.redisk.list(
User,
{
conditions: [
{
key: 'created',
value: new Date('2020-02-23 20:35:00'),
comparator: '>'
},
{
key: 'color',
value: 'blue',
comparator: '='
},
],
type: 'AND'
}
)).toEqual([users[3]]);
});
});
describe('List with greater than condition with limit and offset', () => {
it('should return filteres entities', async () => {
expect(await utils.redisk.list(
User,
{
conditions: [
{
key: 'created',
value: new Date('2020-02-23 20:35:00'),
comparator: '>'
},
],
type: 'AND'
},
1,
1,
)).toEqual([users[4]]);
});
});
describe('List with less than condition', () => {
it('should return filteres entities', async () => {
expect(await utils.redisk.list(
User,
{
conditions: [
{
key: 'created',
value: new Date('2020-02-22 10:34:00'),
comparator: '<'
},
],
type: 'AND'
}
)).toEqual([users[0]]);
});
});
describe('List with less than and greater than condition', () => {
it('should return filteres entities', async () => {
expect(await utils.redisk.list(
User,
{
conditions: [
{
key: 'created',
value: new Date('2020-02-22 10:34:00'),
comparator: '>'
},
{
key: 'created',
value: new Date('2020-02-22 13:10:00'),
comparator: '<'
},
],
type: 'AND'
}
)).toEqual([users[1]]);
});
});
describe('List with order by and condition', () => {
it('should return filteres entities', async () => {
expect(await utils.redisk.list(
User,
{
conditions: [
{
key: 'food',
value: 'tofu',
comparator: '='
},
],
type: 'AND'
},
1,
0,
{
field: 'created',
strategy: 'DESC'
},
)).toEqual([users[3]]);
});
});
describe('List with order by and condition not equal', () => {
it('should return filteres entities', async () => {
expect(await utils.redisk.list(
User,
{
conditions: [
{
key: 'food',
value: 'tofu',
comparator: '!='
},
],
type: 'AND'
},
1,
0,
{
field: 'created',
strategy: 'DESC'
},
)).toEqual([users[4]]);
});
});
describe('List with condition not equal', () => {
it('should return filteres entities', async () => {
expect(await utils.redisk.list(
User,
{
conditions: [
{
key: 'food',
value: 'tofu',
comparator: '!='
},
],
type: 'AND'
},
)).toEqual([users[4], users[1]]);
});
});
import { RediskTestUtils } from './utils/redisk-test-utils';
import { users } from './fixtures/users';
import { User } from './models/user.model';
import { Group } from './models/group.model';
import { User } from './entities/user.entity';
import { Group } from './entities/group.entity';

@@ -59,6 +59,6 @@ let utils: RediskTestUtils;

expect(await utils.redisk.getClient().sinter('user:index:color:' + color)).toEqual([ users[0].id, id ]);
expect(await utils.redisk.getClient().sinter('user:index:food:' + food)).toEqual([ id ]);
expect(await utils.redisk.getClient().zrange('user:index:color:' + color, 0, -1)).toEqual([ users[0].id, id ]);
expect(await utils.redisk.getClient().zrange('user:index:food:' + food, 0, -1)).toEqual([ id ]);
expect(await utils.redisk.getClient().zrange('user:sort:created', 0, -1)).toEqual([ id, users[0].id ]);
expect(await utils.redisk.getClient().zrange('user:index:created', 0, -1)).toEqual([ id, users[0].id ]);

@@ -122,7 +122,7 @@ expect(await utils.redisk.getClient().smembers('user:search:name')).toEqual(

expect(await utils.redisk.getClient().sinter('user:index:color:' + color)).toEqual([ users[0].id ]);
expect(await utils.redisk.getClient().sinter('user:index:color:' + newColor)).toEqual([ id ]);
expect(await utils.redisk.getClient().sinter('user:index:food:' + food)).toEqual([ id ]);
expect(await utils.redisk.getClient().zrange('user:index:color:' + color, 0, -1)).toEqual([ users[0].id ]);
expect(await utils.redisk.getClient().zrange('user:index:color:' + newColor, 0, -1)).toEqual([ id ]);
expect(await utils.redisk.getClient().zrange('user:index:food:' + food, 0, -1)).toEqual([ id ]);
expect(await utils.redisk.getClient().zrange('user:sort:created', 0, -1)).toEqual([ users[0].id, id ]);
expect(await utils.redisk.getClient().zrange('user:index:created', 0, -1)).toEqual([ users[0].id, id ]);

@@ -161,7 +161,7 @@ expect(await utils.redisk.getClient().smembers('user:search:name')).toEqual(

expect(await utils.redisk.getClient().sinter('user:index:color:' + color)).toEqual([ users[0].id ]);
expect(await utils.redisk.getClient().sinter('user:index:color:' + newColor)).toEqual([ id ]);
expect(await utils.redisk.getClient().sinter('user:index:food:' + food)).toEqual([ id ]);
expect(await utils.redisk.getClient().zrange('user:index:color:' + color, 0, -1)).toEqual([ users[0].id ]);
expect(await utils.redisk.getClient().zrange('user:index:color:' + newColor, 0, -1)).toEqual([ id ]);
expect(await utils.redisk.getClient().zrange('user:index:food:' + food, 0, -1)).toEqual([ id ]);
expect(await utils.redisk.getClient().zrange('user:sort:created', 0, -1)).toEqual([ users[0].id, id ]);
expect(await utils.redisk.getClient().zrange('user:index:created', 0, -1)).toEqual([ users[0].id, id ]);

@@ -168,0 +168,0 @@ expect(await utils.redisk.getClient().smembers('user:search:name')).toEqual(

import { RediskTestUtils } from './utils/redisk-test-utils';
import { users } from './fixtures/users';
import { User } from './models/user.model';
import { User } from './entities/user.entity';

@@ -5,0 +5,0 @@ let utils: RediskTestUtils;

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc