{"record":{"id":"f3f20c4127327607","repo":"heibaiying/BigData-Notes","slug":"value-structure-should-be-longitude-latitude","errorCode":null,"errorMessage":"value structure should be longitude:latitude","messagePattern":"value structure should be longitude:latitude","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"notes/Storm集成Redis详解.md","lineNumber":413,"sourceCode":"                    jedisCommand.hset(additionalKey, key, value);\n                    break;\n\n                case SET:\n                    jedisCommand.sadd(key, value);\n                    break;\n\n                case SORTED_SET:\n                    jedisCommand.zadd(additionalKey, Double.valueOf(value), key);\n                    break;\n\n                case HYPER_LOG_LOG:\n                    jedisCommand.pfadd(key, value);\n                    break;\n\n                case GEO:\n                    String[] array = value.split(\":\");\n                    if (array.length != 2) {\n                        throw new IllegalArgumentException(\"value structure should be longitude:latitude\");\n                    }\n\n                    double longitude = Double.valueOf(array[0]);\n                    double latitude = Double.valueOf(array[1]);\n                    jedisCommand.geoadd(additionalKey, longitude, latitude, key);\n                    break;\n\n                default:\n                    throw new IllegalArgumentException(\"Cannot process such data type: \" + dataType);\n            }\n\n            collector.ack(input);\n        } catch (Exception e) {\n            this.collector.reportError(e);\n            this.collector.fail(input);\n        } finally {\n            returnInstance(jedisCommand);\n        }","sourceCodeStart":395,"sourceCodeEnd":431,"githubUrl":"https://github.com/heibaiying/BigData-Notes/blob/3898939aca387c25b3eb4e51ef49dfccca8543ed/notes/Storm集成Redis详解.md#L395-L431","documentation":"This IllegalArgumentException is thrown by the GEO branch of the Redis store bolt's process() logic (whitelisted from Storm's RedisStoreBolt) when the tuple value is not in the 'longitude:latitude' form required by Redis GEOADD. The code splits the value on ':' and expects exactly two parts, which are then parsed as doubles and passed to jedisCommand.geoadd(additionalKey, longitude, latitude, key). Any value without exactly one ':' separator fails before Redis is touched, is reported via collector.reportError(e), and the tuple is failed.","triggerScenarios":"Declaring RedisDataTypeDescription.RedisDataType.GEO in the store mapper while upstream tuples emit values that: contain no ':' (e.g. \"beijing\"), contain extra ':' segments (e.g. \"116.40:39.90:0\"), have empty parts (\":39.90\", \"116.40:\"), or use a different delimiter (comma, space, e.g. \"116.40,39.90\" — note Double.valueOf would also throw NumberFormatException afterwards for malformed numbers). Only exactly-two-part values reach geoadd().","commonSituations":"Feeding GEO-typed bolts from spouts that emit plain text (e.g. a word-count stream reused for a GEO demo); changing the upstream data contract (delimiter switched from ':' to ',' or to a POJO/JSON) without updating the bolt; locale/format issues where coordinates arrive as DMS strings (\"39°54'N\") instead of decimal degrees; test data copy-pasted with the wrong separator.","solutions":["Fix the upstream producer so the value field is exactly \"longitude:latitude\" in decimal degrees, e.g. \"116.407526:39.904030\".","If the upstream format is fixed and different, transform it before this bolt (add a preceding bolt that normalizes the value), or override process() to parse your format and call geoadd() yourself.","If the data is not geographic at all, change the mapper's data type from GEO to the type that matches the data (STRING, HASH, SORTED_SET, ...).","Sanitize in the mapper's getValueFromTuple(): validate and reformat there so bad values fail loudly at the boundary with a clearer message."],"exampleFix":"// before: upstream spout emits\ncollector.emit(new Values(cityName)); // value=\"Beijing\" -> array.length==1 -> throws\n\n// after: upstream spout emits\ncollector.emit(new Values(cityName, \"116.407526:39.904030\")); // \"longitude:latitude\"\n// (key=cityName, value=\"116.407526:39.904030\" -> geoadd(additionalKey, 116.407526, 39.904030, cityName))","handlingStrategy":"validation","validationCode":"String[] parts = value.split(\":\");\nif (parts.length != 2 || !parts[0].matches(\"-?\\\\d+(\\\\.\\\\d+)?\") || !parts[1].matches(\"-?\\\\d+(\\\\.\\\\d+)?\")) {\n    throw new IllegalArgumentException(\"Expected longitude:latitude, got: \" + value);\n}\n// safe to emit into the GEO-typed bolt","typeGuard":"boolean isLongitudeLatitude(String value) {\n    if (value == null) return false;\n    String[] p = value.split(\":\");\n    if (p.length != 2) return false;\n    try {\n        double lon = Double.parseDouble(p[0]);\n        double lat = Double.parseDouble(p[1]);\n        return lon >= -180 && lon <= 180 && lat >= -90 && lat <= 90;\n    } catch (NumberFormatException e) {\n        return false;\n    }\n}","tryCatchPattern":"// In a normalizing bolt upstream of the GEO store bolt:\ntry {\n    collector.emit(new Values(key, lon + \":\" + lat));\n} catch (Exception e) {\n    collector.reportError(e);\n    collector.fail(input); // bad record: log and fail fast rather than retrying poisoned data\n}","preventionTips":["Normalize coordinates to decimal-degree \"longitude:latitude\" at the spout/mapper boundary (getValueFromTuple), never inside the bolt.","Validate with isLongitudeLatitude() before emitting; route bad records to a dead-letter stream instead of letting the bolt fail tuples.","Pin the ':' delimiter in a shared constant used by both producer and consumer.","Add contract tests on the spout's output format whenever the GEO bolt is part of the topology."],"tags":["storm","redis","jedis","geospatial","data-format","illegalargumentexception"],"backgroundTag":null,"analyzedSha":"3898939aca387c25b3eb4e51ef49dfccca8543ed","analyzedAt":"2026-08-14T15:36:11.245Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}