使用mySQL和ColdFusion编写优化且高效的搜索引擎

时间:2010-03-12 17:17:18

标签: mysql search coldfusion search-engine

我有一个搜索页面,其中列出了以下方案。我被告知使用如此多的条件语句会降低性能,但我不知道是否有任何其他方法可以实现相同的目标。

  1. Search.cfm将处理从所有页面上的搜索栏进行的搜索,并带有一个搜索输入(titleName)。
  2. 如果手动访问search.cfm(通过URL而不是通过所有页面上的简单搜索栏),它会显示一个带有三个输入的高级搜索表单(titleName,genreID,platformID(动态生成,但不在此示例代码中)或者它评估searchResponse变量并决定要显示的页面的哪个部分。
    1. 如果简单搜索/高级搜索查询为空,没有结果或少于3个字符则显示错误
    2. 如果任何成功的搜索返回结果,它们会正常返回。有一个处理查询的函数。
  3. 页面顶部逻辑如下:

        <!---DEFINE DEFAULT STATE--->
    <cfparam name="variables.searchResponse" default="">
    
    <!---CHECK TO SEE IF SEARCH A FORM WAS SUBMITTED AND EXECUTE SEARCH IF IT WAS--->
    <cfif IsDefined("Form.simpleSearch") AND Len(Trim(Form.titleName)) LTE 2>
        <cfset variables.searchResponse = "invalidString">
    <cfelseif IsDefined("Form.simpleSearch") AND Len(Trim(Form.titleName)) GTE 3>
        <cfinvoke component="gz.cfcomp.search" method="searchTitles" titleName="#Form.titleName#" genreID="" platformID="" returnvariable="searchResult">
        <cfset variables.searchResponse = "hasResult">
    </cfif>
    
    <!---CHECK IF ADVANCED SEARCH FORM WAS SUBMITTED--->
    <cfif IsDefined("Form.advancedSearch") AND (Len(Trim(Form.titleName)) LTE 2 AND Len(Form.genreID) IS 0 AND Len(Form.platformID) IS 0)>
        <cfset variables.searchResponse = "invalidString">
    <cfelseif IsDefined("Form.advancedSearch") AND variables.searchResponse IS NOT "invalidString">
        <cfinvoke component="gz.cfcomp.search" method="searchTitles" returnvariable="searchResult" titleName="#Form.titleName#" genreID="#Form.genreID#" platformID="#Form.platformID#">
        <cfset variables.searchResponse = "hasResult">
    </cfif>
    
    <!---CHECK IF ANY RECORDS WERE FOUND--->
    <cfif IsDefined("variables.searchResult") AND searchResult.RecordCount IS 0>
        <cfset variables.searchResponse = "noResult">
    </cfif>
    

    我正在使用searchResponse变量根据以下情况决定页面显示的内容:

    <!---ALWAYS DISPLAY SIMPLE SEARCH BAR AS IT'S PART OF THE HEADER--->
    <form name="simpleSearch" action="search.cfm" method="post">
    <input type="hidden" name="simpleSearch" />
    <input type="text" name="titleName" />
    <input type="button" value="Search" onclick="form.submit()" />
    </form>
    
    <!---IF NO SEARCH WAS SUBMITTED DISPLAY DEFAULT FORM--->
    <cfif searchResponse IS "">
        <h1>Advanced Search</h1>
        <!---DISPLAY FORM--->
        <form name="advancedSearch" action="search.cfm" method="post">
            <input type="hidden" name="advancedSearch" />
            <input type="text" name="titleName" />
            <input type="text" name="genreID" />
            <input type="text" name="platformID" />
            <input type="button" value="Search" onclick="form.submit()" />
        </form>
    </cfif>
    
    <!---IF SEARCH IS BLANK OR LESS THAN 3 CHARACTERS DISPLAY ERROR MESSAGE--->
    <cfif searchResponse IS "invalidString">
        <cfoutput>
            <h1>INVALID SEARCH</h1>
        </cfoutput>
    </cfif>
    
    <!---IF SEARCH WAS MADE BUT NO RESULTS WERE FOUND--->
    <cfif searchResponse IS "noResult">
        <cfoutput>
            <h1>NO RESULT FOUND</h1>
        </cfoutput>
    </cfif>
    
    <!---IF SEARCH MADE AND RESULT WAS FOUND--->
    <cfif searchResponse IS "hasResult">
        <cfoutput>
            <h1>Search Results</h1>
        </cfoutput>
        <cfoutput query="earchResult">
            <!---DISPLAY QUERY DATA--->
        </cfoutput>
    </cfif>
    

    我的逻辑是否a)效率不高,因为我的if语句/是否有更好的方法来做到这一点?并且b)你能看到我的代码可以破解的任何场景吗?我已经测试了它,但我一直无法找到任何问题。我无法衡量绩效。任何想法和想法将不胜感激。

    这是我的功能,供参考:

    <!---SEARCH--->
    <cffunction name="searchTitles" hint="This functions searches for a title based on multiple categories" access="public" output="false">
        <cfargument name="titleName" required="no" type="string" hint="Search by title">
        <cfargument name="genreID" required="no" type="string" hint="Search by genre">
        <cfargument name="platformID" required="no" type="string" hint="Search by platform">
        <!--- DEFINE LOCAL VARIABLES - NOTE VARIABLE NAME IS QUERY NAME --->
        <cfset var searchResult = "">
        <!---GET RESULTS--->
        <cfquery name="searchResult" datasource="myDSN">
            SELECT
                games.gameID,
                games.gameReleaseDate AS rDate,
                titles.titleName AS tName,
                titles.titleShortDescription AS sDesc,
                platforms.platformName AS pName,
                genres.genreName AS gName
            FROM
                games
                INNER JOIN titles ON titles.titleID = games.titleID
                INNER JOIN platforms ON games.platformID = platforms.platformID
                INNER JOIN genres ON games.genreID = genres.genreID
            WHERE
                0=0
                <cfif ARGUMENTS.titleName IS NOT "">
                    AND titles.titleName LIKE <cfqueryparam cfsqltype="cf_sql_varchar" value="%#ARGUMENTS.titleName#%">
                </cfif>
                <cfif ARGUMENTS.genreID IS NOT "">
                    AND games.genreID = <cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.genreID#">
                </cfif>
                <cfif ARGUMENTS.platformID IS NOT "">
                    AND games.platformID = <cfqueryparam cfsqltype="cf_sql_varchar" value="#ARGUMENTS.platformID#">
                </cfif>
            ORDER BY
                rDate DESC,
                tName;
        </cfquery>
        <cfreturn searchResult>
    </cffunction>
    

    非常感谢

4 个答案:

答案 0 :(得分:1)

您是否考虑过使用全文搜索而不仅仅是标题,流派和平台等具体内容?全文比普通表查询快得多,代价是更多的磁盘空间使用,并允许对所有数据进行非常灵活的Google样式搜索。请看下面的链接。

http://dev.mysql.com/doc/refman/5.0/en/fulltext-search.html

也许如果你这样做,你可以通过用户输入实现全文搜索,因为它通过它自己的语法提供所有高级搜索表达式。通过这样做,您可能不需要根据他们搜索的内容来分支您的前端逻辑,这将使解决方案更清晰,更快。

答案 1 :(得分:1)

我认为只是一种偏好,但自谷歌问世以来,人们希望一个搜索框可以做任何事情。您可以通过测试输入并有条件地运行单独的查询并返回组合结果,直接从简单框中提供一些高级搜索功能。为了使结果更清晰,您可以使用Coldfusion或javascript突出显示匹配部分。

我也会初始化组件而不是多次使用cfinvoke,但我不确定这是一个要求还是一个偏好。

<!--- By setting a default, we don't have to test for this var's existence repeatedly --->
<cfparam name="form.simpleSearch" default="" />

<cfscript>
// Using cfscript for compactness in this little box, could use tag based cf also

// Init myComponent so that it's ready to receive searches
searcher = createObject('component','myComponent');    

// init query results
simpleResult = queryNew("id");
genres = queryNew("id");
platforms = queryNew("id");
titles = queryNew("id");

// q: shorthand 'Query' var that's trimmed and prechecked for length
q = "";
if (len(trim(form.simpleSearch)) GTE 3) {
    q = trim(form.simpleSearch);
}

// Run simple search - Should returns a query object regardless of result count
if (len(q) {
    simpleResult = searcher.simpleSearch(q);

    /* Not sure what the difference between this and simpleSearch() is unless
    this only looks for exact matches, and simpleSearch() uses LIKE and %
    wildcards for looser results. */
    titles = searcher.titleSearch(q);

    /* Conditionally run advanced searches
       - assumes that genreID and platformID are numeric
       - IF genreID and platformID are not numeric, you could use regex to test for their signature or just run them anyway knowing they wouldn't match in many cases. (BUT you must, must MUST use cfqueryparam if you attempt that)
       - Requires that advancedSearch is split into three separate functions
       - Alternately, the simpleSearch(q) or advancedSearch() could do this all internally
       - All of these functions should return an empty query if no results are found
    if (isNumeric(q)) {
        genres = searcher.genreSearch(q);
        platforms = searcher.platformSearch(q);
    }
}

</cfscript>

...

<!---ALWAYS DISPLAY SIMPLE SEARCH BAR AS IT'S PART OF THE HEADER--->
<cfoutput>    
    <form action="#cgi.script_name#" method="post">
    <input type="text" name="simpleSearch" value="#form.q#" />
    <input type="submit" value="Search" name="submit" />
</form>

<cfif titles.recordcount>
    <h3>Matching Titles</h3>
    <cfloop query="titles">
        ..html to output the titles
    </cfloop>
</cfif>

<cfif genres.recordcount>
    <h3>Matching Genres</h3>
    <cfloop query="genres">
        ..html to output the genres
    </cfloop>
</cfif>

<cfif platforms.recordcount>
    <h3>Matching Platforms</h3>
    <cfloop query="platforms">
        ..html to output the platforms
    </cfloop>
</cfif>

<cfif simpleResult.recordcount>
    <h3>Results</h3>
    <cfloop query="simpleResult">
        ..html to output the simpleResult
    </cfloop>
</cfif>

</cfouput>

当然,有许多不同的方法可以做到这一点。这只是降低简单搜索与高级搜索复杂性的一种方法。只有匹配时才会显示高级结果,并且您可以使用myComponent函数中的查询调整这些结果 - 如果没有结果,只需记住返回空白查询。

答案 2 :(得分:1)

如果你的genreID和platformID参数添加了默认值“”,那么我认为你可以将你的顶级代码重构为:

<cfif StructKeyExists(url, "titleName") and Len(Trim(url.titleName)) lte 2>
    <cfset variables.searchResponse = "invalidString">
<cfelseif StructKeyExists(url, "titleName")>
    <cfinvoke component="gz.cfcomp.search" method="searchTitles" returnvariable="searchResult" argumentcollection="#url#">
    <cfset variables.searchResponse = "hasResult">
</cfif>

<cfif searchResponse eq "hasResult" and searchResult.recordCount eq 0>
    <cfset variables.searchResponse = "noResult">
</cfif>

注意:我建议您将表单方法切换为“获取”以进行此类搜索,以改善用户浏览网站的体验。我已经在代码中将所有表单引用切换为url。

答案 3 :(得分:1)

我的同事告诉我,我们应该简化CFIF声明:

从'CFIF something EQ'“'我们转移到'CFIF Not Len(某事物)',我们更多地使用布尔值为NoResult和HasResult使用布尔值。

无论如何,其他答案都是一些很好的灵感,我需要查看我自己的搜索页面;)