My Cloudsearch index currently returns no results for one-two three but it does return one result (correctly) for one two three (and will also be included, correctly, in the results when searching for two three etc.)
My understanding is that this is because searchable phrases are broken down into their tokens (words) with whitespace and punctuation acting as delimiters. So, one and two become separate tokens, but one-two is not a valid token, so no results are found. From the Cloudsearch docs:
During tokenization, the stream of text in a field is split into separate tokens on detectable boundaries using the word break rules defined in the Unicode Text Segmentation algorithm.
That Unicode document is here.
I would like to be able to search for one-two three and find the relevant result, as well as a few other punctuation characters, like /. Is it possible to configure this with Cloudsearch?
I just realized a simple solution to this that works fine, although it technically does not answer my question. I simply needed to pre-process my query strings before sending them to cloud search by replacing - or / or whatever char I want with a single space.
That way, one-two three actually performs a search for one two three, returning the correct result.
Related
I have an Azure Storage Table set up that possesses lots of values containing hyphens, apostrophes, and other bits of punctuation that the Azure Indexers don't like. Hyphenated-Word gets broken into two tokens — Hyphenated and Word — upon indexing. Accordingly, this means that searching for HyphenatedWord will not yield any results, regardless of any wildcard or fuzzy matching characters. That said, Azure Cognitive Search possesses support for Regex Lucene queries...
As such, I'm trying to find out if there's a Regex pattern I can use to match words with or without hyphens to a given query. As an example, the query homework should match the results homework and home-work.
I know that if I were trying to do the opposite — match unhyphenated words even when a hyphen is provided in the query — I would use something like /home(-)?work/. However, I'm not sure what the inverse looks like — if such a thing exists.
Is there a raw Regex pattern that will perform the kind of matching I'm proposing? Or am I SOL?
Edit: I should point out that the example I provided is unrealistic because I won't always know where a hyphen should be. Optimally, the pattern that performs this matching would be agnostic to the precise placement of a hyphen.
Edit 2: A solution I've discovered that works but isn't exactly optimal (and, though I have no way to prove this, probably isn't performant) is to just break down the query, remove all of the special characters that cause token breaks, and then dynamically build a regex query that has an optional match in between every character in the query. Using the homework example, the pattern would look something like [-'\.! ]?h[-'\.! ]?o[-'\.! ]?m[-'\.! ]?e[-'\.! ]?w[-'\.! ]?o[-'\.! ]?r[-'\.! ]?k[-'\.! ]?...which is perhaps the ugliest thing I've ever seen. Nevertheless, it gets the job done.
My solution to scenarios like this is always to introduce content- and query-processing.
Content processing is easier when you use the push model via the SDK, but you could achieve the same by creating a shadow/copy of your table where the content is manipulated for indexing purposes. You let your original table stay intact. And then you maintain a duplicate table where your text is processed.
Query processing is something you should use regardless. In its simplest form you want to clean the input from the end users before you use it in a query. Additional steps can be to handle special characters like a hyphen. Either escape it, strip it, or whatever depending on what your requirements are.
EXAMPLE
I have to support searches for ordering codes that may contain hyphens or other special characters. The maintainers of our ordering codes may define ordering codes in an inconsistent format. Customers visiting our sites are just as inconsistent.
The requirement is that ABC-123-DE_F-4.56G should match any of
ABC-123-DE_F-4.56G
ABC123-DE_F-4.56G
ABC_123_DE_F_4_56G
ABC.123.DE.F.4.56G
ABC 123 DEF 56 G
ABC123DEF56G
I solve this using my suggested approach above. I use content processing to generate a version of the ordering code without any special characters (using a simple regex). Then, I use query processing to transform the end user's input into an OR-query, like:
<verbatim-user-input-cleaned> OR OrderCodeVariation:<verbatim-user-input-without-special-chars>
So, if the user entered ABC.123.DE.F.4.56G I would effecively search for
ABC.123.DE.F.4.56G OR OrderingCodeVariation:ABC123DEF56G
It sounds like you want to define your own tokenization. Would using a custom tokenizer help? https://learn.microsoft.com/azure/search/index-add-custom-analyzers
To add onto Jennifer's answer, you could consider using a custom analyzer consisting of either of these token filters:
pattern_replace: A token filter which applies a pattern to each token in the stream, replacing match occurrences with the specified replacement string.
pattern_capture: Uses Java regexes to emit multiple tokens, one for each capture group in one or more patterns.
You could use the pattern_replace token filter to replace hyphens with the desired character, maybe an empty character.
I have an index with documents with accented words.
For example this document in Portuguese:
title => 'Ponte metálica'
If i search "metálica" it matches, so no problem.
But usually people search without accents, so it's very usual to search just for "metalica" (note the "a" without accent "á").
But it's not returning any results. I tested it in the AWS console and via endpoint /search. Im using the 2013 API.
I think the Synonyms can't solve this issue since they aren't full words
It looks like you posted the same question in AWS forums and got a reply:
The CloudSearch Portuguese stemmer does not remove accents, so á won't match a, and it does not currently have an option to remove them.
Two work-arounds I can think of:
Remove accents before uploading. (possibly to a different field)
Use a copy field, and the "mulitiple languages" analysis mode. This won't stem words by Portuguese rules, unfortunately, but it does remove accents!
I like the idea of removing the accent before upload, but I also have two other ideas:
Use fuzzy matching, so that you can tolerate one or maybe two "wrong" characters. Might have performance drawback to consider.
Provide an auto-complete/suggestor solution similar to a "did you mean?" type of experience.
I found this Stack Overflow thread from around 2014 that discusses these two possibilities, still using CloudSearch: Implementing "Did you mean?" using Amazon CloudSearch
About the fuzzy matching operator:
You can also perform fuzzy searches with the simple query parser. To perform a fuzzy search, append the ~ operator and a value that indicates how much terms can differ from the user query string and still be considered a match. For example, the specifying planit~1 searches for the term planit and allows matches to differ by up to one character, which means the results will include hits for planet.
And about auto-complete, with fuzzy matching option:
When you request suggestions, Amazon CloudSearch finds all of the documents whose values in the suggester field start with the specified query string—the beginning of the field must match the query string to be considered a match. The return data includes the field value and document ID for each match. You can configure suggesters to find matches for the exact query string, or to perform approximate string matching (fuzzy matching) to correct for typographical errors and misspellings.
I am having a specific set of strings i.e. following 4 Strings
IDE, SATA, SSD, FLOPPY DISK
I want to validate all the comma separated combination of above strings irrespective of the order in which they occur and also if possible irrespective of character case they occur. For Example following should be passed
sata,floppy disk
IDE,SSD
SATA,ide,SSD
ssd,Floppy Disk
If I understand correctly, you might want something like this:
^(?:(?:ide|sata|ssd|floppy\sdisk),)+(?:ide|sata|ssd|floppy\sdisk)$
Demo
I am trying with following Regex, so far it is handling cases, and special characters at starts but will not filter duplicates
^((?i)all|((IDE|SATA|SSD|FLOPPY DISK)(,(IDE|SATA|SSD|FLOPPY DISK))*))$
you can find above regex with some test cases at following link
https://regex101.com/r/BKlfBF/1/tests
I have URLs of the following structure:
https://pinball.globalzone.com/en_US/home?tic=1-dj33jl-dj33jl&goToRegisterNow=true
What I want to do now is to shorten the URLs to be able to group and count similar URL patterns. For instance, I want to cut out https://, the locale en_US/ and the token ?tic=1-dj33jl-dj33jl while keeping the rest. The result should look as follows:
pinball.globalzone.com/home&goToRegisterNow=true
I tried to achieve that by using regexp_extract but this method only lets me extract specific pieces that are always at the same position.
The bigger problem is that the parts I want to cut out are either individual/rule-based (i.e. the locale always contains of two lower case and two upper case letters separated by a underscore) or unique with no guaranteed length (i.e. the token).
Moreover, my resultset will also contain URLs with a different pattern in which I only want to cut the existing parts (e.g. https://pinball.globalzone.com/en_US/forgottenPassword, in which only en_US/ has to be cut out).
If I would have to solve the problem quickly I would just get URLs and write some piece of Java or R code to split the get URLs into pieces and iterate through the array while cutting out all parts I don't need. However, I was wondering if there is a more elegant way to get this result straight out of Hive.
What about
(?:https?:\/\/|\/[a-z]{2}_[A-Z]{2}|[?&]tic=[^&?]*)
It matches the parts you've described as unwanted. Replace that with an empty string should leave you with what you want.
See it here at regex101.
Edit
Updated to check for tic=. Should make it more stable.
And I don't know if it's what you want, but this one allows tic= to be any parameter, not only the first:
(?:https?:\/\/|\/[a-z]{2}_[A-Z]{2}|[?&]tic=[^&?\n]*)
Here at regex101
How would one efficiently match one input string against any number of regular expressions?
One scenario where this might be useful is with REST web services. Let's assume that I have come up with a number of URL patterns for a REST web service's public interface:
/user/with-id/{userId}
/user/with-id/{userId}/profile
/user/with-id/{userId}/preferences
/users
/users/who-signed-up-on/{date}
/users/who-signed-up-between/{fromDate}/and/{toDate}
…
where {…} are named placeholders (like regular expression capturing groups).
Note: This question is not about whether the above REST interface is well-designed or not. (It probably isn't, but that shouldn't matter in the context of this question.)
It may be assumed that placeholders usually do not appear at the very beginning of a pattern (but they could). It can also be safely assumed that it is impossible for any string to match more than one pattern.
Now the web service receives a request. Of course, one could sequentially match the requested URI against one URL pattern, then against the next one, and so on; but that probably won't scale well for a larger number of patterns that must be checked.
Are there any efficient algorithms for this?
Inputs:
An input string
A set of "mutually exclusive" regular expressions (ie. no input string may match more than one expression)
Output:
The regular expression (if any) that the input string matched against.
The Aho-Corasick algorithm is a very fast algorithm to match an input string against a set of patterns (actually keywords), that are preprocessed and organized in a trie, to speedup matching.
There are variations of the algorithm to support regex patterns (ie. http://code.google.com/p/esmre/ just to name one) that are probably worth a look.
Or, you could split the urls in chunks, organize them in a tree, then split the url to match and walk the tree one chunk at a time. The {userId} can be considered a wildcard, or match some specific format (ie. be an int).
When you reach a leaf, you know which url you matched
The standard solution for matching multiple regular expressions against an input stream is a lexer-generator such as Flex (there are lots of these avalable, typically several for each programming langauge).
These tools take a set of regular expressions associated with "tokens" (think of tokens as just names for whatever a regular expression matches) and generates efficient finite-state automata to match all the regexes at the same time. This is linear time with a very small constant in the size of the input stream; hard to ask for "faster" than this. You feed it a character stream, and it emits the token name of the regex that matches "best" (this handles the case where two regexes can match the same string; see the lexer generator for the definition of this), and advances the stream by what was recognized. So you can apply it again and again to match the input stream for a series of tokens.
Different lexer generators will allow you to capture different bits of the recognized stream in differnt ways, so you can, after recognizing a token, pick out the part you care about (e.g., for a literal string in quotes, you only care about the string content, not the quotes).
If there is a hierarchy in the url structure, that should be used to maximize performance. Only an url that starts with /user/ can match any of the first three and so on.
I suggest storing the hierarchy to match in a tree corresponding to the url hierarchy, where each node matches a level in the hierarchy. To match an url, test the url against all roots of the tree where only nodes with regexes for "user" and "users" are. Matching url:s are tested against the children of those nodes until a match is found in a leaf node. A succesful match can be returned as the list of nodes from the root to the leaf. Named groups with property values such as {user-id} can be fetched from the nodes of the successful match.
Use named expressions and the OR operator, i.e. "(?P<re1>...)|(?P<re2>...)|...".
First I though that I couldn't see any good optimization for this process.
However, if you have a really large number of regexes you might want to partition them (I'm not sure if this is technically partitioning).
What I tell you to do is:
Suppose that you have 20 possible urls that start with user:
/user/with-id/X
/user/with-id/X/preferences # instead of preferences, you could have another 10 possibilities like /friends, /history, etc
Then, you also have 20 possible urls starting with users:
/users/who-signed-up-on
/users/who-signed-up-on-between #others: /registered-for, /i-might-like, etc
And the list goes on for /products, /companies, etc instead of users.
What you could do in this case is using "multi-level" matching.
First, match the start of the string. You'd be matching for /products, /companies, /users, one at a time and ignoring the rest of the string. This way, you don't have to test all the 100 possibilities.
After you know the url starts with /users, you can match only the possible urls that start with users.
This way, you would reduce a lot of unneeded matches. You won't match the string for all the /procucts possibilities.