Prepending an * (asterisk) to a Fulltext Search in MySQL - mysql

I understand that the asterisk is a wildcard that can be appended to the end of fulltext search words, but what if my searched keyword is a suffix? For example, I want to be able to search for "ames" and have a result that contains the name "james" returned. Here is my current query which does not work because you cannot prepend asterisks to fulltext searches.
SELECT * FROM table WHERE MATCH(name, about, address) AGAINST ("*$key*" IN BOOLEAN MODE)
I would simply switch to using LIKE, but it would be way too slow for the size of my database.

What you could do is create another column in your database with full-text search index, this new column should have the reversed string of the column you are trying to search on, and you will reverse the search query and use it to search on the reversed column, here is how the query will look like:
SELECT * FROM table WHERE MATCH(column1) AGAINST ("$key*" IN BOOLEAN MODE) OR MATCH(reversedColumn1) AGAINST ("$reveresedkey*" IN BOOLEAN MODE)
the first condition
MATCH(column1) AGAINST ("$key*" IN BOOLEAN MODE)
example:
reversedColumn1==>Jmaes $reveresedkey*==>ames*
will search for words that start with ames ==> no match
the seconds condition
MATCH(reversedColumn1) AGAINST ("$reveresedkey*" IN BOOLEAN MODE)
example:
reversedColumn1==>semaJ $reveresedkey*==>sema*
will search for words that end with ames ==> we have a match
This might not be a bad idea if your text is short:

Can't be done due to limitation of MySQL. Values are indexed left-to-right, not right-to-left. You'll have to stick with LIKE if you want wildcards prepended to search string.

Related

MySQL - Match Against not search on single string [duplicate]

I understand that the asterisk is a wildcard that can be appended to the end of fulltext search words, but what if my searched keyword is a suffix? For example, I want to be able to search for "ames" and have a result that contains the name "james" returned. Here is my current query which does not work because you cannot prepend asterisks to fulltext searches.
SELECT * FROM table WHERE MATCH(name, about, address) AGAINST ("*$key*" IN BOOLEAN MODE)
I would simply switch to using LIKE, but it would be way too slow for the size of my database.
What you could do is create another column in your database with full-text search index, this new column should have the reversed string of the column you are trying to search on, and you will reverse the search query and use it to search on the reversed column, here is how the query will look like:
SELECT * FROM table WHERE MATCH(column1) AGAINST ("$key*" IN BOOLEAN MODE) OR MATCH(reversedColumn1) AGAINST ("$reveresedkey*" IN BOOLEAN MODE)
the first condition
MATCH(column1) AGAINST ("$key*" IN BOOLEAN MODE)
example:
reversedColumn1==>Jmaes $reveresedkey*==>ames*
will search for words that start with ames ==> no match
the seconds condition
MATCH(reversedColumn1) AGAINST ("$reveresedkey*" IN BOOLEAN MODE)
example:
reversedColumn1==>semaJ $reveresedkey*==>sema*
will search for words that end with ames ==> we have a match
This might not be a bad idea if your text is short:
Can't be done due to limitation of MySQL. Values are indexed left-to-right, not right-to-left. You'll have to stick with LIKE if you want wildcards prepended to search string.

Cannot change InnoDB full text minimum word length

I have a MySQL 5.7.31 InnoDB table with full text index enabled...
if I search for a longer word, I get results:
SELECT * FROM my_table WHERE match(my_title) against('landscape in' IN BOOLEAN MODE)
if I search full text for short word (e.g in), I get no results
SELECT * FROM my_table WHERE match(my_title) against('in' IN BOOLEAN MODE)
the data is there, I can find it with like %% query:
SELECT * FROM my_table WHERE my_title LIKE '%in%'
I set these two in /etc/my.cnf, I understand one is for InnoDB and one for MyIsam, I restarted MySQL, I still cannot run the above short full text query.
ft_min_word_len=1
innodb_ft_min_token_size=1
Edit:
If I have a value like landscape in Paris, then I get data for against('+landscape +Paris' IN BOOLEAN MODE) but NOT for against('+landscape +in +Paris' IN BOOLEAN MODE)
Is in a reserved word maybe ?
"in" is probably in the "stop list". Change the specification of the stoplist file.
After changing the min-len or the stoplist, you must rebuild the Fulltext index(es). (Restarting MySQL is not needed.)
An alternative I used on one situation: I added + to long words. For example, against('+landscape in +Paris' IN BOOLEAN MODE) would probably achieve your goal without changing either the min-len or the stopword list.
(Yes, there are several 'differences' between MyISAM and InnoDB. I have not found a definitive list in the docs. Here's my attempt at such a list: http://mysql.rjweb.org/doc.php/myisam2innodb#fulltext )

MySQL match against +(search query) not working but +search +query is

I have a textinput field where users can enter a search query which I am performing a match against on in MySQL. The user can enter multiple words or single words however when I use +(search query) I get no results however when I use +search +query I get the desired results.
Works
MATCH (title) AGAINST ('+new +painting' IN BOOLEAN MODE)
Does not work
MATCH (title) AGAINST ('+(new painting)' IN BOOLEAN MODE)
First time user of MATCH AGAINST so not sure what I am doing wrong.
Thanks
For all titles with the string "new painting":
MATCH (title) AGAINST ('+"new painting"' IN BOOLEAN MODE)
For all titles with boths the strings "new" and "painting":
MATCH (title) AGAINST ('+new +painting' in BOOLEAN MODE)
For more options, and different operators, consult the documentation:
http://dev.mysql.com/doc/refman/5.1/en/fulltext-boolean.html

MySQL: Use plus sign together with wildcard in fulltext search

I've tried the following query without any success; the initial wildcard for every word is for some reason ignored.
SELECT * FROM table WHERE MATCH(field) AGAINST("+*foo* +*bar*" IN BOOLEAN MODE)
I've also tried to use brackets, but with no different result:
SELECT * FROM table WHERE MATCH(field) AGAINST("+(*foo*) +(*bar*)" IN BOOLEAN MODE)
Shouldn't any of these work?
Mysql fulltext indexes support only searching for prefixes. So you can search for "foo*" but not "*foo*"
More information

mysql boolean mode fulltext search with wildcards and literals

I'm pretty new to MySQL full-text searches and I ran into this problem today:
My company table has a record with "e-magazine AG" in the name column. I have a full-text index on the name column.
When I execute this query the record is not found:
SELECT id, name FROM company WHERE MATCH(name) AGAINST('+"e-magazi"*' IN BOOLEAN MODE);
I need to work with quotes because of the dash and to use the wildcard because I implement a "search as you type" functionality.
When I search for the whole term "e-magazine AG", the record is found.
Any ideas what I'm doing wrong here? I read about adding the dash to the list of word characters (config update needed) but I'm searching for a way to do this programmatically.
This clause
MATCH(name) AGAINST('+"e-magazi"*' IN BOOLEAN MODE);
Will search for a AND "e" AND NOT "magazi"; i.e. the - inside "e-magazi" will be interpreted as a not even though it is inside quotation marks.
For this reason it will not work as expected.
A solution is to apply an extra having clause with a LIKE.
I know this having is slow, but it will only be applied to the results of the match, so not too many rows should be involved.
I suggest something like:
SELECT id, name
FROM company
WHERE MATCH(name) AGAINST('magazine' IN BOOLEAN MODE)
HAVING name LIKE '%e-magazi%';
MySQL fulltext treats the word e-magazine in a text as a phrase and not as a word. Because of that it results the two words e and magazine. And while it builds the search index it does not add the e to the index because of the ft_min_word_len (default is 4 chars).
The same length limitation is used for the search query. That is the reason why a search for e-magazine returns exactly the same results as a-magazine because a and - is fully ignored.
But now you want to find the exact phrase e-magazine. By that you use the quotes and that is the complete correct way to find phrases, but MySQL does not support operators for phrases, only for words:
https://dev.mysql.com/doc/refman/5.7/en/fulltext-boolean.html
With this modifier, certain characters have special meaning at the beginning or end of words in the search string
Some people would suggest to use the following query:
SELECT id, name
FROM company
WHERE MATCH(name) AGAINST('e-magazi*' IN BOOLEAN MODE)
HAVING name LIKE 'e-magazi%';
As I said MySQL ignores the e- and searches for the wildcard word magazi*. After those results are optained it uses HAVING to aditionally filter the results for e-magazi* including the e-. By that you will find the phrase e-magazine AG. Of course HAVING is only needed if the search phrase contains the wildcard operator and you should never use quotes. This operator is used by your user and not you!
Note: As long you do not surround the search phrase with % it will find only fields that start with that word. And you do not want to surround it, because it would find bee-magazine as well. So maybe you need an additional OR HAVING name LIKE ' %e-magazi%' OR HAVING NAME LIKE '\\n%e-magazi%' to make it usable inside of texts.
Trick
But finally I prefer a trick so HAVING isn't needed at all:
If you add texts to your database table, add them additionally to a separate fulltext indexed column and replace words like up-to-date with up-to-date uptodate.
If a user searches for up-to-date replace it in the query with uptodate.
By that you can still find specific in user-specific but up-to-date as well (and not only date).
Bonus
If a user searches for -well-known huge ports MySQL treats that as not include *well*, could include *known* and *huge*. Of course you could solve that with an other extra query variant as well, but with the trick above you remove the hyphen so the search query looks simply like that:
SELECT id
FROM texts
WHERE MATCH(text) AGAINST('-wellknown huge ports' IN BOOLEAN MODE)