query-engine.ts

Non-parsing query-engine on top of key-value storage
git clone git://git.finwo.net/lib/query-engine.ts
Log | Files | Refs | README | LICENSE

README.md (3710B)


      1 query-engine.ts
      2 ===============
      3 
      4 Intended usage:
      5 
      6 ```ts
      7 
      8 
      9 // model/user.ts
     10 
     11 import { Document, ID, Version, String, Index, CompositeIndex } from '@finwo/query-engine/decorators';
     12 
     13 @Document({ adapter?: customAdapterInstance })
     14 @CompositeIndex('otherIndexName', {
     15     fields: ['fieldA', 'fieldB']
     16     order?: 'ascending',
     17     unique?: true/false,
     18 })
     19 export class User {
     20 
     21   @Version()
     22   static _version = 0;
     23 
     24   static _migrate(record: Record<??>): User {
     25     // Code to update old version to current
     26   }
     27 
     28   // TODO: define proper annotations
     29   @ID()
     30   _id: string;
     31 
     32   @String()
     33   @Index('indexName'[, { order?: 'ascending', unique?: true/false }])
     34   username: string;
     35 
     36   ...
     37 }
     38 
     39 // main.ts
     40 import QueryEngine from '@finwo/query-engine';
     41 
     42 import { LevelAdapter } from '@finwo/query-engine';
     43 
     44 import { User } from 'model/user.ts';
     45 
     46 const queryEngine = new QueryEngine({
     47     defaultAdapter: new LevelAdapter({
     48         datadir: __dirname + '/data/'
     49     }),
     50     documents: {
     51         documentTypeName: {
     52             model: User,
     53         }
     54     },
     55 });
     56 
     57 // Runtime addition:
     58 queryEngine.defineTable(...)
     59 queryEngine.registerAdapter('name', adapterInstance)
     60 
     61 const data = await queryEngine
     62 
     63     // Start query builder
     64     .query('documentTypeName')
     65 
     66     // Initial result set
     67     .useIndex('indexName', {
     68         strategy: 'merge-strategy TBD: outer (keep both), inner (keep overlap), union, etc....',
     69         filter: {
     70             fieldName: {
     71                 operator: 'lte, gte, ...',
     72                 value: 'some-value',
     73             }
     74         }
     75     })
     76 
     77     // Future feature
     78     // Define symbol to search without running through user-code
     79     .defineSymbol('symbolName', 'fieldName')
     80 
     81     // Filter down further
     82     .useIndex('otherIndex', {
     83         strategy: 'inner',
     84         filter: {
     85             otherField: {
     86                 operator: 'in',
     87                 symbol: 'symbolName',
     88             }
     89         }
     90     })
     91 
     92     // TODO: how to do joins and merges with other document types?
     93 
     94     // And start the query, returns promise to the data
     95     .values();
     96 
     97 // Inserting documents
     98 await queryEngine
     99     .insert('documentTypeName', entity instanceof User?, {
    100         upsert: false, // Throw error if already exists, default
    101         upsert: true, // Quietly overwrite if already exists
    102     })
    103     .insert('documentTypeName', ...)
    104 
    105 // Updating single document
    106 await queryEngine
    107     .update('documentTypeName', 'documentId', {
    108         ..newData
    109     });
    110 
    111 // Updating multiple documents, fixed value
    112 await queryEngine
    113     .query('documentTypeName')
    114     .useIndex(...)
    115     .update({
    116         ...newData
    117     });
    118 
    119 // Updating multiple documents, dynamic value
    120 await queryEngine
    121     .query('documentTypeName')
    122     .useIndex(...)
    123     .update(document => {
    124         // Do some actions here
    125         document.profit = document.income - document.costs;
    126         return document; // <-- something else maybe?
    127     });
    128 
    129 // Deleting documents by _id
    130 await queryEngine
    131     .delete('documentTypeName', 'documentId');
    132 
    133 // Deleting documents by query
    134 await queryEngine
    135     .query('documentTypeName')
    136     .useIndex(...)
    137     .delete()
    138 
    139 ```
    140 
    141 On-disk/storage, per table, we get something like:
    142 
    143 ```
    144 
    145 /cfg
    146     {
    147         indexes: {
    148             _id: {
    149                 fields: ['_id'],
    150                 order: 'ascending',
    151                 unique: true,
    152             },
    153             indexName: {
    154                 fields: ['username'],
    155                 order: 'ascending',
    156                 unique: true,
    157             },
    158         }
    159         // more to be defined
    160     }
    161 
    162 /idx/<indexName>/<hex-encoded-value>
    163     <documentId>
    164     <documentId>
    165     ...
    166 
    167 /obj/<hex-encoded-object-id>
    168     {
    169         _id: <uuid>,
    170         _version: 123,
    171         username: 'alice'
    172     },
    173 
    174 ```