Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 50 additions & 32 deletions packages/common/src/utils/injectModel.js
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ function clearCompiledModel(connection, collectionName) {

function getUniqueFieldFilter(fieldKey, isRequired) {
if (isRequired) {
return { [fieldKey]: { $exists: true } };
return {}; // scan ALL docs, including those missing the field
}

return { [fieldKey]: { $exists: true, $ne: null } };
Expand All @@ -170,44 +170,62 @@ async function findDuplicates(Model, fieldKey, isRequired) {
}

async function createUniqueIndexes(Model, fields = []) {
for (const field of fields) {
if (!field.unique) continue;
if (!UNIQUE_SUPPORTED_TYPES_SET.has(field.type)) continue;
const createdIndexes = [];

const normalizedKey = normalizeKey(field.key);
if (!normalizedKey) continue;

const duplicates = await findDuplicates(
Model,
normalizedKey,
!!field.required,
);

if (duplicates.length > 0) {
const examples = duplicates
.slice(0, 3)
.map((d) => JSON.stringify(d._id))
.join(", ");

throw new Error(
`Cannot create unique index on '${normalizedKey}'. ${duplicates.length} duplicate values exist.${examples ? ` Examples: ${examples}` : ""}`,
const existingIndexes = await Model.collection.indexes();
Copy link

Copilot AI Apr 2, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Model.collection.indexes() will throw a NamespaceNotFound (code 26 / "ns not found") error when the collection doesn’t exist yet. createUniqueIndexes is called immediately after compiling a model for newly-created collections (before any inserts), so this can cause schema/collection creation to fail before any indexes are created. Consider wrapping the indexes() call in a try/catch and treating NamespaceNotFound as an empty index list (or ensuring the collection exists via Model.createCollection() / Model.init() before calling indexes()).

Suggested change
const existingIndexes = await Model.collection.indexes();
let existingIndexes = [];
try {
existingIndexes = await Model.collection.indexes();
} catch (err) {
const isNamespaceNotFound =
err &&
(err.code === 26 ||
err.codeName === "NamespaceNotFound" ||
err.message === "ns not found" ||
(typeof err.message === "string" &&
err.message.toLowerCase().includes("ns not found")));
if (!isNamespaceNotFound) {
throw err;
}
}

Copilot uses AI. Check for mistakes.
const existingIndexNames = new Set(existingIndexes.map((idx) => idx.name));

try {
for (const field of fields) {
if (!field.unique) continue;
if (!UNIQUE_SUPPORTED_TYPES_SET.has(field.type)) continue;

const normalizedKey = normalizeKey(field.key);
if (!normalizedKey) continue;

const duplicates = await findDuplicates(
Model,
normalizedKey,
!!field.required,
);
}

const indexName = `unique_${normalizedKey}_1`;
if (duplicates.length > 0) {
const examples = duplicates
.slice(0, 3)
.map((d) => JSON.stringify(d._id))
.join(", ");

const indexOptions = {
unique: true,
name: indexName,
};
throw new Error(
`Cannot create unique index on '${normalizedKey}'. ${duplicates.length} duplicate values exist.${examples ? ` Examples: ${examples}` : ""}`,
);
}

if (!field.required) {
indexOptions.partialFilterExpression = {
[normalizedKey]: { $exists: true, $ne: null },
const indexName = `unique_${normalizedKey}_1`;

const indexOptions = {
unique: true,
name: indexName,
};
}

await Model.collection.createIndex({ [normalizedKey]: 1 }, indexOptions);
if (!field.required) {
indexOptions.partialFilterExpression = {
[normalizedKey]: { $exists: true, $ne: null },
};
}

const createdName = await Model.collection.createIndex(
{ [normalizedKey]: 1 },
indexOptions,
);
if (!existingIndexNames.has(createdName)) {
createdIndexes.push(createdName);
}
}
} catch (err) {
for (const indexName of createdIndexes) {
await Model.collection.dropIndex(indexName).catch(() => {});
}
throw err;
}
}

Expand Down
Loading