From 449a02dc2425d6b552b5f03218849ebd1a1a758f Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Fri, 6 Feb 2026 11:33:47 -0500 Subject: [PATCH 1/3] feat(firestore): add pipeline string expressions Adds the `ltrim`, `rttrim`, `stringIndexOf`, `stringReplaceOne`, `stringReplaceAll`, `stringRepeat` Pipeline expressions. --- dev/src/pipelines/expression.ts | 436 ++++++++++++++++++++++++++++++++ dev/src/pipelines/index.ts | 6 + dev/system-test/pipeline.ts | 146 +++++++++++ types/firestore.d.ts | 106 ++++++++ 4 files changed, 694 insertions(+) diff --git a/dev/src/pipelines/expression.ts b/dev/src/pipelines/expression.ts index fec0d9501..9eea8887b 100644 --- a/dev/src/pipelines/expression.ts +++ b/dev/src/pipelines/expression.ts @@ -980,6 +980,60 @@ export abstract class Expression return new FunctionExpression('trim', args); } + /** + * @beta + * Trims whitespace or a specified set of characters/bytes from the beginning of a string or byte array. + * + * @example + * ```typescript + * // Trim whitespace from the beginning of the 'userInput' field + * field("userInput").ltrim(); + * + * // Trim quotes from the beginning of the 'userInput' field + * field("userInput").ltrim('"'); + * ``` + * + * @param valueToTrim - Optional. A string or byte array containing the characters/bytes to trim. + * If not specified, whitespace will be trimmed. + * @returns A new `Expression` representing the trimmed string. + */ + ltrim( + valueToTrim?: string | Expression | Uint8Array | Buffer, + ): FunctionExpression { + const args: Expression[] = [this]; + if (valueToTrim) { + args.push(valueToDefaultExpr(valueToTrim)); + } + return new FunctionExpression('ltrim', args); + } + + /** + * @beta + * Trims whitespace or a specified set of characters/bytes from the end of a string or byte array. + * + * @example + * ```typescript + * // Trim whitespace from the end of the 'userInput' field + * field("userInput").rtrim(); + * + * // Trim quotes from the end of the 'userInput' field + * field("userInput").rtrim('"'); + * ``` + * + * @param valueToTrim - Optional. A string or byte array containing the characters/bytes to trim. + * If not specified, whitespace will be trimmed. + * @returns A new `Expression` representing the trimmed string or byte array. + */ + rtrim( + valueToTrim?: string | Expression | Uint8Array | Buffer, + ): FunctionExpression { + const args: Expression[] = [this]; + if (valueToTrim) { + args.push(valueToDefaultExpr(valueToTrim)); + } + return new FunctionExpression('rtrim', args); + } + /** * @beta * Creates an expression that concatenates string expressions together. @@ -1002,6 +1056,98 @@ export abstract class Expression return new FunctionExpression('string_concat', [this, ...exprs]); } + /** + * @beta + * Creates an expression that finds the index of the first occurrence of a substring or byte sequence. + * + * @example + * ```typescript + * // Find the index of "foo" in the 'text' field + * field("text").stringIndexOf("foo"); + * ``` + * + * @param search - The substring or byte sequence to search for. + * @returns A new `Expression` representing the index of the first occurrence. + */ + stringIndexOf( + search: string | Expression | Uint8Array | Buffer, + ): FunctionExpression { + return new FunctionExpression('string_index_of', [ + this, + valueToDefaultExpr(search), + ]); + } + + /** + * @beta + * Creates an expression that repeats a string or byte array a specified number of times. + * + * @example + * ```typescript + * // Repeat the 'label' field 3 times + * field("label").stringRepeat(3); + * ``` + * + * @param repetitions - The number of times to repeat the string or byte array. + * @returns A new `Expression` representing the repeated string or byte array. + */ + stringRepeat(repetitions: number | Expression): FunctionExpression { + return new FunctionExpression('string_repeat', [ + this, + valueToDefaultExpr(repetitions), + ]); + } + + /** + * @beta + * Creates an expression that replaces all occurrences of a substring or byte sequence with a replacement. + * + * @example + * ```typescript + * // Replace all occurrences of "foo" with "bar" in the 'text' field + * field("text").stringReplaceAll("foo", "bar"); + * ``` + * + * @param find - The substring or byte sequence to search for. + * @param replacement - The replacement string or byte sequence. + * @returns A new `Expression` representing the string or byte array with replacements. + */ + stringReplaceAll( + find: string | Expression | Uint8Array | Buffer, + replacement: string | Expression | Uint8Array | Buffer, + ): FunctionExpression { + return new FunctionExpression('string_replace_all', [ + this, + valueToDefaultExpr(find), + valueToDefaultExpr(replacement), + ]); + } + + /** + * @beta + * Creates an expression that replaces the first occurrence of a substring or byte sequence with a replacement. + * + * @example + * ```typescript + * // Replace the first occurrence of "foo" with "bar" in the 'text' field + * field("text").stringReplaceOne("foo", "bar"); + * ``` + * + * @param find - The substring or byte sequence to search for. + * @param replacement - The replacement string or byte sequence. + * @returns A new `Expression` representing the string or byte array with the replacement. + */ + stringReplaceOne( + find: string | Expression | Uint8Array | Buffer, + replacement: string | Expression | Uint8Array | Buffer, + ): FunctionExpression { + return new FunctionExpression('string_replace_one', [ + this, + valueToDefaultExpr(find), + valueToDefaultExpr(replacement), + ]); + } + /** * @beta * Creates an expression that concatenates expression results together. @@ -5879,6 +6025,110 @@ export function trim( return fieldOrExpression(expr).trim(valueToTrim); } +/** + * @beta + * Trims whitespace or a specified set of characters/bytes from the beginning of a string or byte array. + * + * @example + * ```typescript + * // Trim whitespace from the beginning of the 'userInput' field + * ltrim(field("userInput")); + * + * // Trim quotes from the beginning of the 'userInput' field + * ltrim(field("userInput"), '"'); + * ``` + * + * @param fieldName - The name of the field containing the string or byte array. + * @param valueToTrim - Optional. A string or byte array containing the characters/bytes to trim. + * If not specified, whitespace will be trimmed. + * @returns A new `Expression` representing the trimmed string or byte array. + */ +export function ltrim( + fieldName: string, + valueToTrim?: string | Expression | Uint8Array | Buffer, +): FunctionExpression; + +/** + * @beta + * Trims whitespace or a specified set of characters/bytes from the beginning of a string or byte array. + * + * @example + * ```typescript + * // Trim whitespace from the beginning of the 'userInput' field + * ltrim(field("userInput")); + * + * // Trim quotes from the beginning of the 'userInput' field + * ltrim(field("userInput"), '"'); + * ``` + * + * @param expression - The expression representing the string or byte array. + * @param valueToTrim - Optional. A string or byte array containing the characters/bytes to trim. + * If not specified, whitespace will be trimmed. + * @returns A new `Expression` representing the trimmed string or byte array. + */ +export function ltrim( + expression: Expression, + valueToTrim?: string | Expression | Uint8Array | Buffer, +): FunctionExpression; +export function ltrim( + expr: Expression | string, + valueToTrim?: string | Expression | Uint8Array | Buffer, +): FunctionExpression { + return fieldOrExpression(expr).ltrim(valueToTrim); +} + +/** + * @beta + * Trims whitespace or a specified set of characters/bytes from the end of a string or byte array. + * + * @example + * ```typescript + * // Trim whitespace from the end of the 'userInput' field + * rtrim(field("userInput")); + * + * // Trim quotes from the end of the 'userInput' field + * rtrim(field("userInput"), '"'); + * ``` + * + * @param fieldName - The name of the field containing the string or byte array. + * @param valueToTrim - Optional. A string or byte array containing the characters/bytes to trim. + * If not specified, whitespace will be trimmed. + * @returns A new `Expression` representing the trimmed string or byte array. + */ +export function rtrim( + fieldName: string, + valueToTrim?: string | Expression | Uint8Array | Buffer, +): FunctionExpression; + +/** + * @beta + * Trims whitespace or a specified set of characters/bytes from the end of a string or byte array. + * + * @example + * ```typescript + * // Trim whitespace from the end of the 'userInput' field + * rtrim(field("userInput")); + * + * // Trim quotes from the end of the 'userInput' field + * rtrim(field("userInput"), '"'); + * ``` + * + * @param expression - The expression representing the string or byte array. + * @param valueToTrim - Optional. A string or byte array containing the characters/bytes to trim. + * If not specified, whitespace will be trimmed. + * @returns A new `Expression` representing the trimmed string or byte array. + */ +export function rtrim( + expression: Expression, + valueToTrim?: string | Expression | Uint8Array | Buffer, +): FunctionExpression; +export function rtrim( + expr: Expression | string, + valueToTrim?: string | Expression | Uint8Array | Buffer, +): FunctionExpression { + return fieldOrExpression(expr).rtrim(valueToTrim); +} + /** * @beta * Creates an expression that concatenates string functions, fields or constants together. @@ -5929,6 +6179,192 @@ export function stringConcat( ); } +/** + * @beta + * Creates an expression that finds the index of the first occurrence of a substring or byte sequence. + * + * @example + * ```typescript + * // Find the index of "foo" in the 'text' field + * stringIndexOf("text", "foo"); + * ``` + * + * @param fieldName - The name of the field containing the string or byte array. + * @param search - The substring or byte sequence to search for. + * @returns A new {@link @firebase/firestore/pipelines#Expression} representing the index of the first occurrence. + */ +export function stringIndexOf( + fieldName: string, + search: string | Expression | Uint8Array | Buffer, +): FunctionExpression; + +/** + * @beta + * Creates an expression that finds the index of the first occurrence of a substring or byte sequence. + * + * @example + * ```typescript + * // Find the index of "foo" in the 'text' field + * stringIndexOf(field("text"), "foo"); + * ``` + * + * @param expression - The expression representing the string or byte array. + * @param search - The substring or byte sequence to search for. + * @returns A new {@link @firebase/firestore/pipelines#Expression} representing the index of the first occurrence. + */ +export function stringIndexOf( + expression: Expression, + search: string | Expression | Uint8Array | Buffer, +): FunctionExpression; +export function stringIndexOf( + expr: Expression | string, + search: string | Expression | Uint8Array | Buffer, +): FunctionExpression { + return fieldOrExpression(expr).stringIndexOf(search); +} + +/** + * @beta + * Creates an expression that repeats a string or byte array a specified number of times. + * + * @example + * ```typescript + * // Repeat the 'label' field 3 times + * stringRepeat("label", 3); + * ``` + * + * @param fieldName - The name of the field containing the string or byte array. + * @param repetitions - The number of times to repeat the string or byte array. + * @returns A new {@link @firebase/firestore/pipelines#Expression} representing the repeated string or byte array. + */ +export function stringRepeat( + fieldName: string, + repetitions: number | Expression, +): FunctionExpression; + +/** + * @beta + * Creates an expression that repeats a string or byte array a specified number of times. + * + * @example + * ```typescript + * // Repeat the 'label' field 3 times + * stringRepeat(field("label"), 3); + * ``` + * + * @param expression - The expression representing the string or byte array. + * @param repetitions - The number of times to repeat the string or byte array. + * @returns A new {@link @firebase/firestore/pipelines#Expression} representing the repeated string or byte array. + */ +export function stringRepeat( + expression: Expression, + repetitions: number | Expression, +): FunctionExpression; +export function stringRepeat( + expr: Expression | string, + repetitions: number | Expression, +): FunctionExpression { + return fieldOrExpression(expr).stringRepeat(repetitions); +} + +/** + * @beta + * Creates an expression that replaces all occurrences of a substring or byte sequence with a replacement. + * + * @example + * ```typescript + * // Replace all occurrences of "foo" with "bar" in the 'text' field + * stringReplaceAll("text", "foo", "bar"); + * ``` + * + * @param fieldName - The name of the field containing the string or byte array. + * @param find - The substring or byte sequence to search for. + * @param replacement - The replacement string or byte sequence. + * @returns A new {@link @firebase/firestore/pipelines#Expression} representing the string or byte array with replacements. + */ +export function stringReplaceAll( + fieldName: string, + find: string | Expression | Uint8Array | Buffer, + replacement: string | Expression | Uint8Array | Buffer, +): FunctionExpression; + +/** + * @beta + * Creates an expression that replaces all occurrences of a substring or byte sequence with a replacement. + * + * @example + * ```typescript + * // Replace all occurrences of "foo" with "bar" in the 'text' field + * stringReplaceAll(field("text"), "foo", "bar"); + * ``` + * + * @param expression - The expression representing the string or byte array. + * @param find - The substring or byte sequence to search for. + * @param replacement - The replacement string or byte sequence. + * @returns A new {@link @firebase/firestore/pipelines#Expression} representing the string or byte array with replacements. + */ +export function stringReplaceAll( + expression: Expression, + find: string | Expression | Uint8Array | Buffer, + replacement: string | Expression | Uint8Array | Buffer, +): FunctionExpression; +export function stringReplaceAll( + expr: Expression | string, + find: string | Expression | Uint8Array | Buffer, + replacement: string | Expression | Uint8Array | Buffer, +): FunctionExpression { + return fieldOrExpression(expr).stringReplaceAll(find, replacement); +} + +/** + * @beta + * Creates an expression that replaces the first occurrence of a substring or byte sequence with a replacement. + * + * @example + * ```typescript + * // Replace the first occurrence of "foo" with "bar" in the 'text' field + * stringReplaceOne("text", "foo", "bar"); + * ``` + * + * @param fieldName - The name of the field containing the string or byte array. + * @param find - The substring or byte sequence to search for. + * @param replacement - The replacement string or byte sequence. + * @returns A new {@link @firebase/firestore/pipelines#Expression} representing the string or byte array with the replacement. + */ +export function stringReplaceOne( + fieldName: string, + find: string | Expression | Uint8Array | Buffer, + replacement: string | Expression | Uint8Array | Buffer, +): FunctionExpression; + +/** + * @beta + * Creates an expression that replaces the first occurrence of a substring or byte sequence with a replacement. + * + * @example + * ```typescript + * // Replace the first occurrence of "foo" with "bar" in the 'text' field + * stringReplaceOne(field("text"), "foo", "bar"); + * ``` + * + * @param expression - The expression representing the string or byte array. + * @param find - The substring or byte sequence to search for. + * @param replacement - The replacement string or byte sequence. + * @returns A new {@link @firebase/firestore/pipelines#Expression} representing the string or byte array with the replacement. + */ +export function stringReplaceOne( + expression: Expression, + find: string | Expression | Uint8Array | Buffer, + replacement: string | Expression | Uint8Array | Buffer, +): FunctionExpression; +export function stringReplaceOne( + expr: Expression | string, + find: string | Expression | Uint8Array | Buffer, + replacement: string | Expression | Uint8Array | Buffer, +): FunctionExpression { + return fieldOrExpression(expr).stringReplaceOne(find, replacement); +} + /** * @beta * Accesses a value from a map (object) field using the provided key. diff --git a/dev/src/pipelines/index.ts b/dev/src/pipelines/index.ts index 79cd1a389..4db80a641 100644 --- a/dev/src/pipelines/index.ts +++ b/dev/src/pipelines/index.ts @@ -124,5 +124,11 @@ export { type, timestampTruncate, split, + ltrim, + rtrim, + stringIndexOf, + stringRepeat, + stringReplaceAll, + stringReplaceOne, // TODO(new-expression): Add new expression exports above this line } from './expression'; diff --git a/dev/system-test/pipeline.ts b/dev/system-test/pipeline.ts index d2a565d83..191797640 100644 --- a/dev/system-test/pipeline.ts +++ b/dev/system-test/pipeline.ts @@ -50,6 +50,12 @@ import { mod, reverse, trim, + ltrim, + rtrim, + stringIndexOf, + stringRepeat, + stringReplaceAll, + stringReplaceOne, toUpper, toLower, vectorLength, @@ -3854,6 +3860,146 @@ describe.skipClassic('Pipeline class', () => { }); }); + it('test ltrim', async () => { + const snapshot = await firestore + .pipeline() + .collection(randomCol.path) + .replaceWith( + map({ + spacedTitle: " The Hitchhiker's Guide to the Galaxy ", + userNameWithQuotes: '"alice"', + bytes: Uint8Array.from([0x00, 0x01, 0x02, 0x00, 0x00]), + }), + ) + .select( + ltrim('spacedTitle').as('ltrimmedTitle'), + field('userNameWithQuotes').ltrim('"').as('userName'), + field('bytes') + .ltrim(Uint8Array.from([0x00])) + .as('bytes'), + ) + .limit(1) + .execute(); + expectResults(snapshot, { + ltrimmedTitle: "The Hitchhiker's Guide to the Galaxy ", + userName: 'alice"', + bytes: Uint8Array.from([0x01, 0x02, 0x00, 0x00]), + }); + }); + + it('test rtrim', async () => { + const snapshot = await firestore + .pipeline() + .collection(randomCol.path) + .replaceWith( + map({ + spacedTitle: " The Hitchhiker's Guide to the Galaxy ", + userNameWithQuotes: '"alice"', + bytes: Uint8Array.from([0x00, 0x01, 0x02, 0x00, 0x00]), + }), + ) + .select( + rtrim('spacedTitle').as('rtrimmedTitle'), + field('userNameWithQuotes').rtrim('"').as('userName'), + field('bytes') + .rtrim(Uint8Array.from([0x00])) + .as('bytes'), + ) + .limit(1) + .execute(); + expectResults(snapshot, { + rtrimmedTitle: " The Hitchhiker's Guide to the Galaxy", + userName: '"alice', + bytes: Uint8Array.from([0x00, 0x01, 0x02]), + }); + }); + + it('test stringRepeat', async () => { + const snapshot = await firestore + .pipeline() + .collection(randomCol.path) + .replaceWith( + map({ + title: "The Hitchhiker's Guide to the Galaxy", + bytes: Uint8Array.from([0x01, 0x02, 0x03]), + }), + ) + .select( + stringRepeat(field('title'), 2).as('repeatedTitle'), + stringRepeat(field('bytes'), 2).as('repeatedBytes'), + ) + .limit(1) + .execute(); + expectResults(snapshot, { + repeatedTitle: + "The Hitchhiker's Guide to the GalaxyThe Hitchhiker's Guide to the Galaxy", + repeatedBytes: Uint8Array.from([0x01, 0x02, 0x03, 0x01, 0x02, 0x03]), + }); + }); + + it('test stringReplaceAll', async () => { + const snapshot = await firestore + .pipeline() + .collection(randomCol.path) + .replaceWith( + map({ + title: "The Hitchhiker's Guide to the Galaxy", + }), + ) + .select( + stringReplaceAll(field('title'), 'Galaxy', 'Universe').as( + 'replacedAll', + ), + ) + .limit(1) + .execute(); + expectResults(snapshot, { + replacedAll: "The Hitchhiker's Guide to the Universe", + }); + }); + + it('test stringReplaceOne', async () => { + const snapshot = await firestore + .pipeline() + .collection(randomCol.path) + .replaceWith( + map({ + title: "The Hitchhiker's Guide to the Galaxy", + }), + ) + .select(stringReplaceOne(field('title'), 'e', 'X').as('replacedOne')) + .limit(1) + .execute(); + expectResults(snapshot, { + replacedOne: "ThX Hitchhiker's Guide to the Galaxy", + }); + }); + + it('test stringIndexOf', async () => { + const snapshot = await firestore + .pipeline() + .collection(randomCol.path) + .replaceWith( + map({ + title: "The Hitchhiker's Guide to the Galaxy", + bytes: Uint8Array.from([0x01, 0x02, 0x03]), + }), + ) + .select( + stringIndexOf(field('title'), 'Guide').as('indexOfGuide'), + stringIndexOf( + field('bytes'), + constant(Uint8Array.from([0x02])), + ).as('indexOfByte'), + ) + .limit(1) + .execute(); + expectResults(snapshot, { + indexOfGuide: 17, + indexOfByte: 1, + }); + }); + it('test reverse', async () => { const snapshot = await firestore .pipeline() diff --git a/types/firestore.d.ts b/types/firestore.d.ts index 512e91f15..fffc591a7 100644 --- a/types/firestore.d.ts +++ b/types/firestore.d.ts @@ -3892,6 +3892,46 @@ declare namespace FirebaseFirestore { trim( valueToTrim?: string | Expression | Uint8Array | Buffer, ): FunctionExpression; + /** + * @beta + * Trims whitespace or a specified set of characters/bytes from the beginning of a string or byte array. + * + * @example + * ```typescript + * // Trim whitespace from the beginning of the 'userInput' field + * field("userInput").ltrim(); + * + * // Trim quotes from the beginning of the 'userInput' field + * field("userInput").ltrim('"'); + * ``` + * + * @param valueToTrim - Optional. A string or byte array containing the characters/bytes to trim. + * If not specified, whitespace will be trimmed. + * @returns A new `Expression` representing the trimmed string. + */ + ltrim( + valueToTrim?: string | Expression | Uint8Array | Buffer, + ): FunctionExpression; + /** + * @beta + * Trims whitespace or a specified set of characters/bytes from the end of a string or byte array. + * + * @example + * ```typescript + * // Trim whitespace from the end of the 'userInput' field + * field("userInput").rtrim(); + * + * // Trim quotes from the end of the 'userInput' field + * field("userInput").rtrim('"'); + * ``` + * + * @param valueToTrim - Optional. A string or byte array containing the characters/bytes to trim. + * If not specified, whitespace will be trimmed. + * @returns A new `Expression` representing the trimmed string or byte array. + */ + rtrim( + valueToTrim?: string | Expression | Uint8Array | Buffer, + ): FunctionExpression; /** * @beta * Creates an expression that concatenates string expressions together. @@ -3909,6 +3949,72 @@ declare namespace FirebaseFirestore { secondString: Expression | string, ...otherStrings: Array ): FunctionExpression; + /** + * @beta + * Creates an expression that finds the index of the first occurrence of a substring or byte sequence. + * + * @example + * ```typescript + * // Find the index of "foo" in the 'text' field + * field("text").stringIndexOf("foo"); + * ``` + * + * @param search - The substring or byte sequence to search for. + * @returns A new `Expression` representing the index of the first occurrence. + */ + stringIndexOf( + search: string | Expression | Uint8Array | Buffer, + ): FunctionExpression; + /** + * @beta + * Creates an expression that repeats a string or byte array a specified number of times. + * + * @example + * ```typescript + * // Repeat the 'label' field 3 times + * field("label").stringRepeat(3); + * ``` + * + * @param repetitions - The number of times to repeat the string or byte array. + * @returns A new `Expression` representing the repeated string or byte array. + */ + stringRepeat(repetitions: number | Expression): FunctionExpression; + /** + * @beta + * Creates an expression that replaces all occurrences of a substring or byte sequence with a replacement. + * + * @example + * ```typescript + * // Replace all occurrences of "foo" with "bar" in the 'text' field + * field("text").stringReplaceAll("foo", "bar"); + * ``` + * + * @param find - The substring or byte sequence to search for. + * @param replacement - The replacement string or byte sequence. + * @returns A new `Expression` representing the string or byte array with replacements. + */ + stringReplaceAll( + find: string | Expression | Uint8Array | Buffer, + replacement: string | Expression | Uint8Array | Buffer, + ): FunctionExpression; + /** + * @beta + * Creates an expression that replaces the first occurrence of a substring or byte sequence with a replacement. + * + * @example + * ```typescript + * // Replace the first occurrence of "foo" with "bar" in the 'text' field + * field("text").stringReplaceOne("foo", "bar"); + * ``` + * + * @param find - The substring or byte sequence to search for. + * @param replacement - The replacement string or byte sequence. + * @returns A new `Expression` representing the string or byte array with the replacement. + */ + stringReplaceOne( + find: string | Expression | Uint8Array | Buffer, + replacement: string | Expression | Uint8Array | Buffer, + ): FunctionExpression; /** * @beta * Creates an expression that reverses this string or bytes expression. From 6f1b089c8031fa602647db616fb2446487da3dbf Mon Sep 17 00:00:00 2001 From: Owl Bot Date: Fri, 6 Feb 2026 16:39:50 +0000 Subject: [PATCH 2/3] =?UTF-8?q?=F0=9F=A6=89=20Updates=20from=20OwlBot=20po?= =?UTF-8?q?st-processor?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit See https://github.com/googleapis/repo-automation-bots/blob/main/packages/owl-bot/README.md --- dev/system-test/pipeline.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dev/system-test/pipeline.ts b/dev/system-test/pipeline.ts index 191797640..c0f37a3fd 100644 --- a/dev/system-test/pipeline.ts +++ b/dev/system-test/pipeline.ts @@ -3987,10 +3987,9 @@ describe.skipClassic('Pipeline class', () => { ) .select( stringIndexOf(field('title'), 'Guide').as('indexOfGuide'), - stringIndexOf( - field('bytes'), - constant(Uint8Array.from([0x02])), - ).as('indexOfByte'), + stringIndexOf(field('bytes'), constant(Uint8Array.from([0x02]))).as( + 'indexOfByte', + ), ) .limit(1) .execute(); From d683215c7da27170b458293e580e3a0ddf615d03 Mon Sep 17 00:00:00 2001 From: Daniel La Rocque Date: Wed, 11 Feb 2026 10:20:42 -0500 Subject: [PATCH 3/3] add more tests --- dev/system-test/pipeline.ts | 36 +++++++++++++++++++++++++++++++----- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/dev/system-test/pipeline.ts b/dev/system-test/pipeline.ts index c0f37a3fd..4303a7023 100644 --- a/dev/system-test/pipeline.ts +++ b/dev/system-test/pipeline.ts @@ -3874,6 +3874,7 @@ describe.skipClassic('Pipeline class', () => { .select( ltrim('spacedTitle').as('ltrimmedTitle'), field('userNameWithQuotes').ltrim('"').as('userName'), + ltrim(toLower('spacedTitle')).as('ltrimmedTitleLower'), field('bytes') .ltrim(Uint8Array.from([0x00])) .as('bytes'), @@ -3882,6 +3883,7 @@ describe.skipClassic('Pipeline class', () => { .execute(); expectResults(snapshot, { ltrimmedTitle: "The Hitchhiker's Guide to the Galaxy ", + ltrimmedTitleLower: "the hitchhiker's guide to the galaxy ", userName: 'alice"', bytes: Uint8Array.from([0x01, 0x02, 0x00, 0x00]), }); @@ -3901,6 +3903,7 @@ describe.skipClassic('Pipeline class', () => { .select( rtrim('spacedTitle').as('rtrimmedTitle'), field('userNameWithQuotes').rtrim('"').as('userName'), + rtrim(toLower('spacedTitle')).as('rtrimmedTitleLower'), field('bytes') .rtrim(Uint8Array.from([0x00])) .as('bytes'), @@ -3909,6 +3912,7 @@ describe.skipClassic('Pipeline class', () => { .execute(); expectResults(snapshot, { rtrimmedTitle: " The Hitchhiker's Guide to the Galaxy", + rtrimmedTitleLower: " the hitchhiker's guide to the galaxy", userName: '"alice', bytes: Uint8Array.from([0x00, 0x01, 0x02]), }); @@ -3944,17 +3948,30 @@ describe.skipClassic('Pipeline class', () => { .replaceWith( map({ title: "The Hitchhiker's Guide to the Galaxy", + bytes: Uint8Array.from([0x01, 0x02, 0x02]), }), ) .select( - stringReplaceAll(field('title'), 'Galaxy', 'Universe').as( - 'replacedAll', - ), + stringReplaceAll(field('title'), 'the', 'a').as('replacedAll'), + stringReplaceAll(toLower('title'), 'the', 'a').as('replacedAllLower'), + stringReplaceAll( + field('bytes'), + Uint8Array.from([0x01, 0x02, 0x02]), + Uint8Array.from([0x03, 0x03, 0x03]), + ).as('replacedEntireByteArray'), + stringReplaceAll( + field('bytes'), + Uint8Array.from([0x02]), + Uint8Array.from([0x03]), + ).as('replacedMultipleBytes'), ) .limit(1) .execute(); expectResults(snapshot, { - replacedAll: "The Hitchhiker's Guide to the Universe", + replacedAll: "The Hitchhiker's Guide to a Galaxy", + replacedAllLower: "a hitchhiker's guide to a galaxy", + replacedEntireByteArray: Uint8Array.from([0x03, 0x03, 0x03]), + replacedMultipleBytes: Uint8Array.from([0x01, 0x03, 0x03]), }); }); @@ -3965,13 +3982,22 @@ describe.skipClassic('Pipeline class', () => { .replaceWith( map({ title: "The Hitchhiker's Guide to the Galaxy", + bytes: Uint8Array.from([0x01, 0x02, 0x02]), }), ) - .select(stringReplaceOne(field('title'), 'e', 'X').as('replacedOne')) + .select( + stringReplaceOne(field('title'), 'e', 'X').as('replacedOne'), + stringReplaceOne( + field('bytes'), + Uint8Array.from([0x02]), + Uint8Array.from([0x03]), + ).as('replacedOneByte'), + ) .limit(1) .execute(); expectResults(snapshot, { replacedOne: "ThX Hitchhiker's Guide to the Galaxy", + replacedOneByte: new Uint8Array([0x01, 0x03, 0x02]), }); });