MongoDB Documents
Store and query JSON documents with the mongodb connector.
This guide wires an HTTP API to a MongoDB collection: store request bodies as
documents, read them back by id, list them, and total them with an aggregation.
It follows
samples/mongodb-orders.yaml.
Database CRUD builds the same API over SQL, where a row
needs a column mapping in each direction; a message body already is a document.
There is no in-memory MongoDB, so this sample needs a server. One container is enough:
docker run -d --rm -p 27017:27017 --name octo-mongo mongo:7
task run:sample -- mongodb-orders.yamlDeclare the connector
The mongodb connector owns the client. Only
uri is required; database is the default for blocks that do not name one.
env:
- name: MONGODB_URI
default: mongodb://localhost:27017
- name: MONGODB_DATABASE
default: octo-samples
- name: MONGODB_VERIFY_ON_START
default: "true"
connectors:
- name: orders-db
type: mongodb
settings:
uri: ${MONGODB_URI}
database: ${MONGODB_DATABASE}
verifyOnStart: ${MONGODB_VERIFY_ON_START}
maxPoolSize: 4A ${VAR} the env: block never declares is a load error, not an empty string.
The defaults here run against the container above with nothing exported.
maxPoolSize is 4 rather than the default 32 because the sample's flows run
four workers each. The 32 is sized to the workers a flow gets when it does not
choose a count, max(8, GOMAXPROCS * 4), which is 32 on an eight-core host.
Keep the password out of the URI. The connector takes it as its own password
setting and merges it in at startup, so only the credential is a secret.
Store the request body
Storing a body is one block:
- type: mongodb-insert
name: insert-order
settings:
connector: orders-db
collection: '"orders"'
document: bodycollection is a CEL expression, so a flow can route
to a different collection per message; '"orders"' is the expression string
literal orders. document writes one document for an object and all of them
for a list. Posting an order answers with the ids MongoDB generated:
$ curl -s -X POST localhost:8080/api/v1/orders -d '{"sku":"A1","qty":2,"price":10}'
{"insertedCount":1,"insertedIds":[{"$oid":"6a72e1e21393cc3cf3cb59a2"}]}Read it back without converting anything
Documents cross the message boundary as
MongoDB Extended JSON in both
directions, so the {"$oid": ...} from a response goes straight back into a
filter unchanged:
- type: mongodb-find
name: select-order
settings:
connector: orders-db
collection: '"orders"'
filter: '{"_id": {"$oid": vars.id}}'
single: true$ curl -s localhost:8080/api/v1/orders/6a72e1e21393cc3cf3cb59a2
{"_id":{"$oid":"6a72e1e21393cc3cf3cb59a2"},"price":10,"qty":2,"sku":"A1"}single: true returns the document rather than a list, and null when
nothing matched (an answer to branch on, not an error). Without it the result is
a list, [] rather than null when empty, so a foreach
over the body gets zero iterations.
List, newest first
- type: mongodb-find
name: list-orders
settings:
connector: orders-db
collection: '"orders"'
sort: '[{"_id": -1}]'
limit: 50sort is a list of single-key objects, not one object with several keys.
MongoDB applies sort keys in the order written and a CEL object cannot carry
that order, so a multi-key object is refused. One key on its own may be written
either way.
Total it on the server
A pipeline is JSON, written the way it would be typed into mongosh, with
body and vars reachable inside it:
- type: mongodb-aggregate
name: total-by-sku
settings:
connector: orders-db
collection: '"orders"'
pipeline: |
[
{"$group": {"_id": "$sku",
"orders": {"$sum": 1},
"value": {"$sum": {"$multiply": ["$qty", "$price"]}}}},
{"$sort": [{"value": -1}, {"_id": 1}]}
]$ curl -s localhost:8080/api/v1/orders/totals
[{"_id":"A1","orders":2,"value":50},{"_id":"B2","orders":1,"value":50}]Both skus total 50 and the second sort key breaks the tie by _id ascending.
A $sort stage follows the same list-form rule as the find block's sort.
Test it without a server
The sample's suite
(mongodb-orders_test.yaml)
mocks the blocks. A mock replaces the block, not the connector, which still
starts and by default pings the server; verifyOnStart: false lets the config
build without one. One case leaves the block unmocked and points the connector
at a refused port: the dial failure proves the block issued the call.
task runtime:test:samplesTear the container down with docker rm -f octo-mongo.