From 38be94ecb8573a0125a1827d2482e278584c0ad5 Mon Sep 17 00:00:00 2001 From: Avijit Gupta <102968998+AvijitkGupta@users.noreply.github.com> Date: Thu, 23 Jan 2025 13:12:37 +0530 Subject: [PATCH] Initial version with Build and Usage (#8) --- CONTRIBUTING.md | 35 +++-- README.md | 287 ++++++++++++++++++++++++++++++++++++---- images/architecture.png | Bin 0 -> 20412 bytes 3 files changed, 280 insertions(+), 42 deletions(-) create mode 100644 images/architecture.png diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 437c13a0..c7aee604 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,10 +1,10 @@ -# Contibuting to DocumentDB +# Contributing to DocumentDB Thank you for your interest in contributing to DocumentDB. There are several ways through which contributions to the project can be made and you can get involved. ## Asking Questions -[Join us on Discord](https://discord.gg/WXUrVbnt4n) to engage directly with our team and the community with any questions you may have. Our channel inside the Microsoft OSS server is not only a place for immediate assistance but also a hub for discussing project updates, upcoming features, and technical challenges. We host regular meetings where team members and contributors can interact directly to ask questions, share feedback, and discuss their ideas in real time. +[Join us on Discord](https://discord.gg/WXUrVbnt4n) to engage directly with our team and the community with any questions you may have. Our channel inside the Microsoft OSS server is not only a place for immediate assistance but also a hub for discussing project updates, upcoming features, and technical challenges. We host regular meetings where team members and contributors can interact directly to ask questions, share feedback, and discuss their ideas in real time. ## Reporting Issues @@ -20,24 +20,23 @@ The more descriptive the issue is and the more information that can be provided, For each issue created, please include: -* A brief description of the suggestion or issue. +- A brief description of the suggestion or issue. -* Relevant details such as the operating system you were using, if applicable. - -* Clear, step-by-step instructions or context that describe how the issue occurred or how the feature would function. - -* The expected outcome versus the actual result or behavior observed, if relevant. +- Relevant details such as the operating system you were using, if applicable. + +- Clear, step-by-step instructions or context that describe how the issue occurred or how the feature would function. + +- The expected outcome versus the actual result or behavior observed, if relevant. ### Code Contributions -For those looking to contribute code, whether for bug fixes or new features, please ensure your issues and pull requests include the following: +For those looking to contribute code, whether for bug fixes or new features, please ensure your issues and pull requests include the following: + +- A code snippet that reliably reproduces the issue or demonstrates the new feature. -* A code snippet that reliably reproduces the issue or demonstrates the new feature.  - -* An accessible repository link that can be cloned, built, and run, providing a complete environment for testing and verifying the issue or feature.  - -* Clear documentation or comments in the code explaining the changes made and their purpose.  +- An accessible repository link that can be cloned, built, and run, providing a complete environment for testing and verifying the issue or feature. +- Clear documentation or comments in the code explaining the changes made and their purpose. ## Pull Requests @@ -51,13 +50,13 @@ Wherever applicable, pull requests should contain tests. We’re keen to hear your feedback on the future of DocumentDB. Feature Requests can be submitted through the issue tracker. To ensure suggestions are reviewed faster, we do request that the following information be provided for each suggestion: -* Purpose/Motivation for the feature +- Purpose/Motivation for the feature -* Description of the problem space that will be solved or addressed by this feature +- Description of the problem space that will be solved or addressed by this feature -* Description of a few scenarios that will help test and validate this feature +- Description of a few scenarios that will help test and validate this feature -For even quicker engagement and real-time discussions about your feature ideas, join our [Discord channel](https://discord.gg/WXUrVbnt4n). Our team and community members are actively available to provide feedback, answer questions, and collaborate on refining your suggestions before they are formally submitted through the issue tracker. +For even quicker engagement and real-time discussions about your feature ideas, join our [Discord channel](https://discord.gg/WXUrVbnt4n). Our team and community members are actively available to provide feedback, answer questions, and collaborate on refining your suggestions before they are formally submitted through the issue tracker. ## Discussion Etiquette diff --git a/README.md b/README.md index 5cd7cecf..444e9346 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,272 @@ -# Project +# Introduction -> This repo has been populated by an initial template to help get you started. Please -> make sure to update the content to build a great experience for community-building. +`DocumentDB` offers a native implementation of document-oriented NoSQL database, enabling seamless CRUD operations on BSON data types within a PostgreSQL framework. Beyond basic operations, DocumentDB empowers you to execute complex workloads, including full-text searches, geospatial queries, and vector embeddings on your dataset, delivering robust functionality and flexibility for diverse data management needs. -As the maintainer of this project, please make a few updates: +[PostgreSQL](https://www.postgresql.org/about/) is a powerful, open source object-relational database system that uses and extends the SQL language combined with many features that safely store and scale the most complicated data workloads. -- Improving this README.MD file to provide a great experience -- Updating SUPPORT.MD with content about this project's support experience -- Understanding the security reporting process in SECURITY.MD -- Remove this section from the README +## Components -## Contributing +The project comprises of two primary components, which work together to support document operations. -This project welcomes contributions and suggestions. Most contributions require you to agree to a -Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. +- pg_documentdb_core : PostgreSQL extension introducing BSON datatype support and operations for native Postgres. +- pg_documentdb : The public API surface for DocumentDB providing CRUD functionality on documents in the store. -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. +![Architecture](images/architecture.png) -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +## Why DocumentDB ? -## Trademarks +At DocumentDB, we believe in the power of open-source to drive innovation and collaboration. Our commitment to being a fully open-source document database means that we are dedicated to transparency, community involvement, and continuous improvement. We are open-sourced under the most permissive [MIT](https://opensource.org/license/mit) license, where developers and organizations alike have no restrictions incorporating the project into new and existing solutions of their own. DocumentDB introduces the BSON data type and provides APIs for seamless operation within native PostgreSQL, enhancing efficiency and aligning with operational advantages. -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft -trademarks or logos is subject to and must follow -[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). -Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. -Any use of third-party trademarks or logos are subject to those third-party's policies. +DocumentDB also provides a powerful on-premise solution, allowing organizations to maintain full control over their data and infrastructure. This flexibility ensures that you can deploy it in your own environment, meeting your specific security, compliance, and performance requirements. With DocumentDB, you get the best of both worlds: the innovation of open-source and the control of on-premise deployment. + +### Based on Postgres + +DocumentDB is built on top of PostgreSQL, one of the most advanced and reliable open-source relational database systems available. We chose PostgreSQL as our base layer for several reasons: + +1. **Proven Stability and Performance**: PostgreSQL has a long history of stability and performance, making it a trusted choice for mission-critical applications. +2. **Extensibility**: Their extensible architecture allows us to integrate a DocumentDB API on BSON data type seamlessly, providing the flexibility to handle both relational and document data. +3. **Active Community**: PostgreSQL has a vibrant and active community that continuously contributes to its development, ensuring that it remains at the forefront of database technology. +4. **Advanced Features**: PostgreSQL offers a rich set of features, including advanced indexing, full-text search, and powerful querying capabilities, which enhance the functionality of DocumentDB. +5. **Compliance and Security**: PostgreSQL's robust security features and compliance with various standards make it an ideal choice for organizations with stringent security and regulatory requirements. + +By building on PostgreSQL, DocumentDB leverages these strengths to provide a powerful, flexible, and reliable document database that meets the need of modern applications. DocumentDB will continue to benefit from the advancements brought into the PostgreSQL ecosystem. + +## Get Started + +### Pre-requisite + +- Ensure [Docker](https://docs.docker.com/engine/install/) is installed on your system. + +### Building DocumentDB with Docker + +Step 1: Clone the DocumentDB repo. + +```bash +git clone https://github.com/microsoft/documentdb.git +``` + +Step 2: Create the docker image. Navigate to cloned repo. + +```bash +docker build . -f .devcontainer/Dockerfile -t documentdb +``` + +Note: Validate using `docker image ls` + +Step 3: Run the Image as a container + +```bash +docker run -v $(pwd):/home/documentdb/code -it documentdb /bin/bash + +cd code +``` + +(Aligns local location with docker image created, allows de-duplicating cloning repo again within image).
+Note: Validate container is running `docker container ls` + +Step 4: Build & Deploy the binaries + +```bash +make +``` + +Note: Run in case of an unsuccessful build `git config --global --add safe.directory /home/DocumentDB/code` within image. + +```bash +sudo make install +``` + +Note: To run backend postgresql tests after installing you can run `make check`. + +You are all set to work with DocumentDB. + +### Connecting to the Server + +Step 1: Run `start_oss_server.sh` to initialize the DocumentDB server and manage dependencies. + +```bash +./scripts/start_oss_server.sh +``` + +Step 2: Connect to `psql` shell + +```bash +psql -p 9712 -h localhost -d postgres +``` + +## Usage + +Once you have your `DocumentDB` set up running, you can start with creating collections, indexes and perform queries on them. + +### Create a collection + +DocumentDB provides [documentdb_api.create_collection](https://github.com/microsoft/documentdb/wiki/Functions#create_collection) function to create a new collection within a specified database, enabling you to manage and organize your BSON documents effectively. + +```sql +SELECT documentdb_api.create_collection('documentdb','patient'); +``` + +### Perform CRUD operations + +#### Insert documents + +The [documentdb_api.insertOne()](https://github.com/microsoft/documentdb/wiki/Functions#insert_one) command is used to add a single document into a collection. + +```sql +select documentdb_api.insert_one('documentdb','patient', '{ "patient_id": "P001", "name": "Alice Smith", "age": 30, "phone_number": "555-0123", "registration_year": "2023","conditions": ["Diabetes", "Hypertension"]}'); +select documentdb_api.insert_one('documentdb','patient', '{ "patient_id": "P002", "name": "Bob Johnson", "age": 45, "phone_number": "555-0456", "registration_year": "2023", "conditions": ["Asthma"]}'); +select documentdb_api.insert_one('documentdb','patient', '{ "patient_id": "P003", "name": "Charlie Brown", "age": 29, "phone_number": "555-0789", "registration_year": "2024", "conditions": ["Allergy", "Anemia"]}'); +select documentdb_api.insert_one('documentdb','patient', '{ "patient_id": "P004", "name": "Diana Prince", "age": 40, "phone_number": "555-0987", "registration_year": "2024", "conditions": ["Migraine"]}'); +select documentdb_api.insert_one('documentdb','patient', '{ "patient_id": "P005", "name": "Edward Norton", "age": 55, "phone_number": "555-1111", "registration_year": "2025", "conditions": ["Hypertension", "Heart Disease"]}'); +``` + +#### Read document from a collection + +The `documentdb_api.collection` function is used for retrieving the documents in a collection. + +```sql +SELECT document FROM documentdb_api.collection('documentdb','patient'); +``` + +Alternatively, we can apply filter for specific condition using `@@` or `@=` + +```sql +SET search_path TO documentdb_api, documentdb_api_catalog,documentdb_core; +SET documentdb_core.bsonUseEJson TO true; + +SELECT document FROM documentdb_api.collection('documentdb','patient') WHERE document @@ '{"patient_id":"P005"}'; +``` + +We can perform range queries as well. + +```sql +SELECT document FROM documentdb_api.collection('documentdb','patient') +WHERE document @@ '{ "$and": [{ "age": { "$gte": 10 } },{ "age": { "$lte": 35 } }] }'; +``` + +#### Update document in a collection + +DocumentDB uses the [documentdb_api.update](https://github.com/microsoft/documentdb/wiki/Functions#update) function to modify existing documents within a collection. + +The SQL command updates the `age` for patient `P004`. + +```sql +select documentdb_api.update('documentdb', '{"update":"patient", "updates":[{"q":{"patient_id":"P004"},"u":{"$set":{"age":14}}}]}'); +``` + +Similarly, we can update multiple documents using `multi` property. + +```sql +SELECT documentdb_api.update('documentdb', '{"update":"patient", "updates":[{"q":{},"u":{"$set":{"age":50}},"multi":true}]}'); +``` + +#### Delete document from the collection + +DocumentDB uses the [documentdb_api.delete](https://github.com/microsoft/documentdb/wiki/Functions#delete) function for precise document removal based on specified criteria. + +The SQL command deletes the document for patient `P002`. + +```sql +SELECT documentdb_api.delete('documentdb', '{"delete": "patient", "deletes": [{"q": {"patient_id": "P002"}, "limit": 1}]}'); +``` + +### Collection management + +We can review for the available collections and databases by querying `documentdb_api_catalog.collections`. + +```sql +SELECT * FROM documentdb_api_catalog.collections; +``` + +`documentdb_api_catalog.collection_indexes` allows reviewing for the existing indexes on a collection. We can find collection_id from `documentdb_api_catalog.collections`. + +```sql +SELECT * FROM documentdb_api_catalog.collection_indexes WHERE collection_id = 2; +``` + +`ttl` indexes by default gets scheduled through the `pg_cron` scheduler, which could be reviewed by querying the `cron.job` table. + +```sql +select * from cron.job; +``` + +### Indexing + +#### Create an Index + +DocumentDB uses the `documentdb_api.create_indexes_background` function, which allows background index creation without disrupting database operations. + +The SQL command demonstrates how to create a `single field` index on `age` on the `patient` collection of the `documentdb`. + +```sql +SELECT * FROM documentdb_api.create_indexes_background('documentdb', '{ "createIndexes": "patient", "indexes": [{ "key": {"age": 1},"name": "idx_age"}]}'); +``` + +The SQL command demonstrates how to create a `compound index` on fields age and registration_year on the `patient` collection of the `documentdb`. + +```sql +SELECT * FROM documentdb_api.create_indexes_background('documentdb', '{ "createIndexes": "patient", "indexes": [{ "key": {"registration_year": 1, "age": 1},"name": "idx_regyr_age"}]}'); +``` + +#### Drop an Index + +`DocumentDB` uses the `documentdb_api.drop_indexes` function, which allows you to remove an existing index from a collection. The SQL command demonstrates how to drop the index named `id_ab_1` from the `first_collection` collection of the `documentdb`. + +```sql +CALL documentdb_api.drop_indexes('documentdb', '{"dropIndexes": "patient", "index":"idx_age"}'); +``` + +### Perform aggregations `Group by` + +DocumentDB provides the `documentdb_api_catalog.bson_aggregation_pipeline` function, for performing aggregations over the document store. + +The example projects an aggregation on number of patients registered over the years. + +```sql +SELECT document FROM documentdb_api_catalog.bson_aggregation_pipeline('documentdb', '{ "aggregate": "patient", "pipeline": [ { "$group": { "_id": "$registration_year", "count_patients": { "$count": {} } } } ] }'); +``` + +We can perform more complex operations, listing below a few more usage examples. +The example demonstrates an aggregation on patients, categorizing them into buckets defined by registration_year boundaries. + +```sql +SELECT document FROM bson_aggregation_pipeline('documentdb', '{ "aggregate": "patient", "pipeline": [ { "$bucket": { "groupBy": "$registration_year", "boundaries": ["2022","2023","2024"], "default": "unknown" } } ] }'); +``` + +This query performs an aggregation on the `patient` collection to group documents by `registration_year`. It collects unique patient conditions for each registration year using the `$addToSet` operator. + +```sql +SELECT document FROM documentdb_api_catalog.bson_aggregation_pipeline('documentdb', '{ "aggregate": "patient", "pipeline": [ { "$group": { "_id": "$registration_year", "conditions": { "$addToSet": { "conditions" : "$conditions" } } } } ] }'); +``` + +### Join data from multiple collections + +Let's create an additional collection named `appointment` to demonstrate how a join operation can be performed. + +```sql +select documentdb_api.insert_one('documentdb','appointment', '{"appointment_id": "A001", "patient_id": "P001", "doctor_name": "Dr. Milind", "appointment_date": "2023-01-20", "reason": "Routine checkup" }'); +select documentdb_api.insert_one('documentdb','appointment', '{"appointment_id": "A002", "patient_id": "P001", "doctor_name": "Dr. Moore", "appointment_date": "2023-02-10", "reason": "Follow-up"}'); +select documentdb_api.insert_one('documentdb','appointment', '{"appointment_id": "A004", "patient_id": "P003", "doctor_name": "Dr. Smith", "appointment_date": "2024-03-12", "reason": "Allergy consultation"}'); +select documentdb_api.insert_one('documentdb','appointment', '{"appointment_id": "A005", "patient_id": "P004", "doctor_name": "Dr. Moore", "appointment_date": "2024-04-15", "reason": "Migraine treatment"}'); +select documentdb_api.insert_one('documentdb','appointment', '{"appointment_id": "A007","patient_id": "P001", "doctor_name": "Dr. Milind", "appointment_date": "2024-06-05", "reason": "Blood test"}'); +select documentdb_api.insert_one('documentdb','appointment', '{ "appointment_id": "A009", "patient_id": "P003", "doctor_name": "Dr. Smith","appointment_date": "2025-01-20", "reason": "Follow-up visit"}'); +``` + +The example presents each patient along with the doctors visited. + +```sql +SELECT document FROM documentdb_api_catalog.bson_aggregation_pipeline('documentdb', '{ "aggregate": "patient", "pipeline": [ { "$lookup": { "from": "appointment","localField": "patient_id", "foreignField": "patient_id", "as": "appointment" } },{"$unwind":"$appointment"},{"$project":{"_id":0,"name":1,"appointment.doctor_name":1,"appointment.appointment_date":1}} ]}'); +``` + +### Community + +- Please refer to page for contributing to our [Roadmap list](https://github.com/orgs/microsoft/projects/1407/views/1). +- [FerretDB](https://github.com/FerretDB/FerretDB) integration allows using DocumentDB as backend engine. + +Contributors and users can join the [Discord Server](https://aka.ms/documentdb_discord) for quick collaboration. + +### FAQs + +Q1. While performing `make check` if you encounter error `FATAL: "/home/documentdb/code/pg_documentdb_core/src/test/regress/tmp/data" has wrong ownership`? + +Please drop the `/home/documentdb/code/pg_documentdb_core/src/test/regress/tmp/` directory and rerun the `make check`. diff --git a/images/architecture.png b/images/architecture.png new file mode 100644 index 0000000000000000000000000000000000000000..3e77b4e68d5f6befb902a2c4c13b640cb53532b9 GIT binary patch literal 20412 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 literal 0 HcmV?d00001