Suggestive search? aka "Do you mean.... ‘chips’?”

ProgressDevelop

New Member
Hi,

We have a business requirement to offer suggested alternatives if a search is initially unsuccessful.

E.g. if a customer inputs a typo of ‘chaps’ in a search field we need to first search for ‘chaps’ in a description field. If this is unsuccessful we then need to re-search for ‘similar’ words/ strings and suggest an alternative. I.e. if ‘chips’ is found during the re-search we need to return “Do you mean.... ‘chips’?” This could also be looped to return several alternatives. I.e. “Do you mean.... ‘chips’, ‘chops’?”

Can anyone offer some suggestions to how this could be done?

Progress 10.1C running under HP-UX so it needs to be CHUI.

Cheers
Keith
 
I think what you need is beyond the scope of using phonetic matches. But, if phonetic matches will do for you, you can just Google "double metaphone" and you will find a lot of stuff.

What you need is similar to what Google is offering when you do a search on Google. Google also provides this feature, the Google search which will also return "did you mean ...", as a web service too. If it's feasable for you you could use this instead of re-inventing the wheel. But you need to have internet access in order to be able to use the Google web services, plus I don't think Google will sign an SLA for this ...

Regards, RealHeavyDude.
 
I think the simplest way is, collect all the search strings into a table, and write an algorithm to it (thats what google has done I think), compare: first/last part, begins, matches, number of queries etc.
 
RHD is putting you on the right track since what you are asking for is some version of soundex or metaphone match. But, implementation will depend on what you are really trying to accomplish. If you have a name, then this kind of match does the ticket easily. I.e., compute the metaphone string, store it, then on the search compute the metaphone string of the input and look for matches and display anything that comes up the same. But, if you have a description with multiple words, this same simple trick isn't going to work for you. What you might do is to compute the coded version of each word in the description longer than a certain number of characters and then make a word index out of that and do a contains search. Or, you could extract all the unique words from the description into another table, provide each of those with the metaphone string. Then, if you do a contains search on a word index of the description and fail, you could propose any of the matching words from your "dictionary" and, if so instructed, then do a contains search with the corresponding unencoded word.

Bit of work ...
 
Thanks for all your replies.

I’ll investigate using an api as it might be a good option considering the project involves web services and I don’t want to go down the custom dictionary route or try to re-invent the wheel either.

At the moment it is probably a ‘nice to have’ and I don’t know how much time I will be given to devote to this.

Keith
 
I don't know how to do it in MFG/QAD (I don't even have a clue), but I think you could look for some "Natural Language Processing" literature out there on the web.
There are 2 ways (at least that I know) to take this kind of problem: one of them is the probabilistic approach, for example, giving some logic between the probable words (the dictionary of the system), and the words typed by the user.
The other one is the symbolic approach, where the logic implemented has to be capable of processing the word, find its components, and try to find matches between the words that the system can handle, and the rest of the words (if they do not exist in the system at first instance).
Hope this helps,
Greetings!
 
Try this:

Code:
/* client_check.p */

DEFINE VARIABLE hWebService AS HANDLE NO-UNDO.
DEFINE VARIABLE hcheckSoap AS HANDLE NO-UNDO.
CREATE SERVER hWebService.
hWebService:CONNECT("-WSDL 'http://ws.cdyne.com/SpellChecker/check.asmx?WSDL'").
RUN checkSoap SET hcheckSoap ON hWebService.
DEFINE VARIABLE BodyText AS CHARACTER NO-UNDO.
DEFINE VARIABLE DocumentSummary AS LONGCHAR NO-UNDO.

BodyText = "4GL".

RUN CheckTextBodyV2 IN hcheckSoap(INPUT BodyText, OUTPUT DocumentSummary).
/**************************************************************/
DEFINE VARIABLE hParser AS HANDLE NO-UNDO.
DEFINE VARIABLE dictTag AS CHARACTER NO-UNDO.

CREATE SAX-READER hParser.
hParser:HANDLER = THIS-PROCEDURE.
hParser:SET-INPUT-SOURCE("LONGCHAR", DocumentSummary).
hParser:SAX-PARSE( ) NO-ERROR.
IF ERROR-STATUS:ERROR THEN DO:
  IF ERROR-STATUS:NUM-MESSAGES > 0 THEN
    MESSAGE ERROR-STATUS:GET-MESSAGE(1) VIEW-AS ALERT-BOX.
  ELSE
    MESSAGE RETURN-VALUE VIEW-AS ALERT-BOX.
END.
DELETE OBJECT hParser.
DELETE PROCEDURE hcheckSoap.
hWebService:DISCONNECT().
DELETE OBJECT hWebService.
/**************************************************************/
PROCEDURE StartDocument:
END.
/**************************************************************/
PROCEDURE StartElement:
  DEFINE INPUT PARAMETER namespaceURI AS CHARACTER.
  DEFINE INPUT PARAMETER localName AS CHARACTER.
  DEFINE INPUT PARAMETER qname AS CHARACTER.
  DEFINE INPUT PARAMETER attributes AS HANDLE.
   dictTag = localName.

END.
/**************************************************************/
PROCEDURE Characters:
  DEFINE INPUT PARAMETER charData AS MEMPTR.
  DEFINE INPUT PARAMETER numChars AS INTEGER.
  DEFINE VARIABLE mResult AS CHARACTER NO-UNDO.
  mResult = TRIM(GET-STRING(charData, 1, GET-SIZE(charData))).
    MESSAGE mResult
        VIEW-AS ALERT-BOX INFO BUTTONS OK.
END.
/**************************************************************/
PROCEDURE EndElement:
  DEFINE INPUT PARAMETER namespaceURI AS CHARACTER.
  DEFINE INPUT PARAMETER localName AS CHARACTER.
  DEFINE INPUT PARAMETER qName AS CHARACTER.
  dictTag = "".
END.
/**************************************************************/
PROCEDURE EndDocument:
END.
/**************************************************************/
 
Back
Top