Zack
Zack

Reputation: 13972

Escaping special characters in elasticsearch

I am using the elasticsearch python client to make some queries to the elasticsearch instance that we are hosting.

I noticed that some characters need to be escaped. Specifically, these...

+ - && || ! ( ) { } [ ] ^ " ~ * ? : \

Is there a clean way to do this beyond what I've already got in mind? Surely there is a cleaner way than doing

term
    .replace("+", "\+")
    .replace("-", "\-")

    # ....etc

I was hoping there was an API call that I could use, but I can't find one in the docs. This seems like a common enough issue that it should have been solved by someone.

Does anyone know the "correct" way of doing this?

EDIT : I am still not sure if there is an API call, but I got things concise enough to where I am happy.

def needs_escaping(character):                                                                                                                                                                                        

    escape_chars = {                                                                                                                                                                                               
        '\\' : True, '+' : True, '-' : True, '!' : True,                                                                                                                                                           
        '(' : True, ')' : True, ':' : True, '^' : True,                                                                                                                                                            
        '[' : True, ']': True, '\"' : True, '{' : True,                                                                                                                                                            
        '}' : True, '~' : True, '*' : True, '?' : True,                                                                                                                                                            
        '|' : True, '&' : True, '/' : True                                                                                                                                                                         
    }                                                                                                                                                                                                              
    return escape_chars.get(character, False)   


sanitized = ''
for character in query:                                                                                                                                                                                            

    if needs_escaping(character):                                                                                                                                                                                 
        sanitized += '\\%s' % character                                                                                                                                                                           
    else:                                                                                                                                                                                                      
        sanitized += character 

Upvotes: 6

Views: 34033

Answers (4)

rmaleki
rmaleki

Reputation: 752

Yes, those characters will need to be replaced within the content you want to search in a query_string query.

import re

def escape_elasticsearch_query(query):
    return re.sub(
        '(\+|\-|\=|&&|\|\||\>|\<|\!|\(|\)|\{|\}|\[|\]|\^|"|~|\*|\?|\:|\\\|\/)',
        "\\\\\\1",
        query,
    )

Upvotes: 0

Eugene Sokolov
Eugene Sokolov

Reputation: 11

to answer the question directly, below is a cleaner python solution using re.sub

import re
KIBANA_SPECIAL = '+ - & | ! ( ) { } [ ] ^ " ~ * ? : \\'.split(' ')
re.sub('([{}])'.format('\\'.join(KIBANA_SPECIAL)), r'\\\1', val)

however a better solution is to properly parse out the bad characters that get sent to elasticsearch:

import six.moves.urllib as urllib
urllib.parse.quote_plus(val)

Upvotes: 0

HubertL
HubertL

Reputation: 19544

I adapted this code I found there:

escapeRules = {'+': r'\+',
               '-': r'\-',
               '&': r'\&',
               '|': r'\|',
               '!': r'\!',
               '(': r'\(',
               ')': r'\)',
               '{': r'\{',
               '}': r'\}',
               '[': r'\[',
               ']': r'\]',
               '^': r'\^',
               '~': r'\~',
               '*': r'\*',
               '?': r'\?',
               ':': r'\:',
               '"': r'\"',
               '\\': r'\\;',
               '/': r'\/',
               '>': r' ',
               '<': r' '}

def escapedSeq(term):
    """ Yield the next string based on the
        next character (either this char
        or escaped version """
    for char in term:
        if char in escapeRules.keys():
            yield escapeRules[char]
        else:
            yield char

def escapeESArg(term):
    """ Apply escaping to the passed in query terms
        escaping special characters like : , etc"""
    term = term.replace('\\', r'\\')   # escape \ first
    return "".join([nextStr for nextStr in escapedSeq(term)])

Upvotes: 0

femtoRgon
femtoRgon

Reputation: 33341

Yes, those characters will need to be replaced within content you want to search in a query_string query. To do that (assuming you are using PyLucene), you should be able to use QueryParserBase.escape(String).

Barring that, you could always adapt the QueryParserBase.escape source code to your needs:

public static String escape(String s) {
  StringBuilder sb = new StringBuilder();
  for (int i = 0; i < s.length(); i++) {
    char c = s.charAt(i);
    // These characters are part of the query syntax and must be escaped
    if (c == '\\' || c == '+' || c == '-' || c == '!' || c == '(' || c == ')' || c == ':'
      || c == '^' || c == '[' || c == ']' || c == '\"' || c == '{' || c == '}' || c == '~'
      || c == '*' || c == '?' || c == '|' || c == '&' || c == '/') {
      sb.append('\\');
    }
    sb.append(c);
  }
  return sb.toString();
}

Upvotes: 7

Related Questions