Repository: deepset-ai/haystack

Issue:
Encode dquotes before substituting query in BM25 custom query
**Describe the bug**
For now, the custom query of BM25 cannot handle cases that include the double quote in the query. Such as the case with id `3791` in the `queries.jsonl` of `fiqa` datasets.
Error example: `{"_id": "3791", "text": "\"When people say 'Interest rates are at all time low!\"\" \u2026 Which interest rate are they actually referring to?\"", "metadata": {}}`

**Error message**
JSONDecodeError: Expecting ',' delimiter: line 6 column 27 (char 103)
![image](https://user-images.githubusercontent.com/49433085/208748535-be65a949-5fa8-4af8-b88c-e5ae83175313.png)

**Expected behavior**
Custom query should encode `"` to `\"` before substituting to the `$query`

**Suggest fixing**
The below fix is just can fix the case dquotes, I'm not sure that we should implement this way or not but at least I can use that to evaluate the `fiqa` using my custom query BM25_retriever. The fix is done at the pos: `haystack/document_stores/search_engine.py:1042`

![image](https://user-images.githubusercontent.com/49433085/208747385-7fb3d1b0-2abc-40c5-9d41-5f1a15688597.png)

**To Reproduce**
![image](https://user-images.githubusercontent.com/49433085/208748814-5992389e-7cac-4b62-908c-d94c6ee92ab0.png)
![image](https://user-images.githubusercontent.com/49433085/208748852-338c5136-ceea-4fe0-bd3d-d179ae87e792.png)

I have tried and successfully evaluate fiqa with the custom query by using the above way. If it is okay please let me know and I will try to create a small PR in case you need it. BTW, thank the team for creating this awesome library.


Discussion:

Resolving patch:
diff --git a/haystack/document_stores/search_engine.py b/haystack/document_stores/search_engine.py
--- a/haystack/document_stores/search_engine.py
+++ b/haystack/document_stores/search_engine.py
@@ -1064,7 +1064,7 @@ def _construct_query_body(
         elif custom_query:  # substitute placeholder for query and filters for the custom_query template string
             template = Template(custom_query)
             # replace all "${query}" placeholder(s) with query
-            substitutions = {"query": f'"{query}"'}
+            substitutions = {"query": json.dumps(query)}
             # For each filter we got passed, we'll try to find & replace the corresponding placeholder in the template
             # Example: filters={"years":[2018]} => replaces {$years} in custom_query with '[2018]'
             if filters:


Test patch:
diff --git a/test/nodes/test_retriever.py b/test/nodes/test_retriever.py
--- a/test/nodes/test_retriever.py
+++ b/test/nodes/test_retriever.py
@@ -272,6 +272,14 @@ def test_elasticsearch_custom_query():
     results = retriever.retrieve(query="test", filters={"years": ["2020", "2021"]})
     assert len(results) == 4
 
+    # test linefeeds in query
+    results = retriever.retrieve(query="test\n", filters={"years": ["2020", "2021"]})
+    assert len(results) == 3
+
+    # test double quote in query
+    results = retriever.retrieve(query='test"', filters={"years": ["2020", "2021"]})
+    assert len(results) == 3
+
     # test custom "term" query
     retriever = BM25Retriever(
         document_store=document_store,