{"record":{"id":"0e2dabc8a99f36ff","repo":"Tencent/APIJSON","slug":"originkey-value-originkey-key","errorCode":null,"errorMessage":"originKey + \":value 中字符 \" + originKey + \" 不合法！key$:value 中不允许只有单独的 '?'，必须和 '%', '_' 之一配合使用 ！","messagePattern":"originKey \\+ \":value 中字符 \" \\+ originKey \\+ \" 不合法！key\\$:value 中不允许只有单独的 '\\?'，必须和 '%', '_' 之一配合使用 ！","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"APIJSONORM/src/main/java/apijson/orm/Join.java","lineNumber":307,"sourceCode":"\t\t\t\tk = originKey.substring(0, originKey.length() - 1);\n\t\t\t\tchar c = k.isEmpty() ? 0 : k.charAt(k.length() - 1);\n\n\t\t\t\tString t = \"$\";\n\t\t\t\tif (c == '%' || c == '_' || c == '?') {\n\t\t\t\t\tt = c + t;\n\t\t\t\t\tk = k.substring(0, k.length() - 1);\n\n\t\t\t\t\tchar c2 = k.isEmpty() ? 0 : k.charAt(k.length() - 1);\n\t\t\t\t\tif (c2 == '%' || c2 == '_' || c2 == '?') {\n\t\t\t\t\t\tif (c2 == c) {\n\t\t\t\t\t\t\tthrow new IllegalArgumentException(originKey + \":value 中字符 \" + k + \" 不合法！key$:value 中不允许 key 中有连续相同的占位符！\");\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tt = c2 + t;\n\t\t\t\t\t\tk = k.substring(0, k.length() - 1);\n\t\t\t\t\t}\n\t\t\t\t\telse if (c == '?') {\n\t\t\t\t\t\tthrow new IllegalArgumentException(originKey + \":value 中字符 \" + originKey + \" 不合法！key$:value 中不允许只有单独的 '?'，必须和 '%', '_' 之一配合使用 ！\");\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tsetRelateType(t);\n\t\t\t}\n\t\t\telse if (originKey.endsWith(\"~\")) {\n\t\t\t\tboolean ignoreCase = originKey.endsWith(\"*~\");\n\t\t\t\tsetRelateType(ignoreCase ? \"*~\" : \"~\");\n\t\t\t\tk = originKey.substring(0, originKey.length() - (ignoreCase ? 2 : 1));\n\t\t\t}\n\t\t\telse if (originKey.endsWith(\">=\")) {\n\t\t\t\tsetRelateType(\">=\");\n\t\t\t\tk = originKey.substring(0, originKey.length() - 2);\n\t\t\t}\n\t\t\telse if (originKey.endsWith(\"<=\")) {\n\t\t\t\tsetRelateType(\"<=\");\n\t\t\t\tk = originKey.substring(0, originKey.length() - 2);\n\t\t\t}","sourceCodeStart":289,"sourceCodeEnd":325,"githubUrl":"https://github.com/Tencent/APIJSON/blob/5284052872898eddc449a58f629e5c8d588b8e22/APIJSONORM/src/main/java/apijson/orm/Join.java#L289-L325","documentation":"In the same '$' (LIKE) placeholder parsing: '?' means 'use the value as-is at this position' and only makes sense combined with '%' or '_' (key?%$ → LIKE 'v%', key_?$ → LIKE '_v'). A key ending with just '?$', i.e. a lone '?' with no % or _ companion, throws IllegalArgumentException because '?value' alone would produce a malformed/pointless LIKE pattern.","triggerScenarios":"Join table object contains e.g. \"name?$\":\"a\" or \"status?$\":\"x\" — after stripping the trailing '?', the next char back is neither '%', '_', nor '?' (c == '?' and no c2 companion), so the else-if branch throws.","commonSituations":"Assuming '?' is a single-char wildcard like SQL '_' — in APIJSON '?' is an exact-position marker, not a wildcard; typo when aiming for 'key_?$'; forgetting that plain containment already is the default (key$ → LIKE '%v%') so no placeholder is needed at all.","solutions":["Use the default containment form with no placeholder: \"name$\":\"a\" → LIKE '%a%'.","Combine '?' with % or _: \"name?%$\":\"a\" → LIKE 'a%', \"name_?$\":\"a\" → LIKE '_a'.","For a literal '?' in a pattern, include it in the value string instead of the key."],"exampleFix":"// before\n\"User\":{\"name?$\":\"Tom\"}   // lone '?', rejected\n// after\n\"User\":{\"name$\":\"Tom\"}    // LIKE '%Tom%'\n// or\n\"User\":{\"name?%$\":\"Tom\"} // LIKE 'Tom%'","handlingStrategy":"validation","validationCode":"function assertLikeJoinKey(k) {\n  if (/[?]\\$$/.test(k) && !/[%_][?]\\$$|[?][%_]\\$$/.test(k))\n    throw new Error(\"lone '?' in '\" + k + \"' — combine with % or _ (key?%$, key_?$) or drop it (key$)\");\n}","typeGuard":"function isLikeJoinKey(k) { return !/\\?\\$$/.test(k) || /[%_]\\?\\$|\\?[%_]\\$/.test(k); }","tryCatchPattern":null,"preventionTips":["Learn the placeholder semantics: '?' = insert value verbatim here, '%'/'_' = SQL wildcards; '?' never stands alone.","Default to plain key$ (LIKE '%v%') unless you truly need anchored patterns."],"tags":["apijson","join","like-operator","request-syntax"],"backgroundTag":null,"analyzedSha":"5284052872898eddc449a58f629e5c8d588b8e22","analyzedAt":"2026-08-14T15:15:29.577Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}