Enhance job and subjob schemas with name field and indexing improvements
All checks were successful
farmcontrol/farmcontrol-ws/pipeline/head This commit looks good

- Added an optional `name` field to both `jobSchema` and `subJobSchema` for better identification.
- Updated text indexing to include the new `name` field, improving search capabilities.
- Introduced a function to retrieve G-code file names for jobs, enhancing job naming consistency.
- Implemented logic to update job names based on G-code file names during recalculation, ensuring accurate job representation.
This commit is contained in:
Tom Butcher 2026-11-08 23:59:10 +00:00
parent 4d41e850df
commit ce3b9befeb
2 changed files with 80 additions and 2 deletions

View File

@ -10,10 +10,12 @@ import {
deleteObject,
} from '../../database.js';
import { subJobModel } from './subjob.schema.js';
import { gcodeFileModel } from './gcodefile.schema.js';
const jobSchema = new mongoose.Schema(
{
_reference: { type: String, default: () => generateId()() },
name: { type: String, required: false },
state: {
type: { required: true, type: String },
progress: { type: Number, required: false },
@ -40,7 +42,7 @@ const jobSchema = new mongoose.Schema(
{ timestamps: true }
);
jobSchema.index({ 'state.type': 'text' });
jobSchema.index({ name: 'text', 'state.type': 'text' });
const rollupConfigs = [
{
@ -94,6 +96,19 @@ jobSchema.statics.history = async function (from, to) {
const normalizeId = (value) => value?._id ?? value;
const gcodeFileNameFrom = async (gcodeFileRef) => {
if (gcodeFileRef && typeof gcodeFileRef === 'object' && typeof gcodeFileRef.name === 'string') {
const populatedName = gcodeFileRef.name.trim();
if (populatedName) return populatedName;
}
const gcodeFileId = normalizeId(gcodeFileRef);
if (!gcodeFileId) return '';
const gcodeFile = await gcodeFileModel.findById(gcodeFileId).select('name').lean();
return typeof gcodeFile?.name === 'string' ? gcodeFile.name.trim() : '';
};
jobSchema.statics.recalculate = async function (job, user) {
const jobId = job._id || job;
if (!jobId || job?.state?.type !== 'draft') {
@ -103,6 +118,25 @@ jobSchema.statics.recalculate = async function (job, user) {
const quantity = job.quantity || 1;
const printers = (job.printers || []).map((printer) => normalizeId(printer));
const gcodeFile = normalizeId(job.gcodeFile);
const gcodeFileName = await gcodeFileNameFrom(job.gcodeFile);
const jobName = gcodeFileName ? `${quantity} ${gcodeFileName}` : null;
const subJobName = gcodeFileName || null;
if ((job.name ?? null) !== jobName) {
const jobResult = await editObject({
model: this,
id: jobId,
updateData: {
name: jobName,
updatedAt: new Date(),
},
user,
recalculate: false,
});
if (jobResult?.error) {
throw jobResult;
}
}
const existingSubJobs = await listObjects({
model: subJobModel,
@ -127,6 +161,7 @@ jobSchema.statics.recalculate = async function (job, user) {
const printer = printers[printerCount];
const subJobUpdateData = {
updatedAt: new Date(),
name: subJobName,
printer,
gcodeFile,
number: subJobNumber,
@ -188,3 +223,24 @@ jobSchema.virtual('id').get(function () {
jobSchema.set('toJSON', { virtuals: true });
export const jobModel = mongoose.model('job', jobSchema);
const replaceTextIndex = async (model, field) => {
try {
const indexes = await model.collection.indexes();
const textIndex = indexes.find((index) => Object.values(index.key || {}).includes('text'));
const weights = textIndex?.weights;
const includesField = weights != null && Object.prototype.hasOwnProperty.call(weights, field);
if (textIndex && !includesField) {
await model.collection.dropIndex(textIndex.name);
}
await model.createIndexes();
} catch {
// Collection may not exist until Mongo is connected.
}
};
if (mongoose.connection.readyState === 1) {
replaceTextIndex(jobModel, 'name');
} else {
mongoose.connection.once('open', () => replaceTextIndex(jobModel, 'name'));
}

View File

@ -4,6 +4,7 @@ const { Schema } = mongoose;
const subJobSchema = new mongoose.Schema({
_reference: { type: String, default: () => generateId()() },
name: { type: String, required: false },
printer: {
type: Schema.Types.ObjectId,
ref: 'printer',
@ -43,7 +44,7 @@ const subJobSchema = new mongoose.Schema({
finishedAt: { required: false, type: Date, default: null },
});
subJobSchema.index({ moonrakerJobId: 'text', 'state.type': 'text' });
subJobSchema.index({ name: 'text', moonrakerJobId: 'text', 'state.type': 'text' });
subJobSchema.virtual('id').get(function () {
return this._id;
@ -52,3 +53,24 @@ subJobSchema.virtual('id').get(function () {
subJobSchema.set('toJSON', { virtuals: true });
export const subJobModel = mongoose.model('subJob', subJobSchema);
const replaceTextIndex = async (model, field) => {
try {
const indexes = await model.collection.indexes();
const textIndex = indexes.find((index) => Object.values(index.key || {}).includes('text'));
const weights = textIndex?.weights;
const includesField = weights != null && Object.prototype.hasOwnProperty.call(weights, field);
if (textIndex && !includesField) {
await model.collection.dropIndex(textIndex.name);
}
await model.createIndexes();
} catch {
// Collection may not exist until Mongo is connected.
}
};
if (mongoose.connection.readyState === 1) {
replaceTextIndex(subJobModel, 'name');
} else {
mongoose.connection.once('open', () => replaceTextIndex(subJobModel, 'name'));
}