// Aggregation $substrBytes tests. import "jstests/libs/query/sbe_assert_error_override.js"; import {assertErrorCode} from "jstests/aggregation/extras/utils.js"; let t = db.jstests_aggregation_substr; t.drop(); t.save({}); function assertSubstring(expected, str, offset, len) { assert.eq(expected, t.aggregate({$project: {a: {$substrBytes: [str, offset, len]}}}).toArray()[0].a); } function assertArgsException(args) { assert.commandFailed(t.runCommand("aggregate", {pipeline: [{$substrBytes: args}]})); } function assertException(str, offset, len) { assertArgsException([str, offset, len]); } // Wrong number of arguments. assertArgsException([]); assertArgsException(["foo"]); assertArgsException(["foo", 1]); assertArgsException(["foo", 1, 1, 1]); // Basic offset / length checks. assertSubstring("abcd", "abcd", 0, 4); assertSubstring("abcd", "abcd", 0, 5); assertSubstring("a", "abcd", 0, 1); assertSubstring("ab", "abcd", 0, 2); assertSubstring("b", "abcd", 1, 1); assertSubstring("d", "abcd", 3, 1); assertSubstring("", "abcd", 4, 1); assertSubstring("", "abcd", 3, 0); assertSubstring("cd", "abcd", 2, -1); // Passing a negative number for the start position should return an error. assertException("abcd", -1, 4); assertException("abcd", -1, 0); assertException("abcd", -10, 0); // See server6186.js for additional offset / length checks. // Additional numeric types for offset / length. assertSubstring("bc", "abcd", 1, 2); assertSubstring("bc", "abcd", 1.0, 2.0); assertSubstring("bc", "abcd", NumberInt("1"), NumberInt("2")); assertSubstring("bc", "abcd", NumberLong("1"), NumberLong("2")); assertSubstring("bc", "abcd", NumberInt("1"), NumberLong("2")); assertSubstring("bc", "abcd", NumberLong("1"), NumberInt("2")); assertSubstring("bc", "abcd", NumberDecimal("1"), NumberDecimal("2")); // Integer component is used. assertSubstring("bc", "abcd", 1.2, 2.2); assertSubstring("bc", "abcd", 1.9, 2.9); assertSubstring("cd", "abcd", 2, -1); assertSubstring("abcd", "abcd", 0, -1); // Any negative number for length will return the rest of the string. assertSubstring("cd", "abcd", 2, -5); assertSubstring("", "abcd", 4, -1); assertSubstring("", "abcd", 10, -1); // Non numeric types for offset / length. assertException("abcd", false, 2); assertException("abcd", 1, true); assertException("abcd", "q", 2); assertException("abcd", 1, "r"); assertException("abcd", null, 3); assertException("abcd", 1, undefined); // String coercion. assertSubstring("123", 123, 0, 3); assertSubstring("2", 123, 1, 1); assertSubstring("1970", new Date(0), 0, 4); assertSubstring("", null, 0, 4); assertException(/abc/, 0, 4); // Field path like string. assertSubstring("$a", "a$a", 1, 2); // Multi byte utf-8. assertSubstring("\u0080", "\u0080", 0, 2); assertException("\u0080", 0, 1); assertException("\u0080", 1, 1); assertSubstring("\u0080", "\u0080\u20ac", 0, 2); assertSubstring("\u20ac", "\u0080\u20ac", 2, 3); assertException("\u0080\u20ac", 1, 3); assertException("\u0080\u20ac", 1, 4); assertException("\u0080\u20ac", 0, 3); assertException("\uD834\uDF06", 1, 4); assertException("\uD834\uDF06", 0, 3); assertSubstring("\u0044\u20ac", "\u0080\u0044\u20ac", 2, 4); assertSubstring("\u0044", "\u0080\u0044\u20ac", 2, 1); // The four byte utf-8 character ๐Œ† (have to represent in surrogate halves). assertSubstring("\uD834\uDF06", "\uD834\uDF06", 0, 4); // Operands from document. t.drop(); t.save({ w: "รณ", x: "a", y: "abc", z: "abcde", a: 0, b: 1, c: 2, d: 3, e: 4, f: 5, g: -2, /* Max unsigned int plus one */ k: NumberLong(4294967297), }); assertSubstring("a", "$x", "$a", "$b"); assertSubstring("abcde", "$z", "$a", "$k"); assertSubstring("", "$x", "$k", "$f"); assertSubstring("a", "$x", "$a", "$f"); assertSubstring("b", "$y", "$b", "$b"); assertSubstring("b", "$z", "$b", "$b"); assertSubstring("bcd", "$z", "$b", "$d"); assertSubstring("cde", "$z", "$c", "$f"); assertSubstring("c", "$y", "$c", "$f"); assertException("$w", "$b", "$d"); assertException("$w", "$a", "$c"); assertException("$w", "$a", "$g"); assertException("$w", "$g", "$a"); // String coercion fails. assertErrorCode(t, [{$project: {a: {$substrCP: [new Map(), "$a", "$b"]}}}], 16007, "string coercion failed"); // Computed operands. assertSubstring("cde", "$z", {$add: ["$b", "$b"]}, {$add: ["$c", "$d"]}); assertSubstring("cde", "$z", {$add: ["$b", 1]}, {$add: [2, "$d"]}); // Nested. assert.eq( "e", t .aggregate({ $project: { a: { $substrBytes: [ { $substrBytes: [{$substrBytes: [{$substrBytes: ["abcdefghij", 1, 6]}, 2, 5]}, 0, 3], }, 1, 1, ], }, }, }) .toArray()[0].a, );