Requirement
RFC 0015 §7: measured answers to open questions 2, 4, 5, 6 and 7 on Elasticsearch
RFC 0015 §7 lists what has to be measured before the design is settled, and calls question 6 "the most valuable open question here". I ran those questions on Elasticsearch. Four come back with an answer, and two of them do not match what the RFC currently assumes.
OpenSearch is not covered yet. I will add it to this issue rather than open a second one, since R6 wants a single answer for both engines.
Setup. Jaeger's jaeger-span.json mapping unchanged, plus the §4 Option A multi-fields on both representations: the nested tags.value and the tag.* dynamic template. Two indices, identical except for coerce on the number sub-field. Eight documents: the five from the §4 table, plus a 300 character numeric string, the string "true", and an integer above 2^53. Every attribute uses the same key in both representations, so this also exercises the multi-typed key claim in §5.
Elasticsearch 9.2.2.
Q6: yes. coerce: false makes the sub-field its own discriminator
Same query, same documents, only coerce differs:
coerce default: range tag.probe.number > 2 -> d1_int64 d5_numeric_string d7_over_256 d8_above_2p53
coerce: false: range tag.probe.number > 2 -> d1_int64 d8_above_2p53
d5 is the string "3". Under the default it enters the numeric index, which is the row in §4's table. Under coerce: false it does not, and _ignored names tag.probe.number and tags.value.number for that document. The nested side behaves identically.
Every document was accepted, HTTP 201, in both indices. No rejections anywhere. ignore_malformed absorbed the refused coercion in every case, so #10070, #11498 and #25289 do not reproduce here. That was the single unverified assumption the question rests on, and it holds.
Two consequences:
- On the nested side,
coerce: false alone returns the same set that the type-paired query returns under the default. The type pairing becomes an optimization rather than a correctness requirement.
- On the elevated side, the sub-field becomes authoritative for number versus string, which §2 identifies as the distinction these operators turn on. It still does not separate integer from float, but §2 already records that as costless for
gt/lt/gte/lte.
Q4: the stated reason this is safe does not hold
§7 question 4 says the ignore_above: 256 interaction "should not reach value.number at all, and no number is 256 characters long anyway."
The first half is right. The second half is not. A 300 character numeric string is indexed into .number as 1e+300 under the default, and it matches range > 2. The parent keyword skips it for exceeding ignore_above, so the same value is invisible to equality and visible to ordering, on a type: "string" attribute.
ignore_above is not a bound on what reaches the numeric index. coerce: false removes this too, so it is a second and independent reason to adopt it.
Q7: the boolean sub-field cannot do its stated job
§7 question 7 says the boolean sub-field's one job is on the elevated side, where landing in tag.foo.boolean is the only signal that "true" was a boolean rather than the string.
It does not carry that signal. The string "true" is indexed into .boolean under both configurations, because parsing "true" into a boolean field is not coercion and coerce is not a boolean parameter. I found no setting that separates them.
So after Q6 the two sub-fields are no longer symmetric: .number with coerce: false is authoritative, .boolean is not and cannot be made so. That reads as an argument for dropping it, which would take the elevated multiplier from three fields per key to two.
Q2: the 2^53 rounding is real
9007199254740993 indexed into the double sub-field reads back as 9007199254740992. Off by one, as predicted. Recorded so the question rests on a measurement rather than an inference.
Q5: yes, _ignored is populated and queryable
Under both configurations, and it names the specific sub-field that was skipped rather than just the document. So "which spans had a value that would not coerce" is answerable.
What this suggests for §5
Within Option A, coerce: false on the number sub-field looks strictly better than the default. It delivers authoritative number-versus-string matching on the elevated representation, it closes the ignore_above hole in Q4, and nothing here costs anything for it. It does not change the recommendation, it strengthens it.
Scope: this is mapping behavior only, on the questions §7 asks. Not tested at volume and not through the real write path. Happy to extend the probe if there are cases worth adding, and I will post the OpenSearch run here when it completes.
Requirement
RFC 0015 §7: measured answers to open questions 2, 4, 5, 6 and 7 on Elasticsearch
RFC 0015 §7 lists what has to be measured before the design is settled, and calls question 6 "the most valuable open question here". I ran those questions on Elasticsearch. Four come back with an answer, and two of them do not match what the RFC currently assumes.
OpenSearch is not covered yet. I will add it to this issue rather than open a second one, since R6 wants a single answer for both engines.
Setup. Jaeger's
jaeger-span.jsonmapping unchanged, plus the §4 Option A multi-fields on both representations: the nestedtags.valueand thetag.*dynamic template. Two indices, identical except forcoerceon thenumbersub-field. Eight documents: the five from the §4 table, plus a 300 character numeric string, the string"true", and an integer above 2^53. Every attribute uses the same key in both representations, so this also exercises the multi-typed key claim in §5.Elasticsearch 9.2.2.
Q6: yes.
coerce: falsemakes the sub-field its own discriminatorSame query, same documents, only
coercediffers:d5is the string"3". Under the default it enters the numeric index, which is the row in §4's table. Undercoerce: falseit does not, and_ignorednamestag.probe.numberandtags.value.numberfor that document. The nested side behaves identically.Every document was accepted, HTTP 201, in both indices. No rejections anywhere.
ignore_malformedabsorbed the refused coercion in every case, so #10070, #11498 and #25289 do not reproduce here. That was the single unverified assumption the question rests on, and it holds.Two consequences:
coerce: falsealone returns the same set that the type-paired query returns under the default. Thetypepairing becomes an optimization rather than a correctness requirement.gt/lt/gte/lte.Q4: the stated reason this is safe does not hold
§7 question 4 says the
ignore_above: 256interaction "should not reachvalue.numberat all, and no number is 256 characters long anyway."The first half is right. The second half is not. A 300 character numeric string is indexed into
.numberas1e+300under the default, and it matchesrange > 2. The parent keyword skips it for exceedingignore_above, so the same value is invisible to equality and visible to ordering, on atype: "string"attribute.ignore_aboveis not a bound on what reaches the numeric index.coerce: falseremoves this too, so it is a second and independent reason to adopt it.Q7: the
booleansub-field cannot do its stated job§7 question 7 says the boolean sub-field's one job is on the elevated side, where landing in
tag.foo.booleanis the only signal that"true"was a boolean rather than the string.It does not carry that signal. The string
"true"is indexed into.booleanunder both configurations, because parsing"true"into a boolean field is not coercion andcoerceis not a boolean parameter. I found no setting that separates them.So after Q6 the two sub-fields are no longer symmetric:
.numberwithcoerce: falseis authoritative,.booleanis not and cannot be made so. That reads as an argument for dropping it, which would take the elevated multiplier from three fields per key to two.Q2: the 2^53 rounding is real
9007199254740993indexed into thedoublesub-field reads back as9007199254740992. Off by one, as predicted. Recorded so the question rests on a measurement rather than an inference.Q5: yes,
_ignoredis populated and queryableUnder both configurations, and it names the specific sub-field that was skipped rather than just the document. So "which spans had a value that would not coerce" is answerable.
What this suggests for §5
Within Option A,
coerce: falseon thenumbersub-field looks strictly better than the default. It delivers authoritative number-versus-string matching on the elevated representation, it closes theignore_abovehole in Q4, and nothing here costs anything for it. It does not change the recommendation, it strengthens it.Scope: this is mapping behavior only, on the questions §7 asks. Not tested at volume and not through the real write path. Happy to extend the probe if there are cases worth adding, and I will post the OpenSearch run here when it completes.