2010-03-12 3 views
2

У меня есть страница поиска со следующими сценариями, перечисленными ниже. Мне сказали, что использование многих условных утверждений ухудшает производительность, но я не знаю, есть ли другой способ достичь той же цели.Написание оптимизированной и эффективной поисковой системы с mySQL и ColdFusion

  1. Search.cfm будет обрабатывать поиск, сделанный из строки поиска, присутствующей на всех страницах, с одним поисковым вводом (titleName).
  2. Если search.cfm обращается вручную (через URL-адрес, не используя простую панель поиска на всех страницах), он отображает расширенную форму поиска с тремя входами (titleName, genreID, platformID (динамически сгенерированный, но не в этом примере кода) или оценивает переменную searchResponse и решает, какую часть страницы показывать.
    1. Если простой поиск/расширенный поисковый запрос пуст, не имеет результатов или менее 3 символов, он отображает ошибку
    2. Если любой успешный поиск возвращает результаты, они возвращаются в обычном режиме. Существует одна функция обработки запросов.

Логика топ-страницы выглядит следующим образом:

<!---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> 

Является ли моя логика а) неэффективной, потому что мои инструкции if/есть лучший способ сделать это? И б) Можете ли вы увидеть сценарии, где мой код может сломаться? Я тестировал его, но я не смог найти с ним никаких проблем. И у меня нет возможности измерить производительность. Любые мысли и идеи будут высоко оценены.

Вот моя функция, для справки:

<!---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> 

Большое спасибо

ответ

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 в коде.

+0

Спасибо Soldarnal. Замечание здесь: оно работает только с get, поэтому я не уверен, что вы имели в виду, улучшив работу пользователей. Если я ошибаюсь, выбора не будет ... – Mohamad

+0

О, если вы хотите использовать «пост», а не «get», вы можете заменить любой экземпляр «url» обратно «формой», и он должен работать. Извините за путаницу. См. Http://stackoverflow.com/questions/46585/when-do-you-use-post-and-when-do-you-use-get для получения дополнительной информации о GET vs POST. – Soldarnal

1

Вы думали об использовании полнотекстовый поиск, а не только конкретные вещи, такие как название, жанр и платформы? Полный текст намного быстрее, чем обычный запрос таблицы за счет увеличения объема дискового пространства и позволяет использовать очень гибкие поисковые запросы в стиле Google во всех ваших данных. Посмотрите ссылку ниже.

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

Возможно, если вы сделали это, вы могли бы просто осуществить проход, хотя пользовательский ввод в полнотекстовый поиск, так как он предоставляет все современные поисковые выражения через его собственный синтаксис. Делая это, вам, вероятно, не понадобится разветвлять вашу логику переднего конца на основе того, что они ищут, это сделает решение более чистым и быстрым.

+0

Спасибо Джеймсу. На самом деле жанр и платформа фактически ищут идентификаторы, которые автоматически генерируются из запроса. Я пропустил эту часть, чтобы упростить мой код. Поэтому в этом смысле я не уверен, что полный текстовый поиск по-прежнему полезен для меня. Я думал, что использование этой логики для отображения результатов страницы считается лучшей практикой. – Mohamad

+0

Эти идентификаторы могут быть частью полного текстового индекса, вы также можете обновлять индекс с регулярными интервалами, если необходимо изменить идентификатор. Идея состоит в том, что вы можете положить туда все, что захотите. – James

1

Предполагаю, что с момента появления Google люди ожидают, что одно окно поиска сделает все. Вы можете предоставить некоторые расширенные возможности поиска непосредственно из простого окна, протестировав входные данные и условно выполнив отдельные запросы и вернув комбинированные результаты. Чтобы сделать результаты более четкими, вы можете выделить соответствующую часть с 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 - просто не забудьте вернуть пустой запрос, если результата нет.

+0

Спасибо за вход, Дэн. Чтобы прояснить разницу между простым поиском и расширенным поиском а), чтобы различать две разные формы, и б) простой поиск имеет один вход, в то время как расширенный поиск имеет дополнительные условия, такие как жанр и платформа (которые фактически являются динамически сгенерированными раскрывающимися списками). Я внес изменения и объединил функцию поиска в одну. Я различал простой и продвинутый, вызывая тот же метод, но каждый раз передавал ему разные значения. Я обновил код на странице, чтобы включить мою функцию. Могу ли я интегрировать ваше предложение, если мой код в этом формате? – Mohamad

+0

Вы можете использовать код как есть. Это просто другой взгляд на ту же проблему. –

1

Мои коллеги сказали мне, что мы должны упростить Cfif-отчетность:

С «Cfif что-то EQ„“» мы переехали в «Cfif не Len (что-то)», и мы работаем больше с булевы для NoResult и HasResult мы будем использовать логическое значение.

В любом случае, другие ответы - отличное вдохновение, мне нужно проверить мою собственную страницу поиска;)

Смежные вопросы