├── .nvmrc ├── .npmrc ├── src ├── models │ ├── index.ts │ └── Mapping.ts ├── index.ts ├── decorators │ ├── index.ts │ ├── EsField.ts │ ├── EsMapping.ts │ ├── EsProperty.ts │ └── EsSubMapping.ts └── utils │ ├── decorators.ts │ ├── metadata.ts │ └── template.ts ├── tslint.json ├── renovate.json ├── config ├── tsconfig.build.json └── tsconfig.base.json ├── .npmignore ├── .gitignore ├── tsconfig.json ├── .github └── workflows │ ├── test.yml │ └── npm-publish.yml ├── jest.json ├── README.md ├── LICENSE ├── test ├── __snapshots__ │ ├── template.spec.ts.snap │ └── decorators.spec.ts.snap ├── template.spec.ts └── decorators.spec.ts └── package.json /.nvmrc: -------------------------------------------------------------------------------- 1 | 18 2 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | package-lock=false 2 | -------------------------------------------------------------------------------- /src/models/index.ts: -------------------------------------------------------------------------------- 1 | export * from './Mapping'; 2 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@smartive/tslint-config" 3 | } 4 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | 3 | export * from './decorators'; 4 | export * from './utils/template'; 5 | -------------------------------------------------------------------------------- /src/decorators/index.ts: -------------------------------------------------------------------------------- 1 | export * from './EsField'; 2 | export * from './EsProperty'; 3 | export * from './EsMapping'; 4 | export * from './EsSubMapping'; 5 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "local>smartive/renovate-config" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /config/tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.base.json", 3 | "compilerOptions": { 4 | "outDir": "../", 5 | "types": [] 6 | }, 7 | "exclude": [] 8 | } 9 | -------------------------------------------------------------------------------- /src/decorators/EsField.ts: -------------------------------------------------------------------------------- 1 | import { Field } from '../models/Mapping'; 2 | import { generatePropertyDecorator } from '../utils/decorators'; 3 | 4 | // tslint:disable-next-line:variable-name - This is a decorator 5 | export const EsField = generatePropertyDecorator('fields'); 6 | -------------------------------------------------------------------------------- /src/decorators/EsMapping.ts: -------------------------------------------------------------------------------- 1 | import { MappingMeta } from '../models/Mapping'; 2 | import { generateObjectDecorator } from '../utils/decorators'; 3 | 4 | // tslint:disable-next-line:variable-name - This is a decorator 5 | export const EsMapping = generateObjectDecorator(); 6 | -------------------------------------------------------------------------------- /src/decorators/EsProperty.ts: -------------------------------------------------------------------------------- 1 | import { Field } from '../models/Mapping'; 2 | import { generatePropertyDecorator } from '../utils/decorators'; 3 | 4 | // tslint:disable-next-line:variable-name - This is a decorator 5 | export const EsProperty = generatePropertyDecorator('properties'); 6 | -------------------------------------------------------------------------------- /src/decorators/EsSubMapping.ts: -------------------------------------------------------------------------------- 1 | import { SubMappingMeta } from '../models/Mapping'; 2 | import { generateObjectDecorator } from '../utils/decorators'; 3 | 4 | // tslint:disable-next-line:variable-name - This is a decorator 5 | export const EsSubMapping = generateObjectDecorator(); 6 | -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Dependency directory 7 | node_modules 8 | 9 | # Typescript stuff 10 | build 11 | coverage 12 | 13 | *.ts 14 | !*.d.ts 15 | test 16 | 17 | .appveyor.yml 18 | jest.json 19 | .travis.yml 20 | tsconfig.json 21 | tslint.json 22 | config/ 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Dependency directory 7 | node_modules 8 | 9 | # Optional npm cache directory 10 | .npm 11 | 12 | # Optional REPL history 13 | .node_repl_history 14 | 15 | # Typescript stuff 16 | build 17 | coverage 18 | 19 | # Generated files 20 | *.d.ts 21 | *.js 22 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./config/tsconfig.base.json", 3 | "compilerOptions": { 4 | "rootDir": "./", 5 | "strictPropertyInitialization": false, 6 | "experimentalDecorators": true, 7 | "watch": true, 8 | "sourceMap": true 9 | }, 10 | "include": ["./src/**/*", "./test/**/*"] 11 | } 12 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | on: 4 | push: 5 | branches: 6 | - "**" 7 | 8 | jobs: 9 | test: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: actions/setup-node@v3 14 | with: 15 | node-version: 10 16 | - run: npm i 17 | - run: npm test 18 | -------------------------------------------------------------------------------- /jest.json: -------------------------------------------------------------------------------- 1 | { 2 | "collectCoverage": true, 3 | "transform": { 4 | "^.+\\.tsx?$": "ts-jest" 5 | }, 6 | "testMatch": [ 7 | "**/test/**/*.spec.ts" 8 | ], 9 | "testPathIgnorePatterns": [ 10 | "/node_modules/" 11 | ], 12 | "moduleFileExtensions": [ 13 | "ts", 14 | "tsx", 15 | "js", 16 | "json" 17 | ] 18 | } -------------------------------------------------------------------------------- /config/tsconfig.base.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es6", 4 | "module": "commonjs", 5 | "moduleResolution": "node", 6 | "removeComments": false, 7 | "outDir": "../build", 8 | "rootDir": "../src", 9 | "declaration": true, 10 | "sourceMap": false, 11 | "importHelpers": true, 12 | "strict": true, 13 | "experimentalDecorators": true, 14 | "emitDecoratorMetadata": true, 15 | "noUnusedLocals": true, 16 | "noUnusedParameters": true, 17 | "lib": ["es2015"] 18 | }, 19 | "include": ["../src/**/*"] 20 | } 21 | -------------------------------------------------------------------------------- /src/utils/decorators.ts: -------------------------------------------------------------------------------- 1 | import { getMetadata, setMetadata } from './metadata'; 2 | 3 | export function generateObjectDecorator(): (data?: T) => ClassDecorator { 4 | return (data?: T): ClassDecorator => (target: any) => setMetadata(target.prototype, data); 5 | } 6 | 7 | export function generatePropertyDecorator(key: string): (data?: T) => PropertyDecorator { 8 | return (data?: T): PropertyDecorator => (target: Object, name: string | symbol) => { 9 | let mapping = getMetadata<{ [key: string]: T | undefined }>(target, key); 10 | if (!mapping) { 11 | mapping = {}; 12 | setMetadata(target, mapping, key); 13 | } 14 | mapping[name.toString()] = data; 15 | }; 16 | } 17 | -------------------------------------------------------------------------------- /src/utils/metadata.ts: -------------------------------------------------------------------------------- 1 | const KEY = 'es-model'; 2 | 3 | export function setMetadata(target: any, metadata: any, key?: string): void { 4 | if (key) { 5 | Reflect.defineMetadata( 6 | KEY, 7 | metadata, 8 | target, 9 | key, 10 | ); 11 | } else { 12 | Reflect.defineMetadata( 13 | KEY, 14 | metadata, 15 | target, 16 | ); 17 | } 18 | } 19 | 20 | export function getMetadata(target: any, key?: string): T { 21 | if (key) { 22 | return Reflect.getMetadata(KEY, target, key); 23 | } 24 | 25 | return Reflect.getMetadata(KEY, target); 26 | } 27 | 28 | export function getPrimitiveType(objectType: Function, key: string): Function | null { 29 | return Reflect.getMetadata('design:type', objectType, key); 30 | } 31 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![npm](https://img.shields.io/npm/v/@smartive/es-model.svg?maxAge=3600)](https://www.npmjs.com/package/@smartive/es-model) 2 | [![semantic-release](https://img.shields.io/badge/%20%20%F0%9F%93%A6%F0%9F%9A%80-semantic--release-e10079.svg)](https://github.com/semantic-release/semantic-release) 3 | 4 | Annotate your model and generate an elasticsearch index template. 5 | 6 | ## Installation 7 | 8 | To install this package, simply run 9 | 10 | [![NPM](https://nodei.co/npm/@smartive/es-model.png?downloads=true&stars=true)](https://nodei.co/npm/@smartive/es-model/) 11 | 12 | ## Changelog 13 | 14 | The changelog is generated by [semantic release](https://github.com/semantic-release/semantic-release) and is located under 15 | the [release section](https://github.com/smartive/es-model/releases). 16 | 17 | ## Licence 18 | 19 | This software is licenced under the [MIT](LICENSE) licence. 20 | -------------------------------------------------------------------------------- /.github/workflows/npm-publish.yml: -------------------------------------------------------------------------------- 1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/publishing-nodejs-packages 3 | 4 | name: Node.js Package 5 | 6 | on: 7 | workflow_run: 8 | workflows: ["Test"] 9 | branches: [master] 10 | types: 11 | - completed 12 | 13 | jobs: 14 | publish-npm: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | - uses: actions/setup-node@v3 19 | with: 20 | node-version: 10 21 | registry-url: https://registry.npmjs.org/ 22 | - run: npm i 23 | - run: npm run build 24 | - run: npm run semantic-release 25 | env: 26 | NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}} 27 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 smartive 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/__snapshots__/template.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`decorators EsObject generates metadata 1`] = ` 4 | Object { 5 | "a": Object { 6 | "_source": Object { 7 | "enabled": true, 8 | }, 9 | "dynamic": "strict", 10 | "properties": Object { 11 | "b": Object { 12 | "properties": Object { 13 | "aProperty": Object { 14 | "type": "keyword", 15 | }, 16 | }, 17 | "type": "object", 18 | }, 19 | "c": Object { 20 | "properties": Object { 21 | "aProperty": Object { 22 | "type": "keyword", 23 | }, 24 | }, 25 | "type": "nested", 26 | }, 27 | "keyword": Object { 28 | "type": "keyword", 29 | }, 30 | "keywordArray": Object { 31 | "type": "keyword", 32 | }, 33 | "text": Object { 34 | "type": "text", 35 | }, 36 | "withFields": Object { 37 | "fields": Object { 38 | "keyword": Object { 39 | "type": "keyword", 40 | }, 41 | }, 42 | "type": "keyword", 43 | }, 44 | }, 45 | }, 46 | } 47 | `; 48 | -------------------------------------------------------------------------------- /src/models/Mapping.ts: -------------------------------------------------------------------------------- 1 | // Types according to https://www.elastic.co/guide/en/elasticsearch/reference/current/mapping-types.html 2 | export type CoreDatatype = 3 | | 'text' 4 | | 'keyword' 5 | | 'date' 6 | | 'date_nanos' 7 | | 'long' 8 | | 'integer' 9 | | 'short' 10 | | 'byte' 11 | | 'double' 12 | | 'float' 13 | | 'half_float' 14 | | 'scaled_float' 15 | | 'boolean' 16 | | 'binary' 17 | | 'integer_range' 18 | | 'float_range' 19 | | 'long_range' 20 | | 'double_range' 21 | | 'date_range'; 22 | export type ComplexDatatype = 'object' | 'nested'; 23 | export type SpecialisedDatatype = 24 | | 'geo_point' 25 | | 'geo_shape' 26 | | 'completion' 27 | | 'ip' 28 | | 'completion' 29 | | 'token_count' 30 | | 'murmur3' 31 | | 'annotated-text'; 32 | 33 | export type Field = { 34 | type?: CoreDatatype | SpecialisedDatatype | Function; 35 | search_analyzer?: string; 36 | analyzer?: string; 37 | format?: 'date' | 'hour_minute'; 38 | }; 39 | 40 | export type Fields = { 41 | [name: string]: Field; 42 | }; 43 | 44 | export type MappingMeta = { 45 | name?: string; 46 | dynamic?: 'strict'; 47 | _source?: { 48 | enabled: boolean; 49 | }; 50 | }; 51 | 52 | export type SubMappingMeta = { 53 | type?: CoreDatatype | ComplexDatatype; 54 | include_in_parent?: boolean; 55 | }; 56 | -------------------------------------------------------------------------------- /test/template.spec.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | 3 | import { EsField } from '../src/decorators/EsField'; 4 | import { EsMapping } from '../src/decorators/EsMapping'; 5 | import { EsProperty } from '../src/decorators/EsProperty'; 6 | import { EsSubMapping } from '../src/decorators/EsSubMapping'; 7 | import { buildMappings } from '../src/utils/template'; 8 | 9 | describe('decorators', () => { 10 | describe('EsObject ', () => { 11 | it('generates metadata', () => { 12 | @EsSubMapping() 13 | class Obj { 14 | @EsProperty() 15 | aProperty: string; 16 | } 17 | 18 | @EsSubMapping({ type: 'nested' }) 19 | class Nested { 20 | @EsProperty() 21 | aProperty: string; 22 | } 23 | 24 | @EsSubMapping({ type: 'keyword' }) 25 | class WithFields { 26 | @EsField() 27 | keyword: string; 28 | } 29 | 30 | @EsMapping({ 31 | dynamic: 'strict', 32 | _source: { 33 | enabled: true, 34 | }, 35 | }) 36 | class A { 37 | @EsProperty() 38 | keyword: string; 39 | 40 | @EsProperty({ type: String }) 41 | keywordArray: string[]; 42 | 43 | @EsProperty({ type: 'text' }) 44 | text: string; 45 | 46 | @EsProperty({ type: Obj }) 47 | b: Obj; 48 | 49 | @EsProperty({ type: Nested }) 50 | c: Nested; 51 | 52 | @EsProperty({ type: WithFields }) 53 | withFields: WithFields; 54 | } 55 | 56 | expect(buildMappings([A])).toMatchSnapshot(); 57 | }); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /test/__snapshots__/decorators.spec.ts.snap: -------------------------------------------------------------------------------- 1 | // Jest Snapshot v1, https://goo.gl/fbAQLP 2 | 3 | exports[`decorators EsObject generates metadata 1`] = ` 4 | Object { 5 | "_source": Object { 6 | "enabled": true, 7 | }, 8 | "dynamic": "strict", 9 | } 10 | `; 11 | 12 | exports[`decorators EsObject generates metadata 2`] = ` 13 | Object { 14 | "b": Object { 15 | "type": [Function], 16 | }, 17 | "c": Object { 18 | "type": [Function], 19 | }, 20 | "keyword": undefined, 21 | "keywordArray": Object { 22 | "type": [Function], 23 | }, 24 | "text": Object { 25 | "type": "text", 26 | }, 27 | "withFields": Object { 28 | "type": [Function], 29 | }, 30 | } 31 | `; 32 | 33 | exports[`decorators EsObject generates metadata 3`] = `undefined`; 34 | 35 | exports[`decorators EsObject generates metadata 4`] = `undefined`; 36 | 37 | exports[`decorators EsObject generates metadata 5`] = ` 38 | Object { 39 | "aProperty": undefined, 40 | } 41 | `; 42 | 43 | exports[`decorators EsObject generates metadata 6`] = `undefined`; 44 | 45 | exports[`decorators EsObject generates metadata 7`] = ` 46 | Object { 47 | "type": "nested", 48 | } 49 | `; 50 | 51 | exports[`decorators EsObject generates metadata 8`] = ` 52 | Object { 53 | "aProperty": undefined, 54 | } 55 | `; 56 | 57 | exports[`decorators EsObject generates metadata 9`] = `undefined`; 58 | 59 | exports[`decorators EsObject generates metadata 10`] = ` 60 | Object { 61 | "type": "keyword", 62 | } 63 | `; 64 | 65 | exports[`decorators EsObject generates metadata 11`] = `undefined`; 66 | 67 | exports[`decorators EsObject generates metadata 12`] = ` 68 | Object { 69 | "keyword": undefined, 70 | } 71 | `; 72 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@smartive/es-model", 3 | "version": "0.0.0-development", 4 | "description": "Annotate your datastructure to generate an elasticsearch index template", 5 | "main": "index.js", 6 | "typings": "index.d.ts", 7 | "scripts": { 8 | "clean": "del-cli ./build ./coverage", 9 | "build": "npm run clean && tsc -p ./config/tsconfig.build.json", 10 | "develop": "npm run clean && tsc -p .", 11 | "lint": "tslint -c ./tslint.json -p ./config/tsconfig.build.json", 12 | "test": "npm run lint && npm run clean && jest -c ./jest.json", 13 | "test:watch": "npm run clean && jest -c ./jest.json --watch", 14 | "typedoc": "del-cli ./docs && typedoc --ignoreCompilerErrors --out ./docs --mode file --tsconfig ./config/tsconfig.build.json ./src/", 15 | "semantic-release": "semantic-release" 16 | }, 17 | "engines": { 18 | "node": ">=10", 19 | "npm": "*" 20 | }, 21 | "publishConfig": { 22 | "access": "public" 23 | }, 24 | "repository": { 25 | "type": "git", 26 | "url": "https://github.com/smartive/es-model.git" 27 | }, 28 | "keywords": [ 29 | "elasticsearch" 30 | ], 31 | "author": "Nicola Marcacci Rossi ", 32 | "license": "MIT", 33 | "bugs": { 34 | "url": "https://github.com/smartive/es-model/issues" 35 | }, 36 | "homepage": "https://github.com/smartive/es-model#readme", 37 | "dependencies": { 38 | "reflect-metadata": "^0.1.13" 39 | }, 40 | "devDependencies": { 41 | "@smartive/tslint-config": "^7.0.1", 42 | "@types/jest": "^24.0.17", 43 | "del-cli": "^2.0.0", 44 | "jest": "^24.8.0", 45 | "semantic-release": "^15.13.18", 46 | "ts-jest": "^24.0.2", 47 | "tslint": "^5.18.0", 48 | "tsutils": "^3.14.1", 49 | "typedoc": "^0.24.0", 50 | "typescript": "^5.0.0" 51 | }, 52 | "peerDependencies": { 53 | "typescript": "3.x || 4.x || 5.x" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/decorators.spec.ts: -------------------------------------------------------------------------------- 1 | import 'reflect-metadata'; 2 | 3 | import { EsField } from '../src/decorators/EsField'; 4 | import { EsMapping } from '../src/decorators/EsMapping'; 5 | import { EsProperty } from '../src/decorators/EsProperty'; 6 | import { EsSubMapping } from '../src/decorators/EsSubMapping'; 7 | import { getMetadata } from '../src/utils/metadata'; 8 | 9 | describe('decorators', () => { 10 | describe('EsObject ', () => { 11 | it('generates metadata', () => { 12 | @EsSubMapping() 13 | class Obj { 14 | @EsProperty() 15 | aProperty: string; 16 | } 17 | 18 | @EsSubMapping({ type: 'nested' }) 19 | class Nested { 20 | @EsProperty() 21 | aProperty: string; 22 | } 23 | 24 | @EsSubMapping({ type: 'keyword' }) 25 | class WithFields { 26 | @EsField() 27 | keyword: string; 28 | } 29 | 30 | @EsMapping({ 31 | dynamic: 'strict', 32 | _source: { 33 | enabled: true, 34 | }, 35 | }) 36 | class A { 37 | @EsProperty() 38 | keyword: string; 39 | 40 | @EsProperty({ type: String }) 41 | keywordArray: string[]; 42 | 43 | @EsProperty({ type: 'text' }) 44 | text: string; 45 | 46 | @EsProperty({ type: Obj }) 47 | b: Obj; 48 | 49 | @EsProperty({ type: Nested }) 50 | c: Nested; 51 | 52 | @EsProperty({ type: WithFields }) 53 | withFields: WithFields; 54 | } 55 | 56 | expect(getMetadata(A.prototype)).toMatchSnapshot(); 57 | expect(getMetadata(A.prototype, 'properties')).toMatchSnapshot(); 58 | expect(getMetadata(A.prototype, 'fields')).toMatchSnapshot(); 59 | expect(getMetadata(Obj.prototype)).toMatchSnapshot(); 60 | expect(getMetadata(Obj.prototype, 'properties')).toMatchSnapshot(); 61 | expect(getMetadata(Obj.prototype, 'fields')).toMatchSnapshot(); 62 | expect(getMetadata(Nested.prototype)).toMatchSnapshot(); 63 | expect(getMetadata(Nested.prototype, 'properties')).toMatchSnapshot(); 64 | expect(getMetadata(Nested.prototype, 'fields')).toMatchSnapshot(); 65 | expect(getMetadata(WithFields.prototype)).toMatchSnapshot(); 66 | expect(getMetadata(WithFields.prototype, 'properties')).toMatchSnapshot(); 67 | expect(getMetadata(WithFields.prototype, 'fields')).toMatchSnapshot(); 68 | }); 69 | }); 70 | }); 71 | -------------------------------------------------------------------------------- /src/utils/template.ts: -------------------------------------------------------------------------------- 1 | import { Field, Fields, MappingMeta, SubMappingMeta } from '../models/Mapping'; 2 | import { getMetadata, getPrimitiveType } from './metadata'; 3 | 4 | export interface EsIndexTemplateMappings { 5 | [name: string]: EsIndexTemplateMapping; 6 | } 7 | 8 | export interface EsIndexTemplateMapping { 9 | dynamic?: 'strict'; 10 | _source?: { 11 | enabled: boolean; 12 | }; 13 | properties: EsIndexTemplateMappingFields; 14 | } 15 | 16 | export interface EsIndexTemplateMappingFields { 17 | [name: string]: EsIndexTemplateMappingField; 18 | } 19 | 20 | export interface EsIndexTemplateMappingField { 21 | type: string; 22 | include_in_parent?: boolean; 23 | search_analyzer?: string; 24 | analyzer?: string; 25 | format?: string; 26 | properties?: EsIndexTemplateMappingFields; 27 | fields?: EsIndexTemplateMappingFields; 28 | } 29 | 30 | export function buildMappings(types: Function[]): EsIndexTemplateMappings { 31 | const mappings: EsIndexTemplateMappings = {}; 32 | 33 | for (const type of types) { 34 | const { name, mapping } = buildMapping(type.prototype); 35 | mappings[name || type.name.toLowerCase()] = mapping; 36 | } 37 | 38 | return mappings; 39 | } 40 | 41 | function buildMapping(type: Function): { name: string | undefined, mapping: EsIndexTemplateMapping } { 42 | const mapping: EsIndexTemplateMapping = { 43 | properties: {}, 44 | }; 45 | let name: string | undefined; 46 | const meta = getMetadata(type); 47 | 48 | if (meta) { 49 | name = meta.name; 50 | mapping._source = meta._source; 51 | mapping.dynamic = meta.dynamic; 52 | } 53 | 54 | const properties = getMetadata(type, 'properties'); 55 | if (properties) { 56 | mapping.properties = buildFields(properties, type); 57 | } 58 | 59 | return { name, mapping }; 60 | } 61 | 62 | function buildFields(data: Fields, type: Function): EsIndexTemplateMappingFields { 63 | const fields: EsIndexTemplateMappingFields = {}; 64 | for (const name of Object.keys(data)) { 65 | fields[name] = buildField(data[name], name, type); 66 | } 67 | return fields; 68 | } 69 | 70 | const PRIMITIVE_TYPES: { [key: string]: string } = { 71 | String: 'keyword', 72 | Boolean: 'boolean', 73 | }; 74 | 75 | function buildField(field: Field, name: string, type: Function): EsIndexTemplateMappingField { 76 | let res: EsIndexTemplateMappingField = { 77 | type: 'keyword', 78 | }; 79 | const primitiveType = getPrimitiveType(type, name); 80 | if (primitiveType && PRIMITIVE_TYPES[primitiveType.name]) { 81 | res.type = PRIMITIVE_TYPES[primitiveType.name]; 82 | } 83 | 84 | if (field) { 85 | res = { 86 | ...field, 87 | ...res, 88 | }; 89 | if (field.type) { 90 | if (field.type instanceof Function) { 91 | if (PRIMITIVE_TYPES[field.type.name]) { 92 | res.type = PRIMITIVE_TYPES[field.type.name]; 93 | } else { 94 | res.type = 'object'; 95 | 96 | const prototype = field.type.prototype; 97 | 98 | const fieldTypeMeta = getMetadata(prototype); 99 | if (fieldTypeMeta) { 100 | res = { ...res, ...fieldTypeMeta }; 101 | } 102 | 103 | const properties = getMetadata(prototype, 'properties'); 104 | if (properties) { 105 | res.properties = buildFields(properties, prototype); 106 | } 107 | const fields = getMetadata(prototype, 'fields'); 108 | if (fields) { 109 | res.fields = buildFields(fields, prototype); 110 | } 111 | } 112 | } else if (field.type) { 113 | res.type = field.type; 114 | } 115 | } 116 | } 117 | 118 | return res; 119 | } 120 | --------------------------------------------------------------------------------