{"id":13804091,"url":"https://github.com/Meteor-Community-Packages/meteor-publish-composite","last_synced_at":"2025-05-13T17:31:14.902Z","repository":{"id":13950769,"uuid":"16650846","full_name":"Meteor-Community-Packages/meteor-publish-composite","owner":"Meteor-Community-Packages","description":"Meteor.publishComposite provides a flexible way to publish a set of related documents from various collections using a reactive join","archived":false,"fork":false,"pushed_at":"2024-09-05T14:37:22.000Z","size":651,"stargazers_count":553,"open_issues_count":11,"forks_count":58,"subscribers_count":15,"default_branch":"master","last_synced_at":"2024-11-11T22:21:36.465Z","etag":null,"topics":["hacktoberfest","meteor","mongodb"],"latest_commit_sha":null,"homepage":"https://atmospherejs.com/reywood/publish-composite","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/Meteor-Community-Packages.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["storytellercz","jankapunkt"],"patreon":null,"open_collective":null,"ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2014-02-08T19:14:49.000Z","updated_at":"2024-09-17T08:10:35.000Z","dependencies_parsed_at":"2024-01-16T13:38:21.185Z","dependency_job_id":"c732e863-097c-4a0e-9593-87d7e6833c72","html_url":"https://github.com/Meteor-Community-Packages/meteor-publish-composite","commit_stats":null,"previous_names":[],"tags_count":44,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Meteor-Community-Packages%2Fmeteor-publish-composite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Meteor-Community-Packages%2Fmeteor-publish-composite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Meteor-Community-Packages%2Fmeteor-publish-composite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Meteor-Community-Packages%2Fmeteor-publish-composite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Meteor-Community-Packages","download_url":"https://codeload.github.com/Meteor-Community-Packages/meteor-publish-composite/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225247827,"owners_count":17444122,"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":["hacktoberfest","meteor","mongodb"],"created_at":"2024-08-04T01:00:41.677Z","updated_at":"2025-05-13T17:31:14.878Z","avatar_url":"https://github.com/Meteor-Community-Packages.png","language":"JavaScript","funding_links":["https://github.com/sponsors/storytellercz","https://github.com/sponsors/jankapunkt"],"categories":["Collections","Packages"],"sub_categories":[],"readme":"# meteor-publish-composite\n\n`publishComposite(...)` provides a flexible way to publish a set of related documents from various collections using a reactive join. This makes it easy to publish a whole tree of documents at once. The published collections are reactive and will update when additions/changes/deletions are made.\n\n## Project\n\n[![Project Status: Active – The project has reached a stable, usable state and is being actively developed.](https://www.repostatus.org/badges/latest/active.svg)](https://www.repostatus.org/#active)\n![GitHub](https://img.shields.io/github/license/Meteor-Community-Packages/meteor-publish-composite)\n[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)\n![GitHub tag (latest SemVer)](https://img.shields.io/github/v/tag/Meteor-Community-Packages/meteor-publish-composite?label=latest\u0026sort=semver)\n[![](https://img.shields.io/badge/semver-2.0.0-success)](http://semver.org/spec/v2.0.0.html) \u003c!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section --\u003e\n[![All Contributors](https://img.shields.io/badge/all_contributors-9-orange.svg?style=flat-square)](#contributors-)\n\u003c!-- ALL-CONTRIBUTORS-BADGE:END --\u003e\n\nThis project differs from many other parent/child relationship mappers in its flexibility. The relationship between a parent and its children can be based on almost anything. For example, let's say you have a site that displays news articles. On each article page, you would like to display a list at the end containing a couple of related articles. You could use `publishComposite` to publish the primary article, scan the body for keywords which are then used to search for other articles, and publish these related articles as children. Of course, the keyword extraction and searching are up to you to implement.\n\n*Found a problem with this package? [See below for instructions on reporting](#reporting-issuesbugs).*\n\n## Installation\n\n```sh\n$ meteor add reywood:publish-composite\n```\n\n\n## Usage\n\nThis package exports a function on the server:\n\n#### publishComposite(name, options)\n\nArguments\n\n* **`name`** -- *string*\n\n    The name of the publication\n\n* **`options`** -- *object literal or callback function (supports async)*\n\n    An object literal specifying the configuration of the composite publication **or** a function that returns said object literal. If a function is used, it will receive the arguments passed to `Meteor.subscribe('myPub', arg1, arg2, ...)` (much like the `func` argument of [`Meteor.publish`](http://docs.meteor.com/#meteor_publish)). Basically, if your publication will take **no** arguments, pass an object literal for this argument. If your publication **will** take arguments, use a function that returns an object literal.\n\n    The object literal must have a `find` property, and can optionally have `children` and `collectionName` properties.\n\n    * **`find`** -- *function (required)*\n\n        A function that returns a MongoDB cursor (e.g., `return Meteor.users.find({ active: true });`)\n\n    * **`children`** -- *array (optional)* or *function (supports async)*\n\n        - An array containing any number of object literals with this same structure\n        - A function with top level documents as arguments. It helps dynamically build\n        the array based on conditions ( like documents fields values)\n\n    * **`collectionName`** -- *string (optional)*\n\n        A string specifying an alternate collection name to publish documents to (see [this blog post][blog-collection-name] for more details)\n\n    Example:\n\n    ```javascript\n    {\n        find() {\n            // Must return a cursor containing top level documents\n        },\n        children: [\n            {\n                find(topLevelDocument) {\n                    // Called for each top level document. Top level document is passed\n                    // in as an argument.\n                    // Must return a cursor of second tier documents.\n                },\n                children: [\n                    {\n                        collectionName: 'alt', // Docs from this find will be published to the 'alt' collection\n                        find(secondTierDocument, topLevelDocument) {\n                            // Called for each second tier document. These find functions\n                            // will receive all parent documents starting with the nearest\n                            // parent and working all the way up to the top level as\n                            // arguments.\n                            // Must return a cursor of third tier documents.\n                        },\n                        children: [\n                           // Repeat as many levels deep as you like\n                        ]\n                    }\n                ]\n            },\n            {\n                find(topLevelDocument) {\n                    // Also called for each top level document.\n                    // Must return another cursor of second tier documents.\n                }\n                // The children property is optional at every level.\n            }\n        ]\n    }\n    ```\n\n    Example with children as function:\n\n    ```javascript\n    {\n      find() {\n          return Notifications.find();\n      },\n      async children(parentNotification) {\n        // children is a function (can be asynchronous) that returns an array of objects.\n        // It takes parent documents as arguments and dynamically builds children array.\n\n        // async children function allows the use of await to dynamically build children array\n        const userAllowsNotifications = await CustomUserLibrary.allowsNotifications(); \n\n        if (!userAllowsNotifications) {\n          return [];\n        }\n    \n        if (parentNotification.type === 'about_post') {\n          return [{\n            find(notification) {\n              return Posts.find(parentNotification.objectId);\n            }\n          }];\n        }\n        return [\n          {\n            find(notification) {\n              return Comments.find(parentNotification.objectId);\n            }\n          }\n        ]\n      }\n    }\n    ```\n\n## Examples\n\n### Example 1: A publication that takes **no** arguments.\n\nFirst, we'll create our publication on the server.\n\n```javascript\n// Server\nimport { publishComposite } from 'meteor/reywood:publish-composite';\n\npublishComposite('topTenPosts', {\n    find() {\n        // Find top ten highest scoring posts\n        return Posts.find({}, { sort: { score: -1 }, limit: 10 });\n    },\n    children: [\n        {\n            find(post) {\n                // Find post author. Even though we only want to return\n                // one record here, we use \"find\" instead of \"findOne\"\n                // since this function should return a cursor.\n                return Meteor.users.find(\n                    { _id: post.authorId },\n                    { fields: { profile: 1 } });\n            }\n        },\n        {\n            find(post) {\n                // Find top two comments on post\n                return Comments.find(\n                    { postId: post._id },\n                    { sort: { score: -1 }, limit: 2 });\n            },\n            children: [\n                {\n                    find(comment, post) {\n                        // Find user that authored comment.\n                        return Meteor.users.find(\n                            { _id: comment.authorId },\n                            { fields: { profile: 1 } });\n                    }\n                }\n            ]\n        }\n    ]\n});\n```\n\nNext, we subscribe to our publication on the client.\n\n```javascript\n// Client\nMeteor.subscribe('topTenPosts');\n```\n\nNow we can use the published data in one of our templates.\n\n```handlebars\n\u003ctemplate name=\"topTenPosts\"\u003e\n    \u003ch1\u003eTop Ten Posts\u003c/h1\u003e\n    \u003cul\u003e\n        {{#each posts}}\n            \u003cli\u003e{{title}} -- {{postAuthor.profile.name}}\u003c/li\u003e\n        {{/each}}\n    \u003c/ul\u003e\n\u003c/template\u003e\n```\n\n```javascript\nTemplate.topTenPosts.helpers({\n    posts() {\n        return Posts.find({}, { sort: { score: -1 }, limit: 10 });\n    },\n\n    postAuthor() {\n        // We use this helper inside the {{#each posts}} loop, so the context\n        // will be a post object. Thus, we can use this.authorId.\n        return Meteor.users.findOne(this.authorId);\n    }\n})\n```\n\n### Example 2: A publication that **does** take arguments\n\nNote a function is passed for the `options` argument to `publishComposite`.\n\n```javascript\n// Server\nimport { publishComposite } from 'meteor/reywood:publish-composite';\n\npublishComposite('postsByUser', function(userId, limit) {\n    return {\n        find() {\n            // Find posts made by user. Note arguments for callback function\n            // being used in query.\n            return Posts.find({ authorId: userId }, { limit: limit });\n        },\n        children: [\n            // This section will be similar to that of the previous example.\n        ]\n    }\n});\n```\n\n```javascript\n// Client\nvar userId = 1, limit = 10;\nMeteor.subscribe('postsByUser', userId, limit);\n```\n\n### Example 3: A publication from async function\n\nNote a function is passed for the `options` argument to `publishComposite`.\n\n```javascript\n// Server\nimport { publishComposite } from 'meteor/reywood:publish-composite';\n\npublishComposite('postsByUser', async function(userId) {\n    const user = await Users.findOneAsync(userId)\n    const limit = user.limit\n\n    return {\n        find() {\n            // Find posts made by user. Note arguments for callback function\n            // being used in query.\n            return Posts.find({ authorId: userId }, { limit: limit });\n        },\n        children: [\n            // This section will be similar to that of the previous example.\n            // Note from above, children can be a function (optionally async) in order to dynamically build the children array\n        ]\n    }\n});\n```\n\n## Known issues\n\n**Avoid publishing very large sets of documents**\n\nThis package is great for publishing small sets of related documents. If you use it for large sets of documents with many child publications, you'll probably experience performance problems. Using this package to publish documents for a page with infinite scrolling is probably a bad idea. It's hard to offer exact numbers (i.e. don't publish more than X parent documents with Y child publications) so some experimentation may be necessary on your part to see what works for your application.\n\n**Arrow functions**\n\nYou will not be able to access `this.userId` inside your `find` functions if you use [arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions).\n\n## Testing\n\nRun the following:\n```shell\nmeteor test-packages reywood:publish-composite --driver-package meteortesting:mocha\n```\nThe tests are executing a combination of methods and subscriptions. The quickest option was to add a pause after each\noperation (see usage of `sleep()` in `./tests/server.js`), to allow for the publications to send down all the\ndocuments. However, this is flaky, so you may want to refresh the browser if you notice tests failing for no \napparent reason.\n\n## Reporting issues/bugs\n\nIf you are experiencing an issue with this package, please create a GitHub repo with the simplest possible Meteor app that demonstrates the problem. This will go a long way toward helping me to diagnose the problem.\n\n## More info\n\nFor more info on how to use `publishComposite`, check out these blog posts:\n\n* [Publishing Reactive Joins in Meteor][blog-reactive-joins]\n* [Publishing to an Alternative Client-side Collection in Meteor][blog-collection-name]\n\nNote that these articles use the old pre-import notation, `Meteor.publishComposite`, which is still available for backward compatibility.\n\n\n[blog-reactive-joins]: http://braindump.io/meteor/2014/09/12/publishing-reactive-joins-in-meteor.html\n[blog-collection-name]: http://braindump.io/meteor/2014/09/20/publishing-to-an-alternative-clientside-collection-in-meteor.html\n\n## Alternatives\n\nWhile we are happy that you find this package of value, there are limitations, especially on high traffic applications.\nThere are also other solutions that can solve the problems that publish-composite solves, so here is a list of possible alternatives:\n\n### MongoDB Aggregations\n\nMongoDB itself has a functionality called Aggregations which allows you to combine data from multiple collections into\none document. It also has other useful features that you can utilize. The downside is that unless you use [reactive-aggregate](https://atmospherejs.com/tunguska/reactive-aggregate)\npackage the aggregations are not reactive and things it is not the easiest to learn or master.\n\n* [Learn more](https://www.mongodb.com/docs/manual/meta/aggregation-quick-reference/)\n\n### GraphQL\n\nGraphQL allows you to specify exactly which data you need and even embed child documents. Apollo GraphQL also has an [official package]((https://atmospherejs.com/meteor/apollo))\nand there is the `apollo` starter skeleton in Meteor itself to get you started quickly.\n\n* [Official Meteor Apollo package](https://atmospherejs.com/meteor/apollo)\n\n## Contributors ✨\n\nThanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section --\u003e\n\u003c!-- prettier-ignore-start --\u003e\n\u003c!-- markdownlint-disable --\u003e\n\u003ctable\u003e\n  \u003ctbody\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"http://braindump.io\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/1796302?v=4?s=100\" width=\"100px;\" alt=\"Sean Dwyer\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eSean Dwyer\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/commits?author=reywood\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/commits?author=reywood\" title=\"Documentation\"\u003e📖\u003c/a\u003e \u003ca href=\"#ideas-reywood\" title=\"Ideas, Planning, \u0026 Feedback\"\u003e🤔\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/sebakerckhof\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/88471?v=4?s=100\" width=\"100px;\" alt=\"Seba Kerckhof\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eSeba Kerckhof\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/commits?author=sebakerckhof\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/pulls?q=is%3Apr+reviewed-by%3Asebakerckhof\" title=\"Reviewed Pull Requests\"\u003e👀\u003c/a\u003e \u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/commits?author=sebakerckhof\" title=\"Tests\"\u003e⚠️\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/rclai\"\u003e\u003cimg src=\"https://avatars0.githubusercontent.com/u/1316261?v=4?s=100\" width=\"100px;\" alt=\"Richard Lai\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eRichard Lai\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/issues?q=author%3Arclai\" title=\"Bug reports\"\u003e🐛\u003c/a\u003e \u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/commits?author=rclai\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/zimme\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/1215414?v=4?s=100\" width=\"100px;\" alt=\"Simon Fridlund\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eSimon Fridlund\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/commits?author=zimme\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/patrickml\"\u003e\u003cimg src=\"https://avatars3.githubusercontent.com/u/7581369?v=4?s=100\" width=\"100px;\" alt=\"Patrick Lewis\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003ePatrick Lewis\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/commits?author=patrickml\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/nabiltntn\"\u003e\u003cimg src=\"https://avatars1.githubusercontent.com/u/223719?v=4?s=100\" width=\"100px;\" alt=\"nabiltntn\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003enabiltntn\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/commits?author=nabiltntn\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/czeslaaw\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/1939060?v=4?s=100\" width=\"100px;\" alt=\"Krzysztof Czech\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eKrzysztof Czech\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/commits?author=czeslaaw\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n    \u003ctr\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://github.com/StorytellerCZ\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/1715235?v=4?s=100\" width=\"100px;\" alt=\"Jan Dvorak\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eJan Dvorak\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/commits?author=StorytellerCZ\" title=\"Code\"\u003e💻\u003c/a\u003e \u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/commits?author=StorytellerCZ\" title=\"Documentation\"\u003e📖\u003c/a\u003e \u003ca href=\"#infra-StorytellerCZ\" title=\"Infrastructure (Hosting, Build-Tools, etc)\"\u003e🚇\u003c/a\u003e \u003ca href=\"#maintenance-StorytellerCZ\" title=\"Maintenance\"\u003e🚧\u003c/a\u003e \u003ca href=\"#tool-StorytellerCZ\" title=\"Tools\"\u003e🔧\u003c/a\u003e\u003c/td\u003e\n      \u003ctd align=\"center\" valign=\"top\" width=\"14.28%\"\u003e\u003ca href=\"https://duodeka.nl/\"\u003e\u003cimg src=\"https://avatars2.githubusercontent.com/u/12446296?v=4?s=100\" width=\"100px;\" alt=\"Koen [XII]\"/\u003e\u003cbr /\u003e\u003csub\u003e\u003cb\u003eKoen [XII]\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e\u003cbr /\u003e\u003ca href=\"https://github.com/Meteor-Community-Packages/meteor-publish-composite/commits?author=KoenLav\" title=\"Code\"\u003e💻\u003c/a\u003e\u003c/td\u003e\n    \u003c/tr\u003e\n  \u003c/tbody\u003e\n\u003c/table\u003e\n\n\u003c!-- markdownlint-restore --\u003e\n\u003c!-- prettier-ignore-end --\u003e\n\n\u003c!-- ALL-CONTRIBUTORS-LIST:END --\u003e\n\nThis project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMeteor-Community-Packages%2Fmeteor-publish-composite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FMeteor-Community-Packages%2Fmeteor-publish-composite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FMeteor-Community-Packages%2Fmeteor-publish-composite/lists"}