473,513 Members | 2,463 Online
Bytes | Software Development & Data Engineering Community
+ Post

Home Posts Topics Members FAQ


Searching Part 2

By Blair Ireland
Senior Editor, TheScripts.com

elsif ($form{'action'} eq "search_two") {
    &search_two;
}

This, as you can see, calls the sub &search_two.... outlined below;

sub search_two {
    my (@results) = &search;
    $search_num = @results;
    if ($search_num < 1) {  &nomatches; }
    &multi_match_view(@results) if ($search_num > 0);
}

As small as this looks.... this is one of the most complicated subs around. Once I explain it once though, the similar sub procedures will all just fall into place. The &search returns into the array, @results. This array contains all of the search results. Time to explain that sub search though, so make sure your coffee is ready.

sub search {
    my (%dat);
    my ($or_match) = 0;
    my ($findit,$param) = "";
    my @search_terms = ();

This is the initial declaration of some of the variables going to be used in this sub. Some are automatically set to a null value ("" or 0), and all are set to my, so they only exist within the sub procedure.

    ($form{'type'} eq 'phrase') ?
        (@search_terms = ($form{'search_term'})) :
        (@search_terms = split (/\s/, $form{'search_term'}));

This uses an operand you are probably not familiar with. The operator is called the ternary conditional operator, just as in C++. It is much like if-elsif-else statements. If the argument before the ? is true, the argument before the : is returned, otherwise the argument after the : is returned. So, in simpler, it looks like this:

    $status = ($n == 1) ? 1 : 2; And works like this:     if ($n == 1) { $status = 1; } 
    else { $status = 2; }

Anyways, so it looks if the form field named 'type' is equal to 'phrase'. If so, @search_terms will be filled with a single entry, the one found in the form field 'search_term'. If not, @search_terms becomes filled with all the individual words in 'search_term'. The split function separates them by the space in between different words.

    if ($form{'boolean'} eq "or") { $or_match = 1;}

This looks in the form field 'boolean' for a value of 'or'. If so, $or_match is initialized. Otherwise, it is left null.

    if ($or_match) { $param = '||' } else { $param = '&&'; }

Now, looking if $or_match was initialized or not, $param value becomes its appropriate boolean value. Or is also known as ||, and and is also known as &&.

    foreach $term (@search_terms) {

Now the script is looking through the @search_terms array generated a few lines back.

        next if (length($term) < 2); # skips single letter terms

Just like the comment says, if the length of the $term variable is less then 2 long (meaning 1 or less really), then it is a single, pretty much useless character. Therefore, the loop skips to the next iteration if this is so.

        if ($form{'field'} eq "everything") {
            $findit .= "/\Q$term\E/oi $param ";
        }

Here, the first part of our regular expression being created to search is made. When using 'everything', the variable we are searching is just $_, so we just need to have the / /oi part of the search. Notice the positioning of $param? This is where the boolean is placed, whether it is an || (or search) or an && (and search). It is just adding it into the string $findit, for each iteration of the foreach $term line.

        else {
            $findit .= "\$dat{\$form{'field'}} =~ /\Q$term\E/oi $param ";
        }

Pretty much the exact same as above, except the variable being searched here is not $_, bit is referenced as $dat{$form{'field'}. I escaped the $'s so that a value isnt actually inserted here, but it is exactly as shown. So, with this ones use, we must specify what variable we are looking in, plus the =~ operator.

    }
chop ($findit); chop ($findit); chop ($findit);

Chop just takes the variable fed into it, and cuts off the last character. We do this three time as the loop above would create an extra $param (2 characters), and a space, which we no longer need.

$reg = eval "sub { $findit; }";

Here is a rather complicated part if you don't understand what this syntax is. Eval is basically a perl function that evaluates code, or executes it. When contained in blocks, the code is parsed once and catches exceptions. In our use of eval, the code is parsed and executed as its own little perl program. So, in turn, now we have an anonymous sub routine that contains our regular expression we created in $findit. $reg is now set as a reference to this sub. The sub routine is used in the format

&{$reg}. $@ and print "Error Processing Search" and return;

This is error checking. $@ is a variable perl creates if an eval produces an error. So, if our $findit regular expression has syntax errors, perl creates the $@ variable, and you don't receive a nasty 500 error. Specifically in this condition, perl would print "Error Processing Search", then exit out of the sub procedure &search.

    open(DATA, $file);

Here we open $file as DATA in read-only mode

    while (<DATA>) {

This loop returns true and repeats for the length of DATA

        (/^\s*$/) and next; # Looks for blank lines
        chomp $_;

Removes the extra newline character in $_.

        if ($form{'field'} eq "everything") {
            if (&{$reg}) {    push @search_results,$_;  }
        }

Here, the user is searching in all fields. Therefore, you just really have to look at the line, without any parsing at all. Here, we make our first use of the &{$reg} sub. So, any records that pass the regular expression test are pushed into the array @search_results.         else {
            @record = &grab_data($_);
            %dat = &process_record(@record);
            if (&{$reg}) { push @search_results,$_;    }
        }

This is extremely similar to the if block above, except this one checks all of the different fields made available in the database. It probably doesn't really look much different then the above, except if you remember the contents of the $findit variable in this situation. Lets have a look at that again then. The \$dat{\$form{'field'}} =~ portion of $findit comes into play here. In this context though, it would look like $dat{$form{'field'}} =~ to the perl interpreter. $form{'field'} is just the name of the database field you are searching in. This is defined in &search_form. Don't remember? Just go back and have a look. Anyways, @record = &grab_data($_) is taking the line of input, decoding encoded characters, and pushing them into an array. This sub routine will be explained in greater detail after this section. The next line sends to the &process_record sub the array @record, and changes it into hash form, represented by %dat. This sub routine will be explained after I explain &grab_data, still a few steps ahead, so don't worry about it yet. Since the info is in %dat, the $dat{$form{'field'}} really does make sense,as the $form{'field'} just has a database field name, which is a key in the %dat array.

Now does that $findit regular expression make sense? Thought so..... I hope that does.

    }
    close (DATA);
@search_results = &search_sorter(@search_results);

« Searching Part 1 Searching Part 3 »

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.