MongoServerError: cannot index parallel arrays [certifications] [skills]
If you've never hit this error, your schemas have been lucky. A team once brought me a document shaped like this during a design review:
{
_id: 1,
name: "engineer-42",
skills: ["mongodb", "cassandra", "python"],
certifications: ["dba", "atlas-admin"]
}
They wanted fast queries on skill and certification combinations, so the natural move was:
db.engineers.createIndex({ skills: 1, certifications: 1 })
// MongoServerError: cannot index parallel arrays
MongoDB refuses. Not "performs badly", refuses. And the reason is one of those internals that, once you see it, changes how you model.
Why: multikey indexes are per element
When you index an array field, MongoDB creates a multikey index, one index entry per array element. Index skills on the document above and you get three entries, one for "mongodb", one for "cassandra", one for "python", each pointing at the same document.
Now imagine a compound index across two independent arrays. To answer any combination query, MongoDB would need an entry for every element of the cross product. Three skills times two certifications is six entries. Harmless here. But two arrays of 1,000 elements each is a million index entries for a single document. One insertOne fans out into a million B-tree writes. The server declines to let you build that footgun, and honestly, good.
There's a quieter problem hiding in the schema too. The combinations are meaningless. Nothing in two parallel arrays says which skill relates to which certification. The data model can't answer the question the index was supposed to serve.
The fix: one array of subdocuments
{
_id: 1,
name: "engineer-42",
quals: [
{ skill: "mongodb", cert: "dba" },
{ skill: "mongodb", cert: "atlas-admin" },
{ skill: "cassandra", cert: null }
]
}
db.engineers.createIndex({ "quals.skill": 1, "quals.cert": 1 })
// works
This is allowed because both indexed paths traverse the same array. Index entries are generated per array element, so each entry is a real (skill, cert) pair that actually existed together in the data, the correlation the parallel arrays destroyed. The index gets smaller and the model gets more truthful. That combination is rare.
One caveat worth knowing before you rely on it. To match both fields within the same element, query with $elemMatch:
db.engineers.find({ quals: { $elemMatch: { skill: "mongodb", cert: "dba" } } })
Without $elemMatch, MongoDB matches documents where some element has the skill and some other element has the cert. The parallel arrays bug, reincarnated at query time.
The lab
db.t.insertOne({ a: [1,2,3], b: ["x","y"] })
db.t.createIndex({ a: 1, b: 1 }) // watch it fail
db.t2.insertOne({ pairs: [{a:1,b:"x"},{a:2,b:"y"}] })
db.t2.createIndex({ "pairs.a": 1, "pairs.b": 1 }) // watch it work
db.t2.find({ pairs: { $elemMatch: { a: 1, b: "y" } } }) // zero results, and that's correct
That last line is the whole lesson in one query. a:1 and b:"y" never occurred in the same element, so the document doesn't match. Parallel arrays would have happily lied to you.
Some errors are the database being difficult. This one is the database refusing to let your schema make a promise it can't keep.
Top comments (0)