CouchDB设计文档保存失败:表达式无法解析为函数及修复问询
Let's break down what's going wrong with your design document and fix it step by step. The error "Expression does not eval to a function" and failed document creation stem from several key misunderstandings about how CouchDB's validate_doc_update works, plus syntax and logic errors in your code.
Key Issues in Your Current Code
1. Wrong Return Pattern for validate_doc_update
CouchDB's validate_doc_update function doesn't return arrays like [doc, "message"]—that's a pattern for other CouchDB handlers (like update functions). Instead:
- If the update is allowed, you don't return anything (or return
undefined). - If it's not allowed, you throw an error with
throw new Error("Your error message").
Your current code returns arrays, which is why CouchDB complains it can't evaluate to a valid function.
2. Using Invalid Variables & Functions
req: This variable doesn't exist in thevalidate_doc_updatecontext—your parameters arenewDoc, oldDoc, userCtx, secObj.doc: You referencedocbut your incoming document isnewDoc(for creates/updates) oroldDoc(for existing docs).emit(): This is only used in view map functions, not in validation functions.- HTML-escaped characters:
&&should be&&,<should be<,+ =should be+=—these syntax errors break the function.
3. Mixing Validation & Business Logic
You're trying to calculate the average of points and update the document inside the validation function, but validate_doc_update is only meant to check permissions and document validity—it shouldn't modify documents or run aggregation logic. That logic belongs in an update handler or a separate process that uses CouchDB views to compute averages.
Fixed Design Document
First, let's fix the validate_doc_update function to handle only permission checks correctly, then add the aggregation logic in the right place:
{ "_id": "_design/marea", "language": "javascript", "validate_doc_update": "function(newDoc, oldDoc, userCtx, secObj) { // Allow users with '2018marea' role OR the original document owner to make changes const hasPermission = userCtx.roles.indexOf('2018marea') !== -1 || (oldDoc && userCtx.name === oldDoc.name); if (!hasPermission) { throw new Error('Cannot Overwrite Data: You do not have permission to modify this document'); } // Optional: Add check to ensure 'points' field exists for new documents if (!oldDoc && newDoc.points === undefined) { throw new Error('New document must include a "points" field'); } }", // Update handler to calculate and save team points average "updates": { "update_team_average": "function(doc, req) { if (!doc) { return [null, 'Empty Database: No document found to update']; } // Fetch all documents for the current doc's team using our view const teamViewResults = req.query.view('/_design/marea/_view/by_team', { key: doc.team }); const pointsArray = teamViewResults.rows .map(row => row.value.points) .filter(point => typeof point === 'number'); // Calculate average const totalPoints = pointsArray.reduce((sum, point) => sum + point, 0); doc.team_points_average = pointsArray.length > 0 ? totalPoints / pointsArray.length : 0; doc.edited_by = req.userCtx.name; return [doc, 'Successfully updated team points average']; }" }, // View to fetch documents grouped by team "views": { "by_team": { "map": "function(doc) { if (doc.team && typeof doc.points === 'number') { emit(doc.team, { points: doc.points }); } }" } } }
How This Works
Validated Permissions: The
validate_doc_updatenow properly checks if the user has the2018marearole or is the document owner. If not, it throws a clear error. It also optionally enforces that new documents include apointsfield.Team Average Calculation:
- The
by_teamview maps documents by theirteamfield, emitting the team as the key and thepointsvalue. - The
update_team_averageupdate handler uses this view to fetch all documents for the current doc's team, calculates the average, and updates the document with the result and editor's name.
- The
Correct Syntax: All escaped characters are fixed, and we're using valid variables for each context (e.g.,
reqis available in update handlers, not validation).
Usage Notes
- To create a document: Users with the
2018marearole can POST to the database normally, as long as the document includespoints(if you kept that check). - To update the team average: Call the update handler with a request like
POST /your-database/_design/marea/_update/update_team_average/{target-document-id}.
内容的提问来源于stack exchange,提问作者ShashJar

