Why field with array of ISODate detected as date by $type operator

I have a field which contain array of ISODate like this

joined_at: [new ISODate("2022-02-15T17:12:46.837Z")]

when I’m filtering those data with {$type: "array"}, It correctly appears in search results. But, when I’m filtering with {$type: "date"}, it also appears in search results. Why?

MongoDB version 4.2

Hello @Nabil_Muh_Firdaus, Welcome to the MongoDB developer community forum,

It will check the inner element’s type of array, and it’s a default behavior of $type operator,

Consider the example with sample documents:

{ _id: 1, joined_at: [ISODate("2022-02-15T17:12:46.837Z"), "2022-02-15T17:12:46.837Z"] },
{ _id: 2, joined_at: ISODate("2022-02-15T17:12:46.837Z") },
{ _id: 3, joined_at: "2022-02-15T17:12:46.837Z" }

Query 1:

{ joined_at: { $type: "array" } }
// Result:
{ _id: 1, joined_at: [ISODate("2022-02-15T17:12:46.837Z"), "2022-02-15T17:12:46.837Z"] }

Query 2:

{ joined_at: { $type: "date" } }
// Result: 
{ _id: 1, joined_at: [ISODate("2022-02-15T17:12:46.837Z"), "2022-02-15T17:12:46.837Z"] },
{ _id: 2, joined_at: ISODate("2022-02-15T17:12:46.837Z") },

Query 3:

{ joined_at: { $type: "string" } }
// Result: 
{ _id: 1, joined_at: [ISODate("2022-02-15T17:12:46.837Z"), "2022-02-15T17:12:46.837Z"] },
{ _id: 3, joined_at: "2022-02-15T17:12:46.837Z" }

For more details, refer to the documentation:

4 Likes

May be the following variation is suited for your use-case.

{ "joined_at" : { "$elemMatch" : { "$type" : "date" } } }

Documents where joined_at is not an array won’t be selected.

A slightly more complex variation would be to use version of $type available inside $expr using:

{ "$expr" : { "$eq" : [ { "$type" : "$joined_at" } , "array" ] } }
2 Likes

You may skip reading this post as the correct solution is in the next post

I forgot to mention the flexibility of $type operator for this scenario, :slight_smile:

The alternative to this, Just need to wrap the type into an array brackets.

{ "joined_at" : { "$type" : ["date"] } }

I just tried

on the documents

{  _id: 0 ,  a: 'x' }
{  _id: 1 ,  a: [ 'x' ] }

with

c.find( { "a" : { "$type" : "string" } } )

and the document with _id:0 was still found. With the $expr version only _id:1 is found.

The query

c.find( { "a.0" : { "$exists" : true }  , "a" : { "$type" : "string" } } )

will also only find _id:1.

1 Like

You may skip reading this post as the correct solution is in the next post

This is a solution to check the field type should be an array and have at least one date type value in it.
The alternative to $elemMatch approach, as ia updated my previous post.


You are right, this is still a problem in non-array type, and your solution will work for this scenario, to check only non-array and date type field’s value only.

{ "$expr": { "$eq": [{ "$type": "$joined_at" }, "date"] } }

The brackets [ ] are meant to be able to specify multiple types for the element array. The $elemMatch version will ONLY find arrays. The [ ] version will find non-array that matches the type specification.

In

What I really meant was

c.find( { "a" : { "$type" : [ "string" ] } } )

stills selects non-array field a that are string.

For example,
c.find( { "a" : { "$type" : [ "string" , "date" ] } } )

will find documents with

a : "aStringValue" 
a : aDateValue
a : [ "aStringValue" ]
a : [ aDateValue ]
1 Like

Ahh got it, you are totally right :+1:
Removed the post so the query doesn’t mislead the others.

1 Like

Thank you guys, actually I just wondering that it is “likely” to be buggy.

I think it just about I’m not reading docs carefully, since it is clearly stated in docs that

For documents where field is an array, $type returns documents in which at least one array element matches a type passed to $type.

Thank you, I like your second solution. I believe your solution will be useful for others

1 Like

Do not worry. Most of us do that often. More important, your question started an interesting discussion where I learned about the [ ] shared by turivishal. Despite the fact that the version still had issues, the [ ] was still useful.

Do you think it would be a good idea to bring back your posts since some of the replies do not really make sense without the original post?

May be you could simply add a big warning at the beginning and mentioned why the post is kept despite not being the final solution.

I did that in the post How to use conditionals (and more?) to combine and reduce data - #4 by steevej.

1 Like

Definitely, I have just reposted the answers.

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.