Cacomania: MongoDB with PHP "… BadValue $in needs an array"

Cacomania

MongoDB with PHP "… BadValue $in needs an array"

Guido Krömer - 28. June 2014 - Tags: ,

Recently I had a problem when using MongoDB with PHP using the $in operator which ends up with the following error: "Can't canonicalize query: BadValue $in needs an array".

I did a simple query and used the $in operator thousand times before, but what went wrong? Let's take a look at the simplified code below, an indexed array of tags is given to filter for blog posts in a collection, but before passing the tags to the query I removed invalid tags from the array using unset().

foreach ($tags as $key => $tag) {
    if (!valid($tag)) {
        unset($tags[$key]);
    }
}

$posts = $blogPostCollection->find(['Tag' => ['$in' => $tags]]);

Using unset was the problem, but why? When iterating over an array after unsetting a value everything seems to be right:

php > $ids = ['a', 'b', 'c', 'd'];
php > unset($ids[1]);
php > foreach ($ids as $id) { echo "$id "; }
a c d 

The problem is that unset() produces holes in an indexed array, c has still the index two… :

php > $ids = ['a', 'b', 'c', 'd'];
php > var_export($ids);
array (
  0 => 'a',
  1 => 'b',
  2 => 'c',
  3 => 'd',
)
php > unset($ids[1]);
php > var_export($ids);
array (
  0 => 'a',
  2 => 'c',
  3 => 'd',
)

One solution is using array_splice() instead of unset() which prevents holes in an index array:

php > $ids = ['a', 'b', 'c', 'd'];
php > array_splice($ids, 1, 1);
php > var_export($ids);
array (
  0 => 'a',
  1 => 'c',
  2 => 'd',
)

Or you could use array_values() on the array after an unset operation which creates an new array containing all values from the origin array.

php >$ids = ['a', 'b', 'c', 'd'];
php > unset($ids[1]);
php > var_export(array_values($ids));
array (
  0 => 'a',
  1 => 'c',
  2 => 'd',
)

I hope this little post helped you with this or a similar problem, feel free to leave a comment.