Text search in access database-find the exactly matched word(s) (1 Viewer)

Petercheng

Registered User.
Local time
Yesterday, 22:22
Joined
Jan 12, 2012
Messages
14
Hi Guys,

Hi Guys,
I need to your help in design of query in Access to find record containing fields with EXACTLY matched word(s).
For examples, there are three records each containing a field with the following texts:
Field 1: I have an apple.
Field 2: I have a pineapple.
Field 3: I have an apple, pineapple.

My question is: (1) How do I design a query that finds field 1 ONLY (because the field has the word “apple” in its text.). I tries something like—like “*apple*”; like “apple*”, but none of them works.
(2) How do I design a query that will find field 3 ONLY .

Thanks
Peter
 
The Criteria;
Code:
Like "*apple*"
should find all three of your records.

The following should work to locate only record 3;
Code:
Like "*apple*" And Like "*pine*"
 
John,

Thanks for your prompt reply. BUT what I need is a criteria that find the first record ONLY, not all of them, particularly record 2, which does not "apple".
Criteria
Like "*apple*" And Like "*pine*"
locate record 2, not 3.

Further more, using this type of criteria, using part of word (such as appl+wildcard ) can also locate records where the word part exists in a word in the record, so it is NOT the EXACTLY matched word in my search.

Therefore, I probably need something else for such function...

Thanks

Peter
c
 
John has given the guidelines.
We just need to look in to it.

Just check out below :
Code:
Like "* Apple."


Code:
TheText Like "*pineapple*" And TheText Like "* apple*"

Do the examples that you have provided, cover every thing that the fields will throw up ?

Thanks
 
I don't think you can do what you want in a query, you'll need to use some VBA and open a recordset on your table and compare the word you are searching for step by step.

If you split your tablefield into an array and compare each array element to your searchword using StrComp() function you might achive your goal, and if you collect the recordID of the record if a match if found you can build a new SQL string using an IN() clause with the recordID's to retrive the records where a match was found.

See attached db for some pointers.

Good luck

JR:)
 

Attachments

Janr,

Thanks a lot. I test the program you send me, and it is exactly what I want. A only issue is that it can only process (search) one word a time. Is it possible to make it to process more than one word? for example, if I type apple, it comes up with four records. If I type apple (space) mushroms, into the search box, I would like it come up with record 5, where both words exist. Of course, it would be even better, if it can process multiple words...

Would that be possible?

Thanks!
 
Sure its possible but remember that this sort of looping will become slower and slower when the searchstring gets longer and the amount of records to be searched is growing.

See attached db.

If you don't want to download the db you can copy the code into a Standard module and just call it from wherever.

Code:
Option Compare Text
Option Explicit

Public InClause As String
Private IntAnd As Integer
Private SearchItem  As Variant

Function MySearch(SearchWord As String, _
                  StrTable As String, _
                  SearchField As String, _
                  RecordID As String, _
                  Optional IdAsString As Boolean = False) As Boolean

Dim varItem     As Variant
Dim x           As Variant

If InStr(SearchWord, " ") > 0 Then
    SearchItem = Split(SearchWord, " ")
    ' Pass on the search to MultiSearch
    MySearch = MultiSearch(SearchWord, StrTable, SearchField, RecordID, IdAsString)
    Exit Function
End If

InClause = vbNullString

With CurrentDb.OpenRecordset("Select [" & RecordID & "]," & "[" & SearchField & "] From [" & StrTable & "]", dbOpenSnapshot)
    If .RecordCount > 0 Then
        Do Until .EOF
            ' First check to see if there are more than one word, if so split it into an array
            If InStr(.Fields(1), " ") > 0 Then
                varItem = Split(.Fields(1), " ")
                    'Split all the words into an array and search the array to find the searchword
                For Each x In varItem
                    'Check to see if the word is perhaps at the end of a sentence and do a check with the
                    'ending character removed
                    If IsWordEnd(Right(x, 1)) Then
                        If StrComp(SearchWord, Left(x, Len(x) - 1), vbTextCompare) = 0 Then
                            AddInClause .Fields(0), IdAsString
                            Exit For
                        End If
                    Else
                        If StrComp(SearchWord, x, vbTextCompare) = 0 Then
                            AddInClause .Fields(0), IdAsString
                            Exit For
                        End If
                    End If
                Next
                .MoveNext
            Else
                'If there is only one word in the record, check for a match and move on to the next record
                If IsWordEnd(Right(.Fields(1), 1)) Then
                    If StrComp(SearchWord, Left(.Fields(1), Len(.Fields(1)) - 1), vbTextCompare) = 0 Then
                        AddInClause .Fields(0), IdAsString
                    End If
                Else
                    If StrComp(SearchWord, .Fields(1), vbTextCompare) = 0 Then
                        AddInClause .Fields(0), IdAsString
                    End If
                End If
                .MoveNext
            End If
        Loop
    End If
End With

'Finally if we found any records, remove the trailing comma from InClause-string variable
If Len(InClause) > 0 Then
    InClause = Left(InClause, Len(InClause) - 1)
    MySearch = True
Else
    MySearch = False
End If
End Function

Function MultiSearch(SearchWord As String, _
                     StrTable As String, _
                     SearchField As String, _
                     RecordID As String, _
                     Optional IdAsString As Boolean = False) As Boolean
                     
Dim varItem     As Variant
Dim i           As Variant
Dim x           As Variant

InClause = vbNullString
IntAnd = 0

With CurrentDb.OpenRecordset("Select [" & RecordID & "]," & "[" & SearchField & "] From [" & StrTable & "]", dbOpenSnapshot)
    If .RecordCount > 0 Then
        Do Until .EOF
            ' First check to see if there are more than one word, if so split it into an array
            If InStr(.Fields(1), " ") > 0 Then
                varItem = Split(.Fields(1), " ")
                 
                For Each i In SearchItem
                    'Split all the words into an array and search the array to find the searchword
                    For Each x In varItem
                        If IsWordEnd(Right(x, 1)) Then
                            If StrComp(i, Left(x, Len(x) - 1), vbTextCompare) = 0 Then
                                IntAnd = IntAnd + 1
                            End If
                        Else
                            If StrComp(i, x, vbTextCompare) = 0 Then
                                IntAnd = IntAnd + 1
                            Else
                                IntAnd = IntAnd + 0
                            End If
                        End If
                    Next x
                Next i
                
                If IntAnd = UBound(SearchItem) + 1 Then
                    AddInClause .Fields(0), IdAsString
                    IntAnd = 0
                    .MoveNext
                Else
                    IntAnd = 0
                    .MoveNext
                End If
            Else
                'If there is only one word in the record, move on to the next record
                IntAnd = 0
                .MoveNext
            End If
        Loop
    End If
End With

'Finally if we found any records, remove the trailing comma from InClause-string variable
If Len(InClause) > 0 Then
    InClause = Left(InClause, Len(InClause) - 1)
    MultiSearch = True
Else
    MultiSearch = False
End If

ExitPoint:
Set SearchItem = Nothing

End Function

Private Sub AddInClause(RecordID As Variant, IsIdString As Boolean)
If IsIdString Then
    InClause = InClause & Chr(34) & RecordID & Chr(34) & ","
Else
    InClause = InClause & RecordID & ","
End If
End Sub

Function IsWordEnd(AnyString As String) As Boolean
    Select Case AnyString
        Case ".", " ", ",", ";", "/", ":"
            IsWordEnd = True
        Case Else
            IsWordEnd = False
    End Select
End Function

good luck

JR
 

Attachments

Last edited:
JR,

Thanks a lot for your kind help, and this is what I need exactly. Just one more question: as you said, the speed of search will become lower with increase data size. Do you have a idea for the searching time for a database with 100,000 memo fields, each have 300 words, for search for up to 3 words? Will it take minute to complete?

Thanks

Peter
 
Do you have a idea for the searching time for a database with 100,000 memo fields, each have 300 words, for search for up to 3 words? Will it take minute to complete

Don't know really you have to test it, on todays PC's it should only be milliseconds. But a millisecond here and there would add up eventually.


JR
 
JR,

I am testing the search program you wrote with my data. It works well. However. It comes up with a "run-time error'3021": no current record" message, when I enter a word "DNA". When I opened the debug window, the code .MoveNext is highlighted. Any way to fix the problem?


Thanks

Peter
 
I understand that you have a problem with the code I gave you.

You get an Run-time error and the code stops at .MoveNext, but I have that all over the procedure so it is impossible do debug it from where I am, if you want you could post a samplebase with confidential data removed so I can take a look.

But from what the error indicate is that you somehow got to the end of a recordset (.EOF) and attempt to move the cursor after .EOF that will cause it to break.

JR
 
JR,
Attached is a samplebase, which is the same base you sent me, except that I changed the last record to “Meal and fish.”, in place of “Apple.”
Now, when you enter any word of the last record, such as Meal, and, and fish, the program comes out a Run-time error “3021” no current record, no matter where I placed the cursor before running the form. Any idea to fix it?


Also, the program will find records even if an unmatched word is entered as long as there is one (not two) space before it. For example, If I entered “KKK”, no match is shown up. However, if I enter” KKK” (one space before KKK), it show up a record where there is no word KKK. Forthermore, if there are two spaces prior to the second searched word, the situation will happen again. The same for the third search word. Any idea to fix it?

Thanks a lot.
Peter
 

Attachments

Now, when you enter any word of the last record, such as Meal, and, and fish, the program comes out a Run-time error “3021” no current record, no matter where I placed the cursor before running the form. Any idea to fix it?

The new/edited record has to be saved to the table BEFORE you can search for it. Thats an easy fix, you add this code in the cmdSearch_Click() event before you call the MySearch() function.

Code:
If Me.Dirty Then Me.Dirty = False

Also the Run-time error was caused by an Error in the code which was my mistake :(

Code:
For Each x In varItem
                    'Check to see if the word is perhaps at the end of a sentence and do a check with the
                    'ending character removed
                    If IsWordEnd(Right(x, 1)) Then
                        If StrComp(SearchWord, Left(x, Len(SearchWord)), vbTextCompare) = 0 Then
                            AddInClause .Fields(0)
                           [COLOR="Red"] .MoveNext[/COLOR]
                            Exit For
                        End If
                    Else
                        If StrComp(SearchWord, x, vbTextCompare) = 0 Then
                            AddInClause .Fields(0)
                           [COLOR="red"] .MoveNext[/COLOR]
                            Exit For
                        End If
                    End If
                Next
                .MoveNext

The .MoveNext marked in RED shoulden't be there so remove them, I have edited my code in the previous post for this fault. Sorry..

Also, the program will find records even if an unmatched word is entered as long as there is one (not two) space before it.

That's was a wierd one, I have no answer to why but the space will interpered as a word so the best way to avoid it is to NOT add those. To trap for it you must clean up the searchstring BEFORE you send it to the function, Either split it in to an array and remove the array elements which is "empty" and JOIN the array back into as string again. I would not even try that one because I don't know how to.

Another way is to build a clean searchstring using a user defined function, some thing like this:

Code:
Private Function CleanString(AnyString As String) As String
Dim var     As Variant
Dim i       As Variant
Dim tmpStr  As String

var = Split(AnyString, " ")
For Each i In var
    If Len(i & "") > 0 Then
       tmpStr = tmpStr & i & " "
    End If
Next
CleanString = Trim(tmpStr) ' remove the last empty space
End Function

See attached db with the fixes

As a personal note I woulden't use space as a delimiter between words a more logical choise would have been to use "+" instead to avoid all this issues

JR
 

Attachments

JR,
Thanks a lot for your fixes. After some testing, I find it works much better now. However, I still find a very [FONT=&quot]weird situation: if I do a search with a string of words from record five ( which is: applepie and mushroom makes for apple and mushrooms), in many case, it shows up with “could not find any records that matches the search”. For examples, if I enter “applepie and mushroom”; or “and mushroom makes” or “for apple and mushrooms”, it all comes up with no match message. However, if I enter similar strings of words from other records, it comes up with matched record. It just sounds strange. Hope you will have some idea on this issue.[/FONT]
[FONT=&quot]One more question: Will it be possible to add a button on Myform that will clean up the search string (something like re-set), so new search can be initiated with clean search box. [/FONT]
[FONT=&quot] [/FONT]
[FONT=&quot]At the left low corner of the Myform on footer, there is a record displaying number of “hits”, how can I display this somewhere in Myform, so it becomes more visible?[/FONT]
[FONT=&quot] [/FONT]
[FONT=&quot]Thanks for your continued help! [/FONT]
:p
 
For examples, if I enter “applepie and mushroom”; or “and mushroom makes” or “for apple and mushrooms”, it all comes up with no match message. However, if I enter similar strings of words from other records, it comes up with matched record. It just sounds strange. Hope you will have some idea on this issue.

Nothing stange at all considering that the test for a matching record in the code is this:

Code:
If IntAnd = UBound(SearchItem) + 1 Then

In you search for "applepie and mushroom" IntAnd should equal to 3 for a match, but in actuallity it is 4. Look at the string and you see it will find the word "applepie" 1 time, "mushroom" 1 time "and" 2 times.

The code for a match should read:

Code:
If IntAnd [COLOR=red]>[/COLOR]= UBound(SearchItem) + 1 Then

Will it be possible to add a button on Myform that will clean up the search string (something like re-set), so new search can be initiated with clean search box

Sure just put somting like this in the click_event of the button

Me.txtSearch = vbNullString

At the left low corner of the Myform on footer, there is a record displaying number of “hits”, how can I display this somewhere in Myform, so it becomes more visible?

You can create a custom function in the form module and use the form's Recordset.Clone to get a count and us it your lable's Caption property

ex:

Code:
Function Getcount() As Long
Dim rs As Recordset
Set rs = Me.RecordsetClone
    rs.MoveLast
Getcount = rs.RecordCount
End Function

JR
 
JR,

Thanks a lot for your kind help. Greatly appreciated!

Now I have found another interesting issue ( a problem for me):

Some partial word can produce search match (which is something I want to avoid). For example, if I enter a partial word for apple as a, app, appl, the program comes up with some (but not all) records containing word apple. However, this is NOT true for other partial words, such as b, ba, ban.. for banana. Is it possible to have the program only find records with fully-matched complete words: That is, search with words a, ap, app, appl will not result in match with word apple? Thanks for input.

Peter
 
JR,

Thank again for your help. I tested it in the samplebase, it works well. However, when testing on real database (which has the same datastructure as the samplebase, except there are a few more columns), it comes up with a massage: [run time error “94”, Invalid use of Null]. It happens sometime with any search words entered. In debug window, code line [If IsWordEnd(Right(.Fields(1), 1)) Then] is highlighted. Anyway to fix it?
Peter
 
The error is just what it said, some of your records holds a NULL value in the field you want to search and using functions like Right() on a null field without using Nz() function as a wrapper will cause an error.

Since there is no point searching a null anyway, a null is a null, create a query the filters out any null records and pass on the queryname to the function instead of your tablename.

ex:
Code:
Select ID, SearchField, someotherField, etc
From Tablename
Where NOT [SearchField] IS NULL;

JR
 
JR,
Thanks again for your help. Now I have get two other questions:
1. (1) When searching the real database, I got some confused results. For example, When searching using single words, I got 217 records for “DNA”, 10 records for “PCR”. However, when searching with both words “DNA PCR”, I got 50 records, instead of equal to or less than 10 records (since only 10 records were found with single word “PCR” search. Is it possible to change the program so that it will find records where all search words exist?
2. (2) When entering unmatched word(s) following the first search word, instead of showing “could not any records that match the search”, it come up with various number of records (where there is no match to the search word(s). For examples, search for “study”, it finds 773 records, and “study FFF”, it finds 130 records, search for “gene”, it finds 436 records, and “gene GGGG” come up with 89 records. Search for “PCR”, it finds 10 records, “PCR KKKK”, it comes up with 1 record. Do you have any idea to fix this issue?
Thanks, Peter
 

Users who are viewing this thread

Back
Top Bottom