Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up

A Mongoose Plugin. Automatically assign values to different fields on a document.

License

NotificationsYou must be signed in to change notification settings

mernxl/mongoose-id-assigner

Repository files navigation

npmBuild StatuscodecovDependencies StateTypeScript compatibleFlowType compatibleCode Style Prettiersemantic-release

AMongoose Plugin. Easily Manage fields that need an id(unique value) on your mongoose model. This plugin does the work of generating, incrementing, and assigning those values(unique) to those fields.

This plugin assigns values base on your configurations, be itMongoDB's ObjectId,UUID,Number Increments,String Generators or you provide your customnextIdFunction.

It creates a collection with nameid_assigner that store thenextId for a configured field. This only happens if you have fields configured with typeString orNumber. AsObjectId andUUID can be generated locally unique, they do not use this collection for assigning values.

This is the perfect tool if you wish to work withdiscriminators and have_id field(and/or other fields) values different amongst the discriminators instances. See examples below for demonstration.


Table of Content

Installation

yarn add mongoose-id-assignerornpm install mongoose-id-assigner

If you wish to useUUIDs FieldTypes, then you need to add this package,uuid.

yarn add uuid

Basic Usage

You could create the plugin from theMongooseIdAssigner Constructor, which returns a instance which you could use to querynextIds.

from constructor(schema: Schema, options: AssignerPluginOptions): MongooseIdAssigner

...constExampleIA=newMongooseIdAssigner(exampleSchema,options);exampleModel=mongoose.model('Example',exampleSchema);// always call the initialise, to ensure assigner initialised before calling getNextId// assigners are always auto initialised on every first saveawaitExampleIA.initialise(exampleModel);awaitExampleIA.getNextId('fieldName');// nextId...

Alternatively you have the plugin setup by calling the staticplugin method.

static plugin(schema: Schema, options: AssignerPluginOptions): MongooseIdAssigner

...exampleSchema.plugin(MongooseIdAssigner.plugin,options);...

AssignerPluginOptions

ParameterTypeDescription
modelNameString(Required)Name of the Model your are working with. If discriminators, then provide baseModel Name.
fieldsAssignerFieldsConfigMap (Optional)The configuration Map of the fields you want the assigner to assign ids to. If undefined, then plugin assigns ids to_id field, (ObjectId).
discriminatorsDiscriminatorConfigMap (Optional)An Object with key being adiscriminatorName and value a Configuration Map for fields on that discriminator that need unique values. Any discriminator without a fieldConfig will use that of the baseModelk

Points to Note

  • At every Network Assigner init(i.e Assigner with Number, String FieldConfigTypes), the Assigner(for a Model) refreshes and syncs with the db stored options. Take example micro-service cluster,the last app to init always gives most recent field configs, if the db have a field that is not in the most recent field config, it is auto dropped.Therefore always make sure all your micro-service clusters start up with same fieldsConfigs as the last to start rewrites the db and only keeps nextIds.

  • Always Setup plugin before creating Mongoose Model for that schema.

  • Always set FieldConfig Type to reflect schema's path Type, that is if schema Type isNumber, then useNumber FieldConfigType.SeeExampleSchema below with its associated IdAssigners.

Examples

Lets create our Mongoose Schema.

// schema.js  We would be reusing this schema for brevity (clone)import*asmongoosefrom'mongoose';constExampleSchema=newmongoose.Schema({_id:String,photoId:Number,emailId:String,personId:String,uuidField:Buffer,uuidFieldString:String,uuidFieldBuffer:Buffer,objectIdField:mongoose.Schema.Types.ObjectId,});

Configuration methods

Method 1: Quick Config, Intended for usage withschema.plugin(...). It initialises only when the first document is about to be saved.

Options Type:AssignerPluginOptions.

constoptions:AssignerPluginOptions={modelName:'ExampleModel',fields:{// if no _id field config, assigner auto adds _id field with type = "ObjectId"uuidFieldString:'UUID',}};ExampleSchema.plugin(MongooseIdAssigner.plugin,options);// Saving the first doc triggers the IdAssigner initialisation for this model.constExampleModel=mongoose.model('ExampleModel',ExampleSchema);constdoc=awaitExampleModel.create({name:'Mernxl'});console.log(doc._id)--->'5b57a1d929239e59b4e3d7f3'// schema field type is Stringconsole.log(doc.uuidFieldString)--->'7729e2e0-8f8b-11e8-882d-2dade78bb893'

Method 2: Using theMongooseIdAssigner constructor, it takes in theschema as first parameter, then options as second, returning IdAssigner Instance.You can now initialise and use the IdAssigner instance to requestnextIds for particular fields.

Options Type:AssignerOptions.

constoptions:AssignerPluginOptions={modelName:'ExampleModel',fields:{_id:{type:FieldConfigTypes.String,separator:'T',nextId:'34T5565',},uuidFieldBuffer:{type:FieldConfigTypes.UUID,version:4,asBinary:true,versionOptions:{rng:(Function)Random #generatorfunctionthatreturnsanArray[16]ofbytevalues(0-255)// UUID doc},},},};constExampleIA=newMongooseIdAssigner(ExampleSchema,options);constExampleModel=mongoose.model('ExampleModel',ExampleSchema);constdoc1=awaitExampleModel.create({name:'Mongoose'});console.log(doc1._id);--->'34T5565'console.log(doc1.uuidFieldBuffer);--->Binary{_bsontype:'Binary',sub_type:4,position:36,buffer:B...}// it getOnly, does not write off in db// TODO: Implement hold nextId with timeoutconsole.log((awaitExampleIA.getNextId('_id')))--->'34T5566'constdoc2=awaitExampleModel.create({name:'IdAssigner'});console.log(doc2._id);--->'34T5566'console.log(doc2.uuidFieldBuffer);--->Binary{_bsontype:'Binary',sub_type:4,position:36,buffer:B...}

Working with Discriminators

You may have a discriminator Instance and want to have different id Types, or fields on one discriminator need to be autogenerated uniquely, here is an example;

constCharacterSchema=newmongoose.Schema({_id:String,kind:String,someId:Number,friends:[String],},{discriminatorKey:'kind',});constPersonSchema=newmongoose.Schema({license:String,});constDroidSchema=newmongoose.Schema({make:String,});constoptions:AssignerPluginOptions={modelName:'example10',fields:{someId:4444,},discriminators:{Person:{_id:FieldConfigTypes.ObjectId,license:'786-TSJ-000',// default separator `-`},Droid:{_id:FieldConfigTypes.UUID,make:{type:FieldConfigTypes.String,nextId:'18Y4433',separator:'Y',},},},};constCharacterIA=newMongooseIdAssigner(characterSchema,options);constcharacterModel=mongoose.model('example10',characterSchema);constpersonModel=characterModel.discriminator('Person',personSchema);constdroidModel=characterModel.discriminator('Droid',droidSchema);constcharacter=awaitcharacterModel.create({friends:'placeholder'});constperson=awaitpersonModel.create({friends:'placeholder'});constdroid=awaitdroidModel.create({friends:'placeholder'});constperson1=awaitpersonModel.create({friends:'placeholder'});constdroid1=awaitdroidModel.create({friends:'placeholder'});console.log(character._id)--->'5b59d98617e2edc57ede52b8'console.log(character.someId)--->4444console.log((awaitCharacterIA.getNextId('someId')))--->4445console.log(person._id)--->'5b59d98617e2edc57ede52ba'console.log(person.someId)--->4445console.log(person.license)--->'786-TSJ-000'console.log(droid._id)--->'48db52c0-90df-11e8-b43b-ffe3d317727b'console.log(droid.someId)--->4446console.log(droid.make)--->'18Y4433'console.log((awaitCharacterIA.getNextId('make','Droid')))--->'18Y4434'console.log(person._id)--->'5b59d98617e2edc57ede52bb'console.log(person1.someId)--->4447console.log(person1.license)--->'786-TSJ-001'console.log((awaitCharacterIA.getNextId('license','Person')))--->'786-TSJ-002'console.log(droid1._id)--->'eb185fb0-90df-11e8-800d-dff50a2d22a3'console.log(droid1.someId)--->4448console.log(droid1.make)--->'18Y4434'

NextIdWIP

It may arise that you need to query and use the nextId to at the front-end. In this case, you just need toget an instance of the Assigner, then use thegetNextId method. It is async method as it queries forNumber andString cases.

Strain test

  • Performs the task below on a locally hosted db instance.
  • On CI/CD environment, tests ran usingmongodb-memory-server, on v3.4, v3.6, v4.0 and v4.2 on node v12, v10, v8
// using ts :)import*asmongoosefrom'mongoose';import{AssignerOptions,FieldConfigTypes,localStateStore,MongooseIdAssigner}from'./src';describe('MongooseIdAssigner',()=>{it('should be robust enough to avoid duplicates',async()=>{constoptions:AssignerPluginOptions={modelName:'example8',fields:{_id:'33333',photoId:44444,emailId:'55555',personId:{type:FieldConfigTypes.String,nextId:'SPEC-7382-4344-3232',separator:'-',},uuidFieldString:{type:FieldConfigTypes.UUID,},uuidFieldBuffer:{type:FieldConfigTypes.UUID,version:1,asBinary:true,},objectIdField:FieldConfigTypes.ObjectId,},};try{constExampleIA=newMongooseIdAssigner(exampleSchema,options);exampleModel=mongoose.model('example8',exampleSchema);expect(ExampleIA.readyState).toBe(0);// initialising// initialise to ensure that// model is set and db is connected// before performing heavy tasks// or you can set max event listeners to 100 to suppress warnings on waitsawaitExampleIA.initialise(exampleModel);expect(ExampleIA.readyState).toBe(1);constpromises=[];for(leti=0;i<100;i++){promises.push(exampleModel.create({personId:'mernxl'}));}constdocs:any[]=awaitPromise.all(promises);for(leti=0;i<100;i++){const_id=docs[i]._id;constphotoId=docs[i].photoId;constemailId=docs[i].emailId;constpersonId=docs[i].personId;constuuidFieldString=docs[i].uuidFieldString;constuuidFieldBuffer=docs[i].uuidFieldBuffer;constobjectIdField=docs[i].objectIdField;expect(typeofphotoId).toBe('number');expect(typeofemailId).toBe('string');expect(personId).toMatch(/(SPEC-7382-4344-3)\d+/);expect(objectIdField).toBeInstanceOf(Types.ObjectId);expect(typeofuuidFieldString).toBe('string');expect(uuidFieldBuffer).toBeInstanceOf(Binary);for(constcDocofdocs){if(_id===cDoc._id){continue;}expect(photoId).not.toBe(cDoc.photoId);expect(emailId).not.toBe(cDoc.emailId);expect(personId).not.toBe(cDoc.personId);expect(objectIdField).not.toBe(cDoc.objectIdField);expect(uuidFieldString).not.toEqual(cDoc.uuidFieldString);expect(uuidFieldBuffer).not.toEqual(cDoc.uuidFieldBuffer);}}}catch(e){expect(e).toBeUndefined();}});});

TypeDefinitions

/** * If Options does not contain fields(AssignerFieldsConfigMap), * Then setup assigner for _id field, does not use network */interfaceAssignerOptions{fields?:AssignerFieldsConfigMap;discriminators?:DiscriminatorConfigMap;}interfaceAssignerPluginOptions{modelName:string;fields?:AssignerFieldsConfigMap;discriminators?:DiscriminatorConfigMap;}/** * fieldConfig = string, then nextId = string, default incrementer, * fieldConfig = number, then nextId = number, incrementBy = 1 * fieldConfig = boolean(true), then fieldType = ObjectId * fieldConfig = GUID | UUID, then use UUID v1 */interfaceAssignerFieldsConfigMap{[fieldName:string]:|FieldConfig|string|number|boolean|FieldConfigTypes.GUID|FieldConfigTypes.UUID;}/** * A map of discriminatorName(modelName) and its own AssignerFieldsConfigMap */interfaceDiscriminatorConfigMap{[discriminatorName:string]:AssignerFieldsConfigMap;}/** * *@property {Boolean} noSpace - noSpace, insure we consume all possible values, i.e. we must have 1, 2, 3, 4 * order doesn't matter but all those keys must be present, no 1, 3, 4, 6. * If noSpace is true, then on holdTimeout, that nextId will be use on any newly saving doc, else nextId discarded * *@property {Number} maxHold[50] - As there may be performance issues when holding ids, maxHold will be set, *@property {String} holdTimeout - default timeout string, must be parse-able to number by `ms` plugin *@property {Number} holdTimeout - default timeout millis, gotten id stays onHold for this length of time *@property {Boolean} holdTimeout - if true, will always getNextId with default timeout of `1 week` else use getOnly on getNextIds */typeFieldConfig={index?:boolean;unique?:boolean;noSpace?:boolean;}&(|DefaultFieldConfig|StringFieldConfig|NumberFieldConfig|UUIDFieldConfig);enumFieldConfigTypes{UUID='UUID',GUID='GUID',String='String',Number='Number',ObjectId='ObjectId',}exportinterfaceDefaultFieldConfig{type:FieldConfigTypes.ObjectId;}exportinterfaceStringFieldConfig{type:FieldConfigTypes.String;nextId:string;separator?:string;nextIdFunction?:(nextId:string)=>string;}exportinterfaceNumberFieldConfig{type:FieldConfigTypes.Number;nextId:number;incrementBy?:number;nextIdFunction?:(nextId:number,incrementBy?:number)=>number;}exportinterfaceUUIDFieldConfig{type:FieldConfigTypes.UUID|FieldConfigTypes.GUID;asBinary?:boolean;// default stringversion?:1|4;// supports 1 and 4, default 1versionOptions?:any;}

CONTRIBUTIONS

If you find a bug, do well to create anissue so we fix things up in a flash.Have an awesome new feature to add to library, feel free to open aPR

NextIdFunctions (Incrementer)

If you have a superb nextIdFunction, and you wish it can be added to the list of nextIdFunctions, feel free to drop apull request and submit your function.

LICENSE

MIT


[8]ページ先頭

©2009-2025 Movatter.jp