私は現在、検索システムの一部として Irony を使用しており、誤った構文に関して数え切れないほどの問題を抱えていましたが、それらを大幅に絞り込むことができましたが、特定のキーワードに関連しているように見える最後の問題に行き詰まっています。
「or」または「and」の後に何かが続くものを検索すると、SQL 検索文字列で構文エラーが返されます。
他の問題については、不正な文字を単にエスケープするか、検索から完全に削除しましたが、これらの単語が検索の一部である可能性があるため、これは実行可能な解決策ではないようです.
Irony へのリンク: http://irony.codeplex.com/
if (!string.IsNullOrEmpty(fTextSearch))
{
sql.AppendLine("UNION");
sql.AppendLine(commonClause);
sql.AppendLine(string.Format("AND CONTAINS(nt.text, '{0}', LANGUAGE 'English')", fTextSearch));
}
IList<Product> results = query.List<Product>();
ユニオンが文字列に追加されると、異常が発生してエラーがスローされ、エラーが最後の行に表示されます。
実際のエラーは次のとおりです。
Syntax error near 'gum' in the full-text search condition 'orbit gum'.
Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.
Exception Details: System.Data.SqlClient.SqlException: Syntax error near 'gum' in the full-text search condition 'orbit gum'.
他の問題が現在どのように解決されているか。
private string[] syntaxErrorChars = { "{", "}", "[", "]", "and", "(", ")" };
private string[] errorChars = { "'", "&", "/", "\\", "~", "@" };
if (compiler.Parser.GetErrors().Count == 0)
{
AstNode root = compiler.Parse(phrase.ToLower());
if (compiler.Parser.GetErrors().Count == 0)
{
try
{
fTextSearch = SearchGrammar.ConvertQuery(root, SearchGrammar.TermType.Inflectional);
}
catch
{
fTextSearch = phrase;
}
}
else
{
fTextSearch = phrase;
}
}
else
{
fTextSearch = phrase;
}
//Escape Search String
StringBuilder sb = new StringBuilder();
string[] splitString = fTextSearch.Split(errorChars, StringSplitOptions.None);
int numNewCharactersAdded = 0;
foreach (string itm in splitString)
{
sb.Append(itm); //append string
if (fTextSearch.Length > (sb.Length - numNewCharactersAdded))
{
sb.Append(fTextSearch[sb.Length - numNewCharactersAdded]); //append splitting character
sb.Append(fTextSearch[sb.Length - numNewCharactersAdded - 1]); //append it again
numNewCharactersAdded++;
}
}
if(!string.IsNullOrEmpty(sb.ToString()))
fTextSearch = sb.ToString();
//Escaping Finished
//Remove Syntax breaking characters
for (int i = 0; i < syntaxErrorChars.Count(); i++)
{
fTextSearch = fTextSearch.Replace(syntaxErrorChars[i], string.Empty);
}
//Finished Syntax breaking characters.
エラーが発生したときの実際のクエリは次のとおりです。
SELECT DISTINCT prod.prod_id as node1_76_0_, prod.stock_code as stock2_95_0_, prod.supplier_part_number as supplier3_95_0_, prod.sold_in_multiple as sold4_95_0_, prod.free as free95_0_, prod.poa as poa95_0_, prod.allow_back_order as allow7_95_0_, prod.stock_threshold as stock8_95_0_, prod.low_stock_threshold as low9_95_0_, prod.tax_type as tax10_95_0_, prod.nominal_sale as nominal11_95_0_, prod.nominal_purchase as nominal12_95_0_, prod.commodity_code as commodity13_95_0_, prod.preferred_alternative as preferred14_95_0_, prod.role as role95_0_, prod.stock_type as stock16_95_0_, prod.weight as weight95_0_, prod.imported_by as imported18_95_0_, prod.import_code as import19_95_0_, prod.customisable as customi20_95_0_, prod.customisable_prompt as customi21_95_0_, prod.agent_only as agent22_95_0_, prod.sell_to_retail as sell23_95_0_, prod.sell_to_trade as sell24_95_0_, prod.sell_by_decimal as sell25_95_0_, prod.length as length95_0_, prod.breadth as breadth95_0_, prod.width as width95_0_, prod.hazardous as hazardous95_0_, prod.fragile as fragile95_0_, prod.liquid as liquid95_0_, prod.palletised as palletised95_0_, prod.attractive as attractive95_0_, prod.boxed as boxed95_0_, prod.charge_code as charge35_95_0_, prod.discount_code as discount36_95_0_, prod.floor_price as floor37_95_0_, prod.cost_price as cost38_95_0_, prod.crm as crm95_0_, prod.category as category95_0_, prod.sub_category as sub41_95_0_, prod_1_.version_number as version2_76_0_, prod_1_.[name] as name3_76_0_, prod_1_.visible_on_website as visible4_76_0_, prod_1_.url_part as url5_76_0_, prod_1_.parent as parent76_0_, prod_1_.bnd_id as bnd7_76_0_, prod_1_.template as template76_0_, prod_1_.custom_string_1 as custom9_76_0_, prod_1_.custom_string_2 as custom10_76_0_, prod_1_.custom_string_3 as custom11_76_0_, prod_1_.custom_string_4 as custom12_76_0_, prod_1_.custom_string_5 as custom13_76_0_, prod_1_.custom_bool_1 as custom14_76_0_, prod_1_.custom_bool_2 as custom15_76_0_, prod_1_.custom_bool_3 as custom16_76_0_, prod_1_.custom_bool_4 as custom17_76_0_, prod_1_.custom_bool_5 as custom18_76_0_, prod_1_.custom_decimal_1 as custom19_76_0_, prod_1_.custom_decimal_2 as custom20_76_0_, prod_1_.custom_decimal_3 as custom21_76_0_, prod_1_.custom_decimal_4 as custom22_76_0_, prod_1_.custom_decimal_5 as custom23_76_0_, prod_1_.created as created76_0_, prod_1_.updated as updated76_0_, prod_1_.created_by as created26_76_0_, prod_1_.updated_by as updated27_76_0_, prod_1_.flags as flags76_0_, prod_1_.active as active76_0_
FROM tbl_ecom_cat_product prod
INNER JOIN tbl_ecom_cat_node prod_1_ ON prod_1_.node_id = prod.prod_id
LEFT OUTER JOIN tbl_ecom_cat_node_text nt ON prod.prod_id = nt.node_id
LEFT OUTER JOIN tbl_ecom_shipping_service AS s ON prod.role = s.shs_id
WHERE (
s.shs_id IS NULL
)
AND ( prod_1_.visible_on_website = 1 )
AND ( prod_1_.name LIKE ?
OR prod.stock_code LIKE ?
OR prod.supplier_part_number LIKE ?
)
AND ( prod_1_.name LIKE ?
OR prod.stock_code LIKE ?
OR prod.supplier_part_number LIKE ?
)
UNION
SELECT DISTINCT prod.prod_id as node1_76_0_, prod.stock_code as stock2_95_0_, prod.supplier_part_number as supplier3_95_0_, prod.sold_in_multiple as sold4_95_0_, prod.free as free95_0_, prod.poa as poa95_0_, prod.allow_back_order as allow7_95_0_, prod.stock_threshold as stock8_95_0_, prod.low_stock_threshold as low9_95_0_, prod.tax_type as tax10_95_0_, prod.nominal_sale as nominal11_95_0_, prod.nominal_purchase as nominal12_95_0_, prod.commodity_code as commodity13_95_0_, prod.preferred_alternative as preferred14_95_0_, prod.role as role95_0_, prod.stock_type as stock16_95_0_, prod.weight as weight95_0_, prod.imported_by as imported18_95_0_, prod.import_code as import19_95_0_, prod.customisable as customi20_95_0_, prod.customisable_prompt as customi21_95_0_, prod.agent_only as agent22_95_0_, prod.sell_to_retail as sell23_95_0_, prod.sell_to_trade as sell24_95_0_, prod.sell_by_decimal as sell25_95_0_, prod.length as length95_0_, prod.breadth as breadth95_0_, prod.width as width95_0_, prod.hazardous as hazardous95_0_, prod.fragile as fragile95_0_, prod.liquid as liquid95_0_, prod.palletised as palletised95_0_, prod.attractive as attractive95_0_, prod.boxed as boxed95_0_, prod.charge_code as charge35_95_0_, prod.discount_code as discount36_95_0_, prod.floor_price as floor37_95_0_, prod.cost_price as cost38_95_0_, prod.crm as crm95_0_, prod.category as category95_0_, prod.sub_category as sub41_95_0_, prod_1_.version_number as version2_76_0_, prod_1_.[name] as name3_76_0_, prod_1_.visible_on_website as visible4_76_0_, prod_1_.url_part as url5_76_0_, prod_1_.parent as parent76_0_, prod_1_.bnd_id as bnd7_76_0_, prod_1_.template as template76_0_, prod_1_.custom_string_1 as custom9_76_0_, prod_1_.custom_string_2 as custom10_76_0_, prod_1_.custom_string_3 as custom11_76_0_, prod_1_.custom_string_4 as custom12_76_0_, prod_1_.custom_string_5 as custom13_76_0_, prod_1_.custom_bool_1 as custom14_76_0_, prod_1_.custom_bool_2 as custom15_76_0_, prod_1_.custom_bool_3 as custom16_76_0_, prod_1_.custom_bool_4 as custom17_76_0_, prod_1_.custom_bool_5 as custom18_76_0_, prod_1_.custom_decimal_1 as custom19_76_0_, prod_1_.custom_decimal_2 as custom20_76_0_, prod_1_.custom_decimal_3 as custom21_76_0_, prod_1_.custom_decimal_4 as custom22_76_0_, prod_1_.custom_decimal_5 as custom23_76_0_, prod_1_.created as created76_0_, prod_1_.updated as updated76_0_, prod_1_.created_by as created26_76_0_, prod_1_.updated_by as updated27_76_0_, prod_1_.flags as flags76_0_, prod_1_.active as active76_0_
FROM tbl_ecom_cat_product prod
INNER JOIN tbl_ecom_cat_node prod_1_ ON prod_1_.node_id = prod.prod_id
LEFT OUTER JOIN tbl_ecom_cat_node_text nt ON prod.prod_id = nt.node_id
LEFT OUTER JOIN tbl_ecom_shipping_service AS s ON prod.role = s.shs_id
WHERE (
s.shs_id IS NULL
)
AND ( prod_1_.visible_on_website = 1 )
AND CONTAINS(nt.text, 'orbit gum', LANGUAGE 'English')
]
Name: wildcard1 - Value: %gum%
Name: wildcard0 - Value: %orbit%
[SQL: SELECT DISTINCT prod.prod_id as node1_76_0_, prod.stock_code as stock2_95_0_, prod.supplier_part_number as supplier3_95_0_, prod.sold_in_multiple as sold4_95_0_, prod.free as free95_0_, prod.poa as poa95_0_, prod.allow_back_order as allow7_95_0_, prod.stock_threshold as stock8_95_0_, prod.low_stock_threshold as low9_95_0_, prod.tax_type as tax10_95_0_, prod.nominal_sale as nominal11_95_0_, prod.nominal_purchase as nominal12_95_0_, prod.commodity_code as commodity13_95_0_, prod.preferred_alternative as preferred14_95_0_, prod.role as role95_0_, prod.stock_type as stock16_95_0_, prod.weight as weight95_0_, prod.imported_by as imported18_95_0_, prod.import_code as import19_95_0_, prod.customisable as customi20_95_0_, prod.customisable_prompt as customi21_95_0_, prod.agent_only as agent22_95_0_, prod.sell_to_retail as sell23_95_0_, prod.sell_to_trade as sell24_95_0_, prod.sell_by_decimal as sell25_95_0_, prod.length as length95_0_, prod.breadth as breadth95_0_, prod.width as width95_0_, prod.hazardous as hazardous95_0_, prod.fragile as fragile95_0_, prod.liquid as liquid95_0_, prod.palletised as palletised95_0_, prod.attractive as attractive95_0_, prod.boxed as boxed95_0_, prod.charge_code as charge35_95_0_, prod.discount_code as discount36_95_0_, prod.floor_price as floor37_95_0_, prod.cost_price as cost38_95_0_, prod.crm as crm95_0_, prod.category as category95_0_, prod.sub_category as sub41_95_0_, prod_1_.version_number as version2_76_0_, prod_1_.[name] as name3_76_0_, prod_1_.visible_on_website as visible4_76_0_, prod_1_.url_part as url5_76_0_, prod_1_.parent as parent76_0_, prod_1_.bnd_id as bnd7_76_0_, prod_1_.template as template76_0_, prod_1_.custom_string_1 as custom9_76_0_, prod_1_.custom_string_2 as custom10_76_0_, prod_1_.custom_string_3 as custom11_76_0_, prod_1_.custom_string_4 as custom12_76_0_, prod_1_.custom_string_5 as custom13_76_0_, prod_1_.custom_bool_1 as custom14_76_0_, prod_1_.custom_bool_2 as custom15_76_0_, prod_1_.custom_bool_3 as custom16_76_0_, prod_1_.custom_bool_4 as custom17_76_0_, prod_1_.custom_bool_5 as custom18_76_0_, prod_1_.custom_decimal_1 as custom19_76_0_, prod_1_.custom_decimal_2 as custom20_76_0_, prod_1_.custom_decimal_3 as custom21_76_0_, prod_1_.custom_decimal_4 as custom22_76_0_, prod_1_.custom_decimal_5 as custom23_76_0_, prod_1_.created as created76_0_, prod_1_.updated as updated76_0_, prod_1_.created_by as created26_76_0_, prod_1_.updated_by as updated27_76_0_, prod_1_.flags as flags76_0_, prod_1_.active as active76_0_
FROM tbl_ecom_cat_product prod
INNER JOIN tbl_ecom_cat_node prod_1_ ON prod_1_.node_id = prod.prod_id
LEFT OUTER JOIN tbl_ecom_cat_node_text nt ON prod.prod_id = nt.node_id
LEFT OUTER JOIN tbl_ecom_shipping_service AS s ON prod.role = s.shs_id
WHERE (
s.shs_id IS NULL
)
AND ( prod_1_.visible_on_website = 1 )
AND ( prod_1_.name LIKE ?
OR prod.stock_code LIKE ?
OR prod.supplier_part_number LIKE ?
)
AND ( prod_1_.name LIKE ?
OR prod.stock_code LIKE ?
OR prod.supplier_part_number LIKE ?
)
UNION
SELECT DISTINCT prod.prod_id as node1_76_0_, prod.stock_code as stock2_95_0_, prod.supplier_part_number as supplier3_95_0_, prod.sold_in_multiple as sold4_95_0_, prod.free as free95_0_, prod.poa as poa95_0_, prod.allow_back_order as allow7_95_0_, prod.stock_threshold as stock8_95_0_, prod.low_stock_threshold as low9_95_0_, prod.tax_type as tax10_95_0_, prod.nominal_sale as nominal11_95_0_, prod.nominal_purchase as nominal12_95_0_, prod.commodity_code as commodity13_95_0_, prod.preferred_alternative as preferred14_95_0_, prod.role as role95_0_, prod.stock_type as stock16_95_0_, prod.weight as weight95_0_, prod.imported_by as imported18_95_0_, prod.import_code as import19_95_0_, prod.customisable as customi20_95_0_, prod.customisable_prompt as customi21_95_0_, prod.agent_only as agent22_95_0_, prod.sell_to_retail as sell23_95_0_, prod.sell_to_trade as sell24_95_0_, prod.sell_by_decimal as sell25_95_0_, prod.length as length95_0_, prod.breadth as breadth95_0_, prod.width as width95_0_, prod.hazardous as hazardous95_0_, prod.fragile as fragile95_0_, prod.liquid as liquid95_0_, prod.palletised as palletised95_0_, prod.attractive as attractive95_0_, prod.boxed as boxed95_0_, prod.charge_code as charge35_95_0_, prod.discount_code as discount36_95_0_, prod.floor_price as floor37_95_0_, prod.cost_price as cost38_95_0_, prod.crm as crm95_0_, prod.category as category95_0_, prod.sub_category as sub41_95_0_, prod_1_.version_number as version2_76_0_, prod_1_.[name] as name3_76_0_, prod_1_.visible_on_website as visible4_76_0_, prod_1_.url_part as url5_76_0_, prod_1_.parent as parent76_0_, prod_1_.bnd_id as bnd7_76_0_, prod_1_.template as template76_0_, prod_1_.custom_string_1 as custom9_76_0_, prod_1_.custom_string_2 as custom10_76_0_, prod_1_.custom_string_3 as custom11_76_0_, prod_1_.custom_string_4 as custom12_76_0_, prod_1_.custom_string_5 as custom13_76_0_, prod_1_.custom_bool_1 as custom14_76_0_, prod_1_.custom_bool_2 as custom15_76_0_, prod_1_.custom_bool_3 as custom16_76_0_, prod_1_.custom_bool_4 as custom17_76_0_, prod_1_.custom_bool_5 as custom18_76_0_, prod_1_.custom_decimal_1 as custom19_76_0_, prod_1_.custom_decimal_2 as custom20_76_0_, prod_1_.custom_decimal_3 as custom21_76_0_, prod_1_.custom_decimal_4 as custom22_76_0_, prod_1_.custom_decimal_5 as custom23_76_0_, prod_1_.created as created76_0_, prod_1_.updated as updated76_0_, prod_1_.created_by as created26_76_0_, prod_1_.updated_by as updated27_76_0_, prod_1_.flags as flags76_0_, prod_1_.active as active76_0_
FROM tbl_ecom_cat_product prod
INNER JOIN tbl_ecom_cat_node prod_1_ ON prod_1_.node_id = prod.prod_id
LEFT OUTER JOIN tbl_ecom_cat_node_text nt ON prod.prod_id = nt.node_id
LEFT OUTER JOIN tbl_ecom_shipping_service AS s ON prod.role = s.shs_id
WHERE (
s.shs_id IS NULL
)
AND ( prod_1_.visible_on_website = 1 )
AND CONTAINS(nt.text, 'orbit gum', LANGUAGE 'English')