diff --git a/.wordlist.txt b/.wordlist.txt index 77f02ca..5096f20 100644 --- a/.wordlist.txt +++ b/.wordlist.txt @@ -686,6 +686,50 @@ dylib xcode Trendshift +UDF +UDFs +StringUtils +UpperCaseOdd +unparseable +toTimeZone +jaccard +Jaccard +levenshtein +submap +Submap +removeKeys +removeKey +fromPairs +fromJsonList +fromJsonMap +toJson +bitwise +Bitwise +Jaro +PascalCase +UpperCamelCase +Winkler +camelCase +decapitalize +indexOf +indexesOf +jaroWinkler +lpad +regexGroups +rpad +snakeCase +swapCase +Deduplicate +Deduplication +deduplication +sanitization +sprintf +fullMatch +abc +upperCamelCase +shiftLeft +shiftRight + DBaaS GCP VPC diff --git a/udfs/flex/bitwise/and.md b/udfs/flex/bitwise/and.md new file mode 100644 index 0000000..01286d0 --- /dev/null +++ b/udfs/flex/bitwise/and.md @@ -0,0 +1,59 @@ +# bitwise.and + +## Description +Performs a bitwise AND operation on two integers. Each bit in the result is 1 only if the corresponding bits in both operands are 1. + +## Syntax +```cypher +flex.bitwise.and(a, b) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `a` | number (integer) | Yes | First operand | +| `b` | number (integer) | Yes | Second operand | + +## Returns +**Type:** number (integer) + +The result of the bitwise AND operation. + +## Examples + +### Example 1: Basic AND Operation +```cypher +RETURN flex.bitwise.and(12, 10) AS result +``` + +**Output:** +``` +result +------ +8 +``` +(Binary: 1100 AND 1010 = 1000 = 8) + +### Example 2: Checking Permission Flags +```cypher +WITH 7 AS userPermissions // 0111 (read=1, write=2, execute=4) +WITH userPermissions, 2 AS writeFlag +RETURN flex.bitwise.and(userPermissions, writeFlag) > 0 AS hasWrite +``` + +### Example 3: Masking Bits +```cypher +MATCH (d:Device) +WITH d, flex.bitwise.and(d.flags, 15) AS lowerNibble +RETURN d.id, lowerNibble +``` + +## Notes +- Operates on 32-bit signed integers in JavaScript +- Both operands are converted to integers if needed +- Commonly used for flag checking and bit masking + +## See Also +- [bitwise.or](./or.md) - Bitwise OR operation +- [bitwise.xor](./xor.md) - Bitwise XOR operation +- [bitwise.not](./not.md) - Bitwise NOT operation diff --git a/udfs/flex/bitwise/not.md b/udfs/flex/bitwise/not.md new file mode 100644 index 0000000..981f952 --- /dev/null +++ b/udfs/flex/bitwise/not.md @@ -0,0 +1,72 @@ +# bitwise.not + +## Description +Performs a bitwise NOT operation (one's complement) on an integer, inverting all bits. + +## Syntax +```cypher +flex.bitwise.not(a) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `a` | number (integer) | Yes | The operand to invert | + +## Returns +**Type:** number (integer) + +The result of the bitwise NOT operation (one's complement). + +## Examples + +### Example 1: Basic NOT Operation +```cypher +RETURN flex.bitwise.not(5) AS result +``` + +**Output:** +``` +result +------ +-6 +``` +(Binary: NOT 0101 = ...11111010 in 32-bit two's complement = -6) + +### Example 2: Inverting All Bits +```cypher +RETURN flex.bitwise.not(0) AS result +``` + +**Output:** +``` +result +------ +-1 +``` +(All bits become 1 in two's complement = -1) + +### Example 3: Double NOT Returns Original +```cypher +WITH 42 AS value +RETURN flex.bitwise.not(flex.bitwise.not(value)) AS restored +``` + +**Output:** +``` +restored +-------- +42 +``` + +## Notes +- Operates on 32-bit signed integers in JavaScript +- Result uses two's complement representation +- NOT operation inverts all bits including sign bit +- Formula: `~n = -(n + 1)` +- Less commonly used than AND, OR, XOR in typical applications + +## See Also +- [bitwise.and](./and.md) - Bitwise AND operation +- [bitwise.or](./or.md) - Bitwise OR operation +- [bitwise.xor](./xor.md) - Bitwise XOR operation diff --git a/udfs/flex/bitwise/or.md b/udfs/flex/bitwise/or.md new file mode 100644 index 0000000..334006e --- /dev/null +++ b/udfs/flex/bitwise/or.md @@ -0,0 +1,65 @@ +# bitwise.or + +## Description +Performs a bitwise OR operation on two integers. Each bit in the result is 1 if at least one of the corresponding bits in the operands is 1. + +## Syntax +```cypher +flex.bitwise.or(a, b) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `a` | number (integer) | Yes | First operand | +| `b` | number (integer) | Yes | Second operand | + +## Returns +**Type:** number (integer) + +The result of the bitwise OR operation. + +## Examples + +### Example 1: Basic OR Operation +```cypher +RETURN flex.bitwise.or(12, 10) AS result +``` + +**Output:** +``` +result +------ +14 +``` +(Binary: 1100 OR 1010 = 1110 = 14) + +### Example 2: Combining Permission Flags +```cypher +WITH 1 AS readFlag, 2 AS writeFlag +RETURN flex.bitwise.or(readFlag, writeFlag) AS readWritePermission +``` + +**Output:** +``` +readWritePermission +------------------- +3 +``` +(Binary: 01 OR 10 = 11 = 3) + +### Example 3: Setting Multiple Flags +```cypher +MATCH (u:User {id: 123}) +SET u.permissions = flex.bitwise.or(u.permissions, 4) // Add execute permission +``` + +## Notes +- Operates on 32-bit signed integers in JavaScript +- Both operands are converted to integers if needed +- Commonly used for combining flags and setting bits + +## See Also +- [bitwise.and](./and.md) - Bitwise AND operation +- [bitwise.xor](./xor.md) - Bitwise XOR operation +- [bitwise.not](./not.md) - Bitwise NOT operation diff --git a/udfs/flex/bitwise/shiftLeft.md b/udfs/flex/bitwise/shiftLeft.md new file mode 100644 index 0000000..c270d72 --- /dev/null +++ b/udfs/flex/bitwise/shiftLeft.md @@ -0,0 +1,77 @@ +# bitwise.shiftLeft + +## Description +Performs a left bit shift operation, moving all bits to the left by the specified number of positions. Zero bits are shifted in from the right. + +## Syntax +```cypher +flex.bitwise.shiftLeft(a, positions) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `a` | number (integer) | Yes | The value to shift | +| `positions` | number (integer) | Yes | Number of positions to shift left | + +## Returns +**Type:** number (integer) + +The result of shifting the bits left by the specified positions. + +## Examples + +### Example 1: Basic Left Shift +```cypher +RETURN flex.bitwise.shiftLeft(5, 2) AS result +``` + +**Output:** +``` +result +------ +20 +``` +(Binary: 0101 << 2 = 10100 = 20) + +### Example 2: Multiply by Power of Two +```cypher +WITH 7 AS value +RETURN + flex.bitwise.shiftLeft(value, 1) AS times2, + flex.bitwise.shiftLeft(value, 2) AS times4, + flex.bitwise.shiftLeft(value, 3) AS times8 +``` + +**Output:** +``` +times2 | times4 | times8 +-------|--------|------- +14 | 28 | 56 +``` +(Left shift by n is equivalent to multiplying by 2^n) + +### Example 3: Creating Bit Masks +```cypher +RETURN flex.bitwise.shiftLeft(1, 3) AS mask +``` + +**Output:** +``` +mask +---- +8 +``` +(Creates mask with bit 3 set: 1000) + +## Notes +- Operates on 32-bit signed integers in JavaScript +- Left shift by n is equivalent to multiplying by 2^n +- Bits shifted off the left are discarded +- Zero bits are shifted in from the right +- Useful for multiplication by powers of 2 and creating bit masks + +## See Also +- [bitwise.shiftRight](./shiftRight.md) - Shift bits to the right +- [bitwise.and](./and.md) - Bitwise AND operation +- [bitwise.or](./or.md) - Bitwise OR operation diff --git a/udfs/flex/bitwise/shiftRight.md b/udfs/flex/bitwise/shiftRight.md new file mode 100644 index 0000000..ff9072f --- /dev/null +++ b/udfs/flex/bitwise/shiftRight.md @@ -0,0 +1,92 @@ +# bitwise.shiftRight + +## Description +Performs a sign-propagating right bit shift operation, moving all bits to the right by the specified number of positions. The sign bit is copied to fill the leftmost positions. + +## Syntax +```cypher +flex.bitwise.shiftRight(a, positions) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `a` | number (integer) | Yes | The value to shift | +| `positions` | number (integer) | Yes | Number of positions to shift right | + +## Returns +**Type:** number (integer) + +The result of shifting the bits right by the specified positions, with sign extension. + +## Examples + +### Example 1: Basic Right Shift +```cypher +RETURN flex.bitwise.shiftRight(20, 2) AS result +``` + +**Output:** +``` +result +------ +5 +``` +(Binary: 10100 >> 2 = 00101 = 5) + +### Example 2: Divide by Power of Two +```cypher +WITH 56 AS value +RETURN + flex.bitwise.shiftRight(value, 1) AS div2, + flex.bitwise.shiftRight(value, 2) AS div4, + flex.bitwise.shiftRight(value, 3) AS div8 +``` + +**Output:** +``` +div2 | div4 | div8 +-----|------|----- +28 | 14 | 7 +``` +(Right shift by n is equivalent to integer division by 2^n) + +### Example 3: Sign Extension with Negative Numbers +```cypher +RETURN flex.bitwise.shiftRight(-8, 2) AS result +``` + +**Output:** +``` +result +------ +-2 +``` +(Sign bit is preserved in right shift) + +### Example 4: Extracting Higher Bits +```cypher +WITH 255 AS value // 11111111 +RETURN flex.bitwise.shiftRight(value, 4) AS upperNibble +``` + +**Output:** +``` +upperNibble +----------- +15 +``` +(Extracts upper 4 bits: 00001111 = 15) + +## Notes +- Operates on 32-bit signed integers in JavaScript +- Uses arithmetic (sign-propagating) right shift +- Right shift by n is equivalent to integer division by 2^n (truncated toward negative infinity) +- Sign bit is copied to fill vacated positions (sign extension) +- Bits shifted off the right are discarded +- Useful for division by powers of 2 and extracting bit fields + +## See Also +- [bitwise.shiftLeft](./shiftLeft.md) - Shift bits to the left +- [bitwise.and](./and.md) - Bitwise AND operation +- [bitwise.or](./or.md) - Bitwise OR operation diff --git a/udfs/flex/bitwise/xor.md b/udfs/flex/bitwise/xor.md new file mode 100644 index 0000000..99e02d2 --- /dev/null +++ b/udfs/flex/bitwise/xor.md @@ -0,0 +1,75 @@ +# bitwise.xor + +## Description +Performs a bitwise XOR (exclusive OR) operation on two integers. Each bit in the result is 1 if the corresponding bits in the operands are different. + +## Syntax +```cypher +flex.bitwise.xor(a, b) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `a` | number (integer) | Yes | First operand | +| `b` | number (integer) | Yes | Second operand | + +## Returns +**Type:** number (integer) + +The result of the bitwise XOR operation. + +## Examples + +### Example 1: Basic XOR Operation +```cypher +RETURN flex.bitwise.xor(12, 10) AS result +``` + +**Output:** +``` +result +------ +6 +``` +(Binary: 1100 XOR 1010 = 0110 = 6) + +### Example 2: Toggling Bits +```cypher +WITH 5 AS value, 3 AS toggleMask +RETURN flex.bitwise.xor(value, toggleMask) AS toggled +``` + +**Output:** +``` +toggled +------- +6 +``` +(Binary: 0101 XOR 0011 = 0110) + +### Example 3: Simple Encryption/Decryption +```cypher +WITH 42 AS data, 17 AS key +WITH flex.bitwise.xor(data, key) AS encrypted +RETURN flex.bitwise.xor(encrypted, key) AS decrypted +``` + +**Output:** +``` +decrypted +--------- +42 +``` +(XOR with same key twice returns original value) + +## Notes +- Operates on 32-bit signed integers in JavaScript +- Both operands are converted to integers if needed +- XOR with same value twice returns the original value +- Commonly used for toggling flags and simple encryption + +## See Also +- [bitwise.and](./and.md) - Bitwise AND operation +- [bitwise.or](./or.md) - Bitwise OR operation +- [bitwise.not](./not.md) - Bitwise NOT operation diff --git a/udfs/flex/collections/frequencies.md b/udfs/flex/collections/frequencies.md new file mode 100644 index 0000000..bfdf6c5 --- /dev/null +++ b/udfs/flex/collections/frequencies.md @@ -0,0 +1,77 @@ +# coll.frequencies + +## Description +Counts the frequency of each element in a list, returning a map where keys are the unique elements and values are their occurrence counts. + +## Syntax +```cypher +flex.coll.frequencies(list) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `list` | list | Yes | The list to analyze | + +## Returns +**Type:** map (object) + +A map where each key is a unique element from the list and each value is the count of how many times that element appears. Returns an empty map if input is not an array. + +## Examples + +### Example 1: Basic Frequency Count +```cypher +WITH ['apple', 'banana', 'apple', 'cherry', 'banana', 'apple'] AS fruits +RETURN flex.coll.frequencies(fruits) AS counts +``` + +**Output:** +``` +counts +--------------------------------------- +{apple: 3, banana: 2, cherry: 1} +``` + +### Example 2: Tag Analysis +```cypher +MATCH (d:Document) +WITH collect(d.tags) AS allTagLists +UNWIND allTagLists AS tags +UNWIND tags AS tag +WITH collect(tag) AS flatTags +RETURN flex.coll.frequencies(flatTags) AS tagCounts +``` + +### Example 3: Finding Most Common Values +```cypher +MATCH (u:User) +WITH collect(u.country) AS countries +WITH flex.coll.frequencies(countries) AS freq +UNWIND keys(freq) AS country +RETURN country, freq[country] AS count +ORDER BY count DESC +LIMIT 10 +``` + +### Example 4: Word Frequency Analysis +```cypher +MATCH (doc:Document) +WITH split(toLower(doc.content), ' ') AS words +WITH flex.coll.frequencies(words) AS wordCounts +UNWIND keys(wordCounts) AS word +WHERE wordCounts[word] > 5 +RETURN word, wordCounts[word] AS frequency +ORDER BY frequency DESC +``` + +## Notes +- Returns empty map if input is not an array or is `null` +- `null` and `undefined` values are stored with key `"null"` +- All elements are converted to string keys in the result map +- Useful for analytics, statistics, and data exploration +- Can be combined with `keys()` and sorting for top-N analysis + +## See Also +- [coll.union](./union.md) - Get unique elements (keys would give unique items) +- [coll.intersection](./intersection.md) - Find common elements diff --git a/udfs/flex/collections/intersection.md b/udfs/flex/collections/intersection.md new file mode 100644 index 0000000..f1c9b6e --- /dev/null +++ b/udfs/flex/collections/intersection.md @@ -0,0 +1,71 @@ +# coll.intersection + +## Description +Finds the common elements between two lists, returning a new list containing only the elements that appear in both input lists. + +## Syntax +```cypher +flex.coll.intersection(list1, list2) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `list1` | list | Yes | The first list | +| `list2` | list | Yes | The second list | + +## Returns +**Type:** list + +A new list containing only the elements that exist in both input lists. Preserves the order from the first list. + +## Examples + +### Example 1: Basic Intersection +```cypher +WITH [1, 2, 3, 4] AS a, [3, 4, 5, 6] AS b +RETURN flex.coll.intersection(a, b) AS result +``` + +**Output:** +``` +result +------- +[3, 4] +``` + +### Example 2: Finding Common Tags +```cypher +MATCH (d1:Document {id: 'doc1'}) +MATCH (d2:Document {id: 'doc2'}) +WITH flex.coll.intersection(d1.tags, d2.tags) AS commonTags +WHERE size(commonTags) > 0 +RETURN commonTags +``` + +### Example 3: Finding Users with Shared Interests +```cypher +MATCH (u1:User {id: $userId1}) +MATCH (u2:User {id: $userId2}) +WITH flex.coll.intersection(u1.interests, u2.interests) AS sharedInterests +RETURN u1.name, u2.name, sharedInterests, size(sharedInterests) AS commonCount +``` + +### Example 4: Filter by Allowed Values +```cypher +WITH ['admin', 'read', 'write', 'delete'] AS allowed +MATCH (u:User) +WITH u, flex.coll.intersection(u.permissions, allowed) AS validPerms +RETURN u.name, validPerms +``` + +## Notes +- Returns elements that exist in both lists +- Preserves the order from the first list +- If an element appears multiple times in list1, each occurrence is checked against list2 +- Efficient implementation using Set for fast lookup +- Equivalent to mathematical set intersection operation + +## See Also +- [coll.union](./union.md) - Combine all unique elements from both lists +- [sim.jaccard](../similarity/jaccard.md) - Calculate similarity coefficient using intersection diff --git a/udfs/flex/collections/shuffle.md b/udfs/flex/collections/shuffle.md new file mode 100644 index 0000000..45e9b2a --- /dev/null +++ b/udfs/flex/collections/shuffle.md @@ -0,0 +1,69 @@ +# coll.shuffle + +## Description +Randomly shuffles the elements of a list using the Fisher-Yates algorithm. Returns a new list with elements in random order without modifying the original. + +## Syntax +```cypher +flex.coll.shuffle(list) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `list` | list | Yes | The list to shuffle | + +## Returns +**Type:** list + +A new list containing the same elements in a randomized order. Returns an empty list if input is not an array. + +## Examples + +### Example 1: Basic Shuffle +```cypher +WITH [1, 2, 3, 4, 5] AS numbers +RETURN flex.coll.shuffle(numbers) AS shuffled +``` + +**Output:** (example, actual order will vary) +``` +shuffled +----------- +[3, 1, 5, 2, 4] +``` + +### Example 2: Random Sample Selection +```cypher +MATCH (q:Question) +WITH collect(q) AS allQuestions +WITH flex.coll.shuffle(allQuestions) AS randomized +RETURN randomized[0..10] AS quizQuestions +``` + +### Example 3: Randomizing Recommendations +```cypher +MATCH (u:User {id: $userId})-[:LIKES]->(p:Product) +MATCH (p)-[:SIMILAR_TO]->(rec:Product) +WITH collect(DISTINCT rec) AS recommendations +RETURN flex.coll.shuffle(recommendations)[0..5] AS randomRecs +``` + +### Example 4: Random Team Assignment +```cypher +MATCH (p:Player) +WITH collect(p.name) AS players +WITH flex.coll.shuffle(players) AS shuffled +RETURN shuffled[0..5] AS team1, shuffled[5..10] AS team2 +``` + +## Notes +- Returns empty list if input is not an array or is `null` +- Uses the Fisher-Yates shuffle algorithm for uniform random distribution +- Creates a new list; does not modify the original +- Each element appears exactly once in the result +- Order is truly random on each execution + +## See Also +- [coll.zip](./zip.md) - Combine two lists element-by-element +- [coll.union](./union.md) - Combine unique elements from lists diff --git a/udfs/flex/collections/union.md b/udfs/flex/collections/union.md new file mode 100644 index 0000000..210ae05 --- /dev/null +++ b/udfs/flex/collections/union.md @@ -0,0 +1,69 @@ +# coll.union + +## Description +Combines two lists and returns a new list containing all unique elements from both lists. Duplicates are automatically removed, treating the inputs as sets. + +## Syntax +```cypher +flex.coll.union(list1, list2) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `list1` | list | Yes | The first list | +| `list2` | list | Yes | The second list | + +## Returns +**Type:** list + +A new list containing all unique elements from both input lists. The order is not guaranteed. + +## Examples + +### Example 1: Basic Union +```cypher +WITH [1, 2, 3] AS a, [3, 4, 5] AS b +RETURN flex.coll.union(a, b) AS result +``` + +**Output:** +``` +result +-------------- +[1, 2, 3, 4, 5] +``` + +### Example 2: Combining Tags from Multiple Nodes +```cypher +MATCH (d1:Document {id: 'doc1'}) +MATCH (d2:Document {id: 'doc2'}) +RETURN flex.coll.union(d1.tags, d2.tags) AS allTags +``` + +### Example 3: Merging User Interests +```cypher +MATCH (u:User) +WITH collect(u.interests) AS interestLists +RETURN reduce(result = [], list IN interestLists | + flex.coll.union(result, list) +) AS allUniqueInterests +``` + +### Example 4: Finding All Related Categories +```cypher +MATCH (p:Product {id: 123}) +MATCH (similar:Product)-[:SIMILAR_TO]-(p) +RETURN flex.coll.union(p.categories, similar.categories) AS combinedCategories +``` + +## Notes +- Automatically removes duplicates from the result +- Works with any comparable data types +- Order of elements in the result is not guaranteed +- Equivalent to mathematical set union operation +- Both lists are spread and deduplicated using Set + +## See Also +- [coll.intersection](./intersection.md) - Find common elements between lists +- [sim.jaccard](../similarity/jaccard.md) - Calculate similarity between sets diff --git a/udfs/flex/collections/zip.md b/udfs/flex/collections/zip.md new file mode 100644 index 0000000..4c8927a --- /dev/null +++ b/udfs/flex/collections/zip.md @@ -0,0 +1,73 @@ +# coll.zip + +## Description +Combines two lists element-by-element into a list of pairs. Each pair contains one element from the first list and the corresponding element from the second list. The resulting list has the length of the shorter input list. + +## Syntax +```cypher +flex.coll.zip(list1, list2) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `list1` | list | Yes | The first list | +| `list2` | list | Yes | The second list | + +## Returns +**Type:** list of lists + +A list where each element is a two-element array `[item1, item2]` combining corresponding elements from both lists. Returns an empty list if either input is not an array. + +## Examples + +### Example 1: Basic Zipping +```cypher +WITH ['a', 'b', 'c'] AS letters, [1, 2, 3] AS numbers +RETURN flex.coll.zip(letters, numbers) AS pairs +``` + +**Output:** +``` +pairs +---------------------------- +[["a", 1], ["b", 2], ["c", 3]] +``` + +### Example 2: Different Length Lists +```cypher +WITH ['x', 'y', 'z'] AS keys, [10, 20] AS values +RETURN flex.coll.zip(keys, values) AS result +``` + +**Output:** +``` +result +-------------------- +[["x", 10], ["y", 20]] +``` +(Only pairs up to the length of the shorter list) + +### Example 3: Creating Key-Value Pairs +```cypher +MATCH (p:Product) +WITH collect(p.name) AS names, collect(p.price) AS prices +RETURN flex.coll.zip(names, prices) AS productData +``` + +### Example 4: Pairing Related Data +```cypher +WITH ['Mon', 'Tue', 'Wed'] AS days, [120, 135, 98] AS sales +UNWIND flex.coll.zip(days, sales) AS pair +RETURN pair[0] AS day, pair[1] AS salesAmount +``` + +## Notes +- Returns empty list if either input is not an array or is `null` +- Result length is limited by the shorter of the two input lists +- Excess elements from the longer list are ignored +- Useful for pairing related data or creating key-value associations + +## See Also +- [map.fromPairs](../map/fromPairs.md) - Convert pairs to a map +- [coll.union](./union.md) - Combine lists as sets diff --git a/udfs/flex/date/format.md b/udfs/flex/date/format.md new file mode 100644 index 0000000..b7aa70f --- /dev/null +++ b/udfs/flex/date/format.md @@ -0,0 +1,94 @@ +# date.format + +## Description +Formats a date/time value using a simple token-based pattern. Supports common date/time tokens and optional timezone offset adjustment. + +## Syntax +```cypher +flex.date.format(datetime, pattern, timezone) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `datetime` | Date/number/string | Yes | The date/time value to format | +| `pattern` | string | No | Format pattern using tokens (default: `'YYYY-MM-DDTHH:mm:ss[Z]'`) | +| `timezone` | string | No | Timezone offset like `"+02:00"` or `"-05:00"` | + +### Supported Pattern Tokens +| Token | Description | Example | +|-------|-------------|---------| +| `YYYY` | 4-digit year | `2024` | +| `MM` | 2-digit month (01-12) | `03` | +| `DD` | 2-digit day (01-31) | `15` | +| `HH` | 2-digit hour (00-23) | `14` | +| `mm` | 2-digit minute (00-59) | `30` | +| `ss` | 2-digit second (00-59) | `45` | +| `SSS` | 3-digit milliseconds | `123` | +| `[Z]` | Literal 'Z' character | `Z` | + +## Returns +**Type:** string + +A formatted date/time string according to the pattern. Returns `null` if the input date is invalid. + +## Examples + +### Example 1: Basic Date Formatting +```cypher +WITH datetime('2024-03-15T14:30:00Z') AS dt +RETURN flex.date.format(dt, 'YYYY-MM-DD') AS date +``` + +**Output:** +``` +date +---------- +2024-03-15 +``` + +### Example 2: Full DateTime with Time +```cypher +WITH datetime('2024-03-15T14:30:45Z') AS dt +RETURN flex.date.format(dt, 'YYYY-MM-DD HH:mm:ss') AS formatted +``` + +**Output:** +``` +formatted +------------------- +2024-03-15 14:30:45 +``` + +### Example 3: Custom Format with Timezone +```cypher +WITH datetime('2024-03-15T14:30:00Z') AS dt +RETURN flex.date.format(dt, 'DD/MM/YYYY HH:mm', '+02:00') AS localTime +``` + +**Output:** +``` +localTime +----------------- +15/03/2024 16:30 +``` +(Adjusted for +02:00 timezone) + +### Example 4: Formatting Node Timestamps +```cypher +MATCH (e:Event) +RETURN e.name, flex.date.format(e.timestamp, 'YYYY-MM-DD') AS eventDate +ORDER BY e.timestamp DESC +``` + +## Notes +- Returns `null` for invalid date inputs +- Default pattern is ISO8601-like: `'YYYY-MM-DDTHH:mm:ss[Z]'` +- Timezone parameter adjusts the displayed time for the given offset +- All calculations are UTC-based internally +- Milliseconds are optional in the pattern + +## See Also +- [date.parse](./parse.md) - Parse string to date +- [date.truncate](./truncate.md) - Truncate date to specific unit +- [date.toTimeZone](./toTimeZone.md) - Convert date to timezone diff --git a/udfs/flex/date/parse.md b/udfs/flex/date/parse.md new file mode 100644 index 0000000..a9c7d76 --- /dev/null +++ b/udfs/flex/date/parse.md @@ -0,0 +1,73 @@ +# date.parse + +## Description +Parses a date/time string into a Date object using an optional pattern. Supports explicit patterns and falls back to standard Date parsing for other formats. + +## Syntax +```cypher +flex.date.parse(dateString, pattern, timezone) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `dateString` | string | Yes | The date/time string to parse | +| `pattern` | string | No | Format pattern (supported: `'YYYY-MM-DD'`, `'YYYY-MM-DDTHH:mm:ss'` or `'YYYY-MM-DD HH:mm:ss'`, or auto-detect) | +| `timezone` | string | No | Timezone offset like `"+02:00"` to interpret input in specific timezone | + +## Returns +**Type:** Date + +A Date object representing the parsed date/time. Returns `null` if parsing fails. + +## Examples + +### Example 1: Parse Date Only +```cypher +RETURN flex.date.parse('2024-03-15', 'YYYY-MM-DD') AS date +``` + +**Output:** +``` +date +----------------------------- +2024-03-15T00:00:00.000Z (Date object) +``` + +### Example 2: Parse DateTime +```cypher +RETURN flex.date.parse('2024-03-15T14:30:00', 'YYYY-MM-DDTHH:mm:ss') AS datetime +``` + +### Example 3: Auto-detect ISO Format +```cypher +RETURN flex.date.parse('2024-03-15T14:30:00Z') AS dt +``` + +### Example 4: Parse with Timezone Context +```cypher +WITH '2024-03-15 10:00:00' AS localTime +RETURN flex.date.parse(localTime, 'YYYY-MM-DDTHH:mm:ss', '+05:00') AS utcTime +``` +(Interprets input as being in +05:00 timezone and converts to UTC) + +### Example 5: Batch Import with Date Parsing +```cypher +UNWIND $events AS event +CREATE (e:Event { + name: event.name, + date: flex.date.parse(event.dateString, 'YYYY-MM-DD') +}) +``` + +## Notes +- Returns `null` for invalid or unparseable date strings +- Supported explicit patterns: `'YYYY-MM-DD'`, `'YYYY-MM-DDTHH:mm:ss'` and `'YYYY-MM-DD HH:mm:ss'` (both `T` and space separators are accepted) +- Falls back to JavaScript Date constructor for other formats (ISO8601, etc.) +- Timezone parameter interprets the input time as local time in that offset +- All dates are normalized to UTC internally + +## See Also +- [date.format](./format.md) - Format date to string +- [date.truncate](./truncate.md) - Truncate date to specific unit +- [date.toTimeZone](./toTimeZone.md) - Convert date to timezone diff --git a/udfs/flex/date/toTimeZone.md b/udfs/flex/date/toTimeZone.md new file mode 100644 index 0000000..4f382e0 --- /dev/null +++ b/udfs/flex/date/toTimeZone.md @@ -0,0 +1,78 @@ +# date.toTimeZone + +## Description +Converts a date/time instant to represent the wall clock time in a given timezone offset. The returned Date represents the same instant but shifted so UTC fields reflect local time. + +## Syntax +```cypher +flex.date.toTimeZone(datetime, timezone) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `datetime` | Date/number/string | Yes | The date/time value to convert | +| `timezone` | string | Yes | Timezone offset like `"+02:00"`, `"-05:00"`, or `"+0530"` | + +## Returns +**Type:** Date + +A Date object adjusted to show local time in the given timezone. Returns `null` if input is invalid. Returns original date if timezone format is invalid. + +## Examples + +### Example 1: Convert UTC to Eastern Time +```cypher +WITH datetime('2024-03-15T14:00:00Z') AS utc +RETURN flex.date.toTimeZone(utc, '-05:00') AS eastern +``` + +**Output:** +``` +eastern +-------------------------- +2024-03-15T09:00:00.000Z +``` +(UTC fields now show 09:00 which is the local time in -05:00) + +### Example 2: Convert to Multiple Time Zones +```cypher +WITH datetime('2024-03-15T12:00:00Z') AS utc +RETURN + flex.date.format(utc, 'HH:mm') AS utcTime, + flex.date.format(flex.date.toTimeZone(utc, '+00:00'), 'HH:mm') AS london, + flex.date.format(flex.date.toTimeZone(utc, '+01:00'), 'HH:mm') AS paris, + flex.date.format(flex.date.toTimeZone(utc, '+05:30'), 'HH:mm') AS india, + flex.date.format(flex.date.toTimeZone(utc, '-05:00'), 'HH:mm') AS newYork +``` + +### Example 3: Display User Events in Local Time +```cypher +MATCH (u:User {id: $userId}) +MATCH (e:Event)-[:ASSIGNED_TO]->(u) +WITH e, flex.date.toTimeZone(e.timestamp, u.timezone) AS localTime +RETURN e.name, flex.date.format(localTime, 'YYYY-MM-DD HH:mm') AS localDisplay +ORDER BY e.timestamp +``` + +### Example 4: Adjust for Daylight Saving Time Context +```cypher +WITH datetime('2024-07-15T12:00:00Z') AS summer +RETURN flex.date.format( + flex.date.toTimeZone(summer, '-04:00'), + 'YYYY-MM-DD HH:mm' +) AS edtTime +``` + +## Notes +- Returns `null` for invalid date inputs +- Invalid timezone format returns the original date unchanged +- Timezone format accepts `+HH:MM`, `-HH:MM`, `+HHMM`, or `-HHMM` +- Does not handle DST transitions automatically +- The returned Date's UTC methods will show the local time +- Useful for displaying times in user's local timezone + +## See Also +- [date.format](./format.md) - Format date with timezone support +- [date.parse](./parse.md) - Parse date with timezone context +- [date.truncate](./truncate.md) - Truncate date to specific unit diff --git a/udfs/flex/date/truncate.md b/udfs/flex/date/truncate.md new file mode 100644 index 0000000..c2c2bf0 --- /dev/null +++ b/udfs/flex/date/truncate.md @@ -0,0 +1,96 @@ +# date.truncate + +## Description +Truncates a date/time value to the specified unit (e.g., day, month, year), setting all smaller units to their minimum values. All operations are UTC-based. + +## Syntax +```cypher +flex.date.truncate(datetime, unit) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `datetime` | Date/number/string | Yes | The date/time value to truncate | +| `unit` | string | Yes | The unit to truncate to | + +### Supported Units +| Unit | Description | Truncates To | +|------|-------------|--------------| +| `'minute'` | Truncate to start of minute | Sets seconds and milliseconds to 0 | +| `'hour'` | Truncate to start of hour | Sets minutes, seconds, and milliseconds to 0 | +| `'day'` | Truncate to start of day | Sets time to 00:00:00.000 | +| `'week'` | Truncate to start of week | Sets to Monday 00:00:00.000 | +| `'month'` | Truncate to start of month | Sets to 1st day at 00:00:00.000 | +| `'quarter'` | Truncate to start of quarter | Sets to 1st day of quarter month at 00:00:00.000 | +| `'year'` | Truncate to start of year | Sets to January 1st at 00:00:00.000 | + +## Returns +**Type:** Date + +A new Date object truncated to the specified unit. Returns `null` if input is invalid. Returns the original date if unit is unrecognized. + +## Examples + +### Example 1: Truncate to Day +```cypher +WITH datetime('2024-03-15T14:30:45Z') AS dt +RETURN flex.date.truncate(dt, 'day') AS truncated +``` + +**Output:** +``` +truncated +-------------------------- +2024-03-15T00:00:00.000Z +``` + +### Example 2: Truncate to Month +```cypher +WITH datetime('2024-03-15T14:30:45Z') AS dt +RETURN flex.date.truncate(dt, 'month') AS truncated +``` + +**Output:** +``` +truncated +-------------------------- +2024-03-01T00:00:00.000Z +``` + +### Example 3: Group Events by Week +```cypher +MATCH (e:Event) +WITH flex.date.truncate(e.timestamp, 'week') AS week, count(*) AS eventCount +RETURN week, eventCount +ORDER BY week +``` + +### Example 4: Monthly Aggregation +```cypher +MATCH (s:Sale) +WITH flex.date.truncate(s.date, 'month') AS month, sum(s.amount) AS totalSales +RETURN month, totalSales +ORDER BY month +``` + +### Example 5: Quarter Analysis +```cypher +MATCH (o:Order) +WITH flex.date.truncate(o.orderDate, 'quarter') AS quarter, count(*) AS orders +RETURN quarter, orders +ORDER BY quarter DESC +``` + +## Notes +- Returns `null` for invalid date inputs +- All operations use UTC timezone +- Week starts on Monday (ISO week convention) +- Quarters: Q1 (Jan-Mar), Q2 (Apr-Jun), Q3 (Jul-Sep), Q4 (Oct-Dec) +- Unknown units return the original normalized date +- Useful for time-series aggregation and bucketing + +## See Also +- [date.format](./format.md) - Format date to string +- [date.parse](./parse.md) - Parse string to date +- [date.toTimeZone](./toTimeZone.md) - Convert date to timezone diff --git a/udfs/flex/index.md b/udfs/flex/index.md new file mode 100644 index 0000000..8a51ba7 --- /dev/null +++ b/udfs/flex/index.md @@ -0,0 +1,130 @@ +# FLEX Function Reference + +FLEX is FalkorDB's open source community UDF package, available at [github.com/FalkorDB/flex](https://github.com/FalkorDB/flex). +It contains a variety of useful functionality, including: + +- String and set similarity metrics for fuzzy matching and comparison +- Date and time manipulation, formatting, and parsing +- Low-level bitwise operations on integers + +We welcome contributions to extend this library with additional functionality. + +The following sections document all FLEX (FalkorDB Library for Extensions) functions. + +## Function Categories + +### Similarity Functions (`flex.sim.*`) + +Set similarity metrics for fuzzy matching and comparison. + +| Function | Description | +|----------|-------------| +| [sim.jaccard](./similarity/jaccard.md) | Calculate Jaccard similarity coefficient between sets | + +### Text Functions (`flex.text.*`) + +String manipulation, formatting, case conversion utilities, and string similarity metrics. + +| Function | Description | +|----------|-------------| +| [text.capitalize](./text/capitalize.md) | Capitalize the first character of a string | +| [text.decapitalize](./text/decapitalize.md) | Lowercase the first character of a string | +| [text.swapCase](./text/swapCase.md) | Swap the case of all characters in a string | +| [text.camelCase](./text/camelCase.md) | Convert string to camelCase format | +| [text.upperCamelCase](./text/upperCamelCase.md) | Convert string to UpperCamelCase (PascalCase) | +| [text.snakeCase](./text/snakeCase.md) | Convert string to snake_case format | +| [text.format](./text/format.md) | Format string with placeholder substitution | +| [text.indexOf](./text/indexOf.md) | Find first occurrence of substring | +| [text.indexesOf](./text/indexesOf.md) | Find all occurrences of substring | +| [text.join](./text/join.md) | Join array elements with delimiter | +| [text.lpad](./text/lpad.md) | Pad string on the left to target length | +| [text.rpad](./text/rpad.md) | Pad string on the right to target length | +| [text.regexGroups](./text/regexGroups.md) | Extract regex matches and capture groups | +| [text.repeat](./text/repeat.md) | Repeat string multiple times | +| [text.replace](./text/replace.md) | Replace text using regex pattern | +| [text.jaroWinkler](./text/jaroWinkler.md) | Compute Jaro-Winkler similarity for short strings | +| [text.levenshtein](./text/levenshtein.md) | Compute Levenshtein edit distance between strings | + +### Collection Functions (`flex.coll.*`) + +Operations on lists and arrays including set operations and transformations. + +| Function | Description | +|----------|-------------| +| [coll.zip](./collections/zip.md) | Combine two lists element-by-element into pairs | +| [coll.union](./collections/union.md) | Combine lists and return unique elements | +| [coll.intersection](./collections/intersection.md) | Find common elements between lists | +| [coll.shuffle](./collections/shuffle.md) | Randomly shuffle list elements | +| [coll.frequencies](./collections/frequencies.md) | Count frequency of each element in list | + +### Map Functions (`flex.map.*`) + +Map/object manipulation for property management and transformation. + +| Function | Description | +|----------|-------------| +| [map.merge](./map/merge.md) | Shallow merge multiple maps | +| [map.fromPairs](./map/fromPairs.md) | Convert list of key-value pairs to map | +| [map.submap](./map/submap.md) | Extract subset of keys from map | +| [map.removeKey](./map/removeKey.md) | Remove single key from map | +| [map.removeKeys](./map/removeKeys.md) | Remove multiple keys from map | + +### JSON Functions (`flex.json.*`) + +JSON serialization and parsing utilities. + +| Function | Description | +|----------|-------------| +| [json.toJson](./json/toJson.md) | Serialize value to JSON string | +| [json.fromJsonMap](./json/fromJsonMap.md) | Parse JSON string to map | +| [json.fromJsonList](./json/fromJsonList.md) | Parse JSON string to list | + +### Date Functions (`flex.date.*`) + +Date and time manipulation, formatting, and parsing. + +| Function | Description | +|----------|-------------| +| [date.format](./date/format.md) | Format date/time with pattern and timezone | +| [date.parse](./date/parse.md) | Parse date/time string with optional pattern | +| [date.truncate](./date/truncate.md) | Truncate date to specific unit (day, month, etc.) | +| [date.toTimeZone](./date/toTimeZone.md) | Convert date to timezone offset | + +### Bitwise Functions (`flex.bitwise.*`) + +Low-level bitwise operations on integers. + +| Function | Description | +|----------|-------------| +| [bitwise.and](./bitwise/and.md) | Bitwise AND operation | +| [bitwise.or](./bitwise/or.md) | Bitwise OR operation | +| [bitwise.xor](./bitwise/xor.md) | Bitwise XOR (exclusive OR) operation | +| [bitwise.not](./bitwise/not.md) | Bitwise NOT (one's complement) operation | +| [bitwise.shiftLeft](./bitwise/shiftLeft.md) | Left bit shift operation | +| [bitwise.shiftRight](./bitwise/shiftRight.md) | Right bit shift with sign extension | + +## Common Use Cases + +### Data Cleaning and Normalization +- `text.camelCase`, `text.snakeCase` - Normalize field names +- `text.replace` - Remove or sanitize unwanted characters +- `coll.union` - Deduplicate lists + +### Fuzzy Matching and Search +- `text.levenshtein` - Find similar strings with edit distance +- `text.jaroWinkler` - Match names and short strings +- `sim.jaccard` - Compare sets and tag similarity + +### Data Aggregation and Analysis +- `date.truncate` - Group by time periods +- `coll.frequencies` - Count occurrences +- `map.submap` - Select relevant fields + +### API and Data Exchange +- `json.toJson`, `json.fromJsonMap` - JSON serialization +- `map.removeKeys` - Filter sensitive data +- `text.format` - Build formatted messages + +### Permission and Flag Management +- `bitwise.and`, `bitwise.or` - Check and set permission flags +- `bitwise.xor` - Toggle flags diff --git a/udfs/flex/json/fromJsonList.md b/udfs/flex/json/fromJsonList.md new file mode 100644 index 0000000..66b394a --- /dev/null +++ b/udfs/flex/json/fromJsonList.md @@ -0,0 +1,75 @@ +# json.fromJsonList + +## Description +Parses a JSON string and returns it as a list (array). Safely handles malformed JSON by returning an empty list on parse errors. + +## Syntax +```cypher +flex.json.fromJsonList(jsonString) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `jsonString` | string | Yes | A JSON string representing an array | + +## Returns +**Type:** list + +A list parsed from the JSON string. Returns an empty list `[]` if parsing fails or if the input is not a valid JSON array. + +## Examples + +### Example 1: Basic JSON Array Parsing +```cypher +WITH '[1, 2, 3, 4, 5]' AS json +RETURN flex.json.fromJsonList(json) AS numbers +``` + +**Output:** +``` +numbers +----------- +[1, 2, 3, 4, 5] +``` + +### Example 2: Parsing Complex Arrays +```cypher +WITH '[{"id":1,"name":"Alice"},{"id":2,"name":"Bob"}]' AS json +WITH flex.json.fromJsonList(json) AS users +UNWIND users AS user +RETURN user.id, user.name +``` + +### Example 3: Processing Stored List Data +```cypher +MATCH (p:Product) +WHERE p.tagsJson IS NOT NULL +WITH p, flex.json.fromJsonList(p.tagsJson) AS tags +UNWIND tags AS tag +RETURN p.name, tag +``` + +### Example 4: Handling Malformed JSON +```cypher +WITH '[invalid, json]' AS badJson +RETURN flex.json.fromJsonList(badJson) AS result +``` + +**Output:** +``` +result +------ +[] +``` +(Returns empty list for invalid JSON) + +## Notes +- Returns empty list `[]` if input is not valid JSON +- Returns empty list if the JSON represents a non-array value (e.g., object, string) +- Safe to use without error handling as it won't throw exceptions +- Useful for parsing list data, batch imports, or stored JSON arrays + +## See Also +- [json.fromJsonMap](./fromJsonMap.md) - Parse JSON string to map +- [json.toJson](./toJson.md) - Serialize value to JSON string diff --git a/udfs/flex/json/fromJsonMap.md b/udfs/flex/json/fromJsonMap.md new file mode 100644 index 0000000..072407d --- /dev/null +++ b/udfs/flex/json/fromJsonMap.md @@ -0,0 +1,73 @@ +# json.fromJsonMap + +## Description +Parses a JSON string and returns it as a map (object). Safely handles malformed JSON by returning an empty map on parse errors. + +## Syntax +```cypher +flex.json.fromJsonMap(jsonString) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `jsonString` | string | Yes | A JSON string representing an object | + +## Returns +**Type:** map (object) + +A map parsed from the JSON string. Returns an empty map `{}` if parsing fails or if the input is not a valid JSON object. + +## Examples + +### Example 1: Basic JSON Parsing +```cypher +WITH '{"name":"Alice","age":30,"active":true}' AS json +RETURN flex.json.fromJsonMap(json) AS user +``` + +**Output:** +``` +user +------------------------------- +{name: 'Alice', age: 30, active: true} +``` + +### Example 2: Parsing Stored JSON Properties +```cypher +MATCH (n:Node) +WHERE n.jsonData IS NOT NULL +WITH n, flex.json.fromJsonMap(n.jsonData) AS parsed +RETURN n.id, parsed.field1, parsed.field2 +``` + +### Example 3: Processing API Responses +```cypher +WITH '{"id":123,"email":"user@example.com","role":"admin"}' AS apiResponse +WITH flex.json.fromJsonMap(apiResponse) AS data +CREATE (u:User {id: data.id, email: data.email, role: data.role}) +``` + +### Example 4: Handling Malformed JSON +```cypher +WITH '{invalid json}' AS badJson +RETURN flex.json.fromJsonMap(badJson) AS result +``` + +**Output:** +``` +result +------ +{} +``` +(Returns empty map for invalid JSON) + +## Notes +- Returns empty map `{}` if input is not valid JSON +- Returns empty map if the JSON represents a non-object value (e.g., array, string) +- Safe to use without error handling as it won't throw exceptions +- Useful for parsing configuration, API responses, or stored JSON data + +## See Also +- [json.fromJsonList](./fromJsonList.md) - Parse JSON string to list +- [json.toJson](./toJson.md) - Serialize value to JSON string diff --git a/udfs/flex/json/toJson.md b/udfs/flex/json/toJson.md new file mode 100644 index 0000000..e20d6b2 --- /dev/null +++ b/udfs/flex/json/toJson.md @@ -0,0 +1,72 @@ +# json.toJson + +## Description +Serializes a value to a JSON string. Handles various data types including objects, arrays, strings, numbers, booleans, and `null`. + +## Syntax +```cypher +flex.json.toJson(value) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `value` | any | Yes | The value to serialize to JSON | + +## Returns +**Type:** string + +A JSON string representation of the value. Returns `null` if serialization fails or if the input is `undefined`. + +## Examples + +### Example 1: Serialize a Map +```cypher +WITH {name: 'Alice', age: 30, active: true} AS user +RETURN flex.json.toJson(user) AS json +``` + +**Output:** +``` +json +--------------------------------------- +'{"name":"Alice","age":30,"active":true}' +``` + +### Example 2: Serialize a List +```cypher +WITH [1, 2, 3, 4, 5] AS numbers +RETURN flex.json.toJson(numbers) AS json +``` + +**Output:** +``` +json +----------- +'[1,2,3,4,5]' +``` + +### Example 3: Preparing Data for Export +```cypher +MATCH (p:Product) +WITH collect({id: p.id, name: p.name, price: p.price}) AS products +RETURN flex.json.toJson(products) AS jsonExport +``` + +### Example 4: Storing JSON in Properties +```cypher +MATCH (u:User {id: 123}) +WITH u, {lastLogin: u.lastLogin, preferences: u.preferences} AS metadata +SET u.metadataJson = flex.json.toJson(metadata) +``` + +## Notes +- Returns `null` if serialization fails (e.g., circular references) +- `undefined` values are normalized to `null` +- Dates are serialized in ISO format +- Functions and symbols cannot be serialized and will cause `null` return +- Useful for API responses, data export, or storing complex structures + +## See Also +- [json.fromJsonMap](./fromJsonMap.md) - Parse JSON string to map +- [json.fromJsonList](./fromJsonList.md) - Parse JSON string to list diff --git a/udfs/flex/map/fromPairs.md b/udfs/flex/map/fromPairs.md new file mode 100644 index 0000000..59d0734 --- /dev/null +++ b/udfs/flex/map/fromPairs.md @@ -0,0 +1,75 @@ +# map.fromPairs + +## Description +Converts a list of key-value pairs into a map. Each pair should be a two-element array `[key, value]`. + +## Syntax +```cypher +flex.map.fromPairs(pairs) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `pairs` | list | Yes | A list of two-element arrays, each containing `[key, value]` | + +## Returns +**Type:** map (object) + +A map where each key-value pair from the input list becomes a property. Returns an empty map if input is not an array. + +## Examples + +### Example 1: Basic Conversion +```cypher +WITH [['name', 'Alice'], ['age', 30], ['city', 'NYC']] AS pairs +RETURN flex.map.fromPairs(pairs) AS result +``` + +**Output:** +``` +result +------------------------------------ +{name: 'Alice', age: 30, city: 'NYC'} +``` + +### Example 2: Converting Zipped Data +```cypher +WITH ['name', 'age', 'email'] AS keys, + ['Bob', 25, 'bob@example.com'] AS values +WITH flex.coll.zip(keys, values) AS pairs +RETURN flex.map.fromPairs(pairs) AS user +``` + +**Output:** +``` +user +------------------------------------------ +{name: 'Bob', age: 25, email: 'bob@example.com'} +``` + +### Example 3: Dynamic Property Creation +```cypher +MATCH (p:Product) +WITH collect([p.id, p.price]) AS pricePairs +RETURN flex.map.fromPairs(pricePairs) AS priceMap +``` + +### Example 4: Converting Query Results to Lookup Map +```cypher +MATCH (c:Country) +WITH collect([c.code, c.name]) AS countryPairs +WITH flex.map.fromPairs(countryPairs) AS lookup +RETURN lookup['US'] AS usaName, lookup['UK'] AS ukName +``` + +## Notes +- Returns empty map if input is not an array or is `null` +- Each pair must be a two-element array; invalid pairs are skipped +- If a key is `null` or `undefined`, the pair is ignored +- Duplicate keys result in the last value being used +- Keys are converted to strings as map property names + +## See Also +- [coll.zip](../collections/zip.md) - Create pairs from two lists +- [map.submap](./submap.md) - Extract subset of keys from a map diff --git a/udfs/flex/map/merge.md b/udfs/flex/map/merge.md new file mode 100644 index 0000000..ac99596 --- /dev/null +++ b/udfs/flex/map/merge.md @@ -0,0 +1,75 @@ +# map.merge + +## Description +Performs a shallow merge of multiple maps into a new map. When keys conflict, values from later maps override earlier ones. Non-object inputs are ignored. + +## Syntax +```cypher +flex.map.merge(map1, map2, ...) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `map1` | map | No | First map to merge | +| `map2` | map | No | Second map to merge | +| `...` | map | No | Additional maps to merge | + +## Returns +**Type:** map (object) + +A new map containing all keys and values from the input maps. Later maps override earlier ones for duplicate keys. + +## Examples + +### Example 1: Basic Merge +```cypher +WITH {a: 1, b: 2} AS map1, {b: 3, c: 4} AS map2 +RETURN flex.map.merge(map1, map2) AS result +``` + +**Output:** +``` +result +------------------ +{a: 1, b: 3, c: 4} +``` +(Note: `b` from map2 overrides `b` from map1) + +### Example 2: Merging Node Properties +```cypher +MATCH (u:User {id: 123}) +WITH {role: 'admin', status: 'active'} AS defaults +RETURN flex.map.merge(defaults, properties(u)) AS userWithDefaults +``` + +### Example 3: Combining Configuration +```cypher +WITH {host: 'localhost', port: 6379} AS defaults, + {port: 7000, password: 'secret'} AS config +RETURN flex.map.merge(defaults, config) AS finalConfig +``` + +**Output:** +``` +finalConfig +-------------------------------------------------- +{host: 'localhost', port: 7000, password: 'secret'} +``` + +### Example 4: Merging Multiple Maps +```cypher +WITH {a: 1} AS base, {b: 2} AS extra1, {c: 3} AS extra2 +RETURN flex.map.merge(base, extra1, extra2) AS combined +``` + +## Notes +- Non-object inputs are silently ignored +- Performs shallow merge (nested objects are not deeply merged) +- Later maps take precedence for duplicate keys +- Returns a new map; does not modify input maps +- Useful for applying defaults, combining configuration, or merging properties + +## See Also +- [map.submap](./submap.md) - Extract specific keys from a map +- [map.removeKeys](./removeKeys.md) - Remove keys from a map diff --git a/udfs/flex/map/removeKey.md b/udfs/flex/map/removeKey.md new file mode 100644 index 0000000..aea1a6b --- /dev/null +++ b/udfs/flex/map/removeKey.md @@ -0,0 +1,76 @@ +# map.removeKey + +## Description +Creates a new map with a single specified key removed. The original map is not modified. + +## Syntax +```cypher +flex.map.removeKey(map, key) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `map` | map | Yes | The map to remove the key from | +| `key` | string | Yes | The key to remove | + +## Returns +**Type:** map (object) + +A new map containing all properties from the input map except the specified key. Returns an empty map if input is not a valid object. + +## Examples + +### Example 1: Basic Key Removal +```cypher +WITH {name: 'Alice', age: 30, email: 'alice@example.com'} AS user +RETURN flex.map.removeKey(user, 'email') AS sanitized +``` + +**Output:** +``` +sanitized +----------------------- +{name: 'Alice', age: 30} +``` + +### Example 2: Removing Sensitive Data +```cypher +MATCH (u:User) +WITH properties(u) AS userProps +RETURN u.id, flex.map.removeKey(userProps, 'password') AS safeProps +``` + +### Example 3: Cleaning Response Data +```cypher +MATCH (p:Product {id: 123}) +WITH properties(p) AS props +WITH flex.map.removeKey(props, 'internalId') AS cleaned +RETURN cleaned AS product +``` + +### Example 4: Removing Non-Existent Key +```cypher +WITH {a: 1, b: 2} AS map +RETURN flex.map.removeKey(map, 'c') AS result +``` + +**Output:** +``` +result +--------- +{a: 1, b: 2} +``` +(Key doesn't exist, so map is returned unchanged) + +## Notes +- Returns empty map if input is not a valid object or is `null` +- If key is `null`, returns a shallow copy of the map +- If the key doesn't exist, returns a copy with all original properties +- Creates a new map; does not modify the original +- Useful for sanitizing data, removing sensitive fields, or filtering properties + +## See Also +- [map.removeKeys](./removeKeys.md) - Remove multiple keys at once +- [map.submap](./submap.md) - Keep only specific keys (inverse operation) +- [map.merge](./merge.md) - Combine multiple maps diff --git a/udfs/flex/map/removeKeys.md b/udfs/flex/map/removeKeys.md new file mode 100644 index 0000000..f35ade3 --- /dev/null +++ b/udfs/flex/map/removeKeys.md @@ -0,0 +1,77 @@ +# map.removeKeys + +## Description +Creates a new map with multiple specified keys removed. The original map is not modified. + +## Syntax +```cypher +flex.map.removeKeys(map, keys) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `map` | map | Yes | The map to remove keys from | +| `keys` | list | Yes | An array of key names to remove | + +## Returns +**Type:** map (object) + +A new map containing all properties from the input map except the specified keys. Returns an empty map if input is not a valid object. + +## Examples + +### Example 1: Basic Multiple Key Removal +```cypher +WITH {name: 'Alice', age: 30, email: 'alice@example.com', password: 'secret'} AS user +RETURN flex.map.removeKeys(user, ['password', 'email']) AS sanitized +``` + +**Output:** +``` +sanitized +----------------------- +{name: 'Alice', age: 30} +``` + +### Example 2: Removing Internal Fields +```cypher +MATCH (p:Product) +WITH properties(p) AS props +RETURN flex.map.removeKeys(props, ['internalId', 'createdBy', 'updatedAt']) AS public +``` + +### Example 3: Filtering Node Properties for API Response +```cypher +MATCH (u:User {id: $userId}) +WITH properties(u) AS allProps +WITH flex.map.removeKeys(allProps, ['password', 'salt', 'resetToken']) AS safeProps +RETURN safeProps AS user +``` + +### Example 4: Removing Non-Existent Keys +```cypher +WITH {a: 1, b: 2, c: 3} AS map +RETURN flex.map.removeKeys(map, ['d', 'e']) AS result +``` + +**Output:** +``` +result +----------------- +{a: 1, b: 2, c: 3} +``` +(Non-existent keys are ignored) + +## Notes +- Returns empty map if input is not a valid object or is `null` +- `null` values in the keys array are ignored +- Keys that don't exist in the map are silently ignored +- Creates a new map; does not modify the original +- More efficient than calling `removeKey` multiple times +- Useful for bulk removal of sensitive or internal fields + +## See Also +- [map.removeKey](./removeKey.md) - Remove a single key +- [map.submap](./submap.md) - Keep only specific keys (inverse operation) +- [map.merge](./merge.md) - Combine multiple maps diff --git a/udfs/flex/map/submap.md b/udfs/flex/map/submap.md new file mode 100644 index 0000000..1983f81 --- /dev/null +++ b/udfs/flex/map/submap.md @@ -0,0 +1,82 @@ +# map.submap + +## Description +Creates a new map containing only the specified keys from the input map. This is the inverse of `removeKeys`. + +## Syntax +```cypher +flex.map.submap(map, keys) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `map` | map | Yes | The source map to extract keys from | +| `keys` | list | Yes | An array of key names to include in the result | + +## Returns +**Type:** map (object) + +A new map containing only the specified keys and their values from the input map. Returns an empty map if input is not a valid object or keys is not an array. + +## Examples + +### Example 1: Basic Submap Extraction +```cypher +WITH {name: 'Alice', age: 30, email: 'alice@example.com', city: 'NYC'} AS user +RETURN flex.map.submap(user, ['name', 'email']) AS contact +``` + +**Output:** +``` +contact +--------------------------------- +{name: 'Alice', email: 'alice@example.com'} +``` + +### Example 2: Selecting Specific Node Properties +```cypher +MATCH (p:Product) +RETURN flex.map.submap(properties(p), ['id', 'name', 'price']) AS summary +``` + +### Example 3: Building API Response with Selected Fields +```cypher +MATCH (u:User {id: $userId}) +WITH properties(u) AS allProps +RETURN flex.map.submap(allProps, ['id', 'name', 'email', 'role']) AS userInfo +``` + +### Example 4: Handling Non-Existent Keys +```cypher +WITH {a: 1, b: 2} AS map +RETURN flex.map.submap(map, ['a', 'c', 'd']) AS result +``` + +**Output:** +``` +result +------ +{a: 1} +``` +(Only existing keys are included) + +### Example 5: Dynamic Field Selection +```cypher +WITH ['name', 'price', 'category'] AS requestedFields +MATCH (p:Product {id: 123}) +RETURN flex.map.submap(properties(p), requestedFields) AS response +``` + +## Notes +- Returns empty map if input is not a valid object or keys is not an array +- `null` values in the keys array are ignored +- Non-existent keys are silently skipped +- Creates a new map; does not modify the original +- Useful for selecting specific fields, building API responses, or data projection +- More efficient than manually picking each field + +## See Also +- [map.removeKeys](./removeKeys.md) - Remove specific keys (inverse operation) +- [map.removeKey](./removeKey.md) - Remove a single key +- [map.merge](./merge.md) - Combine multiple maps diff --git a/udfs/flex/similarity/jaccard.md b/udfs/flex/similarity/jaccard.md new file mode 100644 index 0000000..a2eeaa2 --- /dev/null +++ b/udfs/flex/similarity/jaccard.md @@ -0,0 +1,76 @@ +# sim.jaccard + +## Description +Computes the Jaccard similarity coefficient between two sets (lists). The Jaccard index measures the similarity between two sets by dividing the size of their intersection by the size of their union. It returns a value between 0 (no similarity) and 1 (identical sets). + +## Syntax +```cypher +flex.sim.jaccard(list1, list2) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `list1` | list | Yes | The first list to compare | +| `list2` | list | Yes | The second list to compare | + +## Returns +**Type:** number (float) + +A value between 0 and 1 representing the Jaccard similarity coefficient: +- `1.0` indicates identical sets +- `0.0` indicates no common elements +- Returns `null` for invalid inputs + +## Examples + +### Example 1: Basic Set Similarity +```cypher +// Compare two tag lists +RETURN flex.sim.jaccard(['tag1', 'tag2', 'tag3'], ['tag2', 'tag3', 'tag4']) AS similarity +``` + +**Output:** +``` +similarity +---------- +0.5 +``` +(2 common elements / 4 total unique elements = 0.5) + +### Example 2: Finding Similar Documents by Tags +```cypher +// Find documents with similar tags to a reference document +MATCH (ref:Document {id: 'doc123'}) +MATCH (other:Document) +WHERE other.id <> ref.id +WITH ref, other, flex.sim.jaccard(ref.tags, other.tags) AS similarity +WHERE similarity > 0.3 +RETURN other.title, similarity +ORDER BY similarity DESC +LIMIT 10 +``` + +### Example 3: User Interest Matching +```cypher +// Find users with similar interests +MATCH (u1:User {id: $userId}) +MATCH (u2:User) +WHERE u1 <> u2 +WITH u1, u2, flex.sim.jaccard(u1.interests, u2.interests) AS match_score +WHERE match_score > 0.5 +RETURN u2.name, u2.interests, match_score +ORDER BY match_score DESC +``` + +## Notes +- Treats input lists as sets (duplicates within each list don't affect the result) +- Returns `null` if either input is not an array +- Order of elements doesn't matter +- Works with any comparable data types (strings, numbers, etc.) +- Ideal for comparing categorical attributes, tags, or interest lists + +## See Also +- [text.levenshtein](../text/levenshtein.md) - Edit distance for string comparison +- [coll.intersection](../collections/intersection.md) - Get common elements between sets +- [coll.union](../collections/union.md) - Combine sets diff --git a/udfs/flex/text/camelCase.md b/udfs/flex/text/camelCase.md new file mode 100644 index 0000000..3cf87ea --- /dev/null +++ b/udfs/flex/text/camelCase.md @@ -0,0 +1,64 @@ +# text.camelCase + +## Description +Converts a string to camelCase format by removing non-alphanumeric characters and capitalizing the first letter of each word except the first. + +## Syntax +```cypher +flex.text.camelCase(string) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string | Yes | The string to convert to camelCase | + +## Returns +**Type:** string + +The input string converted to camelCase format. Returns `null` if input is `null`. + +## Examples + +### Example 1: Basic Usage +```cypher +RETURN flex.text.camelCase('hello world') AS result +``` + +**Output:** +``` +result +---------- +helloWorld +``` + +### Example 2: Converting Field Names +```cypher +RETURN flex.text.camelCase('user_first_name') AS result +``` + +**Output:** +``` +result +------------- +userFirstName +``` + +### Example 3: Normalizing Property Names +```cypher +WITH ['first-name', 'last_name', 'Email Address'] AS fields +UNWIND fields AS field +RETURN field AS original, flex.text.camelCase(field) AS camelCase +``` + +## Notes +- Returns `null` for `null` input +- Removes all non-alphanumeric characters +- First character is always lowercase +- Subsequent words start with uppercase +- Useful for normalizing field names to JavaScript/JSON conventions + +## See Also +- [text.upperCamelCase](./upperCamelCase.md) - Convert to UpperCamelCase (PascalCase) +- [text.snakeCase](./snakeCase.md) - Convert to snake_case format +- [text.capitalize](./capitalize.md) - Capitalize first character only diff --git a/udfs/flex/text/capitalize.md b/udfs/flex/text/capitalize.md new file mode 100644 index 0000000..3501906 --- /dev/null +++ b/udfs/flex/text/capitalize.md @@ -0,0 +1,50 @@ +# text.capitalize + +## Description +Capitalizes the first character of a string, converting it to uppercase while leaving the rest of the string unchanged. + +## Syntax +```cypher +flex.text.capitalize(string) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string | Yes | The string to capitalize | + +## Returns +**Type:** string + +The input string with its first character converted to uppercase. Returns `null` if the input is `null`, and empty string if input is empty. + +## Examples + +### Example 1: Basic Usage +```cypher +RETURN flex.text.capitalize('hello world') AS result +``` + +**Output:** +``` +result +------------- +Hello world +``` + +### Example 2: Capitalizing Node Properties +```cypher +MATCH (p:Person) +RETURN p.id, flex.text.capitalize(p.name) AS capitalizedName +``` + +## Notes +- Returns `null` for `null` input +- Returns empty string for empty string input +- Only affects the first character +- Does not change the case of subsequent characters + +## See Also +- [text.decapitalize](./decapitalize.md) - Lowercase the first character +- [text.camelCase](./camelCase.md) - Convert to camelCase format +- [text.upperCamelCase](./upperCamelCase.md) - Convert to UpperCamelCase format diff --git a/udfs/flex/text/decapitalize.md b/udfs/flex/text/decapitalize.md new file mode 100644 index 0000000..d8becde --- /dev/null +++ b/udfs/flex/text/decapitalize.md @@ -0,0 +1,50 @@ +# text.decapitalize + +## Description +Converts the first character of a string to lowercase while leaving the rest of the string unchanged. + +## Syntax +```cypher +flex.text.decapitalize(string) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string | Yes | The string to decapitalize | + +## Returns +**Type:** string + +The input string with its first character converted to lowercase. Returns `null` if the input is `null`, and empty string if input is empty. + +## Examples + +### Example 1: Basic Usage +```cypher +RETURN flex.text.decapitalize('Hello World') AS result +``` + +**Output:** +``` +result +------------- +hello World +``` + +### Example 2: Processing Field Names +```cypher +WITH ['FirstName', 'LastName', 'Email'] AS fields +UNWIND fields AS field +RETURN flex.text.decapitalize(field) AS jsonKey +``` + +## Notes +- Returns `null` for `null` input +- Returns empty string for empty string input +- Only affects the first character +- Does not change the case of subsequent characters + +## See Also +- [text.capitalize](./capitalize.md) - Uppercase the first character +- [text.camelCase](./camelCase.md) - Convert to camelCase format diff --git a/udfs/flex/text/format.md b/udfs/flex/text/format.md new file mode 100644 index 0000000..9cdeb4d --- /dev/null +++ b/udfs/flex/text/format.md @@ -0,0 +1,65 @@ +# text.format + +## Description +Formats a string by replacing numbered placeholders `{0}`, `{1}`, `{2}`, etc. with corresponding values from a parameters array. Similar to sprintf-style formatting. + +## Syntax +```cypher +flex.text.format(template, parameters) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `template` | string | Yes | The format string containing `{0}`, `{1}`, etc. placeholders | +| `parameters` | list | Yes | Array of values to substitute into the template | + +## Returns +**Type:** string + +The formatted string with placeholders replaced by parameter values. Returns `null` if template is `null`. + +## Examples + +### Example 1: Basic String Formatting +```cypher +RETURN flex.text.format('Hello {0}, you are {1} years old!', ['Alice', 30]) AS result +``` + +**Output:** +``` +result +-------------------------------- +Hello Alice, you are 30 years old! +``` + +### Example 2: Dynamic Query Messages +```cypher +MATCH (u:User {id: 123}) +WITH u, flex.text.format('User {0} ({1}) logged in at {2}', [u.name, u.email, u.lastLogin]) AS message +RETURN message +``` + +### Example 3: Building URLs or Paths +```cypher +WITH ['users', 'profile', '12345'] AS parts +RETURN flex.text.format('/{0}/{1}/{2}', parts) AS path +``` + +**Output:** +``` +path +------------------------ +/users/profile/12345 +``` + +## Notes +- Returns `null` if template is `null` +- Placeholders are zero-indexed: `{0}`, `{1}`, `{2}`, etc. +- Same placeholder can be used multiple times in template +- Parameters are replaced in order of array index +- Useful for building dynamic messages, logs, or formatted output + +## See Also +- [text.replace](./replace.md) - Replace text using regex patterns +- [text.join](./join.md) - Join array elements with delimiter diff --git a/udfs/flex/text/indexOf.md b/udfs/flex/text/indexOf.md new file mode 100644 index 0000000..9337435 --- /dev/null +++ b/udfs/flex/text/indexOf.md @@ -0,0 +1,67 @@ +# text.indexOf + +## Description +Finds the first occurrence of a substring within a string, optionally starting from a specific offset and ending at a specific position. + +## Syntax +```cypher +flex.text.indexOf(string, substring, offset, to) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string | Yes | The string to search in | +| `substring` | string | Yes | The substring to search for | +| `offset` | number | No | Starting position for search (default: 0) | +| `to` | number | No | Ending position for search (default: -1, meaning end of string) | + +## Returns +**Type:** number (integer) + +The zero-based index of the first occurrence of the substring, or `-1` if not found. Returns `null` if the input string is `null`. + +## Examples + +### Example 1: Basic Search +```cypher +RETURN flex.text.indexOf('hello world', 'world') AS position +``` + +**Output:** +``` +position +-------- +6 +``` + +### Example 2: Search with Offset +```cypher +RETURN flex.text.indexOf('hello hello', 'hello', 3) AS position +``` + +**Output:** +``` +position +-------- +6 +``` +(Finds the second "hello" starting from position 3) + +### Example 3: Filtering Nodes by Substring Position +```cypher +MATCH (p:Product) +WHERE flex.text.indexOf(p.description, 'premium') >= 0 +RETURN p.name, p.description +``` + +## Notes +- Returns `null` if input string is `null` +- Returns `-1` if substring is not found +- Uses zero-based indexing +- The `offset` parameter allows starting search from a specific position +- The `to` parameter limits search to a specific range + +## See Also +- [text.indexesOf](./indexesOf.md) - Find all occurrences of a substring +- [text.replace](./replace.md) - Replace substring occurrences diff --git a/udfs/flex/text/indexesOf.md b/udfs/flex/text/indexesOf.md new file mode 100644 index 0000000..71d8da8 --- /dev/null +++ b/udfs/flex/text/indexesOf.md @@ -0,0 +1,71 @@ +# text.indexesOf + +## Description +Finds all occurrences of a substring within a string, returning an array of all matching positions. Optionally search within a specific range. + +## Syntax +```cypher +flex.text.indexesOf(string, substring, from, to) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string | Yes | The string to search in | +| `substring` | string | Yes | The substring to search for | +| `from` | number | No | Starting position for search (default: 0) | +| `to` | number | No | Ending position for search (default: -1, meaning end of string) | + +## Returns +**Type:** list of numbers + +An array containing the zero-based indices of all occurrences of the substring. Returns an empty array if no matches are found. Returns `null` if the input string is `null`. + +## Examples + +### Example 1: Find All Occurrences +```cypher +RETURN flex.text.indexesOf('hello hello hello', 'hello') AS positions +``` + +**Output:** +``` +positions +----------- +[0, 6, 12] +``` + +### Example 2: Find Occurrences in Range +```cypher +RETURN flex.text.indexesOf('abcabcabc', 'abc', 1, 9) AS positions +``` + +**Output:** +``` +positions +--------- +[3, 6] +``` +(Skips first 'abc' at position 0, finds those within range) + +### Example 3: Count Keyword Occurrences +```cypher +MATCH (d:Document) +WITH d, flex.text.indexesOf(d.content, 'important') AS occurrences +WHERE size(occurrences) > 2 +RETURN d.title, size(occurrences) AS importanceScore +ORDER BY importanceScore DESC +``` + +## Notes +- Returns `null` if input string is `null` +- Returns empty array if substring is not found +- Uses zero-based indexing +- The `from` parameter allows starting search from a specific position +- The `to` parameter limits search to a specific range +- Useful for counting occurrences or analyzing text patterns + +## See Also +- [text.indexOf](./indexOf.md) - Find first occurrence only +- [text.replace](./replace.md) - Replace substring occurrences +- [text.regexGroups](./regexGroups.md) - Find matches using regex patterns diff --git a/udfs/flex/text/jaroWinkler.md b/udfs/flex/text/jaroWinkler.md new file mode 100644 index 0000000..bc01687 --- /dev/null +++ b/udfs/flex/text/jaroWinkler.md @@ -0,0 +1,71 @@ +# text.jaroWinkler + +## Description +Computes the Jaro-Winkler similarity between two strings. This metric is particularly effective for short strings like names and addresses. It gives more favorable ratings to strings that match from the beginning. Returns a value between 0 (no similarity) and 1 (exact match). + +## Syntax +```cypher +flex.text.jaroWinkler(string1, string2) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string1` | string | Yes | The first string to compare | +| `string2` | string | Yes | The second string to compare | + +## Returns +**Type:** number (float) + +A similarity score between 0 and 1: +- `1.0` indicates an exact match +- `0.0` indicates no similarity +- Higher values indicate greater similarity + +## Examples + +### Example 1: Name Matching +```cypher +// Compare similar names +RETURN flex.text.jaroWinkler('Martha', 'Marhta') AS similarity +``` + +**Output:** +``` +similarity +---------- +0.961 +``` + +### Example 2: Fuzzy Name Search +```cypher +// Find people with names similar to "William" +MATCH (p:Person) +WHERE flex.text.jaroWinkler(p.firstName, 'William') > 0.85 +RETURN p.firstName, p.lastName, flex.text.jaroWinkler(p.firstName, 'William') AS score +ORDER BY score DESC +``` + +### Example 3: Deduplication by Company Name +```cypher +// Find potential duplicate company records +MATCH (c1:Company) +MATCH (c2:Company) +WHERE id(c1) < id(c2) +WITH c1, c2, flex.text.jaroWinkler(c1.name, c2.name) AS similarity +WHERE similarity > 0.9 +RETURN c1.name, c2.name, similarity +ORDER BY similarity DESC +``` + +## Notes +- Particularly effective for short strings (names, addresses) +- Gives higher weight to strings that match from the beginning +- Handles `null` values by returning appropriate default values +- Case-sensitive comparison +- More forgiving than exact match but stricter than pure Jaro similarity +- Commonly used in record linkage and deduplication tasks + +## See Also +- [text.levenshtein](./levenshtein.md) - Edit distance metric for string comparison +- [sim.jaccard](../similarity/jaccard.md) - Set-based similarity diff --git a/udfs/flex/text/join.md b/udfs/flex/text/join.md new file mode 100644 index 0000000..0e9bc72 --- /dev/null +++ b/udfs/flex/text/join.md @@ -0,0 +1,77 @@ +# text.join + +## Description +Joins an array of strings into a single string using a specified delimiter. + +## Syntax +```cypher +flex.text.join(array, delimiter) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `array` | list | Yes | The array of strings to join | +| `delimiter` | string | Yes | The separator to insert between elements | + +## Returns +**Type:** string + +A single string with all array elements concatenated, separated by the delimiter. Returns `null` if the array is `null` or undefined. + +## Examples + +### Example 1: Basic String Joining +```cypher +RETURN flex.text.join(['apple', 'banana', 'cherry'], ', ') AS result +``` + +**Output:** +``` +result +---------------------- +apple, banana, cherry +``` + +### Example 2: Building CSV Lines +```cypher +MATCH (u:User) +WITH [u.id, u.name, u.email] AS fields +RETURN flex.text.join(fields, ',') AS csvLine +``` + +### Example 3: Creating Tags String +```cypher +MATCH (p:Post) +RETURN p.title, flex.text.join(p.tags, ' #') AS hashtags +``` + +**Output:** +``` +title | hashtags +----------------|------------------ +My First Post | tech #coding #js +``` + +### Example 4: Building Paths +```cypher +WITH ['home', 'user', 'documents', 'file.txt'] AS parts +RETURN flex.text.join(parts, '/') AS path +``` + +**Output:** +``` +path +-------------------------- +home/user/documents/file.txt +``` + +## Notes +- Returns `null` if input array is `null` +- Empty strings in the array are included in the output +- Delimiter can be any string, including empty string +- Commonly used for CSV generation, path building, or tag formatting + +## See Also +- [text.format](./format.md) - Format strings with placeholders +- [coll.zip](../collections/zip.md) - Combine two lists diff --git a/udfs/flex/text/levenshtein.md b/udfs/flex/text/levenshtein.md new file mode 100644 index 0000000..ca9e21f --- /dev/null +++ b/udfs/flex/text/levenshtein.md @@ -0,0 +1,68 @@ +# text.levenshtein + +## Description +Computes the Levenshtein edit distance between two strings. The edit distance is the minimum number of single-character edits (insertions, deletions, or substitutions) required to change one string into another. This is useful for fuzzy string matching, spell checking, and finding similar records. + +## Syntax +```cypher +flex.text.levenshtein(string1, string2) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string1` | string | Yes | The first string to compare | +| `string2` | string | Yes | The second string to compare | + +## Returns +**Type:** number (integer) + +The minimum number of single-character edits needed to transform `string1` into `string2`. Returns `0` if the strings are identical. + +## Examples + +### Example 1: Basic String Comparison +```cypher +// Compare two similar strings +RETURN flex.text.levenshtein('kitten', 'sitting') AS distance +``` + +**Output:** +``` +distance +-------- +3 +``` + +### Example 2: Finding Similar User Names +```cypher +// Find users with names similar to "Sarah" within edit distance of 2 +MATCH (u:User) +WHERE flex.text.levenshtein(u.name, 'Sarah') <= 2 +RETURN u.name, u.email, flex.text.levenshtein(u.name, 'Sarah') AS distance +ORDER BY distance +``` + +### Example 3: Fuzzy Matching with Multiple Candidates +```cypher +// Find the closest matching product name +WITH 'iPhone' AS search_term +MATCH (p:Product) +WITH p, flex.text.levenshtein(p.name, search_term) AS distance +WHERE distance <= 3 +RETURN p.name, distance +ORDER BY distance +LIMIT 5 +``` + +## Notes +- Handles `null` values gracefully by treating them as empty strings +- The function is symmetric: `levenshtein(a, b) = levenshtein(b, a)` +- Empty strings return the length of the non-empty string as distance +- Two `null` values return distance of `0` +- Optimized for performance with memory-efficient implementation +- Case-sensitive comparison (use `toLower()` if case-insensitive matching is needed) + +## See Also +- [sim.jaccard](../similarity/jaccard.md) - Set-based similarity for collections +- [text.jaroWinkler](./jaroWinkler.md) - Alternative string similarity metric diff --git a/udfs/flex/text/lpad.md b/udfs/flex/text/lpad.md new file mode 100644 index 0000000..3c1d71e --- /dev/null +++ b/udfs/flex/text/lpad.md @@ -0,0 +1,77 @@ +# text.lpad + +## Description +Pads the start (left side) of a string with a specified character until it reaches the desired length. + +## Syntax +```cypher +flex.text.lpad(string, length, padChar) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string/number | Yes | The string to pad (will be converted to string if number) | +| `length` | number | Yes | The target length after padding | +| `padChar` | string | No | The character to use for padding (default: space ' ') | + +## Returns +**Type:** string + +The padded string. If the original string is already longer than the target length, it is returned unchanged. Returns `null` if input is `null`. + +## Examples + +### Example 1: Basic Left Padding +```cypher +RETURN flex.text.lpad('5', 3, '0') AS result +``` + +**Output:** +``` +result +------ +005 +``` + +### Example 2: Formatting Numbers with Leading Zeros +```cypher +MATCH (o:Order) +RETURN flex.text.lpad(toString(o.id), 8, '0') AS orderId +``` + +**Output:** +``` +orderId +-------- +00000123 +00000456 +``` + +### Example 3: Aligning Text +```cypher +WITH ['Total:', 'Subtotal:', 'Tax:'] AS labels +UNWIND labels AS label +RETURN flex.text.lpad(label, 12, ' ') AS aligned +``` + +**Output:** +``` +aligned +-------------- + Total: + Subtotal: + Tax: +``` + +## Notes +- Returns `null` if input is `null` +- Converts numbers to strings automatically +- Default padding character is a space +- If string is already longer than target length, returns original string +- Useful for formatting IDs, aligning columns, or creating fixed-width output + +## See Also +- [text.rpad](./rpad.md) - Pad the end (right side) of a string +- [text.repeat](./repeat.md) - Repeat a string multiple times +- [text.format](./format.md) - Format strings with placeholders diff --git a/udfs/flex/text/regexGroups.md b/udfs/flex/text/regexGroups.md new file mode 100644 index 0000000..8c3a5bf --- /dev/null +++ b/udfs/flex/text/regexGroups.md @@ -0,0 +1,70 @@ +# text.regexGroups + +## Description +Extracts all matches and capture groups from a string using a regular expression pattern. Returns a nested array where each match contains the full match and any captured groups. + +## Syntax +```cypher +flex.text.regexGroups(string, regex) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string | Yes | The string to search in | +| `regex` | string | Yes | The regular expression pattern (applied globally) | + +## Returns +**Type:** list of lists + +A nested array where each inner array represents one match and contains the full match followed by any capture groups. Returns `null` if input string is `null`. + +## Examples + +### Example 1: Extract Email Components +```cypher +WITH 'Contact: john@example.com or jane@test.org' AS text +RETURN flex.text.regexGroups(text, '(\\w+)@(\\w+\\.\\w+)') AS matches +``` + +**Output:** +``` +matches +---------------------------------------------------- +[["john@example.com", "john", "example.com"], + ["jane@test.org", "jane", "test.org"]] +``` + +### Example 2: Parse Date Components +```cypher +WITH '2024-01-15 and 2024-12-25' AS dates +RETURN flex.text.regexGroups(dates, '(\\d{4})-(\\d{2})-(\\d{2})') AS parsed +``` + +**Output:** +``` +parsed +------------------------------------------------- +[["2024-01-15", "2024", "01", "15"], + ["2024-12-25", "2024", "12", "25"]] +``` + +### Example 3: Extract URLs and Protocol +```cypher +MATCH (d:Document) +WITH d, flex.text.regexGroups(d.content, '(https?)://([\\w.]+)') AS urls +WHERE size(urls) > 0 +RETURN d.title, urls +``` + +## Notes +- Returns `null` if input string is `null` +- The regex is applied globally (finds all matches) +- Each match array contains: [fullMatch, group1, group2, ...] +- Useful for parsing structured text, extracting data patterns +- More powerful than simple string search for complex patterns + +## See Also +- [text.replace](./replace.md) - Replace text using regex +- [text.indexOf](./indexOf.md) - Find simple substring position +- [text.indexesOf](./indexesOf.md) - Find all substring positions diff --git a/udfs/flex/text/repeat.md b/udfs/flex/text/repeat.md new file mode 100644 index 0000000..ae10fd5 --- /dev/null +++ b/udfs/flex/text/repeat.md @@ -0,0 +1,85 @@ +# text.repeat + +## Description +Repeats a string a specified number of times. + +## Syntax +```cypher +flex.text.repeat(string, count) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string | Yes | The string to repeat | +| `count` | number | Yes | The number of times to repeat the string | + +## Returns +**Type:** string + +A new string consisting of the input string repeated the specified number of times. Returns `null` if input is `null`. + +## Examples + +### Example 1: Basic Repetition +```cypher +RETURN flex.text.repeat('Ha', 3) AS result +``` + +**Output:** +``` +result +------ +HaHaHa +``` + +### Example 2: Creating Separators +```cypher +RETURN flex.text.repeat('-', 40) AS separator +``` + +**Output:** +``` +separator +---------------------------------------- +---------------------------------------- +``` + +### Example 3: Building Star Ratings +```cypher +MATCH (r:Review) +RETURN r.product, flex.text.repeat('★', r.rating) AS stars +``` + +**Output:** +``` +product | stars +------------|------- +Laptop | ★★★★★ +Mouse | ★★★★ +Keyboard | ★★★ +``` + +### Example 4: Indentation +```cypher +WITH 2 AS level +RETURN flex.text.repeat(' ', level) + 'Nested Item' AS indented +``` + +**Output:** +``` +indented +---------------- + Nested Item +``` + +## Notes +- Returns `null` if input is `null` +- Count must be a non-negative integer +- Useful for creating visual elements, separators, or formatting +- Can be combined with other text functions for complex formatting + +## See Also +- [text.lpad](./lpad.md) - Pad the start of a string +- [text.rpad](./rpad.md) - Pad the end of a string +- [text.format](./format.md) - Format strings with placeholders diff --git a/udfs/flex/text/replace.md b/udfs/flex/text/replace.md new file mode 100644 index 0000000..08015ef --- /dev/null +++ b/udfs/flex/text/replace.md @@ -0,0 +1,80 @@ +# text.replace + +## Description +Replaces all occurrences of a substring matching a regular expression pattern with a replacement string. + +## Syntax +```cypher +flex.text.replace(string, regex, replacement) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string | Yes | The string to perform replacements on | +| `regex` | string | Yes | The regular expression pattern to match (applied globally) | +| `replacement` | string | Yes | The string to replace matches with | + +## Returns +**Type:** string + +A new string with all pattern matches replaced by the replacement string. Returns `null` if input string is `null`. + +## Examples + +### Example 1: Basic Text Replacement +```cypher +RETURN flex.text.replace('hello world', 'world', 'universe') AS result +``` + +**Output:** +``` +result +-------------- +hello universe +``` + +### Example 2: Remove Non-Numeric Characters +```cypher +WITH 'Phone: (555) 123-4567' AS phone +RETURN flex.text.replace(phone, '[^0-9]', '') AS cleaned +``` + +**Output:** +``` +cleaned +----------- +5551234567 +``` + +### Example 3: Sanitize User Input +```cypher +MATCH (c:Comment) +WITH c, flex.text.replace(c.text, '<[^>]+>', '') AS sanitized +RETURN sanitized AS cleanComment +``` + +### Example 4: Normalize Whitespace +```cypher +WITH ' Multiple spaces here ' AS text +RETURN flex.text.replace(text, '\\s+', ' ') AS normalized +``` + +**Output:** +``` +normalized +------------------------- + Multiple spaces here +``` + +## Notes +- Returns `null` if input string is `null` +- Uses global replacement (replaces all occurrences) +- Pattern is treated as a regular expression +- Useful for data cleaning, sanitization, and text transformation +- Can use regex patterns for complex replacements + +## See Also +- [text.regexGroups](./regexGroups.md) - Extract matches with capture groups +- [text.indexOf](./indexOf.md) - Find substring position +- [text.format](./format.md) - Format strings with placeholders diff --git a/udfs/flex/text/rpad.md b/udfs/flex/text/rpad.md new file mode 100644 index 0000000..e43f22e --- /dev/null +++ b/udfs/flex/text/rpad.md @@ -0,0 +1,68 @@ +# text.rpad + +## Description +Pads the end (right side) of a string with a specified character until it reaches the desired length. + +## Syntax +```cypher +flex.text.rpad(string, length, padChar) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string/number | Yes | The string to pad (will be converted to string if number) | +| `length` | number | Yes | The target length after padding | +| `padChar` | string | No | The character to use for padding (default: space ' ') | + +## Returns +**Type:** string + +The padded string. If the original string is already longer than the target length, it is returned unchanged. Returns `null` if input is `null`. + +## Examples + +### Example 1: Basic Right Padding +```cypher +RETURN flex.text.rpad('test', 8, '-') AS result +``` + +**Output:** +``` +result +---------- +test---- +``` + +### Example 2: Creating Fixed-Width Fields +```cypher +MATCH (p:Product) +RETURN flex.text.rpad(p.name, 20, ' ') AS productName, p.price +``` + +**Output:** +``` +productName | price +---------------------|------- +Laptop | 999.99 +Mouse | 29.99 +``` + +### Example 3: Building Formatted Tables +```cypher +WITH [['Name', 15], ['Email', 25], ['Role', 10]] AS columns +UNWIND columns AS col +RETURN flex.text.rpad(col[0], col[1], ' ') AS header +``` + +## Notes +- Returns `null` if input is `null` +- Converts numbers to strings automatically +- Default padding character is a space +- If string is already longer than target length, returns original string +- Useful for creating aligned columns, fixed-width output, or formatted tables + +## See Also +- [text.lpad](./lpad.md) - Pad the start (left side) of a string +- [text.repeat](./repeat.md) - Repeat a string multiple times +- [text.format](./format.md) - Format strings with placeholders diff --git a/udfs/flex/text/snakeCase.md b/udfs/flex/text/snakeCase.md new file mode 100644 index 0000000..cf8b4cc --- /dev/null +++ b/udfs/flex/text/snakeCase.md @@ -0,0 +1,70 @@ +# text.snakeCase + +## Description +Converts a string to snake_case format by separating words with underscores and converting all characters to lowercase. + +## Syntax +```cypher +flex.text.snakeCase(string) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string | Yes | The string to convert to snake_case | + +## Returns +**Type:** string + +The input string converted to snake_case format. Returns the original string if no matches are found. Returns `null` if input is `null`. + +## Examples + +### Example 1: Basic Usage +```cypher +RETURN flex.text.snakeCase('HelloWorld') AS result +``` + +**Output:** +``` +result +----------- +hello_world +``` + +### Example 2: Converting Multiple Formats +```cypher +WITH ['camelCase', 'PascalCase', 'kebab-case', 'space separated'] AS inputs +UNWIND inputs AS input +RETURN input AS original, flex.text.snakeCase(input) AS snake +``` + +**Output:** +``` +original | snake +----------------|------------------ +camelCase | camel_case +PascalCase | pascal_case +kebab-case | kebab_case +space separated | space_separated +``` + +### Example 3: Database Column Naming +```cypher +MATCH (u:User) +WITH u, keys(u) AS properties +UNWIND properties AS prop +RETURN prop AS camelCase, flex.text.snakeCase(prop) AS dbColumn +``` + +## Notes +- Returns `null` for `null` input +- Handles multiple word boundary detection patterns +- All characters are converted to lowercase +- Words are separated by underscores +- Common for database column names and Python conventions +- Returns the original string if no word boundaries are detected + +## See Also +- [text.camelCase](./camelCase.md) - Convert to camelCase format +- [text.upperCamelCase](./upperCamelCase.md) - Convert to UpperCamelCase format diff --git a/udfs/flex/text/swapCase.md b/udfs/flex/text/swapCase.md new file mode 100644 index 0000000..0f964fa --- /dev/null +++ b/udfs/flex/text/swapCase.md @@ -0,0 +1,55 @@ +# text.swapCase + +## Description +Swaps the case of each character in a string - uppercase characters become lowercase and vice versa. + +## Syntax +```cypher +flex.text.swapCase(string) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string | Yes | The string to swap case | + +## Returns +**Type:** string + +A new string with all uppercase characters converted to lowercase and all lowercase characters converted to uppercase. Returns `null` if input is `null`. + +## Examples + +### Example 1: Basic Usage +```cypher +RETURN flex.text.swapCase('Hello World') AS result +``` + +**Output:** +``` +result +------------- +hELLO wORLD +``` + +### Example 2: Swapping Mixed Case +```cypher +RETURN flex.text.swapCase('aBc123XyZ') AS result +``` + +**Output:** +``` +result +---------- +AbC123xYz +``` + +## Notes +- Returns `null` for `null` input +- Non-alphabetic characters remain unchanged +- Applies to all characters in the string, not just the first + +## See Also +- [text.capitalize](./capitalize.md) - Uppercase the first character +- [text.camelCase](./camelCase.md) - Convert to camelCase format +- [text.snakeCase](./snakeCase.md) - Convert to snake_case format diff --git a/udfs/flex/text/upperCamelCase.md b/udfs/flex/text/upperCamelCase.md new file mode 100644 index 0000000..a777110 --- /dev/null +++ b/udfs/flex/text/upperCamelCase.md @@ -0,0 +1,63 @@ +# text.upperCamelCase + +## Description +Converts a string to UpperCamelCase (also known as PascalCase) format by removing non-alphanumeric characters and capitalizing the first letter of each word including the first. + +## Syntax +```cypher +flex.text.upperCamelCase(string) +``` + +## Parameters +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `string` | string | Yes | The string to convert to UpperCamelCase | + +## Returns +**Type:** string + +The input string converted to UpperCamelCase format. Returns `null` if input is `null`. + +## Examples + +### Example 1: Basic Usage +```cypher +RETURN flex.text.upperCamelCase('hello world') AS result +``` + +**Output:** +``` +result +---------- +HelloWorld +``` + +### Example 2: Converting Class Names +```cypher +RETURN flex.text.upperCamelCase('user_account') AS result +``` + +**Output:** +``` +result +----------- +UserAccount +``` + +### Example 3: Normalizing Entity Names +```cypher +MATCH (e:Entity) +RETURN e.rawName, flex.text.upperCamelCase(e.rawName) AS className +``` + +## Notes +- Returns `null` for `null` input +- Removes all non-alphanumeric characters +- First character is always uppercase (unlike camelCase) +- Also known as PascalCase +- Useful for class names, type names, and entity naming conventions + +## See Also +- [text.camelCase](./camelCase.md) - Convert to camelCase (first letter lowercase) +- [text.snakeCase](./snakeCase.md) - Convert to snake_case format +- [text.capitalize](./capitalize.md) - Capitalize first character only diff --git a/udfs/index.md b/udfs/index.md new file mode 100644 index 0000000..5b64645 --- /dev/null +++ b/udfs/index.md @@ -0,0 +1,421 @@ +# UDFs - User Defined Functions + +Every databases comes with a set of built-in functions, for example among FalkorDB functions you'll find: +`abs` - computes the absolute value of a number +`pow` - computes v^x +`trim` - removes leading and trailing spaces. + +These are baked into the DB and are part of its source code, introducing a new function e.g. `UpperCaseOdd`isn't always trivial, +the function needs to be usable to a wide audience for it to be considered, in the past we've rejected requests for adding new functions as these were too specific and we didn't believe they've added a significant value for most of our users. + +But now, with the support of UDFs, everyone can extend FalkorDB's functionality with their own set of functions. Following is an introduction to UDFs, how to manage & use them within FalkorDB. + + +## Example +In order to introduce UDFs, please review the following, a complete example which loads a new UDF library "StringUtils" that includes a single function "UpperCaseOdd", once loaded the script puts it to use. + +```python +from falkordb import FalkorDB + +# Connect to FalkorDB +db = FalkorDB(host='localhost', port=6379) + +# Define UDF library name & script +lib = "StringUtils" + +# UpperCaseOdd implementation in JavaScript +script = """ +function UpperCaseOdd(s) { + return s.split('') + .map((char, i) => { + if (i % 2 !== 0) { + return char.toUpperCase(); + } + return char; + }) + .join(''); +}; + +// expose UDF to FalkorDB +falkor.register('UpperCaseOdd', UpperCaseOdd); +""" + +# Load UDF into the database +db.udf_load(lib, script) + +# Call UDF +graph = db.select_graph("G") +s = graph.query("RETURN StringUtils.UpperCaseOdd('abcdef')").result_set[0][0] +print(f"s: {s}") # prints 'AbCdEf' +``` +## Commands Specification + +Although conveniently available through `FalkorDB-PY` Python client, FalkorDB exposes its UDF functionality via a set of new `GRAPH.UDF ` commands. + +### GRAPH.UDF LOAD [REPLACE]