{"id":18398698,"url":"https://github.com/mongodb-developer/docbridge","last_synced_at":"2025-07-05T08:05:02.024Z","repository":{"id":208655009,"uuid":"722102278","full_name":"mongodb-developer/docbridge","owner":"mongodb-developer","description":"A (currently experimental) Object-Document Mapping library.","archived":false,"fork":false,"pushed_at":"2024-04-24T14:13:59.000Z","size":46,"stargazers_count":16,"open_issues_count":0,"forks_count":1,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-07T05:35:37.682Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mongodb-developer.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.txt","code_of_conduct":null,"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}},"created_at":"2023-11-22T12:47:22.000Z","updated_at":"2025-03-29T17:19:11.000Z","dependencies_parsed_at":"2023-12-13T13:41:05.715Z","dependency_job_id":"c8c96a0d-b817-4f16-93da-cc4a8dd8927e","html_url":"https://github.com/mongodb-developer/docbridge","commit_stats":null,"previous_names":["mongodb-developer/docbridge"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/mongodb-developer/docbridge","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mongodb-developer%2Fdocbridge","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mongodb-developer%2Fdocbridge/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mongodb-developer%2Fdocbridge/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mongodb-developer%2Fdocbridge/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mongodb-developer","download_url":"https://codeload.github.com/mongodb-developer/docbridge/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mongodb-developer%2Fdocbridge/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":263707029,"owners_count":23499075,"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":[],"created_at":"2024-11-06T02:24:00.470Z","updated_at":"2025-07-05T08:05:02.003Z","avatar_url":"https://github.com/mongodb-developer.png","language":"Python","readme":"# Docbridge\n\nThis is an **experimental** Object-Document Mapping library for MongoDB.\nYou can watch it being developed *live* on [MongoDB's YouTube channel](https://www.youtube.com/@MongoDB)!\n\n## Mission Statement\n\n* Managing large amounts of data in MongoDB while keeping a data schema flexible is challenging.\n* This ODM is not an active record implementation, mapping documents in the database directly into similar objects in code.\n* This ODM *is* designed to abstract underlying documents, mapping potentially multiple document schemata into a shared object representation.\n* It should also simplify the evolution of documents in the database, automatically migrating individual documents' schemas either on-read or on-write.\n* There should be \"escape hatches\" so that unforeseen mappings can be implemented, hiding away the implementation code behind hopefully reuseable components.\n\n## What Does It Do?\n\nThe library currently doesn't interact directly with MongoDB - what it _does_ do is wrap BSON documents returned by [PyMongo] or [Motor].\n\nFor example, let's say you have a BSON document like this:\n\n```python\nuser_data_bson = {'_id': ObjectId('657072b56731c9e580e9dd70'),\n    'bio': 'Music conference able doctor degree debate. Participant usually above '\n        'relate.',\n    'birth_date': datetime.datetime(1999, 7, 6, 0, 0),\n    'email': 'deanjacob@yahoo.com',\n    'follower_count': 59,\n    'full_name': 'Deborah White',\n    'user_id': '4',\n    'user_name': '@tanya15',\n    'followers': [{'_id': ObjectId('657072b66731c9e580e9dda6'),\n                'bio': 'Rich beautiful color life. Relationship instead win '\n                       'join enough board successful.',\n                'user_id': '58',\n                'user_name': '@rduncan'},\n               {'_id': ObjectId('657072b66731c9e580e9dd99'),\n                'bio': 'Picture day couple democratic morning. Environment '\n                       'manage opportunity option star food she. Occur imagine '\n                       'population single avoid.',\n                'user_id': '45',\n                'user_name': '@paynericky'},\n               ]}\n```\n\nYou can define a wrapper for it like this:\n\n```python\nfrom docbridge import Document\n\n\nclass UserProfile(Document):\n    pass\n```\n\nThe wrapper doesn't currently do very much - it just makes the `dict` returned by PyMongo look more like a regular Python class:\n\n```python\nprofile = UserProfile(user_data_bson, db=None)\nprint(repr(profile._id))  # ObjectId('657072b56731c9e580e9dd70')\nprint(repr(profile.user_id))  # \"4\"\n```\n\nThe real power of the library (like with most [ODM]s) comes from attaching field definitions to the class, to transform the way data is looked up on the underlying document.\n\nHere is how the `Field` class can be used to configure mappings to different field names in the underlying document, or to transform the data in the underlying field, to convert a string to an int:\n\n```python\nfrom docbridge import Document, Field\n\n\nclass UserProfile(Document):\n    id = Field(field_name=\"_id\")  # id maps to the _id doc field.\n    user_id = Field(transform=int)  # user_id transforms the field value to an int\n\n\nprofile = UserProfile(user_data_bson, db=None)\nprint(repr(profile.id))  # ObjectId('657072b56731c9e580e9dd70')\nprint(repr(profile.user_id))  # 4 \u003c- This is an int now!\nprint(\n    repr(profile.follower_count)\n)  # 59 \u003c- You can still access other doc fields as attributes.\n```\n\n## Fallthrough Fields\n\nThere are other types of field, though.\nFallthroughField is one of them.\nIt allows you to _try_ to look up a field by one name,\nand if the field is missing,\nit will try other names that it's been configured with.\n\n**Note:** This field type will _probably_ disappear, as I may merge its\nfunctionality into `Field`.\n\n```python\nfrom docbridge import Document, FallthroughField\n\nclass UserProfile(Document):\n    # The `name` attribute will look up the \"full_name\" field,\n    # and fall back to the \"name\" if it's missing.\n    name = FallthroughField(\n        field_names=[\n            \"full_name\",  # v2\n            \"name\",  # v1\n        ]\n    )\n\nprofile = UserProfile({\"full_name\", \"Mark Smith\"})\nassert profile.name == \"Mark Smith\"  # Works\n\nprofile = UserProfile({\"name\", \"Mark Smith\"})\nassert profile.name == \"Mark Smith\"  # Also works!\n```\n\n## The Subset Pattern\n\nSome support already exists for abstracting [MongoDB Design Patterns][mongodb-patterns],\nlike the [Subset Pattern][subset].\nThe subset pattern preserves document size at a reasonable level by only embedding a subset of related data - for example, only the first 10 followers on a social media profile. The rest of the followers would be stored in their own collection, and loaded only when necessary.\n\n```python\nclass Follower(Document):\n    _id = Field(transform=str)\n\nclass Profile(Document):\n        _id = Field(transform=str)\n        followers = SequenceField(\n            type=Follower,\n            superset_collection=\"followers\",\n            # The following query will be executed on \"followers\" if the field\n            # is iterated past the embedded follower subdocuments.\n            superset_query=lambda ob: [\n                {\n                    \"$match\": {\"user_id\": ob.user_id},\n                },\n                {\"$unwind\": \"$followers\"},\n                {\"$replaceRoot\": {\"newRoot\": \"$followers\"}},\n            ],\n        )\n\n# Print all the profile's followers to the screen,\n# including those in the followers collection:\nprofile = Profile(user_data_bson, db=test_db)\nfor follower in profile:\n    print(follower.id)\n```\n\n# Live Streams on YouTube\n\nI've been developing docbridge on YouTube. You can catch the live streams at 2pm GMT on Wednesdays, or you can view the recordings:\n\n## Episode 1: Building a Simple Data Access Layer\n\nIntroducing my plans for the library, and building out the `Document` class, and the `Simple` and `Fallthrough` classes. (The latter two get renamed later to `Field` and `FallthroughField`)\n\n[![Building a Simple Data Access Layer](https://img.youtube.com/vi/dXXkuLjjHBA/0.jpg)](https://www.youtube.com/watch?v=dXXkuLjjHBA)\n\n\n## Episode 2: Testing and Publishing a Python Module\n\nWriting some Pytest test fixtures that will run tests in a transaction, and roll back any changes to the database. Then (attempting to) publish my module to PyPI!\n\n[![Testing and Publishing a Python Module](https://img.youtube.com/vi/X9QqA0alA8Q/0.jpg)](https://www.youtube.com/watch?v=X9QqA0alA8Q)\n\n## Episode 3: Subsets \u0026 Joins - Part 1\n\nJoins are a fundamental part of data modeling in MongoDB! This episode adds a field type for embedded arrays, and in the next episode it'll be extended to look up data in other collections!\n\n[![Subsets \u0026 Joins: Part 1](https://img.youtube.com/vi/YvZeA_jvYrY/0.jpg)](https://www.youtube.com/watch?v=YvZeA_jvYrY)\n\n## Episode 4: Subsets \u0026 Joins - Part 2\n\nMore metaprogramming to turn a sequence of items that is split across documents and collections into a single Python sequence.\n\n[![Subsets \u0026 Joins: Part 2](https://img.youtube.com/vi/TJVLkVUUzGk/0.jpg)](https://www.youtube.com/watch?v=TJVLkVUUzGk)\n\n## Episode 5: Updating Data - Part 1\n\nIt's all very well reading data from the database, but it's also nice to be able\nto update it!\n\n[![Updating Data - Part 1](https://img.youtube.com/vi/Ab_NmiKP2_w/0.jpg)](https://www.youtube.com/watch?v=Ab_NmiKP2_w)\n\n## Episode 6: Updating Data - Part 2\n\nIt turns out there's quite a lot of work to record and replay updates.\nLet's get on with it!\n\n[![Updating Data - Part 2](https://img.youtube.com/vi/2kIrKr0n9WY/0.jpg)](https://www.youtube.com/watch?v=2kIrKr0n9WY)\n\n## Episode 7: Updating Data - Part 3\n\nIt turns out there's quite a lot of work to record and replay updates.\nLet's get on with it!\n\n[![Updating Data - Part 3](https://img.youtube.com/vi/3bW8Zzm8dpE/0.jpg)](https://www.youtube.com/watch?v=3bW8Zzm8dpE)\n\n\n\n[PyMongo]: https://pymongo.readthedocs.io/en/stable/\n[Motor]: https://motor.readthedocs.io/en/stable/\n[ODM]: https://www.mongodb.com/developer/products/mongodb/mongodb-orms-odms-libraries/\n[subset]: https://www.mongodb.com/blog/post/building-with-patterns-the-subset-pattern\n[mongodb-patterns]: https://www.mongodb.com/blog/post/building-with-patterns-a-summary","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmongodb-developer%2Fdocbridge","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmongodb-developer%2Fdocbridge","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmongodb-developer%2Fdocbridge/lists"}