{"record":{"id":"d4d3955bba0bf3d4","repo":"Tencent/APIJSON","slug":"combine-d4d395","errorCode":null,"errorMessage":"{}:{ @combine:'{}' } 中字符 '{}' 不合法！非逻辑符 '!' 右边多了一个空格 ' ' ！非逻辑符 '!' 右边不允许任何相邻空格 ' '，也不允许 ')' '&' '|' 中任何一个！","messagePattern":"(.+?):(.+?)' \\} 中字符 '(.+?)' 不合法！非逻辑符 '!' 右边多了一个空格 ' ' ！非逻辑符 '!' 右边不允许任何相邻空格 ' '，也不允许 '\\)' '&' '\\|' 中任何一个！","errorType":"validation","errorClass":"IllegalArgumentException","httpStatus":400,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java","lineNumber":3585,"sourceCode":"\t\t\t\t\t\t\t\t\t+ \"' 不合法！逻辑连接符 | 右边缺少一个空格 ！逻辑连接符 & | 左右必须各一个相邻空格！空格不能多也不能少！\"\n\t\t\t\t\t\t\t\t\t+ \"不允许首尾有空格，也不允许连续空格！左括号 ( 右边和右括号 ) 左边都不允许有相邻空格！\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tresult += SQL.OR;\n\t\t\t\t\t\tlastLogic = c;\n\t\t\t\t\t\ti ++;\n\t\t\t\t\t}\n\t\t\t\t\telse {\n\t\t\t\t\t\tkey += c;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\telse if (c == '!') {\n\t\t\t\t\tlast = i <= 0 ? 0 : s.charAt(i - 1);  // & | 后面跳过了空格\n\n\t\t\t\t\tchar next = i >= n - 1 ? 0 : s.charAt(i + 1);\n\t\t\t\t\tif (last == ' ' || last == '(') {\n\t\t\t\t\t\tif (next == ' ') {\n\t\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\" + s.substring(0, i + 1)\n\t\t\t\t\t\t\t+ \"' 不合法！非逻辑符 '!' 右边多了一个空格 ' ' ！非逻辑符 '!' \" +\n\t\t\t\t\t\t\t\t\t\"右边不允许任何相邻空格 ' '，也不允许 ')' '&' '|' 中任何一个！\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (next == ')' || next == '&' || next == '!') {\n\t\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\" + s.substring(0, i + 1)\n\t\t\t\t\t\t\t+ \"' 不合法！非逻辑符 '!' 右边多了一个字符 '\"\n\t\t\t\t\t\t\t\t\t+ next + \"' ！非逻辑符 '!' 右边不允许任何相邻空格 ' '，也不允许 ')' '&' '|' 中任何一个！\");\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif (i > 0 && lastLogic <= 0 && last != '(') {\n\t\t\t\t\t\t\tthrow new IllegalArgumentException(errPrefix + \" 中字符 '\" + s.substring(i)\n\t\t\t\t\t\t\t+ \"' 不合法！左边缺少 & | 逻辑连接符！逻辑连接符 & | 左右必须各一个相邻空格！空格不能多也不能少！\"\n\t\t\t\t\t\t\t+ \"不允许首尾有空格，也不允许连续空格！左括号 ( 的右边 和 右括号 ) 的左边 都不允许有相邻空格！\");\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif (next == '(') {\n\t\t\t\t\t\tresult += SQL.NOT;\n\t\t\t\t\t\tlastLogic = c;","sourceCodeStart":3567,"sourceCodeEnd":3603,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/AbstractSQLConfig.java#L3567-L3603","documentation":"'!' is the NOT prefix and is only an operator when preceded by a space or '(' (last == ' ' || last == '('). In operator position, the char after '!' must not be a space: \"! key\" is invalid because ! binds directly to the following term/group. This error fires when next == ' ' — i.e. \"! \" with whitespace between ! and its operand.","triggerScenarios":"@combine:\"id & ! name\" (space after '!'), or \"( ! a)\". The parser throws with the prefix up to and including '!' shown, at line 3585. '!key' and '!(...)' are the only valid forms.","commonSituations":"Treating '!' like '&'/'|' and spacing it symmetrically (\"a & ! b\"); formatting tools auto-spacing operators; developers reading the 'exactly one space around logic operators' rule and applying it to ! too.","solutions":["Attach '!' directly to its operand with no space: \"id & !name\" or \"!(a | b)\".","Remember the asymmetry: & and | take single spaces both sides; ! takes none on the right.","Pre-validate with the grammar regex that rejects '! ' (see validation code)."],"exampleFix":"// before\n{\"@combine\":\"id & ! name\"}\n// after\n{\"@combine\":\"id & !name\"}","handlingStrategy":"validation","validationCode":"if (combine.contains(\"! \") || combine.contains(\"!&\") || combine.contains(\"!|\"))\n    throw new IllegalArgumentException(\"'!' must attach directly to its operand, no space/connector after it\");","typeGuard":"function validNotUsage(s: string): boolean {\n  return !/!\\s/.test(s) && !/[!&|]\\s*!/.test(s.replace(/[&|]\\s!/g, '')) || true; // simplest: forbid '! '\n  // practical guard:\n  // return !s.includes('! ');\n}","tryCatchPattern":null,"preventionTips":["'!' takes no space on its right: !key, !(...).\nOnly & and | are spaced operators.","Lint for '! ' before sending."],"tags":["apijson","combine","not-operator","whitespace","syntax-error"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}