Solution 1
Online example for inserting {"value" : 43 ,"when" : 11}
Push middle, same value as following member - Replace following member
Test code here
All possible cases (its 9 cases, 7 + 2 add on empty probes("bonus" case) or only empty readings)
---------------------Empty probes(case 1(the extra case) empty probes)-------------------------------
Before add
{"_id": {"$oid": "61345b2aefdf45b6128444f2"}, "location_id": 1, "probes": []}
Case : Only 1 case exists Member : {value 41, when 3}
{"_id": {"$oid": "61345b2aefdf45b6128444f2"}, "location_id": 1, "probes": [{"probe_id": 4, "readings": [{"value": 41, "when": 3}]}]}
---------------------Empty readings(case 1 empty readings)-------------------------------
Before add
{"_id": {"$oid": "61345b68efdf45b612844650"}, "location_id": 1, "probes": [{"probe_id": 2, "readings": []}]}
Case : Only 1 case exists Member : {value 41, when 3}
{"_id": {"$oid": "61345b68efdf45b612844650"}, "location_id": 1, "probes": [{"probe_id": 2, "readings": [{"value": 41, "when": 3}]}]}
---------------------Not empty readings-------------------------------
Before add
{"_id": {"$oid": "61346a33efdf45b6128496b7"}, "location_id": 1, "probes": [{"probe_id": 2, "readings": [{"value": 42, "when": 5}, {"value": 37, "when": 10}, {"value": 43, "when": 15}, {"value": 41, "when": 20}]}]}
Case : NoConflict start[push front, different value from following member - Add item] Member : {value 41, when 3}
{"_id": {"$oid": "61346a33efdf45b6128496b7"}, "location_id": 1, "probes": [{"probe_id": 2, "readings": [{"value": 41, "when": 3}, {"value": 42, "when": 5}, {"value": 37, "when": 10}, {"value": 43, "when": 15}, {"value": 41, "when": 20}]}]}
Case : Conflict start[push front, same value as following member - Replace following member] Member : {value 42, when 3}
{"_id": {"$oid": "61346a33efdf45b6128496b7"}, "location_id": 1, "probes": [{"probe_id": 2, "readings": [{"value": 42, "when": 3}, {"value": 37, "when": 10}, {"value": 43, "when": 15}, {"value": 41, "when": 20}]}]}
Case : NoConflict middle[push middle, different value from preceding and following** member - Add item] Member : {value 42, when 11}
{"_id": {"$oid": "61346a33efdf45b6128496b7"}, "location_id": 1, "probes": [{"probe_id": 2, "readings": [{"value": 42, "when": 5}, {"value": 37, "when": 10}, {"value": 42, "when": 11}, {"value": 43, "when": 15}, {"value": 41, "when": 20}]}]}
Case : Conflict middle[push middle, same value as preceding member - Do nothing (don't push)] Member : {value 37, when 11}
{"_id": {"$oid": "61346a33efdf45b6128496b7"}, "location_id": 1, "probes": [{"probe_id": 2, "readings": [{"value": 42, "when": 5}, {"value": 37, "when": 10}, {"value": 43, "when": 15}, {"value": 41, "when": 20}]}]}
Case : Conflict middle[push middle, same value as following member - Replace following member] Member : {value 43, when 11}
{"_id": {"$oid": "61346a33efdf45b6128496b7"}, "location_id": 1, "probes": [{"probe_id": 2, "readings": [{"value": 42, "when": 5}, {"value": 37, "when": 10}, {"value": 43, "when": 11}, {"value": 41, "when": 20}]}]}
Case : NoConflict end[push back, different value from preceding* member - Add item] Member : {value 47, when 21}
{"_id": {"$oid": "61346a33efdf45b6128496b7"}, "location_id": 1, "probes": [{"probe_id": 2, "readings": [{"value": 42, "when": 5}, {"value": 37, "when": 10}, {"value": 43, "when": 15}, {"value": 41, "when": 20}, {"value": 47, "when": 21}]}]}
Case : Conflict end[push back, same value as preceding member - Do nothing (don't push)] Member : {value 41, when 21}
{"_id": {"$oid": "61346a33efdf45b6128496b7"}, "location_id": 1, "probes": [{"probe_id": 2, "readings": [{"value": 42, "when": 5}, {"value": 37, "when": 10}, {"value": 43, "when": 15}, {"value": 41, "when": 20}]}]}
Query
its big but its fast, its atomic, all done in 1 query, it doesn't sort the array,it inserts in the right position to keep it sorted
to move this code to your code
replace the 4 with new ObjectID() (add the method call)
replace the 1 with the location ObjectID
replace the 2 with the prob_id ObjectID
i used numbers instead of dates for easy testing, but no need to change the query for that
first $set is to define the new ObjectID(replace the 4 with the method call) in case probes is empty create the probe with empty readings
$map to get inside the array
$reduce to find the position where we will add the new member
the when field defines the position, the query keep the array sorted by adding in the right place
*we could use reduce to make the array not just find the position
but if the array had >500 members because of concat it would be so slow not usable.
when we have the position we check 4 basic cases
- add at empty readings (1 case)
- add at start (2 cases)
- add at middle (3 cases)
- add at end (2 cases)
- each one of them has other cases if Conflict of
value or not
db.collection.update({
"location_id": 1
},
[
{
"$set": {
"prob-id": {
"$cond": [
{
"$eq": [
"$probes",
[]
]
},
4,
2
]
}
}
},
{
"$set": {
"probes": {
"$cond": [
{
"$eq": [
"$probes",
[]
]
},
[
{
"probe_id": "$prob-id",
"readings": []
}
],
"$probes"
]
}
}
},
{
"$set": {
"probes": {
"$map": {
"input": "$probes",
"as": "m1",
"in": {
"$cond": [
{
"$ne": [
"$$m1.probe_id",
"$prob-id"
]
},
"$$m1",
{
"$mergeObjects": [
"$$m1",
{
"readings": {
"$let": {
"vars": {
"size_position": {
"$reduce": {
"input": "$$m1.readings",
"initialValue": [
0,
null,
null
],
"in": {
"$let": {
"vars": {
"index_pos": "$$value",
"m2": "$$this"
},
"in": {
"$let": {
"vars": {
"index": {
"$arrayElemAt": [
"$$index_pos",
0
]
},
"pos": {
"$arrayElemAt": [
"$$index_pos",
1
]
}
},
"in": {
"$cond": [
{
"$and": [
{
"$eq": [
"$$pos",
null
]
},
{
"$gt": [
"$$m2.when",
11
]
}
]
},
[
{
"$add": [
"$$index",
1
]
},
"$$index"
],
[
{
"$add": [
"$$index",
1
]
},
"$$pos"
]
]
}
}
}
}
}
}
}
},
"in": {
"$let": {
"vars": {
"asize": {
"$arrayElemAt": [
"$$size_position",
0
]
},
"position": {
"$arrayElemAt": [
"$$size_position",
1
]
}
},
"in": {
"$switch": {
"branches": [
{
"case": {
"$eq": [
"$$asize",
0
]
},
"then": [
{
"value": 43,
"when": 11
}
]
},
{
"case": {
"$eq": [
"$$position",
null
]
},
"then": {
"$let": {
"vars": {
"prv_member": {
"$arrayElemAt": [
"$$m1.readings",
{
"$subtract": [
"$$asize",
1
]
}
]
}
},
"in": {
"$cond": [
{
"$eq": [
"$$prv_member.value",
43
]
},
"$$m1.readings",
{
"$concatArrays": [
"$$m1.readings",
[
{
"value": 43,
"when": 11
}
]
]
}
]
}
}
}
},
{
"case": {
"$eq": [
"$$position",
0
]
},
"then": {
"$let": {
"vars": {
"next_member": {
"$arrayElemAt": [
"$$m1.readings",
0
]
}
},
"in": {
"$cond": [
{
"$eq": [
"$$next_member.value",
43
]
},
{
"$cond": [
{
"$lt": [
11,
"$$next_member.when"
]
},
{
"$concatArrays": [
[
{
"value": 43,
"when": 11
}
],
{
"$slice": [
"$$m1.readings",
1,
"$$asize"
]
}
]
},
"$$m1.readings"
]
},
{
"$concatArrays": [
[
{
"value": 43,
"when": 11
}
],
"$$m1.readings"
]
}
]
}
}
}
}
],
"default": {
"$let": {
"vars": {
"next_member": {
"$arrayElemAt": [
"$$m1.readings",
"$$position"
]
},
"prv_member": {
"$arrayElemAt": [
"$$m1.readings",
{
"$subtract": [
"$$position",
1
]
}
]
}
},
"in": {
"$switch": {
"branches": [
{
"case": {
"$and": [
{
"$ne": [
"$$next_member.value",
43
]
},
{
"$ne": [
"$$prv_member.value",
43
]
}
]
},
"then": {
"$concatArrays": [
{
"$slice": [
"$$m1.readings",
0,
"$$position"
]
},
[
{
"value": 43,
"when": 11
}
],
{
"$slice": [
"$$m1.readings",
"$$position",
{
"$add": [
"$$asize",
1
]
}
]
}
]
}
},
{
"case": {
"$eq": [
"$$prv_member.value",
43
]
},
"then": "$$m1.readings"
}
],
"default": {
"$concatArrays": [
{
"$slice": [
"$$m1.readings",
0,
"$$position"
]
},
[
{
"value": 43,
"when": 11
}
],
{
"$slice": [
"$$m1.readings",
{
"$add": [
"$$position",
1
]
},
{
"$add": [
"$$asize",
1
]
}
]
}
]
}
}
}
}
}
}
}
}
}
}
}
}
]
}
]
}
}
}
}
},
{
"$unset": [
"prob-id"
]
}
])