Skip to content

Commit e5e6b61

Browse files
authored
PHPLIB-1506 Import mongodb/builder (#1381)
Import files from https://github.com/mongodb/mongo-php-builder Add replace rule in composer.json Add a GitHub Action to validate the generated files are up-to-date
2 parents 24214b6 + 17d2ba8 commit e5e6b61

File tree

923 files changed

+63920
-1
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

923 files changed

+63920
-1
lines changed

.gitattributes

+8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ tests export-ignore
44
benchmark export-ignore
55
docs export-ignore
66
examples export-ignore
7+
generator export-ignore
78
mongo-orchestration export-ignore
89
stubs export-ignore
910
tools export-ignore
@@ -14,6 +15,13 @@ phpunit.evergreen.xml export-ignore
1415
phpunit.xml.dist export-ignore
1516
psalm.xml.dist export-ignore
1617
psalm-baseline.xml export-ignore
18+
rector.php export-ignore
1719

1820
# Prevent generated build files from showing diffs in pull requests
1921
.evergreen/config/generated/** linguist-generated=true
22+
/src/Builder/Accumulator/*.php linguist-generated=true
23+
/src/Builder/Expression/*.php linguist-generated=true
24+
/src/Builder/Query/*.php linguist-generated=true
25+
/src/Builder/Projection/*.php linguist-generated=true
26+
/src/Builder/Stage/*.php linguist-generated=true
27+
/tests/Builder/*/Pipelines.php linguist-generated=true

.github/actions/setup/action.yml

+4
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ inputs:
1111
description: "INI values to pass along to setup-php action"
1212
required: false
1313
default: ""
14+
working-directory:
15+
description: "The directory where composer.json is located, if it is not in the repository root."
16+
required: false
1417

1518
runs:
1619
using: composite
@@ -49,3 +52,4 @@ runs:
4952
# Revert when psalm supports PHP 8.4
5053
# composer-options: "--no-suggest"
5154
composer-options: "--no-suggest ${{ inputs.php-version == '8.4' && '--ignore-platform-req=php+' || '' }}"
55+
working-directory: "${{ inputs.working-directory }}"

.github/workflows/generator.yml

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
name: "Generator"
2+
3+
on:
4+
merge_group:
5+
pull_request:
6+
branches:
7+
- "v*.*"
8+
- "master"
9+
- "feature/*"
10+
push:
11+
branches:
12+
- "v*.*"
13+
- "master"
14+
- "feature/*"
15+
16+
env:
17+
PHP_VERSION: "8.2"
18+
# TODO: change to "stable" once 1.20.0 is released
19+
# DRIVER_VERSION: "stable"
20+
DRIVER_VERSION: "mongodb/[email protected]"
21+
22+
jobs:
23+
psalm:
24+
name: "Diff check"
25+
runs-on: "ubuntu-22.04"
26+
27+
steps:
28+
- name: "Checkout"
29+
uses: "actions/checkout@v4"
30+
31+
- name: "Setup"
32+
uses: "./.github/actions/setup"
33+
with:
34+
php-version: ${{ env.PHP_VERSION }}
35+
driver-version: ${{ env.DRIVER_VERSION }}
36+
working-directory: "generator"
37+
38+
- name: "Run Generator"
39+
run: "generator/generate"
40+
41+
- name: "Check file diff"
42+
run: git add . -N && git diff --exit-code

composer.json

+3
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@
2626
"symfony/phpunit-bridge": "^5.2",
2727
"vimeo/psalm": "^5.13"
2828
},
29+
"replace": {
30+
"mongodb/builder": "*"
31+
},
2932
"autoload": {
3033
"psr-4": { "MongoDB\\": "src/" },
3134
"files": [ "src/functions.php" ]

generator/README.md

+34
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
# Code Generator for MongoDB PHP Library
2+
3+
This subproject is used to generate the code that is committed to the repository.
4+
The `generator` directory is not included in `mongodb/mongodb` package and is not installed by Composer.
5+
6+
## Contributing
7+
8+
Updating the generated code can be done only by modifying the code generator, or its configuration.
9+
10+
To run the generator, you need to have PHP 8.1+ installed and Composer.
11+
12+
1. Move to the `generator` directory: `cd generator`
13+
1. Install dependencies: `composer install`
14+
1. Run the generator: `./generate`
15+
16+
## Configuration
17+
18+
The `generator/config/*.yaml` files contains the list of operators and stages that are supported by the library.
19+
20+
### Test pipelines
21+
22+
Each operator can contain a `tests` section with a list if pipelines. To represent specific BSON objects,
23+
it is necessary to use Yaml tags:
24+
25+
| BSON Type | Example |
26+
|-------------|--------------------------------------------------------|
27+
| Regex | `!bson_regex '^abc'` <br/> `!bson_regex ['^abc', 'i']` |
28+
| Int64 | `!bson_int64 '123456789'` |
29+
| Decimal128 | `!bson_decimal128 '0.9'` |
30+
| UTCDateTime | `!bson_utcdatetime 0` |
31+
| Binary | `!bson_binary 'IA=='` |
32+
33+
To add new test cases to operators, you can get inspiration from the official MongoDB documentation and use
34+
the `generator/js2yaml.html` web page to manually convert a pipeline array from JS to Yaml.

generator/composer.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
"name": "mongodb/code-generator",
3+
"type": "project",
4+
"repositories": [
5+
{
6+
"type": "path",
7+
"url": "../",
8+
"symlink": true
9+
}
10+
],
11+
"replace": {
12+
"symfony/polyfill-php80": "*",
13+
"symfony/polyfill-php81": "*"
14+
},
15+
"require": {
16+
"mongodb/mongodb": "@dev",
17+
"nette/php-generator": "^4.1.5",
18+
"nikic/php-parser": "^5",
19+
"symfony/console": "^7",
20+
"symfony/finder": "^7",
21+
"symfony/yaml": "^7"
22+
},
23+
"license": "Apache-2.0",
24+
"autoload": {
25+
"psr-4": {
26+
"MongoDB\\CodeGenerator\\": "src/"
27+
}
28+
},
29+
"config": {
30+
"sort-packages": true
31+
}
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
# $schema: ../schema.json
2+
name: $accumulator
3+
link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/accumulator/'
4+
type:
5+
- accumulator
6+
encode: object
7+
description: |
8+
Defines a custom accumulator function.
9+
New in MongoDB 4.4.
10+
arguments:
11+
-
12+
name: init
13+
type:
14+
- javascript
15+
description: |
16+
Function used to initialize the state. The init function receives its arguments from the initArgs array expression. You can specify the function definition as either BSON type Code or String.
17+
-
18+
name: initArgs
19+
type:
20+
- resolvesToArray
21+
optional: true
22+
description: |
23+
Arguments passed to the init function.
24+
-
25+
name: accumulate
26+
type:
27+
- javascript
28+
description: |
29+
Function used to accumulate documents. The accumulate function receives its arguments from the current state and accumulateArgs array expression. The result of the accumulate function becomes the new state. You can specify the function definition as either BSON type Code or String.
30+
-
31+
name: accumulateArgs
32+
type:
33+
- resolvesToArray
34+
description: |
35+
Arguments passed to the accumulate function. You can use accumulateArgs to specify what field value(s) to pass to the accumulate function.
36+
-
37+
name: merge
38+
type:
39+
- javascript
40+
description: |
41+
Function used to merge two internal states. merge must be either a String or Code BSON type. merge returns the combined result of the two merged states. For information on when the merge function is called, see Merge Two States with $merge.
42+
-
43+
name: finalize
44+
type:
45+
- javascript
46+
optional: true
47+
description: |
48+
Function used to update the result of the accumulation.
49+
-
50+
name: lang
51+
type:
52+
- string
53+
description: |
54+
The language used in the $accumulator code.
55+
56+
tests:
57+
-
58+
name: 'Use $accumulator to Implement the $avg Operator'
59+
link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/accumulator/#use--accumulator-to-implement-the--avg-operator'
60+
pipeline:
61+
-
62+
$group:
63+
_id: '$author'
64+
avgCopies:
65+
$accumulator:
66+
init:
67+
$code: |-
68+
function() {
69+
return { count: 0, sum: 0 }
70+
}
71+
accumulate:
72+
$code: |-
73+
function(state, numCopies) {
74+
return { count: state.count + 1, sum: state.sum + numCopies }
75+
}
76+
accumulateArgs: [ "$copies" ],
77+
merge:
78+
$code: |-
79+
function(state1, state2) {
80+
return {
81+
count: state1.count + state2.count,
82+
sum: state1.sum + state2.sum
83+
}
84+
}
85+
finalize:
86+
$code: |-
87+
function(state) {
88+
return (state.sum / state.count)
89+
}
90+
lang: 'js'
91+
92+
-
93+
name: 'Use initArgs to Vary the Initial State by Group'
94+
link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/accumulator/#use-initargs-to-vary-the-initial-state-by-group'
95+
pipeline:
96+
-
97+
$group:
98+
_id:
99+
city: '$city'
100+
restaurants:
101+
$accumulator:
102+
init:
103+
$code: |-
104+
function(city, userProfileCity) {
105+
return { max: city === userProfileCity ? 3 : 1, restaurants: [] }
106+
}
107+
initArgs:
108+
- '$city'
109+
- 'Bettles'
110+
accumulate:
111+
$code: |-
112+
function(state, restaurantName) {
113+
if (state.restaurants.length < state.max) {
114+
state.restaurants.push(restaurantName);
115+
}
116+
return state;
117+
}
118+
accumulateArgs:
119+
- '$name'
120+
merge:
121+
$code: |-
122+
function(state1, state2) {
123+
return {
124+
max: state1.max,
125+
restaurants: state1.restaurants.concat(state2.restaurants).slice(0, state1.max)
126+
}
127+
}
128+
finalize:
129+
$code: |-
130+
function(state) {
131+
return state.restaurants
132+
}
133+
lang: 'js'
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# $schema: ../schema.json
2+
name: $addToSet
3+
link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/addToSet/'
4+
type:
5+
- accumulator
6+
- window
7+
encode: single
8+
description: |
9+
Returns an array of unique expression values for each group. Order of the array elements is undefined.
10+
Changed in MongoDB 5.0: Available in the $setWindowFields stage.
11+
arguments:
12+
-
13+
name: expression
14+
type:
15+
- expression
16+
17+
tests:
18+
-
19+
name: 'Use in $group Stage'
20+
link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/addToSet/#use-in--group-stage'
21+
pipeline:
22+
- $group:
23+
_id:
24+
day:
25+
$dayOfYear:
26+
date: '$date'
27+
year:
28+
$year:
29+
date: '$date'
30+
itemsSold:
31+
$addToSet: '$item'
32+
-
33+
name: 'Use in $setWindowFields Stage'
34+
link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/addToSet/#use-in--setwindowfields-stage'
35+
pipeline:
36+
-
37+
$setWindowFields:
38+
partitionBy: '$state'
39+
sortBy:
40+
orderDate: 1
41+
output:
42+
cakeTypesForState:
43+
$addToSet: '$type'
44+
window:
45+
documents:
46+
- 'unbounded'
47+
- 'current'

generator/config/accumulator/avg.yaml

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# $schema: ../schema.json
2+
name: $avg
3+
link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/avg/'
4+
type:
5+
- accumulator
6+
- window
7+
encode: single
8+
description: |
9+
Returns an average of numerical values. Ignores non-numeric values.
10+
Changed in MongoDB 5.0: Available in the $setWindowFields stage.
11+
arguments:
12+
-
13+
name: expression
14+
type:
15+
- resolvesToNumber
16+
tests:
17+
-
18+
name: 'Use in $group Stage'
19+
link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/avg/#use-in--group-stage'
20+
pipeline:
21+
- $group:
22+
_id: '$item'
23+
avgAmount:
24+
$avg:
25+
$multiply:
26+
- '$price'
27+
- '$quantity'
28+
avgQuantity:
29+
$avg: '$quantity'
30+
-
31+
name: 'Use in $setWindowFields Stage'
32+
link: 'https://www.mongodb.com/docs/manual/reference/operator/aggregation/avg/#use-in--setwindowfields-stage'
33+
pipeline:
34+
-
35+
$setWindowFields:
36+
partitionBy: '$state'
37+
sortBy:
38+
orderDate: 1
39+
output:
40+
averageQuantityForState:
41+
$avg: '$quantity'
42+
window:
43+
documents:
44+
- 'unbounded'
45+
- 'current'

0 commit comments

Comments
 (0)