{"id":17124907,"url":"https://github.com/shellyln/open-soql","last_synced_at":"2025-04-13T06:13:15.466Z","repository":{"id":42755078,"uuid":"279294784","full_name":"shellyln/open-soql","owner":"shellyln","description":"Open source implementation of the SOQL.","archived":false,"fork":false,"pushed_at":"2023-03-14T18:17:27.000Z","size":1741,"stargazers_count":16,"open_issues_count":6,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-10-03T23:35:30.133Z","etag":null,"topics":["dml","graph-query","javascript","library","map-reduce","object-query","query-engine","resolvers","soql","sql","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/shellyln.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2020-07-13T12:20:26.000Z","updated_at":"2024-01-14T00:01:30.000Z","dependencies_parsed_at":"2023-02-06T12:16:14.840Z","dependency_job_id":null,"html_url":"https://github.com/shellyln/open-soql","commit_stats":null,"previous_names":[],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellyln%2Fopen-soql","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellyln%2Fopen-soql/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellyln%2Fopen-soql/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/shellyln%2Fopen-soql/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/shellyln","download_url":"https://codeload.github.com/shellyln/open-soql/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":219846950,"owners_count":16556418,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dml","graph-query","javascript","library","map-reduce","object-query","query-engine","resolvers","soql","sql","typescript"],"created_at":"2024-10-14T18:43:39.769Z","updated_at":"2024-10-14T18:43:41.184Z","avatar_url":"https://github.com/shellyln.png","language":"TypeScript","readme":"# Open SOQL\n\n\u003cimg src=\"https://shellyln.github.io/assets/image/open-soql-logo.svg\" title=\"Open SOQL logo\" style=\"width: 100px\"\u003e\n\nOpen source implementation of the [SOQL](https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql.htm).  \nYou can query everything you want by defining the resolvers.\n\nSOQL is an object-oriented query language that allows you to query related data based on an object graph.\n\n\n[![npm](https://img.shields.io/npm/v/open-soql.svg)](https://www.npmjs.com/package/open-soql)\n[![GitHub release](https://img.shields.io/github/release/shellyln/open-soql.svg)](https://github.com/shellyln/open-soql/releases)\n[![.github/workflows/test.yml](https://github.com/shellyln/open-soql/workflows/.github/workflows/test.yml/badge.svg)](https://github.com/shellyln/open-soql/actions)\n[![GitHub forks](https://img.shields.io/github/forks/shellyln/open-soql.svg?style=social\u0026label=Fork)](https://github.com/shellyln/open-soql/fork)\n[![GitHub stars](https://img.shields.io/github/stars/shellyln/open-soql.svg?style=social\u0026label=Star)](https://github.com/shellyln/open-soql)\n\n---\n\n## Table of contents\n\n* [Install](#%EF%B8%8F-install)\n* [Getting started](#-getting-started)\n* [Features](#-features)\n* [Usage](#-usage)\n* [FAQ](#-faq)\n* [License](#%EF%B8%8F-license)\n\n---\n\n## ⚙️ Install\n\n```bash\nnpm install open-soql\n```\n\n\u003e NOTICE:  \n\u003e Use with `webpack \u003e= 5`\n\u003e\n\u003e If you get the error:\n\u003e\n\u003e ```\n\u003e Module not found: Error: Can't resolve '(importing/path/to/filename)'\n\u003e in '(path/to/node_modules/path/to/dirname)'\n\u003e Did you mean '(filename).js'?`\n\u003e ```\n\u003e\n\u003e Add following setting to your `webpack.config.js`.\n\u003e\n\u003e ```js\n\u003e {\n\u003e     test: /\\.m?js/,\n\u003e     resolve: {\n\u003e         fullySpecified: false,\n\u003e     },\n\u003e },\n\u003e ```\n\u003e\n\u003e On `webpack \u003e= 5`, the extension in the request is mandatory for it to be fully specified\n\u003e if the origin is a '*.mjs' file or a '*.js' file where the package.json contains '\"type\": \"module\"'.\n\n\n\n## 🚀 Getting started\n\n### Set up the resolvers\n```ts\nimport { build }                      from 'open-soql/modules/builder';\nimport { staticJsonResolverBuilder,\n         staticCsvResolverBuilder,\n         passThroughResolverBuilder } from 'open-soql/modules/resolvers';\n\n// See `src/types.ts` \u003e `QueryBuilderInfo`\nconst { compile, soql,\n        insert, update, remove, touch, notifyRemoved,\n        transaction,\n        subscribe, unsubscribe, unsubscribeAllBySubscriber } = build({\n\n    functions: [{ // optional: For defining custom functions.\n        type: 'scalar',\n        name: 'string',\n        fn: (ctx, args, records) =\u003e {\n            return String(args[0]);\n        },\n    }, {\n        type: 'scalar',\n        name: 'number',\n        fn: (ctx, args, records) =\u003e {\n            return Number(args[0]);\n        },\n    }, {\n        type: 'immediate-scalar',\n        name: 'cast_string',\n        fn: (ctx, args) =\u003e {\n            return String(args[0]);\n        },\n    }, {\n        type: 'immediate-scalar',\n        name: 'cast_number',\n        fn: (ctx, args) =\u003e {\n            return Number(args[0]);\n        },\n    }, {\n        type: 'aggregate',\n        name: 'count_twice',\n        fn: (ctx, args, records) =\u003e {\n            return records.length * 2;\n        },\n    }],\n    events: { // optional: For resolving transaction and N+1 query problem.\n        beginTransaction: (evt) =\u003e Promise.resolve(),\n        endTransaction: (evt, err) =\u003e Promise.resolve(),\n        beginExecute: (evt) =\u003e Promise.resolve(),\n        endExecute: (evt, err) =\u003e Promise.resolve(),\n        beforeMasterSubQueries: (evt) =\u003e Promise.resolve(),\n        afterMasterSubQueries: (evt) =\u003e Promise.resolve(),\n        beforeDetailSubQueries: (evt) =\u003e Promise.resolve(),\n        afterDetailSubQueries: (evt) =\u003e Promise.resolve(),\n    },\n    resolvers: {\n        query: {\n            Account: (fields, conditions, limit, offset, ctx) =\u003e {\n                // Fetch the `Account` object data.\n                ctx.resolverCapabilities.filtering = true; // True if the resolver can filter records.\n                return Promise.resolve([{ ... }, ... ]);\n            },\n            Contact: (fields, conditions, limit, offset, ctx) =\u003e {\n                // Fetch the `Contact` object data.\n                // `ctx.parent` is a parent record.\n                ctx.resolverCapabilities.filtering = true; // True if the resolver can filter records.\n                return Promise.resolve([{ ... }, ... ]);\n            },\n            Opportunity: (fields, conditions, limit, offset, ctx) =\u003e {\n                // Fetch the `Opportunity` object data.\n                // `ctx.parent` is a parent record.\n                ctx.resolverCapabilities.filtering = true; // True if the resolver can filter records.\n                return Promise.resolve([{ ... }, ... ]);\n            },\n            Event: staticCsvResolverBuilder(  // (CSV string)\n                                              // \"staticJsonResolverBuilder\"(JSON string) and\n                                              // \"passThroughResolverBuilder\"(array of object)\n                                              // are also available.\n                'Event', () =\u003e Promise.resolve(`\n                    Id,      Subject, WhatId\n                    Event/1, Email,   Account/1\n                    Event/2, Phone,   Contact/1\n                `)\n            ),\n        },\n        insert: { // optional: For DML\n            Contact: (records, ctx) =\u003e {\n                return Promise.resolve(records.map((x, i) =\u003e ({...x, id: `Contact/${i}`})));\n            },\n        },\n        update: { // optional: For DML\n            Contact: (records, ctx) =\u003e {\n                return Promise.resolve(records);\n            },\n        },\n        remove: { // optional: For DML\n            Contact: (records, ctx) =\u003e {\n                return Promise.resolve();\n            },\n        },\n    },\n    relationships: { // optional: For relationship query\n        /**\n         * detailResolverName\n         * e.g.: Contact: { account: 'Account' }\n         *       Contact: { account: { resolver: 'Account', id: 'accountId' } }\n         *\n         * NOTE: 'Account' is `masterResolverName`.\n         *       'account' is `masterObjectFieldName`.\n         *       'accountId' is `masterIdName`. (foreign key field name)\n         *       `Contact (resolver) -\u003e account (field name)` direction is `Detail to Master`.\n         * \n         * masterResolverName\n         * e.g.: Account: { contacts: ['Contact'] }\n         *       Account: { contacts: ['Contact', 'account'] }\n         *\n         * NOTE: 'contacts' is details relationship name.\n         *       'Contact' is `detailResolverName` and 'account' is Contact's `masterObjectFieldName`.\n         *       Default masterObjectFieldName is `MasterResolverName`.\n         *       `Account (resolver) -\u003e contacts (relationship name)` direction is `Master to Details`.\n         */\n        Account: {\n            Contacts: ['Contact'],                      // master-\u003edetails relationship\n            Opportunities: ['Opportunity', 'Account'],  // master-\u003edetails relationship\n        },                                              //     (Explicitly specify relationship item)\n        Contact: {\n            Account: 'Account',                         // detail-\u003emaster relationship\n        },\n        Opportunity: {\n            Account: 'Account',                         // detail-\u003emaster relationship\n        },\n        Event: {\n            Account: { resolver: 'Account', id: 'WhatId' },  // detail-\u003emaster relationship\n            Contact: { resolver: 'Contact', id: 'WhatId' },  //     (Explicitly specify Id item)\n            Opportunity: { resolver: 'Opportunity', id: 'WhatId' },\n        },\n    },\n});\n```\n\n### Query\n```ts\nconst result = await soql\u003cPartial\u003cContact\u003e\u003e`\n    Select\n        acc.id         aid\n      , acc.Region     reg\n      , acc.Category   cat\n      , (\n          Select id, Name\n          from acc.Opportunities\n          where Amount \u003e ${10000}\n                         -- It can be number, string, boolean or null.\n          order by DueDate desc limit 5\n        )\n      , string(id)\n      , string(foo)\n      , string(reg)\n      , string(acc.qux)\n    from Contact con, con.Account acc\n    where\n      (\n            number(acc.numOfEmployees) = 5\n        and acc.created \u003e ${{type: 'date', value: '2020-01-01'}}\n                             -- It can be 'date' or 'datetime'.\n        and acc.updated \u003e 2020-01-01\n      ) or (\n            acc.foo = 1\n        and acc.bar = 2\n        and acc.baz = 2\n      ) or not (\n            acc.qux = 1\n        and acc.quux = 2\n        and acc.corge in (Select id from Event)\n      )\n    order by aid, reg, cat\n    limit 10 offset 2\n    -- line comment\n    /* block comment */\n`;\n// result is [{...}, ...]\n```\n\n### Pre-compiled query\n\n* Non-parameterized query.  \n  (Template literal parameters will be interpreted before compiling.)\n```ts\nconst query = compile`Select id from account where id \u003e ${'100'}`;\nconst result = await query.execute\u003cPartial\u003cAccount\u003e\u003e();\n```\n\n* Named parameterized query.\n```ts\nconst query = compile`Select id from account where id \u003e :idGreaterThan`;\nconst result = await query.execute\u003cPartial\u003cAccount\u003e\u003e({ idGreaterThan: '100' });\n```\n\n\u003e You can use parameters on the right side of the conditional expression, function arguments, limit, and offset.\n\n### Aggregate\n```ts\nconst aggregationResult = await soql\u003cContactAgg\u003e`\n    Select\n        count()\n      , count(id) cnt\n      , sum(bar) sum\n      , cast_string(12345) str\n      , cast_number('2234') num\n    from\n        Contact\n    where\n        foo \u003e ''\n    group by Region\n    having count(id) \u003e 0\n`;\n// aggregationResult is [{...}, ...]\n```\n\n### DML (bulk)\n```ts\nconst inserted = await insert('Contact', [{\n    Name: 'foo',\n}]);\n// inserted is [{ Id: 'Contact/1', Name: 'foo' }]\n\nconst updated = await update('Contact', inserted);\n// updated is [{ Id: 'Contact/1', Name: 'foo' }]\n\nawait remove('Contact', updated);\n\nconst selected = await soql\u003cPartial\u003cContact\u003e\u003e`Select Id, Name from Contact`;\nconst updated2 = await update('Contact', selected);\n```\n\n### DML (single record)\n```ts\nconst inserted = await insert('Contact', {\n    Name: 'foo',\n});\n// inserted is { Id: 'Contact/1', Name: 'foo' }\n\nconst updated = await update('Contact', inserted);\n// updated is { Id: 'Contact/1', Name: 'foo' }\n\nawait remove('Contact', updated);\n```\n\n### Execute commands within a transaction\n```ts\nawait transaction(async (commands, tr) =\u003e {\n    const { compile, soql, insert, update, remove, touch, notifyRemoved } = commands;\n\n    const inserted = await insert('Contact', [{\n        Name: 'foo',\n    }]);\n    const selected = await soql\u003cPartial\u003cContact\u003e\u003e`Select Id, Name from Contact`;\n    const updated = await update('Contact', selected);\n    await remove('Contact', updated);\n\n    const query = compile`Select id from account where id \u003e ${'100'}`;\n    const selectedAccounts = await query.execute\u003cPartial\u003cAccount\u003e\u003e();\n});\n```\n\n\n### Publish / Subscribe messaging\n\n#### Without a transaction\n```ts\nconst subscriber: Subscriber = ({on, resolver, id}) =\u003e {\n    switch (on) {\n    case 'insert':\n        ...\n        break;\n    case 'update':\n        ...\n        break;\n    case 'remove':\n        ...\n        break;\n    }\n};\n\n// Subscribe to all changes of the resolver `Contact`.\nsubscribe('Contact', null, subscriber);\n// Subscribe to all changes of the record `Contact(id='Contact/z2')`.\nsubscribe('Contact', 'Contact/z2', subscriber);\n\nawait update('Contact', [ ... ]); // or insert(), remove(), touch()\n// (Fire events on next event loop.)\n\nawait update('Contact', [ ... ]);\n// (Fire events on next event loop.)\n\nawait update('Contact', [ ... ]);\n// (Fire events on next event loop.)\n\n...\n\n// Unsubscribe to all changes of the resolver `Contact`.\nunsubscribe('Contact', null, subscriber);\n// Unsubscribe to all changes of the record `Contact(id='Contact/z2')`.\nunsubscribe('Contact', 'Contact/z2', subscriber);\n```\n\n#### Within a transaction\n```ts\nconst subscriber: Subscriber = ({on, resolver, id}) =\u003e { ... };\n\n// Subscribe to all changes of the resolver `Contact`.\nsubscribe('Contact', null, subscriber);\n// Subscribe to all changes of the record `Contact(id='Contact/z2')`.\nsubscribe('Contact', 'Contact/z2', subscriber);\n\nawait transaction(async (commands, tr) =\u003e {\n    const { compile, soql, insert, update, remove, touch } = commands;\n\n    await update('Contact', [ ... ]); // or insert(), remove(), touch()\n    await update('Contact', [ ... ]);\n    await update('Contact', [ ... ]);\n});\n// (Fire events on next event loop.)\n\n...\n\n// Unsubscribe to all changes of the resolver `Contact`.\nunsubscribe('Contact', null, subscriber);\n// Unsubscribe to all changes of the record `Contact(id='Contact/z2')`.\nunsubscribe('Contact', 'Contact/z2', subscriber);\n```\n\n\nSee also the following usage example repositories:  \n* [https://github.com/shellyln/open-soql-usage-example](https://github.com/shellyln/open-soql-usage-example)\n* [Open SOQL example app with React hooks](https://github.com/shellyln/open-soql-react-hooks-example-app)\n\n\n## 💎 Features\n### Syntax\n\n* `Select` field list\n  * [x] detail-master relationship name\n  * [x] resolver (relationship) alias name\n  * [x] field alias name\n  * [x] function call (aggregate | scalar | immediate_scalar)\n  * [x] nested function call (call functions in actual parameters of functions)\n  * functions\n    * Aggregate functions\n      * [x] `count()`, `count(field)`\n      * [x] `count_distinct(field)`\n      * [x] `sum(field)`\n      * [x] `avg(field)`\n      * [x] `min(field)`\n      * [x] `max(field)`\n      * [ ] `grouping(field)`\n    * Scalar functions\n      * String functions\n        * [ ] `format(field | literal | function call)`\n        * [x] `concat(field | literal | function call, ...)`\n      * Cast functions\n        * [x] `cast_to_string(field | literal | function call)`\n        * [x] `cast_to_number(field | literal | function call)`\n        * [x] `cast_to_boolean(field | literal | function call)`\n      * Calc functions\n        * [x] `add(field | literal | function call, ...)`\n        * [x] `sub(field | literal | function call, ...)`\n        * [x] `mul(field | literal | function call, ...)`\n        * [x] `div(field | literal | function call, ...)`\n        * [x] `mod(field | literal | function call, ...)`\n      * Date and datetime functions (UTC)\n        * [x] `convertTimezone(field | literal | function call)`\n        * [x] `calendar_month(field | literal | convertTimezone(field) | function call)`\n        * [x] `calendar_quarter(field | literal | convertTimezone(field) | function call)`\n        * [x] `calendar_year(field | literal | convertTimezone(field) | function call)`\n        * [x] `day_in_month(field | literal | convertTimezone(field) | function call)`\n        * [x] `day_in_week(field | literal | convertTimezone(field) | function call)`\n        * [x] `day_in_year(field | literal | convertTimezone(field) | function call)`\n        * [x] `day_only(field | literal | convertTimezone(field) | function call)`\n        * [ ] `fiscal_month(field | literal | convertTimezone(field) | function call)`\n        * [ ] `fiscal_quarter(field | literal | convertTimezone(field) | function call)`\n        * [ ] `fiscal_year(field | literal | convertTimezone(field) | function call)`\n        * [x] `hour_in_day(field | literal | convertTimezone(field) | function call)`\n        * [x] `week_in_month(field | literal | convertTimezone(field) | function call)`\n        * [x] `week_in_year(field | literal | convertTimezone(field) | function call)`\n      * Date and datetime functions (local timezone)\n        * [x] `calendar_month_lc(field | literal | function call)`\n        * [x] `calendar_quarter_lc(field | literal | function call)`\n        * [x] `calendar_year_lc(field | literal | function call)`\n        * [x] `day_in_month_lc(field | literal | function call)`\n        * [x] `day_in_week_lc(field | literal | function call)`\n        * [x] `day_in_year_lc(field | literal | function call)`\n        * [x] `day_only_lc(field | literal | function call)`\n        * [ ] `fiscal_month_lc(field | literal | function call)`\n        * [ ] `fiscal_quarter_lc(field | literal | function call)`\n        * [ ] `fiscal_year_lc(field | literal | function call)`\n        * [x] `hour_in_day_lc(field | literal | function call)`\n        * [x] `week_in_month_lc(field | literal | function call)`\n        * [x] `week_in_year_lc(field | literal | function call)`\n  * [ ] `TYPEOF` expression\n* field expressions\n  * [x] field\n  * [x] field alias name\n  * data types\n    * [x] string\n    * [x] number\n    * [x] date\n    * [x] datetime\n    * [x] null\n* `From` clause\n  * [x] resolver (relationship name) alias\n* `Where` clause\n  * [x] field\n  * data types\n    * [x] string\n    * [x] number\n    * [x] date\n    * [x] datetime\n    * [x] null\n  * [x] op1 function call (scalar | immediate_scalar)\n  * [x] op2 function call (immediate_scalar)\n  * [ ] date literals (e.g.: `TODAY`)\n  * logical operators\n    * [x] `and`\n    * [x] `or`\n    * [x] `not`\n  * comparison operators\n    * [x] `=`\n    * [x] `!=`\n    * [x] `\u003c`\n    * [x] `\u003c=`\n    * [x] `\u003e`\n    * [x] `\u003e=`\n    * [x] `like`\n    * [x] `not_like`\n    * [x] `in`\n    * [x] `not_in`\n    * [x] `includes`\n    * [x] `excludes`\n* `Having` clause\n  * [x] field\n  * data types\n    * [x] string\n    * [x] number\n    * [x] date\n    * [x] datetime\n    * [x] null\n  * [x] op1 function call (immediate_scalar | aggregate)\n  * [x] op2 function call (immediate_scalar)\n  * [ ] date literals (e.g.: `TODAY`)\n  * logical operators\n    * [x] `and`\n    * [x] `or`\n    * [x] `not`\n  * comparison operators\n    * [x] `=`\n    * [x] `!=`\n    * [x] `\u003c`\n    * [x] `\u003c=`\n    * [x] `\u003e`\n    * [x] `\u003e=`\n    * [x] `like`\n    * [x] `not_like`\n    * [x] `in`\n    * [x] `not_in`\n    * [x] `includes`\n    * [x] `excludes`\n* `Group by` clause\n    * [x] fields\n    * [x] field alias name\n    * [ ] `ROLLUP`\n    * [ ] `CUBE`\n* `Order by` clause\n    * [x] fields\n    * [x] resolver (relationship) alias name\n    * [x] field alias name\n    * [x] asc/desc\n    * [x] nulls first/last\n* [ ] `Using scope` clause\n* [x] `Limit` clause\n* [x] `Offset` clause\n* [ ] `With` clause\n* [x] `For` clause\n\n### Other features\n* [x] prepared query (pre-compiled query)\n  * [x] named parameterized query\n* standard query resolvers\n  * [x] JSON string\n  * [x] CSV string\n  * [x] Array of object\n* DML\n  * [x] `insert`\n  * [x] `update`\n  * [x] `remove`\n* [x] Publish / Subscribe messaging\n* [x] transaction scope\n* [x] template string\n\n\n---\n\n## 📖 Usage\n\n### 📦 Module `open-soql/modules/builder`\n\n#### 🟢 `build()`\n\n```ts\nexport interface QueryBuilderInfo {\n    functions?: QueryFuncInfo[];\n                // QueryFuncInfo[i].type is 'aggregate' | 'scalar' | 'immediate-scalar'\n    rules?: {\n        idFieldName?: (resolverName: string) =\u003e string;\n        foreignIdFieldName?: (masterResolverName: string | undefined) =\u003e string | undefined;\n    };\n    events?: {\n        beginTransaction?: (evt: ResolverEvent) =\u003e Promise\u003cvoid\u003e;\n        endTransaction?: (evt: ResolverEvent, err: Error | null) =\u003e Promise\u003cvoid\u003e;\n        beginExecute?: (evt: ResolverEvent) =\u003e Promise\u003cvoid\u003e;\n        endExecute?: (evt: ResolverEvent, err: Error | null) =\u003e Promise\u003cvoid\u003e;\n        beforeMasterSubQueries?: (evt: ResolverEvent) =\u003e Promise\u003cvoid\u003e;\n        afterMasterSubQueries?: (evt: ResolverEvent) =\u003e Promise\u003cvoid\u003e;\n        beforeDetailSubQueries?: (evt: ResolverEvent) =\u003e Promise\u003cvoid\u003e;\n        afterDetailSubQueries?: (evt: ResolverEvent) =\u003e Promise\u003cvoid\u003e;\n    };\n    resolvers: {\n        query: {\n            [resolverNames: string]: QueryResolverFn;\n        };\n        insert?: {\n            [resolverNames: string]: InsertResolverFn;\n        };\n        update?: {\n            [resolverNames: string]: UpdateResolverFn;\n        };\n        remove?: {\n            [resolverNames: string]: RemoveResolverFn;\n        };\n    };\n    relationships?: {\n        [detailOrMasterResolverNames: string]: {\n            [fieldOrRelNames: string]:\n                string | { resolver: string, id: string } | [string, string?];\n        };\n    };\n}\n\nexport interface IQuery {\n    public execute\u003cR\u003e(\n        params?: {\n            [paramNames: string]:\n                number | string | null |\n                Array\u003cnumber | string | null\u003e\n        }): Promise\u003cR[]\u003e;\n}\n\nexport interface SubscriberParams {\n    on: 'insert' | 'update' | 'remove';\n    resolver: string;\n    id: any | null;\n}\n\nexport type Subscriber = (params: SubscriberParams) =\u003e void;\n\nexport function build(builder: QueryBuilderInfo): {\n    compile: (strings: TemplateStringsArray | string,\n            ...values: Array\u003cPreparedAtomValue | Array\u003cPreparedAtomValue\u003e\u003e) =\u003e IQuery;\n    soql: (strings: TemplateStringsArray | string,\n            ...values: Array\u003cPreparedAtomValue | Array\u003cPreparedAtomValue\u003e\u003e) =\u003e Promise\u003cR[]\u003e;\n    insert: (resolver: string, obj: T) =\u003e Promise\u003cT extends (infer R)[] ? R[] : T\u003e;\n    update: (resolver: string, obj: T) =\u003e Promise\u003cT extends (infer R)[] ? R[] : T\u003e;\n    remove: (resolver: string, obj: T) =\u003e Promise\u003cvoid\u003e;\n    touch: (resolver: string, obj: T) =\u003e Promise\u003cvoid\u003e;\n    notifyRemoved: (resolver: string, obj: T) =\u003e Promise\u003cvoid\u003e;\n    subscribe: (resolver: string, id: any, fn: Subscriber) =\u003e void,\n    unsubscribe: (resolver: string, id: any, fn: Subscriber) =\u003e void,\n    unsubscribeAllBySubscriber: (resolver: string, fn: Subscriber) =\u003e void,\n    transaction: (\n            callback: (commands: {\n                compile, soql, insert, update, remove, touch\n            }, tr: any) =\u003e Primise\u003cvoid\u003e,\n            trOptions?: any,\n        ) =\u003e Primise\u003cvoid\u003e;\n};\n```\n\n* Set up the resolvers.\n\n##### parameters:\n\n* `builder`: Resolvers and configurations.\n\n\u003e **NOTICE**:  \n\u003e The `immediate-scalar` function does not refer to the fields of a record and\n\u003e must be referentially transparent.\n\n\u003e **NOTICE**:  \n\u003e If query conditions includes computed fields,\n\u003e `QueryResolverFn`'s parameter `conditions` will be `[]`.  \n\u003e To get complete conditions, use parameter `ctx.conditions`.  \n\u003e You can get transformed conditions that include only the fields you specified\n\u003e by using `getIndexFieldConditions()`.\n\n##### returns:\n\n* Functions that execute select queries and DML\n  * `compile`: Compile the query.\n  * `soql`: Select records.\n  * `insert`: Insert record(s).\n  * `update`: Update record(s).\n  * `remove`: Remove record(s).\n  * `touch`: Queues `update` events for subscribers. (to notify remote changes)\n  * `notifyRemoved`: Queues `remove` events for subscribers. (to notify remote changes)\n  * `subscribe`: Subscribe to publishing events.\n  * `unsubscribe`: Unsubscribe to publishing events.\n  * `unsubscribeAllBySubscriber`: Unsubscribe to publishing events.\n  * `transaction`: Execute commands within a transaction.\n\n\n\n### 📦 Module `open-soql/modules/sort`\n\n#### 🟢 `sortRecords()`\n\n```ts\nexport function sortRecords(query: PreparedQuery, records: any[]): any[];\n```\n\n* Sort records.\n\n##### parameters:\n\n* `query`: Prepared query object.\n* `records`: Records to sort.\n\n##### returns:\n\n* Sorted records.\n\n\n\n### 📦 Module `open-soql/modules/filters`\n\n#### 🟢 `applyWhereConditions()`\n\n```ts\nexport function applyWhereConditions(\n    Omit\u003cResolverContext, 'resolverCapabilities'\u003e,\n    conds: PreparedCondition[], records: any[]): any[];\n```\n\n* Filter records by `where` conditions.\n\n##### parameters:\n\n* `ctx`: Context object.\n* `conds`: `where` conditions.\n* `records`: Records to apply the filter.\n\n##### returns:\n\n* Records that the filter applied.\n\n\n\n#### 🟢 `applyHavingConditions()`\n\n```ts\nexport function applyHavingConditions(\n    Omit\u003cResolverContext, 'resolverCapabilities'\u003e,\n    conds: PreparedCondition[], groupedRecsArray: any[][]): any[];\n```\n\n* Filter groups by `having` conditions.\n\n##### parameters:\n\n* `ctx`: Context object.\n* `conds`: `having` conditions.\n* `records`: Groups to apply the filter.\n\n##### returns:\n\n* Groups that the filter applied.\n\n\n\n#### 🟢 `getIndexFieldConditions()`\n\n```ts\nexport function getIndexFieldConditions(\n    ctx: Pick\u003cResolverContext, 'params'\u003e,\n    conds: PreparedCondition[], indexFieldNames: string[]): PreparedCondition[];\n```\n\n* Gets the transformed conditions that include only the fields specified in `indexFieldNames`.\n\n##### parameters:\n\n* `ctx`: Context object.\n* `conds`: Original conditions.\n* `indexFieldNames`: Index fields.\n\n##### returns:\n\n* Transformed conditions.\n\n\n\n#### 🟢 `getSqlConditionString()`\n\n```ts\nexport interface SqlDialect {\n    fieldName: (name: string) =\u003e string;\n    escapeString: (s: string) =\u003e string;\n}\n\nexport function getSqlConditionString(\n    ctx: Pick\u003cResolverContext, 'params'\u003e,\n    conds: PreparedCondition[], dialect: SqlDialect): string;\n```\n\n* Get the SQL condition string.\n\n##### parameters:\n\n* `ctx`: Context object.\n* `conds`: Conditions for converting to SQL conditions.\n* `dialect`: SQL dialect.\n\n##### returns:\n\n* SQL condition string (where clause excludes the `where` keyword).\n\n\n\n#### 🟢 `escapeSqlStringLiteral_Std()`\n\n```ts\nexport function escapeSqlStringLiteral_Std(s: string): string;\n```\n\n* Escape the standard SQL string literal. (pass to `SqlDialect`)\n\n##### parameters:\n\n* `s`: string literal.\n\n##### returns:\n\n* Escaped string.\n\n\n\n#### 🟢 `escapeSqlStringLiteral_MySql()`\n\n```ts\nexport function escapeSqlStringLiteral_MySql(s: string): string;\n```\n\n* Escape the MySQL string literal. (pass to `SqlDialect`)\n\n##### parameters:\n\n* `s`: string literal.\n\n##### returns:\n\n* Escaped string.\n\n\n\n### 📦 Module `open-soql/modules/resolvers`\n\n#### 🟢 `staticJsonResolverBuilder()`\n\n```ts\nexport interface StaticResolverConfig {\n    noCache?: boolean;\n    noFiltering?: boolean;\n    noSorting?: boolean;\n}\n\nexport const staticJsonResolverBuilder:\n    (resolverName: string, fetcher: () =\u003e Promise\u003cstring\u003e,\n     config?: StaticResolverConfig) =\u003e QueryResolverFn;\n```\n\n* Generate the query resolver for static JSON data.\n\n##### parameters:\n\n* `resolverName`: Resolver name.\n* `fetcher`: The function that returns promise of data.\n\n##### returns:\n\n* Query resolver.\n\n\n\n#### 🟢 `staticCsvResolverBuilder()`\n\n```ts\nexport const staticCsvResolverBuilder:\n    (resolverName: string, fetcher: () =\u003e Promise\u003cstring\u003e,\n     config?: StaticResolverConfig) =\u003e QueryResolverFn;\n```\n\n* Generate the query resolver for static CSV data.\n\n##### parameters:\n\n* `resolverName`: Resolver name.\n* `fetcher`: The function that returns promise of data.\n\n##### returns:\n\n* Query resolver.\n\n\n\n#### 🟢 `passThroughResolverBuilder()`\n\n```ts\nexport const passThroughResolverBuilder:\n    (resolverName: string, fetcher: () =\u003e Promise\u003cany[]\u003e,\n     config?: StaticResolverConfig) =\u003e QueryResolverFn;\n```\n\n* Generate the query resolver for static object array data.\n\n##### parameters:\n\n* `resolverName`: Resolver name.\n* `fetcher`: The function that returns promise of data.\n\n##### returns:\n\n* Query resolver.\n\n\n\n---\n\n## 🙋 FAQ\n\n* What does `SOQL` stand for?\n  * 👉 In `Open SOQL`, `SOQL` stands for `SOQL is Object Query Language`.\n  * 👉 In [original SOQL](https://developer.salesforce.com/docs/atlas.en-us.soql_sosl.meta/soql_sosl/sforce_api_calls_soql.htm), `SOQL` stands for `Salesforce Object Query Language`.\n\n\n---\n\n## ⚖️ License\nISC  \nCopyright (c) 2020 Shellyl_N and Authors\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshellyln%2Fopen-soql","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fshellyln%2Fopen-soql","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fshellyln%2Fopen-soql/lists"}