[SERVER-12754] $type not applied to nested elements of specific (dot notation) first-level array element Created: 17/Feb/14  Updated: 06/Dec/22  Resolved: 02/Aug/19

Status: Closed
Project: Core Server
Component/s: Querying
Affects Version/s: 2.5.5
Fix Version/s: None

Type: Bug Priority: Major - P3
Reporter: Thomas Zahn Assignee: Backlog - Query Team (Inactive)
Resolution: Done Votes: 2
Labels: query-44-grooming
Remaining Estimate: Not Specified
Time Spent: Not Specified
Original Estimate: Not Specified
Environment:

> db.serverBuildInfo()
{
"version" : "2.5.6-pre-",
"gitVersion" : "0c065ec66101960e27b4c6cff7f224ec74b8bfed",
"OpenSSLVersion" : "",
"sysInfo" : "Darwin bs-osx-108-x86-64-2.10gen.cc 12.3.0 Darwin Kernel Version 12.3.0: Sun Jan 6 22:37:10 PST 2013; root:xnu-2050.22.13~1/RELEASE_X86_64 x86_64 BOOST_LIB_VERSION=1_49",
"loaderFlags" : "-fPIC -pthread -Wl,-bind_at_load -mmacosx-version-min=10.6",
"compilerFlags" : "-Wnon-virtual-dtor -Woverloaded-virtual -fPIC -fno-strict-aliasing -ggdb -pthread -Wall -Wsign-compare -Wno-unknown-pragmas -Winvalid-pch -Werror -pipe -O3 -Wno-unused-function -Wno-deprecated-declarations -mmacosx-version-min=10.6",
"allocator" : "system",
"versionArray" : [
2,
5,
6,
-100
],
"javascriptEngine" : "V8",
"bits" : 64,
"debug" : false,
"maxBsonObjectSize" : 16777216,
"ok" : 1
}


Attachments: File server12754.js    
Assigned Teams:
Query
Operating System: ALL
Steps To Reproduce:

db.test1.insert(
    { a : [ ["aa", "ab", "ac" ], [10, 11, 12] ]}
);
db.test1.insert(
    { a : [ ["ba", "bb", "bc" ], [20, 21, 22] ]}
);
db.test1.insert(
    { a : [ ["ca", "cb", "cc" ], [30, 31, 32] ]}
);

The query:

> db.test1.find({ "a.0" : { "$type" : 2}})
> db.test1.find({ "a.1" : { "$type" : 1}})
> db.test1.find({ "a" : {"$elemMatch" : { "0" : {"$type" : 2}}}})
{ "_id" : ObjectId("5301dd9721e397fc8e5b537c"), "a" : [  [  "aa",  "ab",  "ac" ],  [  10,  11,  12 ] ] }
{ "_id" : ObjectId("5301dd9721e397fc8e5b537d"), "a" : [  [  "ba",  "bb",  "bc" ],  [  20,  21,  22 ] ] }
{ "_id" : ObjectId("5301dd9721e397fc8e5b537e"), "a" : [  [  "ca",  "cb",  "cc" ],  [  30,  31,  32 ] ] }
> db.test1.find({ "a" : {"$elemMatch" : { "1" : {"$type" : 1}}}})
{ "_id" : ObjectId("5301dd9721e397fc8e5b537c"), "a" : [  [  "aa",  "ab",  "ac" ],  [  10,  11,  12 ] ] }
{ "_id" : ObjectId("5301dd9721e397fc8e5b537d"), "a" : [  [  "ba",  "bb",  "bc" ],  [  20,  21,  22 ] ] }
{ "_id" : ObjectId("5301dd9721e397fc8e5b537e"), "a" : [  [  "ca",  "cb",  "cc" ],  [  30,  31,  32 ] ] }
>

Participants:

 Description   

From the documentation of $type:

If the field holds an array, the $type operator performs the type check against the array elements and not the field.

Unfortunately, the $type operator does not seem to get properly applied if the field is itself a concretely named (dot notation) (first-level) array element.
Consider these arrays:

> db.test1.find()
{ "_id" : ObjectId("5301dd9721e397fc8e5b537c"), "a" : [  [  "aa",  "ab",  "ac" ],  [  10,  11,  12 ] ] }
{ "_id" : ObjectId("5301dd9721e397fc8e5b537d"), "a" : [  [  "ba",  "bb",  "bc" ],  [  20,  21,  22 ] ] }
{ "_id" : ObjectId("5301dd9721e397fc8e5b537e"), "a" : [  [  "ca",  "cb",  "cc" ],  [  30,  31,  32 ] ] }
>

Suppose one now wanted to find those documents where the specific first-level element a.0 is itself an array and contains String elements:

> db.test1.find({ "a.0" : { "$type" : 2}})
> 

Nothing comes up even though a.0 contains String elements in all documents.

Note that trying to achieve this via the $elemMatch route does work:

> db.test1.find({ "a" : {"$elemMatch" : { "0" : {"$type" : 2}}}})
{ "_id" : ObjectId("5301dd9721e397fc8e5b537c"), "a" : [  [  "aa",  "ab",  "ac" ],  [  10,  11,  12 ] ] }
{ "_id" : ObjectId("5301dd9721e397fc8e5b537d"), "a" : [  [  "ba",  "bb",  "bc" ],  [  20,  21,  22 ] ] }
{ "_id" : ObjectId("5301dd9721e397fc8e5b537e"), "a" : [  [  "ca",  "cb",  "cc" ],  [  30,  31,  32 ] ] }
>

However, it seems rather inefficient to first apply a check for a "field" 0 on potentially all array elements of a when one already knows the concrete element to investigate.

(On a side note, I have always found it rather peculiar that $type is applied to the elements of an array field rather than to the field itself when {"$elemMatch" : {"$type" : <id>}} already captures these desired semantics)

This seems somewhat related to SERVER-11455.



 Comments   
Comment by David Storch [ 02/Aug/19 ]

This behavior appears to be working as designed. When a positional path component – like the "0" or "1" in this issue report – resolves to an array index, nested arrays are not implicitly traversed. Consider the simpler example of the document {a: [["foo", "bar"]]}. The path "a.0", when resolved against this document, will treat "0" as an array index. It will cause the match expression evaluation code to extract the zeroth element of the outer array, which in this case is the inner array ["foo", "bar"]. However, the matcher semantics are to not implicitly traverse an inner array after extracting an array element my index. Therefore, neither the query {"a.0": {$eq: "foo"}} nor {"a.0": {$eq: "bar"}} matches this document. (In contrast, {"a.0.0": {$eq: "foo"}} and {"a.0.1": {$eq: "bar"}} both match the document.) This slightly simpler query is exactly analogous to the one with $type in the description of this ticket.

Comment by Benety Goh [ 06/Mar/14 ]

FYI, test script gives the same results in 2.4

Generated at Thu Feb 08 03:29:31 UTC 2024 using Jira 9.7.1#970001-sha1:2222b88b221c4928ef0de3161136cc90c8356a66.