Skip to content

Commit f3fd0b7

Browse files
Node: add JSON.OBJLEN and JSON.OBJKEYS
Signed-off-by: James Xin <james.xin@improving.com>
1 parent 23689db commit f3fd0b7

File tree

2 files changed

+279
-0
lines changed

2 files changed

+279
-0
lines changed

node/src/server-modules/GlideJson.ts

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -724,4 +724,89 @@ export class GlideJson {
724724

725725
return _executeCommand<ReturnTypeJson<number>>(client, args);
726726
}
727+
728+
/**
729+
* Retrieves the number of key-value pairs in the object stored at the specified `path` within the JSON document stored at `key`.
730+
*
731+
* @param client - The client to execute the command.
732+
* @param key - The key of the JSON document.
733+
* @param options - (Optional) Additional parameters:
734+
* - (Optional) path - The path within the JSON document, Defaults to root if not provided.
735+
* @returns ReturnTypeJson<number>:
736+
* - For JSONPath (`path` starts with `$`):
737+
- Returns a list of integer replies for every possible path, indicating the length of the object,
738+
or `null` for JSON values matching the path that are not an object.
739+
If `path` doesn't exist, an empty array will be returned.
740+
- For legacy path (`path` doesn't starts with `$`):
741+
- Returns the length of the object at `path`.
742+
If multiple paths match, the length of the first object match is returned.
743+
If the JSON value at `path` is not an object or if `path` doesn't exist, an error is raised.
744+
- If `key` doesn't exist, `null` is returned.
745+
*
746+
* @example
747+
* ```typescript
748+
* console.log(await GlideJson.set(client, "doc", "$", '{"a": 1.0, "b": {"a": {"x": 1, "y": 2}, "b": 2.5, "c": true}}'));
749+
* // Output: 'OK' - Indicates successful setting of the value at the root path '$' in the key `doc`.
750+
* console.log(await GlideJson.objlen(client, "doc", "$"));
751+
* // Output: [2] - Returns the number of key-value pairs at the root object, which has 2 keys: 'a' and 'b'.
752+
* console.log(await GlideJson.objlen(client, "doc", "."));
753+
* // Output: 2 - Returns the number of key-value pairs for the object matching the path '.', which has 2 keys: 'a' and 'b'.
754+
* ```
755+
*/
756+
static async objlen(
757+
client: BaseClient,
758+
key: GlideString,
759+
options?: { path: GlideString },
760+
): Promise<ReturnTypeJson<number>> {
761+
const args = ["JSON.OBJLEN", key];
762+
763+
if (options) {
764+
args.push(options.path);
765+
}
766+
767+
return _executeCommand<ReturnTypeJson<number>>(client, args);
768+
}
769+
770+
/**
771+
* Retrieves key names in the object values at the specified `path` within the JSON document stored at `key`.
772+
*
773+
* @param client - The client to execute the command.
774+
* @param key - The key of the JSON document.
775+
* @param options - (Optional) Additional parameters:
776+
* - (Optional) path - The path within the JSON document where the key names will be retrieved. Defaults to root if not provided.
777+
* @returns ReturnTypeJson<GlideString[]>:
778+
- For JSONPath (`path` starts with `$`):
779+
- Returns a list of arrays containing key names for each matching object.
780+
If a value matching the path is not an object, an empty array is returned.
781+
If `path` doesn't exist, an empty array is returned.
782+
- For legacy path (`path` starts with `.`):
783+
- Returns a list of key names for the object value matching the path.
784+
If multiple objects match the path, the key names of the first object are returned.
785+
If a value matching the path is not an object, an error is raised.
786+
If `path` doesn't exist, `null` is returned.
787+
If `key` doesn't exist, `null` is returned.
788+
*
789+
* @example
790+
* ```typescript
791+
* console.log(await GlideJson.set(client, "doc", "$", '{"a": 1.0, "b": {"a": {"x": 1, "y": 2}, "b": 2.5, "c": true}}'));
792+
* // Output: 'OK' - Indicates successful setting of the value at the root path '$' in the key `doc`.
793+
* console.log(await GlideJson.objkeys(client, "doc", "$"));
794+
* // Output: [["a", "b"]] - Returns a list of arrays containing the key names for objects matching the path '$'.
795+
* console.log(await GlideJson.objkeys(client, "doc", "."));
796+
* // Output: ["a", "b"] - Returns key names for the object matching the path '.' as it is the only match.
797+
* ```
798+
*/
799+
static async objkeys(
800+
client: BaseClient,
801+
key: GlideString,
802+
options?: { path: GlideString },
803+
): Promise<ReturnTypeJson<GlideString[]>> {
804+
const args = ["JSON.OBJKEYS", key];
805+
806+
if (options) {
807+
args.push(options.path);
808+
}
809+
810+
return _executeCommand<ReturnTypeJson<GlideString[]>>(client, args);
811+
}
727812
}

node/tests/ServerModules.test.ts

Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1193,6 +1193,200 @@ describe("Server Module Tests", () => {
11931193
).rejects.toThrow(RequestError);
11941194
},
11951195
);
1196+
1197+
it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
1198+
"json.objlen tests",
1199+
async (protocol) => {
1200+
client = await GlideClusterClient.createClient(
1201+
getClientConfigurationOption(
1202+
cluster.getAddresses(),
1203+
protocol,
1204+
),
1205+
);
1206+
const key = uuidv4();
1207+
const jsonValue = {
1208+
a: 1.0,
1209+
b: { a: { x: 1, y: 2 }, b: 2.5, c: true },
1210+
};
1211+
1212+
// setup
1213+
expect(
1214+
await GlideJson.set(
1215+
client,
1216+
key,
1217+
"$",
1218+
JSON.stringify(jsonValue),
1219+
),
1220+
).toBe("OK");
1221+
1222+
expect(
1223+
await GlideJson.objlen(client, key, { path: "$" }),
1224+
).toEqual([2]);
1225+
1226+
expect(
1227+
await GlideJson.objlen(client, key, { path: "." }),
1228+
).toEqual(2);
1229+
1230+
expect(
1231+
await GlideJson.objlen(client, key, { path: "$.." }),
1232+
).toEqual([2, 3, 2]);
1233+
1234+
expect(
1235+
await GlideJson.objlen(client, key, { path: ".." }),
1236+
).toEqual(2);
1237+
1238+
expect(
1239+
await GlideJson.objlen(client, key, { path: "$..b" }),
1240+
).toEqual([3, null]);
1241+
1242+
expect(
1243+
await GlideJson.objlen(client, key, { path: "..b" }),
1244+
).toEqual(3);
1245+
1246+
expect(
1247+
await GlideJson.objlen(client, key, { path: "..a" }),
1248+
).toEqual(2);
1249+
1250+
expect(await GlideJson.objlen(client, key)).toEqual(2);
1251+
1252+
// path doesn't exist
1253+
expect(
1254+
await GlideJson.objlen(client, key, {
1255+
path: "$.non_existing_path",
1256+
}),
1257+
).toEqual([]);
1258+
1259+
await expect(
1260+
GlideJson.objlen(client, key, {
1261+
path: "non_existing_path",
1262+
}),
1263+
).rejects.toThrow(RequestError);
1264+
1265+
// Value at path isnt an object
1266+
expect(
1267+
await GlideJson.objlen(client, key, {
1268+
path: "$.non_existing_path",
1269+
}),
1270+
).toEqual([]);
1271+
1272+
await expect(
1273+
GlideJson.objlen(client, key, { path: ".a" }),
1274+
).rejects.toThrow(RequestError);
1275+
1276+
// Non-existing key
1277+
expect(
1278+
await GlideJson.objlen(client, "non_exiting_key", {
1279+
path: "$",
1280+
}),
1281+
).toBeNull();
1282+
1283+
expect(
1284+
await GlideJson.objlen(client, "non_exiting_key", {
1285+
path: ".",
1286+
}),
1287+
).toBeNull();
1288+
1289+
expect(
1290+
await GlideJson.set(
1291+
client,
1292+
key,
1293+
"$",
1294+
'{"a": 1, "b": 2, "c":3, "d":4}',
1295+
),
1296+
).toBe("OK");
1297+
expect(await GlideJson.objlen(client, key)).toEqual(4);
1298+
},
1299+
);
1300+
1301+
it.each([ProtocolVersion.RESP2, ProtocolVersion.RESP3])(
1302+
"json.objkeys tests",
1303+
async (protocol) => {
1304+
client = await GlideClusterClient.createClient(
1305+
getClientConfigurationOption(
1306+
cluster.getAddresses(),
1307+
protocol,
1308+
),
1309+
);
1310+
const key = uuidv4();
1311+
const jsonValue = {
1312+
a: 1.0,
1313+
b: { a: { x: 1, y: 2 }, b: 2.5, c: true },
1314+
};
1315+
1316+
// setup
1317+
expect(
1318+
await GlideJson.set(
1319+
client,
1320+
key,
1321+
"$",
1322+
JSON.stringify(jsonValue),
1323+
),
1324+
).toBe("OK");
1325+
1326+
expect(
1327+
await GlideJson.objkeys(client, key, { path: "$" }),
1328+
).toEqual([["a", "b"]]);
1329+
1330+
expect(
1331+
await GlideJson.objkeys(client, key, { path: "." }),
1332+
).toEqual(["a", "b"]);
1333+
1334+
expect(
1335+
await GlideJson.objkeys(client, key, { path: "$.." }),
1336+
).toEqual([
1337+
["a", "b"],
1338+
["a", "b", "c"],
1339+
["x", "y"],
1340+
]);
1341+
1342+
expect(
1343+
await GlideJson.objkeys(client, key, { path: ".." }),
1344+
).toEqual(["a", "b"]);
1345+
1346+
expect(
1347+
await GlideJson.objkeys(client, key, { path: "$..b" }),
1348+
).toEqual([["a", "b", "c"], []]);
1349+
1350+
expect(
1351+
await GlideJson.objkeys(client, key, { path: "..b" }),
1352+
).toEqual(["a", "b", "c"]);
1353+
1354+
// path doesn't exist
1355+
expect(
1356+
await GlideJson.objkeys(client, key, {
1357+
path: "$.non_existing_path",
1358+
}),
1359+
).toEqual([]);
1360+
1361+
expect(
1362+
await GlideJson.objkeys(client, key, {
1363+
path: "non_existing_path",
1364+
}),
1365+
).toBeNull();
1366+
1367+
// Value at path isnt an object
1368+
expect(
1369+
await GlideJson.objkeys(client, key, { path: "$.a" }),
1370+
).toEqual([[]]);
1371+
1372+
await expect(
1373+
GlideJson.objkeys(client, key, { path: ".a" }),
1374+
).rejects.toThrow(RequestError);
1375+
1376+
// Non-existing key
1377+
expect(
1378+
await GlideJson.objkeys(client, "non_exiting_key", {
1379+
path: "$",
1380+
}),
1381+
).toBeNull();
1382+
1383+
expect(
1384+
await GlideJson.objkeys(client, "non_exiting_key", {
1385+
path: ".",
1386+
}),
1387+
).toBeNull();
1388+
},
1389+
);
11961390
},
11971391
);
11981392

0 commit comments

Comments
 (0)