{"id":32073462,"url":"https://github.com/yurigor/mongoose-slug-updater","last_synced_at":"2025-10-19T07:01:53.456Z","repository":{"id":44596926,"uuid":"152946370","full_name":"YuriGor/mongoose-slug-updater","owner":"YuriGor","description":"Schema-based slug plugin for Mongoose - single/compound - unique over collection/group - nested docs/arrays - relative/abs paths - sync on change: create/save/update/updateOne/updateMany/findOneAndUpdate tracked - $set operator - counter/shortId","archived":false,"fork":false,"pushed_at":"2022-02-06T02:14:50.000Z","size":170,"stargazers_count":41,"open_issues_count":10,"forks_count":13,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-08-09T11:22:33.929Z","etag":null,"topics":["mongoose","permalink","pretty-url","slug","slugify","unique","url"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/YuriGor.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null},"funding":{"github":["YuriGor"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"custom":null}},"created_at":"2018-10-14T06:34:38.000Z","updated_at":"2025-02-13T05:29:19.000Z","dependencies_parsed_at":"2022-09-26T21:50:55.345Z","dependency_job_id":null,"html_url":"https://github.com/YuriGor/mongoose-slug-updater","commit_stats":null,"previous_names":[],"tags_count":26,"template":false,"template_full_name":null,"purl":"pkg:github/YuriGor/mongoose-slug-updater","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YuriGor%2Fmongoose-slug-updater","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YuriGor%2Fmongoose-slug-updater/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YuriGor%2Fmongoose-slug-updater/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YuriGor%2Fmongoose-slug-updater/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/YuriGor","download_url":"https://codeload.github.com/YuriGor/mongoose-slug-updater/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/YuriGor%2Fmongoose-slug-updater/sbom","scorecard":{"id":155750,"data":{"date":"2025-08-11","repo":{"name":"github.com/YuriGor/mongoose-slug-updater","commit":"3465c7fcae0a97ef28b13fef0ca237d8d780b291"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.2,"checks":[{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Code-Review","score":1,"reason":"Found 4/24 approved changesets -- score normalized to 1","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 10 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-16T11:43:02.113Z","repository_id":44596926,"created_at":"2025-08-16T11:43:02.113Z","updated_at":"2025-08-16T11:43:02.113Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279730590,"owners_count":26217527,"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","status":"online","status_checked_at":"2025-10-19T02:00:07.647Z","response_time":64,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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":["mongoose","permalink","pretty-url","slug","slugify","unique","url"],"created_at":"2025-10-19T07:01:16.759Z","updated_at":"2025-10-19T07:01:53.450Z","avatar_url":"https://github.com/YuriGor.png","language":"JavaScript","funding_links":["https://github.com/sponsors/YuriGor"],"categories":[],"sub_categories":[],"readme":"# mongoose-slug-updater\n\nSophisticated slugifier plugin for Mongoose.\n\n\u003e There is also [PlanZed.org](https://planzed.org/) - awesome cloud mind map app created by the author of mongoose-slug-updater.  \nPlz check it, it's free and I need [feedback](https://github.com/YuriGor/PlanZed.org) 😉 \n\n[![npm](https://img.shields.io/npm/v/mongoose-slug-updater.svg)](https://www.npmjs.com/package/mongoose-slug-updater) [![Travis (.org)](https://api.travis-ci.org/YuriGor/mongoose-slug-updater.svg?branch=master)](https://travis-ci.org/YuriGor/mongoose-slug-updater) [![Coverage Status](https://coveralls.io/repos/github/YuriGor/mongoose-slug-updater/badge.svg?branch=master)](https://coveralls.io/github/YuriGor/mongoose-slug-updater?branch=master) \u003cbr\u003e\n[![NPM](https://nodei.co/npm/mongoose-slug-updater.png?compact=true)](https://nodei.co/npm/mongoose-slug-updater/)\n\nFeatures:\n- [Intuitive schema-based declaration](#basic-usage)\n- Single or [compound](#multiple-fields-to-create-the-slug) slugs, based on any fields of the document\n- [Nested docs and arrays support](#nested-docs-relative-and-absolute-paths)\n- [Relative or absolute paths to the related fields](#nested-docs-relative-and-absolute-paths)\n- Initial generation and [updating](#updating-slug-or-keeping-it-permanent) (or not if [permanent](#permanent-option)) on any change of related fields,\n- `create`, `save`, `update`, `updateOne`, `updateMany` or `findOneAndUpdate` methods supported, and you can [switch them on/off](#updating-slug-or-keeping-it-permanent).\n- [$set](#updating-by-deep-path-via-set-operator) update operator support with deep modification paths to any nested doc or array.\n- [Unique slugs](#unique-slug-field) (with unique index or not), collection-wide or [by group](#unique-slug-within-a-group), nested too.\n- Updating of unique slugs in case of changed related fields or group criteria.\n- [Counter](#example-counter) and [shortId](#example-random) styles of duplication conflict resolving.\n- Autoloading of missing related data required to build slug correctly.\n\n## Installation\n\nThe best way to install it is using **npm**\n\n```sh\nnpm install mongoose-slug-updater --save\n```\n\n## Loading\n\n```js\nvar slug = require('mongoose-slug-updater');\n```\n\n## Initialization\n\n```js\nvar mongoose = require('mongoose');\nmongoose.plugin(slug);\n```\n\n## Usage\n\nThis plugin is based on the idea of using the **mongoose schema** as the way to check the use of slug fields.\n\nThe plugin checks and updates automatically the _slug field_ with the correct slug.\n\n### Basic Usage\n\nIf you only want to create the slug based on a simple field.\n\n```js\nvar mongoose = require('mongoose'),\n    slug = require('mongoose-slug-updater'),\n    mongoose.plugin(slug),\n    Schema = mongoose.Schema,\n    schema = new Schema({\n        title: String,\n        slug: { type: String, slug: \"title\" }\n});\n```\n\n### Multiple slug fields\n\nYou can add as many slug fields as you wish\n\n```js\nvar mongoose = require('mongoose'),\n    slug = require('mongoose-slug-updater'),\n    mongoose.plugin(slug),\n    Schema = mongoose.Schema,\n    schema = new Schema({\n        title: String,\n        subtitle: String,\n        slug: { type: String, slug: \"title\" },\n        slug2: { type: String, slug: \"title\" },\n        slug3: { type: String, slug: \"subtitle\" }\n});\n```\n\n### Multiple fields to create the slug\n\nIf you want, you can use more than one field in order to create a new slug field.\n\n```js\nvar mongoose = require('mongoose'),\n    slug = require('mongoose-slug-updater'),\n    mongoose.plugin(slug),\n    Schema = mongoose.Schema,\n    schema = new Schema({\n        title: String,\n        subtitle: String,\n        slug: { type: String, slug: [\"title\", \"subtitle\"] }\n});\n```\n### Transform Slug\n\nThis option accepts a funtion which receives actual field value and can be used to tranform value before generating slug.\n\n```js\nvar mongoose = require('mongoose'),\n    slug = require('mongoose-slug-updater'),\n    mongoose.plugin(slug),\n    Schema = mongoose.Schema,\n    schema = new Schema({\n        title: String,\n        subtitle: String,\n        slug: { type: String, slug: [\"title\", \"subtitle\"], transform: v =\u003e stripHtmlTags(v) }\n});\n```\n\n### Unique slug field\n\nTo create a unique slug field, you must only add add the _unique: true_ parameter in the path (also, this way the default mongo unique index gets created)\n\n```js\nvar mongoose = require('mongoose'),\n    slug = require('mongoose-slug-updater'),\n    mongoose.plugin(slug),\n    Schema = mongoose.Schema,\n    schema = new Schema({\n        title: String,\n        subtitle: String,\n        slug: { type: String, slug: [\"title\", \"subtitle\"], unique: true }\n});\n```\n\nIf `unique` or `uniqueSlug` is set, the plugin searches in the mongo database, and if the slug already exists in the collection, it appends to the slug a separator (default: \"-\") and a random string (generated with the shortid module).\n\n#### example random\n```js\nmongoose.model('Resource').create({\n    title: \"Am I wrong, fallin' in love with you!\",\n    subtitle: \"tell me am I wrong, well, fallin' in love with you\",\n}); // slug -\u003e 'am-i-wrong-fallin-in-love-with-you'\n\nmongoose.model('Resource').create({\n    title: \"Am I wrong, fallin' in love with you!\",\n    subtitle: \"tell me am I wrong, well, fallin' in love with you\",\n}); // slug -\u003e 'am-i-wrong-fallin-in-love-with-you-Nyiy4wW9l'\n\nmongoose.model('Resource').create({\n    title: \"Am I wrong, fallin' in love with you!\",\n    subtitle: \"tell me am I wrong, well, fallin' in love with you\",\n}); // slug -\u003e 'am-i-wrong-fallin-in-love-with-you-NJeskEPb5e'\n```\n\nAlternatively you can modify this behaviour and instead of appending a random string, an incremental counter will be used. For that to happen, you must use the parameter `slugPaddingSize` specifying the total length of the counter:\n\n#### example counter\n```js\nvar mongoose = require('mongoose'),\n    slug = require('mongoose-slug-updater'),\n    mongoose.plugin(slug),\n    Schema = mongoose.Schema,\n    schema = new Schema({\n        title: String,\n        subtitle: String,\n        slug: { type: String, slug: [\"title\", \"subtitle\"], slugPaddingSize: 4,  unique: true }\n});\n\nmongoose.model('Resource').create({\n    title: 'Am I wrong, fallin\\' in love with you!',\n    subtitle: \"tell me am I wrong, well, fallin' in love with you\"\n}) // slug -\u003e 'am-i-wrong-fallin-in-love-with-you'\n\nmongoose.model('Resource').create({\n    title: 'Am I wrong, fallin\\' in love with you!',\n    subtitle: \"tell me am I wrong, well, fallin' in love with you\"\n}) // slug -\u003e 'am-i-wrong-fallin-in-love-with-you-0001'\n\nmongoose.model('Resource').create({\n    title: 'Am I wrong, fallin\\' in love with you!',\n    subtitle: \"tell me am I wrong, well, fallin' in love with you\"\n}) // slug -\u003e 'am-i-wrong-fallin-in-love-with-you-0002'\n```\n\nIf you don't want to define your field as unique for some reasons, but still need slug to be unique,\u003cbr\u003e\nyou can use `uniqueSlug:true` option instead of `unique`.\u003cbr\u003e\nThis option will not cause index creation, but still will be considered by the plugin.\n\n`forceIdSlug` option will append shortId even if no duplicates were found.\u003cbr\u003e\nThis is useful for applications with high chance of concurrent modification of unique fields.\u003cbr\u003e\n\nCheck for conflict made by plugin is not atomic with subsequent insert/update operation,\u003cbr\u003e\nso there is a possibility of external change of data in the moment between check and write.\u003cbr\u003e\nIf this happened, mongo will throw unique index violation error.\u003cbr\u003e\nChances of such case higher for counter unique mode, but with shortId this is possible too.\u003cbr\u003e\nYou can just retry operation, so plugin will check collection again and regenerate correct unique slug.\u003cbr\u003e\nOr you can set `forceIdSlug` option - this will solve the problem completely, but you will pay for this by less readabilty of your slugs, because they will *always* be appended with random string.\n\nIn most cases write operations not so frequent to care about possible conflicts.\n\nnote: `forceIdSlug` option will also overwite `unique` to the `true`, and `slugPaddingSize` option will be ignored.\n\n### Unique slug within a group\n\nSometimes you only want slugs to be unique within a specific group.\u003cbr\u003e\nThis is done with the `uniqueGroupSlug` property which is an array of fields to group by:\n\n#### example unique per group (using the field named 'group')\n\n```js\nResourceGroupedUnique = new mongoose.Schema({\n    title: { type: String },\n    subtitle: { type: String },\n    group: { type: String },\n    uniqueSlug: {\n        type: String,\n        uniqueGroupSlug: ['group'],\n        slugPaddingSize: 4,\n        slug: 'title',\n        index: true,\n    },\n});\n\nmongoose.model('ResourceGroupedUnique').create({\n    title: \"Am I wrong, fallin' in love with you!\",\n    subtitle: \"tell me am I wrong, well, fallin' in love with you\",\n    group: 'group 1',\n}); // slug -\u003e 'am-i-wrong-fallin-in-love-with-you'\n\nmongoose.model('ResourceGroupedUnique').create({\n    title: \"Am I wrong, fallin' in love with you!\",\n    subtitle: \"tell me am I wrong, well, fallin' in love with you\",\n    group: 'group 2',\n}); // slug -\u003e 'am-i-wrong-fallin-in-love-with-you'\n\nmongoose.model('ResourceGroupedUnique').create({\n    title: \"Am I wrong, fallin' in love with you!\",\n    subtitle: \"tell me am I wrong, well, fallin' in love with you\",\n    group: 'group 1',\n}); // slug -\u003e 'am-i-wrong-fallin-in-love-with-you-0001'\n\nmongoose.model('ResourceGroupedUnique').create({\n    title: \"Am I wrong, fallin' in love with you!\",\n    subtitle: \"tell me am I wrong, well, fallin' in love with you\",\n    group: 'group 2',\n}); // slug -\u003e 'am-i-wrong-fallin-in-love-with-you-0001'\n```\n\n**Important: you must not have a `unique: true` option, but it's a good idea to have an `index: true` option.**\n\n### Nested unique slugs\nMongoDB supports unique index for nested arrays elements, but he checks for duplication conflicts only on per-document basis, so inside document duplicate nested array's elements are still allowed. \u003cbr\u003e\nmongoose-slug-updater works differently. It checks slug for duplicates both in current documentts's nested array and in other documents, considering uniqueGroupSlug option, if specified.\n\n#### example of nested unique slugs\n```js\nconst UniqueNestedSchema = new mongoose.Schema({\n  children: [\n    {\n      subchildren: [\n        {\n          title: { type: String },\n          slug: {\n            type: String,\n            slug: 'title',\n            unique: true // 'global' unique slug\n            slugPaddingSize: 4,\n          },\n          slugLocal: {\n            type: String,\n            slug: 'title',\n            index: true,\n            slugPaddingSize: 4,\n            uniqueGroupSlug: '/_id',// slug unique within current document\n          },\n        },\n      ],\n    },\n  ],\n});\n```\n```js\nmongoose.model('UniqueNestedSchema').create({\n  children:[\n    {\n      subchildren:[\n        {\n          title: \"Am I wrong, fallin' in love with you!\"\n          // slug       -\u003e 'am-i-wrong-fallin-in-love-with-you'\n          // slugLocal  -\u003e 'am-i-wrong-fallin-in-love-with-you'\n        },\n        {\n          title: \"Am I wrong, fallin' in love with you!\"\n          // slug       -\u003e 'am-i-wrong-fallin-in-love-with-you-0001'\n          // slugLocal  -\u003e 'am-i-wrong-fallin-in-love-with-you-0001'\n        },\n      ]\n    },\n    {\n      subchildren:[\n        {\n          title: \"Am I wrong, fallin' in love with you!\"\n          // slug       -\u003e 'am-i-wrong-fallin-in-love-with-you-0002'\n          // slugLocal  -\u003e 'am-i-wrong-fallin-in-love-with-you-0002'\n        },\n        {\n          title: \"Am I wrong, fallin' in love with you!\"\n          // slug       -\u003e 'am-i-wrong-fallin-in-love-with-you-0003'\n          // slugLocal  -\u003e 'am-i-wrong-fallin-in-love-with-you-0003'\n        },\n      ]\n    },\n  ]\n});\nmongoose.model('UniqueNestedSchema').create({\n  children:[\n    {\n      subchildren:[\n        {\n          title: \"Am I wrong, fallin' in love with you!\"\n          // slug       -\u003e 'am-i-wrong-fallin-in-love-with-you-0004'\n          // slugLocal  -\u003e 'am-i-wrong-fallin-in-love-with-you'\n        },\n        {\n          title: \"Am I wrong, fallin' in love with you!\"\n          // slug       -\u003e 'am-i-wrong-fallin-in-love-with-you-0005'\n          // slugLocal  -\u003e 'am-i-wrong-fallin-in-love-with-you-0001'\n        },\n      ]\n    },\n    {\n      subchildren:[\n        {\n          title: \"Am I wrong, fallin' in love with you!\"\n          // slug       -\u003e 'am-i-wrong-fallin-in-love-with-you-0006'\n          // slugLocal  -\u003e 'am-i-wrong-fallin-in-love-with-you-0002'\n        },\n        {\n          title: \"Am I wrong, fallin' in love with you!\"\n          // slug       -\u003e 'am-i-wrong-fallin-in-love-with-you-0007'\n          // slugLocal  -\u003e 'am-i-wrong-fallin-in-love-with-you-0003'\n        },\n      ]\n    },\n  ]\n});\n```\n\nIn case of change unique slug related fields (source fields from `slug` option or group criteria from `uniqueGroupSlug`) \u003cbr\u003e\nslug will be regenerated considering latest existing duplicate. Presence or lack of the older duplicates, including original slug, will not be taken into account.\n\n### Updating slug or keeping it permanent\n\nBy default slugs will be created/updated for any related fields changed by any of `create`(it's actually a `save` too), `save`, `update`, `updateOne`, `updateMany` and `findOneAndUpdate` operations.\nYou can specify which of supported methods should be watched:\n\n```js\nconst HooksSchema = new mongoose.Schema({\n    title: { type: String },\n    slug: {\n        type: String,\n        slug: 'title',\n        //by default all hooks are enabled\n        //slugOn:{ save: true, update: true, updateOne: true, updateMany: true, findOneAndUpdate: true }\n    },\n    slugNoSave: { type: String, slug: 'title', slugOn: { save: false } },\n    slugNoUpdate: { type: String, slug: 'title', slugOn: { update: false } },\n    slugNoUpdateOne: { type: String, slug: 'title', slugOn: { updateOne: false } },\n    slugNoUpdateMany: {\n        type: String,\n        slug: 'title',\n        slugOn: { updateMany: false },\n    },\n    slugNoFindOneAndUpdate: {\n        type: String,\n        slug: 'title',\n        slugOn: { findOneAndUpdate: false },\n    },\n});\n```\n\nNote, that flags will affect both creation and updating of documents. \u003cbr\u003e\nIf you disabled `save` and still want slug to be generated initially, `create` method will not work, \u003cbr\u003e\nbecacuse mongoose emits `save` event both for `save` and `create` methods. \u003cbr\u003e\nUse `upsert` option of `update***` methods instead.\n\nFor `update` and `updateMany` methods multiply affected records also handled, but be careful with performance,\nbecause one-by-one iteration over affected documents may happen in case of unique slugs.\u003cbr\u003e\nIn this case `_id` field is required.\n\nFor `update*` family of operations additional queries may be performed, to retrieve data missing in the query (fields not listed in the query but needed for compound or grouped unique slugs).\u003cbr\u003e\n\n#### `permanent` option\n\nIf you want to generate slug initially, but keep it unchanged during further modifications of related fields, use `permanent` flag like this:\n\n```js\nResourcePermanent = new mongoose.Schema({\n    title: { type: String },\n    subtitle: { type: String },\n    otherField: { type: String },\n    slug: { type: String, slug: ['title', 'subtitle'] }, //normal slug\n    titleSlug: { type: String, slug: 'title', permanent: true }, //permanent slug\n    subtitleSlug: {\n        type: String,\n        slug: 'subtitle',\n        permanent: true, //permanent option\n        slugPaddingSize: 4,\n    },\n});\n```\n\n### Nested docs. Relative and absolute paths.\nNested docs and arrays declared inline right in the scheme or as a nested schemas declared separately are also supported.\n\nSlug fields can be declared as relative or absolute(starting with slash) path to any point of current document.\n\nSince MongoDB uses dot path notation, colon `:` symbol used for relative paths as a reference to the parent, same as double dot `..` for file system paths.\n\nExample of scheme with inline nested docs:\n```js\nconst InlineSchema = new mongoose.Schema({\n  // root title\n  title: { type: String },\n  // root slug with relative path to root title\n  slug: { type: String, slug: 'title' },\n  // root slug with  absolute path to root title\n  absoluteSlug: { type: String, slug: '/title' },\n  // root slug with relative path to child title\n  childSlug: { type: String, slug: 'child.title' },\n  // root slug with absolute path to child title\n  absoluteChildSlug: { type: String, slug: '/child.title' },\n  // root slug with relative path to child's subchild title\n  subChildSlug: { type: String, slug: 'child.subChild.title' },\n  // root slug with relative path to the title of first children array element\n  childrenSlug0: { type: String, slug: 'children.0.title' },\n  // root slug with relative path to the title of 5th children array element\n  childrenSlug4: { type: String, slug: 'children.4.title' },\n  // root slug with relative path to the title of 4th subChildren' element of first children array element\n  subChildrenSlug3: { type: String, slug: 'children.0.subChildren.3.title' },\n  // root slug with relative path to the title of 8th subChildren' element of first children array element\n  subChildrenSlug7: { type: String, slug: 'children.0.subChildren.7.title' },\n  subChildrenSlug5SubChild: {\n    type: String,\n    // well, you see)\n    slug: 'children.0.subChildren.5.subChild.title',\n  },\n  subChildrenSlug2SubChild: {\n    type: String,\n    slug: 'children.0.subChildren.2.subChild.title',\n  },\n  child: {\n    title: { type: String },\n    // inside nested doc relative path starts from current object,\n    // so this is slug for child's title\n    slug: { type: String, slug: 'title' },\n    // absolute variant of path above, starting from root\n    absoluteSlug: { type: String, slug: '/child.title' },\n    // child's slug field generated for root title, absolute path\n    absoluteParentSlug: { type: String, slug: '/title' },\n    // relative path with parent reference `:`, so here root title will be used again.\n    relativeParentSlug: { type: String, slug: ':title' },\n    subChild: {\n      title: { type: String },\n      // relative path to the title of current nested doc,\n      // in absolute form it wil be /child.subChild.title\n      slug: { type: String, slug: 'title' },\n      // absolute path to the root title\n      absoluteParentSlug: { type: String, slug: '/title' },\n      // relative path to the parent title, /child.title in this case\n      relativeParentSlug: { type: String, slug: ':title' },\n      // parent of the parent is root, so ::title = /title here\n      relativeGrandParentSlug: { type: String, slug: '::title' },\n    },\n  },\n  // nested arrays work too\n  children: [\n    {\n      title: { type: String },\n      // title of current array element\n      slug: { type: String, slug: 'title' },\n      // root title\n      absoluteRootSlug: { type: String, slug: '/title' },\n      // child's title\n      absoluteChildSlug: { type: String, slug: '/child.title' },\n      // root title. Array itself not counted as a parent and skipped.\n      relativeRootSlug: { type: String, slug: ':title' },\n      // absolute path to 4th element of array\n      absoluteSiblingSlug: { type: String, slug: '/children.3.title' },\n      // same in relative form for 5th element\n      relativeSiblingSlug: { type: String, slug: ':children.4.title' },\n      subChild: {\n        title: { type: String },\n        // current title\n        slug: { type: String, slug: 'title' },\n        // root title\n        absoluteParentSlug: { type: String, slug: '/title' },\n        // child title\n        absoluteChildSlug: { type: String, slug: '/child.title' },\n        // title of current array element, because its a parent of this subChild\n        relativeParentSlug: { type: String, slug: ':title' },\n        // two parents up is a root\n        relativeGrandParentSlug: { type: String, slug: '::title' },\n      },\n      // arrays nested into array elements, welcome to the depth\n      subChildren: [\n        {\n          title: { type: String },\n          // current title\n          slug: { type: String, slug: 'title' },\n          // root title\n          absoluteRootSlug: { type: String, slug: '/title' },\n          // child title\n          absoluteChildSlug: { type: String, slug: '/child.title' },\n          // :--\u003e children :--\u003e root\n          relativeRootSlug: { type: String, slug: '::title' },\n          absoluteSiblingSlug: {\n            type: String,\n            // I don't know who will need it but it works, check yourself in /test\n            slug: '/children.0.subChildren.5.title',\n          },\n          // relative ref to another subChildren's element from current children's element\n          relativeSiblingSlug: { type: String, slug: ':subChildren.6.title' },\n          // hope you got it.\n          subChild: {\n            title: { type: String },\n            slug: { type: String, slug: 'title' },\n            absoluteParentSlug: { type: String, slug: '/title' },\n            absoluteChildSlug: { type: String, slug: '/child.title' },\n            relativeParentSlug: { type: String, slug: ':title' },\n            relativeGrandParentSlug: { type: String, slug: '::title' },\n          },\n        },\n      ],\n    },\n  ],\n});\n```\nExample of nested schemas declared separately:\n```js\nconst SubChildSchema = new mongoose.Schema({\n  title: { type: String },\n  slug: { type: String, slug: 'title' },\n  absoluteRootSlug: { type: String, slug: '/title' },\n  absoluteChildSlug: { type: String, slug: '/child.title' },\n  relativeParentSlug: { type: String, slug: ':title' },// child's title\n  relativeGrandParentSlug: { type: String, slug: '::title' },//parent's title\n});\n\nconst ChildSchema = new mongoose.Schema({\n  title: { type: String },\n  subChild: SubChildSchema,\n  subChildren: [SubChildSchema],\n  slug: { type: String, slug: 'title' },\n  subChildSlug: { type: String, slug: 'subChild.title' },\n  absoluteSlug: { type: String, slug: '/child.title' },\n  absoluteRootSlug: { type: String, slug: '/title' },\n  relativeParentSlug: { type: String, slug: ':title' },//Parent\n  subChildrenSlug2: { type: String, slug: 'subChildren.2.title' },\n  subChildrenSlug3: { type: String, slug: 'subChildren.3.title' },\n});\n\nconst ParentSchema = new mongoose.Schema({\n  title: { type: String },\n  child: ChildSchema,\n  children: [ChildSchema],\n  slug: { type: String, slug: 'title' },\n  absoluteSlug: { type: String, slug: '/title' },\n  childSlug: { type: String, slug: 'child.title' },\n  absoluteChildSlug: { type: String, slug: '/child.title' },\n  subChildSlug: { type: String, slug: 'child.subChild.title' },\n  childrenSlug0: { type: String, slug: 'children.0.title' },\n  childrenSlug4: { type: String, slug: 'children.4.title' },\n  subChildrenSlug3: { type: String, slug: 'children.7.subChildren.3.title' },\n  subChildrenSlug7: { type: String, slug: 'children.3.subChildren.7.title' },\n});\n```\n\n#### Updating by deep path via $set operator\n\nThis will work too:\n```js\n  await SimpleInline.findOneAndUpdate(\n    {/*some criteria*/},\n    {\n      $set: {\n        title: 'New root title',\n        'child.title': 'New nested title',\n        'children.2.title': 'New title for the 3d item of nested array',\n      },\n    }\n  );\n```\nAll the slugs which depend on modified titles will be found and regenerated.\u003cbr\u003e\nThis is recommended way to do partial modifications.\u003cbr\u003e\nWhen you perform updates by object value instead of path:value list,\u003cbr\u003e\nunobvious data loss may happen for nested docs or arrays, if they contain slugs affected by your modification.\u003cbr\u003e\nPlugin always checks will current update operation be made with $set operator or not, and adds extra slug fields to the query as an object fields or $set paths accordingly.\n\nSo if you do have whole document you want to change - better use `save`,\u003cbr\u003e\nbut if you dont have it, but you need to update some particular fields - it's more safe to use $set and paths:values.\n\n### Choose your own options\n\nYou can change any options adding to the plugin\n\n```js\nvar mongoose = require('mongoose'),\n    slug = require('mongoose-slug-updater'),\n    options = {\n        separator: \"-\",\n        lang: \"en\",\n        truncate: 120,\n        backwardCompatible: true//support for the old options names used in the mongoose-slug-generator\n    },\n    mongoose.plugin(slug, options),\n    Schema = mongoose.Schema,\n    schema = new Schema({\n        title: String,\n        subtitle: String,\n        slug: { type: String, slug: [\"title\", \"subtitle\"], unique: true }\n});\n```\n\nYou can find more options in the [speakingURL's npm page](https://www.npmjs.com/package/speakingurl)\n\n## Support\n\nThis plugin is supported by [Yuri Gor](http://yurigor.com/)\n\n### About\n\nThis plugin was initially forked from [mongoose-slug-generator](https://github.com/Kubide/mongoose-slug-generator), which is not maintained currently.\n\nMerged and fixed `uniqueGroupSlug` feature by [rickogden](https://github.com/rickogden).\n\n`update`, `updateOne`, `updateMany` and `findOneAndUpdate` operations support implemented.\n\nNested docs and arrays support implemented.\n\nAbsolute and relative paths added.\n\nUpdating with $set operator and deep paths now works too.\n\nAll the update operators will be implemented soon.\n\nPlugin rewritten with modern js and a lot of tests were added.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyurigor%2Fmongoose-slug-updater","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fyurigor%2Fmongoose-slug-updater","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fyurigor%2Fmongoose-slug-updater/lists"}