$regexFindAll (aggregation)

Definition

  • $regexFindAll

New in version 4.2.

Provides regular expression (regex) pattern matching capability inaggregation expressions. The operator returns an array of documentsthat contains information on each match. If a match is not found,returns an empty array.

MongoDB uses Perl compatible regular expressions (i.e. “PCRE” )version 8.41 with UTF-8 support.

Prior to MongoDB 4.2, aggregation pipeline can only use the queryoperator $regex in the $match stage. For moreinformation on using regex in a query, see $regex.

Syntax

The $regexFindAll operator has the following syntax:

  1. { $regexFindAll: { input: <expression> , regex: <expression>, options: <expression> } }
FieldDescription
inputThe string on which you wish to apply the regex pattern.Can be a string or any valid expression that resolves to a string.
regexThe regex pattern to apply. Can be any valid expression that resolves to either a string or regexpattern /<pattern>/. When using the regex /<patthern>/, youcan also specify the regex options i and m (but not thes or x options):- "pattern"- /<pattern>/- /<pattern>/<options>Alternatively, you can also specify the regex options with theoptions field. To specify the s or x options, youmust use the options field.You cannot specify options in both the regex and the optionsfield.
optionsOptional. The following <options> are available for usewith regular expression.NoteYou cannot specify options in both the regex and theoptions field.
OptionDescription
iCase insensitivity to match both upper and lower cases. Youcan specify the option in the options field or as part ofthe regex field.
mFor patterns that include anchors (i.e. ^ for the start,$ for the end), match at the beginning or end of eachline for strings with multiline values. Without this option,these anchors match at beginning or end of the string.If the pattern contains no anchors or if the string value hasno newline characters (e.g. \n), the m option has noeffect.
x“Extended” capability to ignore all white space charactersin the pattern unless escaped or included in a characterclass.Additionally, it ignores characters in-between and includingan un-escaped hash/pound (#) character and the next newline, so that you may include comments in complicatedpatterns. This only applies to data characters; white spacecharacters may never appear within special charactersequences in a pattern.The x option does not affect the handling of the VTcharacter (i.e. code 11).You can specify the option only in the options field.
sAllows the dot character (i.e. .) to match allcharacters including newline characters.You can specify the option only in the options field.

Returns

The operator returns an array:

  • If the operator does not find a match, the operator returns an emptyarray.

  • If the operator finds a match, the operator returns an array ofdocuments that contains the following information for each match:

    • the matching string in the input,
    • the code pointindex (not byte index) of the matching string in the input, and
    • An array of the strings that corresponds to the groups captured bythe matching string. Capturing groups are specified with parenthesis() in the regex pattern.
  1. [ { "match" : <string>, "idx" : <num>, "captures" : <array of strings> }, ... ]

See also

Behavior

$regexFindAll and Collation

$regexFindAll ignores the collation specified for thecollection, db.collection.aggregate(), and the index, if used.

For example, the create a sample collection with collation strength1 (i.e. compare base character only and ignore other differencessuch as case and diacritics):

  1. db.createCollection( "myColl", { collation: { locale: "fr", strength: 1 } } )

Insert the following documents:

  1. db.myColl.insertMany([
  2. { _id: 1, category: "café" },
  3. { _id: 2, category: "cafe" },
  4. { _id: 3, category: "cafE" }
  5. ])

Using the collection’s collation, the following operation performs acase-insensitive and diacritic-insensitive match:

  1. db.myColl.aggregate( [ { $match: { category: "cafe" } } ] )

The operation returns the following 3 documents:

  1. { "_id" : 1, "category" : "café" }
  2. { "_id" : 2, "category" : "cafe" }
  3. { "_id" : 3, "category" : "cafE" }

However, the aggregation expression $regexFind ignorescollation; that is, the following regular expression pattern matching examplesare case-sensitive and diacritic sensitive:

  1. db.myColl.aggregate( [ { $addFields: { results: { $regexFindAll: { input: "$category", regex: /cafe/ } } } } ] )
  2. db.myColl.aggregate(
  3. [ { $addFields: { results: { $regexFindAll: { input: "$category", regex: /cafe/ } } } } ],
  4. { collation: { locale: "fr", strength: 1 } } // Ignored in the $regexFindAll
  5. )

Both operations return the following:

  1. { "_id" : 1, "category" : "café", "results" : [ ] }
  2. { "_id" : 2, "category" : "cafe", "results" : [ { "match" : "cafe", "idx" : 0, "captures" : [ ] } ] }
  3. { "_id" : 3, "category" : "cafE", "results" : [ ] }

To perform a case-insensitive regex pattern matching, use thei Option instead. Seei Option for an example.

Examples

$regexFindAll and Its Options

To illustrate the behavior of the $regexFindAll operator asdiscussed in this example, create a sample collection products withthe following documents:

  1. db.products.insertMany([
  2. { _id: 1, description: "Single LINE description." },
  3. { _id: 2, description: "First lines\nsecond line" },
  4. { _id: 3, description: "Many spaces before line" },
  5. { _id: 4, description: "Multiple\nline descriptions" },
  6. { _id: 5, description: "anchors, links and hyperlinks" },
  7. { _id: 6, description: "métier work vocation" }
  8. ])

By default, $regexFindAll performs a case-sensitive match.For example, the following aggregation performs a case-sensitive$regexFindAll on the description field. The regexpattern /line/ does not specify any grouping:

  1. db.products.aggregate([
  2. { $addFields: { returnObject: { $regexFindAll: { input: "$description", regex: /line/ } } } }
  3. ])

The operationr returns the following:

  1. {
  2. "_id" : 1,
  3. "description" : "Single LINE description.",
  4. "returnObject" : [ ]
  5. }
  6. {
  7. "_id" : 2,
  8. "description" : "First lines\nsecond line",
  9. "returnObject" : [ { "match" : "line", "idx" : 6, "captures" : [ ]}, { "match" : "line", "idx" : 19, "captures" : [ ] } ]
  10. }
  11. {
  12. "_id" : 3,
  13. "description" : "Many spaces before line",
  14. "returnObject" : [ { "match" : "line", "idx" : 23, "captures" : [ ] } ]
  15. }
  16. {
  17. "_id" : 4,
  18. "description" : "Multiple\nline descriptions",
  19. "returnObject" : [ { "match" : "line", "idx" : 9, "captures" : [ ] }
  20. ] }
  21.  
  22. {
  23. "_id" : 5,
  24. "description" : "anchors, links and hyperlinks",
  25. "returnObject" : [ ]
  26. }
  27. {
  28. "_id" : 6,
  29. "description" : "métier work vocation",
  30. "returnObject" : [ ]
  31. }

The following regex pattern /lin(e|k)/ specifies a grouping(e|k) in the pattern:

  1. db.products.aggregate([
  2. { $addFields: { returnObject: { $regexFindAll: { input: "$description", regex: /lin(e|k)/ } } } }
  3. ])

The operation returns the following:

  1. {
  2. "_id" : 1,
  3. "description" : "Single LINE description.",
  4. "returnObject": [ ]
  5. }
  6. {
  7. "_id" : 2,
  8. "description" : "First lines\nsecond line",
  9. "returnObject" : [ { "match" : "line", "idx" : 6, "captures" : [ "e" ] }, { "match" : "line", "idx" : 19, "captures" : [ "e" ] } ]
  10. }
  11. {
  12. "_id" : 3,
  13. "description" : "Many spaces before line",
  14. "returnObject" : [ { "match" : "line", "idx" : 23, "captures" : [ "e" ] } ]
  15. }
  16. {
  17. "_id" : 4,
  18. "description" : "Multiple\nline descriptions",
  19. "returnObject" : [ { "match" : "line", "idx" : 9, "captures" : [ "e" ] } ]
  20. }
  21. {
  22. "_id" : 5,
  23. "description" : "anchors, links and hyperlinks",
  24. "returnObject" : [ { "match" : "link", "idx" : 9, "captures" : [ "k" ] }, { "match" : "link", "idx" : 24, "captures" : [ "k" ] } ]
  25. }
  26. {
  27. "_id" : 6,
  28. "description" : "métier work vocation",
  29. "returnObject" : [ ]
  30. }

In the return option, the idx field is the code point index and not the byteindex. To illustrate, consider the following example that uses theregex pattern /tier/:

  1. db.products.aggregate([
  2. { $addFields: { returnObject: { $regexFindAll: { input: "$description", regex: /tier/ } } } }
  3. ])

The operation returns the following where only the last recordmatches the pattern and the returned idx is 2 (instead of 3if using a byte index)

  1. { "_id" : 1, "description" : "Single LINE description.", "returnObject" : [ ] }
  2. { "_id" : 2, "description" : "First lines\nsecond line", "returnObject" : [ ] }
  3. { "_id" : 3, "description" : "Many spaces before line", "returnObject" : [ ] }
  4. { "_id" : 4, "description" : "Multiple\nline descriptions", "returnObject" : [ ] }
  5. { "_id" : 5, "description" : "anchors, links and hyperlinks", "returnObject" : [ ] }
  6. { "_id" : 6, "description" : "métier work vocation",
  7. "returnObject" : [ { "match" : "tier", "idx" : 2, "captures" : [ ] } ] }

i Option

Note

You cannot specify options in both the regex and theoptions field.

To perform case-insensitive pattern matching, include the i option as part of the regex field or in the optionsfield:

  1. // Specify i as part of the regex field
  2. { $regexFindAll: { input: "$description", regex: /line/i } }
  3.  
  4. // Specify i in the options field
  5. { $regexFindAll: { input: "$description", regex: /line/, options: "i" } }
  6. { $regexFindAll: { input: "$description", regex: "line", options: "i" } }

For example, the following aggregation performs a case-insensitive$regexFindAll on the description field. The regexpattern /line/ does not specify any grouping:

  1. db.products.aggregate([
  2. { $addFields: { returnObject: { $regexFindAll: { input: "$description", regex: /line/i } } } }
  3. ])

The operation returns the following documents:

  1. {
  2. "_id" : 1,
  3. "description" : "Single LINE description.",
  4. "returnObject" : [ { "match" : "LINE", "idx" : 7, "captures" : [ ] } ]
  5. }
  6. {
  7. "_id" : 2,
  8. "description" : "First lines\nsecond line",
  9. "returnObject" : [ { "match" : "line", "idx" : 6, "captures" : [ ] }, { "match" : "line", "idx" : 19, "captures" : [ ] } ]
  10. }
  11. {
  12. "_id" : 3,
  13. "description" : "Many spaces before line",
  14. "returnObject" : [ { "match" : "line", "idx" : 23, "captures" : [ ] } ]
  15. }
  16. {
  17. "_id" : 4,
  18. "description" : "Multiple\nline descriptions",
  19. "returnObject" : [ { "match" : "line", "idx" : 9, "captures" : [ ] } ]
  20. }
  21. {
  22. "_id" : 5,
  23. "description" : "anchors, links and hyperlinks",
  24. "returnObject" : [ ]
  25. }
  26. { "_id" : 6, "description" : "métier work vocation", "returnObject" : [ ] }

m Option

Note

You cannot specify options in both the regex and theoptions field.

To match the specified anchors (e.g. ^, $) for each line of amultiline string, include the m optionas part of the regex field or in theoptions field:

  1. // Specify m as part of the regex field
  2. { $regexFindAll: { input: "$description", regex: /line/m } }
  3.  
  4. // Specify m in the options field
  5. { $regexFindAll: { input: "$description", regex: /line/, options: "m" } }
  6. { $regexFindAll: { input: "$description", regex: "line", options: "m" } }

The following example includes both the i and the m options tomatch lines starting with either the letter s or S formultiline strings:

  1. db.products.aggregate([
  2. { $addFields: { returnObject: { $regexFindAll: { input: "$description", regex: /^s/im } } } }
  3. ])

The operation returns the following:

  1. {
  2. "_id" : 1,
  3. "description" : "Single LINE description.",
  4. "returnObject" : [ { "match" : "S", "idx" : 0, "captures" : [ ] } ]
  5. }
  6. {
  7. "_id" : 2,
  8. "description" : "First lines\nsecond line",
  9. "returnObject" : [ { "match" : "s", "idx" : 12, "captures" : [ ] } ]
  10. }
  11. {
  12. "_id" : 3,
  13. "description" : "Many spaces before line",
  14. "returnObject" : [ ]
  15. }
  16. {
  17. "_id" : 4,
  18. "description" : "Multiple\nline descriptions",
  19. "returnObject" : [ ]
  20. }
  21. {
  22. "_id" : 5,
  23. "description" : "anchors, links and hyperlinks",
  24. "returnObject" : [ ]
  25. }
  26. { "_id" : 6, "description" : "métier work vocation", "returnObject" : [ ] }

x Option

Note

You cannot specify options in both the regex and theoptions field.

To ignore all unescaped white space characters and comments (denoted bythe un-escaped hash # character and the next new-line character) inthe pattern, include the s option in theoptions field:

  1. // Specify x in the options field
  2. { $regexFindAll: { input: "$description", regex: /line/, options: "x" } }
  3. { $regexFindAll: { input: "$description", regex: "line", options: "x" } }

The following example includes the x option to skip unescaped whitespaces and comments:

  1. db.products.aggregate([
  2. { $addFields: { returnObject: { $regexFindAll: { input: "$description", regex: /lin(e|k) # matches line or link/, options:"x" } } } }
  3. ])

The operation returns the following:

  1. {
  2. "_id" : 1,
  3. "description" : "Single LINE description.",
  4. "returnObject" : [ ]
  5. }
  6. {
  7. "_id" : 2,
  8. "description" : "First lines\nsecond line",
  9. "returnObject" : [ { "match" : "line", "idx" : 6, "captures" : [ "e" ] }, { "match" : "line", "idx" : 19, "captures" : [ "e" ] } ]
  10. }
  11. {
  12. "_id" : 3,
  13. "description" : "Many spaces before line",
  14. "returnObject" : [ { "match" : "line", "idx" : 23, "captures" : [ "e" ] } ]
  15. }
  16. {
  17. "_id" : 4,
  18. "description" : "Multiple\nline descriptions",
  19. "returnObject" : [ { "match" : "line", "idx" : 9, "captures" : [ "e" ] } ]
  20. }
  21. {
  22. "_id" : 5,
  23. "description" : "anchors, links and hyperlinks",
  24. "returnObject" : [ { "match" : "link", "idx" : 9, "captures" : [ "k" ] }, { "match" : "link", "idx" : 24, "captures" : [ "k" ] } ]
  25. }
  26. { "_id" : 6, "description" : "métier work vocation", "returnObject" : [ ] }

s Option

Note

You cannot specify options in both the regex and theoptions field.

To allow the dot character (i.e. .) in the pattern to match allcharacters including the new line character, include the s option in the options field:

  1. // Specify s in the options field
  2. { $regexFindAll: { input: "$description", regex: /m.*line/, options: "s" } }
  3. { $regexFindAll: { input: "$description", regex: "m.*line", options: "s" } }

The following example includes the s option to allow the dotcharacter (i.e. .) to match all characters including new line as wellas the i option to perform a case-insensitive match:

  1. db.products.aggregate([
  2. { $addFields: { returnObject: { $regexFindAll: { input: "$description", regex:/m.*line/, options: "si" } } } }
  3. ])

The operation returns the following:

  1. {
  2. "_id" : 1,
  3. "description" : "Single LINE description.",
  4. "returnObject" : [ ]
  5. }
  6. {
  7. "_id" : 2,
  8. "description" : "First lines\nsecond line",
  9. "returnObject" : [ ]
  10. }
  11. {
  12. "_id" : 3,
  13. "description" : "Many spaces before line",
  14. "returnObject" : [ { "match" : "Many spaces before line", "idx" : 0, "captures" : [ ] } ]
  15. }
  16. {
  17. "_id" : 4,
  18. "description" : "Multiple\nline descriptions",
  19. "returnObject" : [ { "match" : "Multiple\nline", "idx" : 0, "captures" : [ ] } ]
  20. }
  21. {
  22. "_id" : 5,
  23. "description" : "anchors, links and hyperlinks",
  24. "returnObject" : [ ]
  25. }
  26. { "_id" : 6, "description" : "métier work vocation", "returnObject" : [ ] }

Use $regexFindAll to Parse Email from String

Create a sample collection feedback with the following documents:

  1. db.feedback.insertMany([
  2. { "_id" : 1, comment: "Hi, I'm just reading about MongoDB -- aunt.arc.tica@example.com" },
  3. { "_id" : 2, comment: "I wanted to concatenate a string" },
  4. { "_id" : 3, comment: "How do I convert a date to string? Contact me at either cam@mongodb.com or c.dia@mongodb.com" },
  5. { "_id" : 4, comment: "It's just me. I'm testing. fred@MongoDB.com" }
  6. ])

The following aggregation uses the $regexFindAll to extractall emails from the comment field (case insensitive).

  1. db.feedback.aggregate( [
  2. { $addFields: {
  3. "email": { $regexFindAll: { input: "$comment", regex: /[a-z0-9_.+-]+@[a-z0-9_.+-]+\.[a-z0-9_.+-]+/i } }
  4. } },
  5. { $set: { email: "$email.match"} }
  6. ] )
  • First Stage
  • The stage uses the $addFields stage to add a new fieldemail to the document. The new field is an array that containsthe result of performing the $regexFindAll on thecomment field:
  1. { "_id" : 1, "comment" : "Hi, I'm just reading about MongoDB -- aunt.arc.tica@example.com", "email" : [ { "match" : "aunt.arc.tica@example.com", "idx" : 38, "captures" : [ ] } ] }
  2. { "_id" : 2, "comment" : "I wanted to concatenate a string", "email" : [ ] }
  3. { "_id" : 3, "comment" : "How do I convert a date to string? Contact me at either cam@mongodb.com or c.dia@mongodb.com", "email" : [ { "match" : "cam@mongodb.com", "idx" : 56, "captures" : [ ] }, { "match" : "c.dia@mongodb.com", "idx" : 75, "captures" : [ ] } ] }
  4. { "_id" : 4, "comment" : "It's just me. I'm testing. fred@MongoDB.com", "email" : [ { "match" : "fred@MongoDB.com", "idx" : 28, "captures" : [ ] } ] }
  • Second Stage
  • The stage use the $set stage to reset the email array elements tothe "email.match" value(s). If the current value of emailis null, the new value of email is set to null.
  1. { "_id" : 1, "comment" : "Hi, I'm just reading about MongoDB -- aunt.arc.tica@example.com", "email" : [ "aunt.arc.tica@example.com" ] }
  2. { "_id" : 2, "comment" : "I wanted to concatenate a string", "email" : [ ] }
  3. { "_id" : 3, "comment" : "How do I convert a date to string? Contact me at either cam@mongodb.com or c.dia@mongodb.com", "email" : [ "cam@mongodb.com", "c.dia@mongodb.com" ] }
  4. { "_id" : 4, "comment" : "It's just me. I'm testing. fred@MongoDB.com", "email" : [ "fred@MongoDB.com" ] }

Use Captured Groupings to Parse User Name

Create a sample collection feedback with the following documents:

  1. db.feedback.insertMany([
  2. { "_id" : 1, comment: "Hi, I'm just reading about MongoDB -- aunt.arc.tica@example.com" },
  3. { "_id" : 2, comment: "I wanted to concatenate a string" },
  4. { "_id" : 3, comment: "How do I convert a date to string? Contact me at either cam@mongodb.com or c.dia@mongodb.com" },
  5. { "_id" : 4, comment: "It's just me. I'm testing. fred@MongoDB.com" }
  6. ])

To reply to the feedback, assume you want to parse the local-part ofthe email address to use as the name in the greetings. Using thecaptured field returned in the $regexFindAll results,you can parse out the local part of each email address:

  1. db.feedback.aggregate( [
  2. { $addFields: {
  3. "names": { $regexFindAll: { input: "$comment", regex: /([a-z0-9_.+-]+)@[a-z0-9_.+-]+\.[a-z0-9_.+-]+/i } },
  4. } },
  5. { $set: { names: { $reduce: { input: "$names.captures", initialValue: [ ], in: { $concatArrays: [ "$$value", "$$this" ] } } } } }
  6. ] )
  • First Stage
  • The stage uses the $addFields stage to add a new fieldnames to the document. The new field contains the result ofperforming the $regexFindAll on the comment field:
  1. {
  2. "_id" : 1,
  3. "comment" : "Hi, I'm just reading about MongoDB -- aunt.arc.tica@example.com",
  4. "names" : [ { "match" : "aunt.arc.tica@example.com", "idx" : 38, "captures" : [ "aunt.arc.tica" ] } ]
  5. }
  6.  
  7. { "_id" : 2, "comment" : "I wanted to concatenate a string", "names" : [ ] }
  8. {
  9. "_id" : 3,
  10. "comment" : "How do I convert a date to string? Contact me at either cam@mongodb.com or c.dia@mongodb.com",
  11. "names" : [
  12. { "match" : "cam@mongodb.com", "idx" : 56, "captures" : [ "cam" ] },
  13. { "match" : "c.dia@mongodb.com", "idx" : 75, "captures" : [ "c.dia" ] }
  14. ]
  15. }
  16. {
  17. "_id" : 4,
  18. "comment" : "It's just me. I'm testing. fred@MongoDB.com",
  19. "names" : [ { "match" : "fred@MongoDB.com", "idx" : 28, "captures" : [ "fred" ] } ]
  20. }
  • Second Stage
  • The stage use the $set stage with the$reduce operator to reset names to an array that containsthe "$names.captures" elements.
  1. {
  2. "_id" : 1,
  3. "comment" : "Hi, I'm just reading about MongoDB -- aunt.arc.tica@example.com",
  4. "names" : [ "aunt.arc.tica" ]
  5. }
  6. { "_id" : 2, "comment" : "I wanted to concatenate a string", "names" : [ ] }
  7. {
  8. "_id" : 3,
  9. "comment" : "How do I convert a date to string? Contact me at either cam@mongodb.com or c.dia@mongodb.com",
  10. "names" : [ "cam", "c.dia" ]
  11. }
  12. {
  13. "_id" : 4,
  14. "comment" : "It's just me. I'm testing. fred@MongoDB.com",
  15. "names" : [ "fred" ]
  16. }