Did the High Value Belong to the Temperature Sensor?
An array contains several readings. Keep the sensor kind and its value attached to the same member while filtering documents.
15 sections · 4 questions · 1 coding challenge
The arrow keys move between sections too.
Queries Followed Fields Inside the Document
BOTIE
MongoDB’s $elemMatch expresses that at least one array member must satisfy all of its listed criteria. Our workshop makes the same binding visible by expanding each reading with its parent identity, applying both conditions to that row, and then returning each matching asset once.
Sources for this history
P1 Has a Temperature Reading and a High Number, but They Are Different Readings
P1 contains a temperature reading of 65 and a vibration reading of 95. Botie is looking for temperatures of at least 80. A careless query notices that the document contains the word temperature and also contains a value above 80, then returns P1.
The information we need is present, but the conditions have been applied to different array members. We need to keep each reading intact while deciding whether it satisfies the whole request.
Workshop filters use explicit primitive comparisons. They are not a MongoDB query interpreter and do not implement its full missing-value or array semantics.
Step 1 of 2
The tempting match is split across two readings
The request is temperature at least 80.
| Asset / member | Kind | Value | Qualifies alone? |
|---|---|---|---|
| P1 / first | temperature | 65 | no |
| P1 / second | vibration | 95 | no |
| P2 / first | temperature | 85 | yes |
P1 / first
- Kind
- temperature
- Value
- 65
- Qualifies alone?
- no
P1 / second
- Kind
- vibration
- Value
- 95
- Qualifies alone?
- no
P2 / first
- Kind
- temperature
- Value
- 85
- Qualifies alone?
- yes
Step 2 of 2
Do both checks before discarding the member identity
Unwinding makes the binding inspectable.
- 1
Expand
One row per reading, retaining asset_id - 2
Keep kind
temperature rows only - 3
Keep threshold
value at least 80 - 4
Return identity
Each qualifying asset once
One matching member must satisfy the whole condition
Both predicates apply before the reading member is projected away.
Keep each sensor kind beside its value
Expand the reading arrays, filter by kind and threshold on the same expanded row, then keep each matching asset once.
BOTIE
Assets with a qualifying reading
[
{
"asset_id": "P1"
}
]Follow the operations
1. Pump documents and readings
[ { "asset_id": "P1", "readings": [ { "kind": "temperature", "value": 85 } ] }, { "asset_id": "P2", "readings": [ { "kind": "temperature", "value": 65 } ] } ]2. Make one row per array member
[ { "asset_id": "P1", "readings": { "kind": "temperature", "value": 85 } }, { "asset_id": "P2", "readings": { "kind": "temperature", "value": 65 } } ]3. Requested reading kind
temperature
4. Keep matching document values
[ { "asset_id": "P1", "readings": { "kind": "temperature", "value": 85 } }, { "asset_id": "P2", "readings": { "kind": "temperature", "value": 65 } } ]5. Minimum qualifying value
80
6. Keep matching document values
[ { "asset_id": "P1", "readings": { "kind": "temperature", "value": 85 } } ]7. Keep selected document paths
[ { "asset_id": "P1" } ]8. Keep one copy of each result
[ { "asset_id": "P1" } ]9. Result
[ { "asset_id": "P1" } ]
The browser runs an explicit model of the mechanism. It does not connect to MongoDB or Redis, simulate their query planners, or establish their concurrency guarantees.
1. State whether the result is a document or a reading
The request asks for assets with a qualifying reading. Its output contains each asset once. A different request might ask for every matching reading, in which case several results for one asset are useful.
Decide that result grain first. A database can match a whole document because one nested member qualifies, then return the entire document or a chosen projection. Filtering the parent does not automatically remove its other array members.
A matching asset and its matching readings are different result shapes.
2. Follow an ordinary field path
A path such as sensor.latest identifies a nested field when sensor is an object. A path through an array needs additional semantics because there may be several candidate members. In MongoDB, ordinary dotted conditions on array fields can be satisfied by different members unless the query binds them together appropriately.
That distinction explains the P1 error. The document contains a temperature member and a high-value member, but the pair we need does not exist.
Keep the member identity until all conditions referring to that member have been tested.
3. Trace one member through both checks
Expand P1’s array into two rows, each still carrying P1. Keep only temperature rows. Its 65 remains, and the 95 vibration row leaves. Now apply the threshold: the remaining P1 row fails. P2’s 85 passes.
This is an explanatory construction. A database can use another execution plan while preserving the requested same-member meaning. We are learning the semantics before discussing its access cost.
The order of these two pure filters can change without changing the selected members.
4. Bind the conditions with $elemMatch
MongoDB’s $elemMatch requires at least one member of the specified array to match all the criteria inside it. Put the kind and threshold in the same member condition.
The query object below is supplied to find. Its value is a data structure, not a string to splice together from untrusted fragments. Applications should build allowed fields and operators deliberately rather than accepting an arbitrary query object from a client.
A correct predicate still needs tenant and resource scoping in the service that executes it.
Run in your own environment
A same-member reading predicate
Pass this query object to a MongoDB collection find operation containing the lesson’s readings arrays.
Use a disposable MongoDB collection outside the browser. The workshop explicitly expands rows instead of parsing this JSON.
{"readings":{"$elemMatch":{"kind":"temperature","value":{"$gte":80}}}}P1 with temperature 65 and vibration 95 does not match; P2 with temperature 85 does.
A same-member reading predicate. Documentation checked .
Check yourself
Questions on Parts 1 to 4
Question 1 of 2
Does P1 qualify?
Use the information below.
P1 readings are [{kind:"temperature",value:65},{kind:"vibration",value:95}]. We require one temperature reading with value >= 80.5. Return the fields the screen needs
Projection chooses which fields appear in the returned documents. If the screen only needs asset identities, avoid transferring complete histories. In MongoDB an inclusion projection ordinarily includes _id unless it is explicitly excluded. Inclusion and exclusion rules have restrictions, so use the documented form.
Projection does not make private records public. Authorise the query separately, then reduce the returned fields. In our expanded workshop, projection leaves asset_id and deduplication removes repeated matches for the same asset.
Deduplication changes the output grain after the member checks are complete.
6. Treat missing and null as explicit query choices
In MongoDB, equality to null can match an explicitly null field or a missing field. Use the appropriate existence and type conditions when those states mean different things. That differs from simply carrying over SQL’s equality behaviour for NULL.
Our reading fixture requires a numeric value in every member. The challenge therefore does not pretend to reproduce MongoDB type comparison, collation or null semantics. If your production records allow malformed members, decide whether to reject them at ingestion, exclude them, or report them separately.
Make the dataset contract as explicit as the predicate.
7. Write the member test directly in Python
Python any answers whether at least one member passes. The condition inside it reads kind and value from the same reading dictionary. An empty array contains no qualifying member.
The function returns asset IDs in input order. That is our worksheet contract; a real database query needs an explicit sort when presentation order matters.
One member supplies both values to the test.
Inspect the two conditions for each member
A shared loop variable prevents a high vibration value from being attached to the temperature label.
readings = [{"kind":"temperature","value":65},{"kind":"vibration","value":95}]
for reading in readings:
qualifies = reading["kind"] == "temperature" and reading["value"] >= 80
print(reading["kind"], qualifies)temperature False
vibration FalseNeither member qualifies, even though the separate kind and threshold conditions both appear somewhere in the array.
8. Keep the case that split the evidence
The first workshop fixture has only temperature readings. A shortcut that separately checks the presence of temperature and any high value may pass it. The changed fixture adds a high vibration value beside a low temperature. That is the case that distinguishes the intended predicate.
Also keep an empty array and an asset with several qualifying members. Those test absence and output grain, neither of which the initial fixture establishes.
The next lesson changes a document. A correct match must remain part of the write itself when another editor can act concurrently.
Check yourself
Questions on Parts 1 to 8
Question 1 of 2
What should we avoid assuming?
Use the information below.
A MongoDB document matches because one readings member qualifies. The query has no array projection or filtering stage.Try it with Botie
Build with Botie
Write and run Python