Skip to content

Mongo operators#907

Open
fogelito wants to merge 3 commits into
mainfrom
mongo-operators
Open

Mongo operators#907
fogelito wants to merge 3 commits into
mainfrom
mongo-operators

Conversation

@fogelito

@fogelito fogelito commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Summary by CodeRabbit

  • New Features

    • Added support for advanced update expressions during document updates and upserts.
    • Improved upsert handling so complex field operations can be applied in a single request.
  • Bug Fixes

    • Preserved special update expressions without converting them into stored values.
    • Improved update behavior for records using nested array conditions and pipeline-based changes.
    • Enabled broader operator support for database writes.

@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@fogelito, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 19 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 33c64853-cade-43b1-846d-f19c5ad2385a

📥 Commits

Reviewing files that changed from the base of the PR and between acf2a8f and 4bde754.

📒 Files selected for processing (1)
  • src/Database/Adapter/Mongo.php
📝 Walkthrough

Walkthrough

The MongoDB adapter gains aggregation pipeline-based operator support. Casting guards skip Operator instances, five new private methods build and execute pipeline updates, updateDocument/updateDocuments/upsertDocuments are updated to route through the pipeline path when operators are present, and getSupportForOperators() now returns true.

Changes

MongoDB Operator Pipeline Support

Layer / File(s) Summary
Operator casting guards and capability flag
src/Database/Adapter/Mongo.php
Imports Operator, adds early-return guards in castingBefore() and castingAfter() to skip coercion for Operator values, and flips getSupportForOperators() to true.
Pipeline construction and execution helpers
src/Database/Adapter/Mongo.php
Adds buildOperatorPipeline(), updateWithPipeline(), executeUpsert(), getOperatorExpression(), and getArrayFilterCondition() as private methods that translate Operator instances into MongoDB aggregation expressions and execute raw pipeline update commands.
updateDocument / updateDocuments operator routing
src/Database/Adapter/Mongo.php
Replaces unconditional $set updates with conditional logic: calls buildOperatorPipeline() and routes to updateWithPipeline() when operators are present; keeps classic $set otherwise. updateDocuments passes multi: true.
upsertDocuments pipeline routing
src/Database/Adapter/Mongo.php
Introduces $hasPipeline flag, constructs pipeline-based upserts (with $ifNull-based _id generation and $unset handling) when buildOperatorPipeline() returns a result, and routes to executeUpsert() instead of client->upsert().

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Poem

🐇 Hoppity hop through the pipeline stage,
Operators flow like words on a page,
$set and $ifNull and $filter too,
No more false returns — getSupportForOperators is true!
The MongoDB rabbit has tricks up its sleeve,
Aggregation magic you wouldn't believe. ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is related to the change, but it is too vague to communicate the main update clearly. Use a more specific title such as "Support Mongo operators in update and upsert pipelines".
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch mongo-operators

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@greptile-apps

greptile-apps Bot commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR wires up Operator-based aggregation-pipeline updates for MongoDB, enabling atomic field expressions (increment, array mutations, date arithmetic, etc.) to be applied during updateDocument, updateDocuments, and upsertDocuments. It also flips getSupportForOperators() to true so the rest of the framework knows the adapter accepts these operators.

  • buildOperatorPipeline translates Operator instances into a MongoDB $set aggregation stage, wrapping plain values in $literal to prevent misinterpretation as field paths.
  • updateWithPipeline and executeUpsert bypass the client's toObject() wrapper that would corrupt a pipeline array, sending the raw command through query() instead.
  • The upsertDocuments path with a non-empty $attribute (the ON-DUPLICATE-KEY increment branch) does not call buildOperatorPipeline, so Operator instances in the record are passed directly to MongoDB's $set/$inc rather than being translated, causing a runtime error now that operators are advertised as supported.

Confidence Score: 3/5

The core pipeline update path is well-constructed, but the ON-DUPLICATE-KEY upsert branch does not handle Operator instances, producing driver errors whenever operators are combined with that code path — a gap that now matters because the adapter advertises operator support.

The operator translation logic in getOperatorExpression and buildOperatorPipeline is thorough and the two new low-level command helpers correctly avoid the toObject corruption. However, the upsertDocuments branch guarded by !empty($attribute) never routes through buildOperatorPipeline: any Operator in $record (whether the designated attribute or another field) is forwarded raw to MongoDB's $inc or $set, causing an immediate error. This gap affects a real code path that callers can now reach — two of the three callers handle it, but this one does not.

src/Database/Adapter/Mongo.php — specifically the !empty($attribute) branch inside upsertDocuments

Important Files Changed

Filename Overview
src/Database/Adapter/Mongo.php Adds aggregation-pipeline update support for Operator instances across updateDocument, updateDocuments, and upsertDocuments; the upsertDocuments non-empty-$attribute branch does not route Operator-containing records through buildOperatorPipeline, causing runtime errors when operators are present.

Reviews (3): Last reviewed commit: "Greptile address" | Re-trigger Greptile

Comment thread src/Database/Adapter/Mongo.php

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/Database/Adapter/Mongo.php`:
- Around line 1447-1450: Operator values in Mongo::buildOperatorPipeline() are
currently skipped entirely, which leaves literal operands uncast and can pass
raw strings into $literal for typed attributes. Update the operator-handling
path in Mongo so the field expression stays untouched, but the operands used by
buildOperatorPipeline() are cast to the target attribute type before the
pipeline is assembled; also make sure the same casting behavior is applied in
the other operator-related blocks referenced by the review so datetime arrays
and similar typed operands are encoded as Mongo UTCDateTime values instead of
strings.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 10051415-6227-4a2b-ade4-7162b0d73df4

📥 Commits

Reviewing files that changed from the base of the PR and between 0dfae8c and acf2a8f.

📒 Files selected for processing (1)
  • src/Database/Adapter/Mongo.php

Comment on lines +1447 to +1450
// Operators are resolved by the database (aggregation pipeline); skip casting
if (Operator::isOperator($value)) {
continue;
}

@coderabbitai coderabbitai Bot Jun 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | 🏗️ Heavy lift

Cast operator literal operands before building the pipeline.

Line 1448 skips casting the whole Operator, and buildOperatorPipeline() later writes operand values through $literal. Since Database::encode() also skips Operator values, typed operands such as arrayAppend()/arrayInsert() on datetime arrays can be stored as raw strings instead of Mongo UTCDateTime values. Keep the field expression uncast, but cast operator operand literals according to the target attribute before translating them.

Also applies to: 1744-1765, 1921-1935

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Database/Adapter/Mongo.php` around lines 1447 - 1450, Operator values in
Mongo::buildOperatorPipeline() are currently skipped entirely, which leaves
literal operands uncast and can pass raw strings into $literal for typed
attributes. Update the operator-handling path in Mongo so the field expression
stays untouched, but the operands used by buildOperatorPipeline() are cast to
the target attribute type before the pipeline is assembled; also make sure the
same casting behavior is applied in the other operator-related blocks referenced
by the review so datetime arrays and similar typed operands are encoded as Mongo
UTCDateTime values instead of strings.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will fix in a seperate PR
#908

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@fogelito Sounds good — thanks for splitting that follow-up into #908. I’ll treat this one as deferred to that PR.


🧠 Learnings used
Learnt from: abnegate
Repo: utopia-php/database PR: 860
File: src/Database/Adapter/Memory.php:1279-1284
Timestamp: 2026-04-29T03:59:51.515Z
Learning: For all database adapters in src/Database/Adapter (including Memory.php), ensure getMaxIndexLength() is expressed in bytes (not kilobytes) and remains consistent with the expectations of the e2e scope tests under tests/e2e/Adapter/Scopes/. The IndexTests/AttributeTests derive attribute sizes and error messages from getMaxIndexLength() and assume attributes are created using values like getMaxIndexLength() + 300 and getMaxIndexLength() - 68. If you change getMaxIndexLength() (e.g., from 1024 to another value), update the scope tests and any adapter string-limit behavior accordingly to prevent breaking size/error-message assertions.

Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Comment thread src/Database/Adapter/Mongo.php Outdated
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant