On this page

Skip to content

Elasticsearch QueryString Query Syntax Notes

I recently started storing logs in Elasticsearch, but doing so requires a convenient UI for querying. For me, Kibana's features are too complex, and using Postman for queries is cumbersome. I eventually discovered a tool called Elasticvue, which I chose because it supports various browser extensions and desktop application versions.

The query interface for Elasticvue is primarily based on the query_string syntax, which is an extended version of the Lucene query syntax in Elasticsearch. For users familiar with SQL, query_string is easier to pick up than other DSL syntaxes, so I have compiled some commonly used syntax for quick reference.

You can check the Elasticvue official website for various browser extensions or installation files/links for the desktop application.

Tested version: Elasticsearch 9.1.4

Basic Syntax

Basic API Structure

json
{
  "query": {
    "query_string": {
      "query": "your query string here",     // Required parameter: query string (use "*" to search all documents)
      "default_field": "content",            // Optional parameter: default search field; if not specified, defaults to "*", searching all fields
      "default_operator": "OR"               // Optional parameter: default operator, defaults to OR
      // Other optional parameters
    }
  },
  "size": 10,                                // Optional parameter: number of results to return, defaults to 10
  "from": 0,                                 // Optional parameter: starting position, defaults to 0
  "sort": []                                 // Optional parameter: result sorting
}

When no field is specified, QueryString searches across all searchable fields.

Query Syntax

Simple keyword search:

text
apple

Multiple terms (defaults to OR connection):

text
apple banana

Search Behavior for Different Field Types

Elasticsearch uses different analysis strategies for different field types (official documentation):

  • text field: Uses the standard analyzer, which tokenizes and converts to lowercase.
  • keyword field: Uses the keyword analyzer, keeping the complete string unchanged.
  • Numeric/Date/Boolean fields: Do not use an analyzer; they index the raw value.
  • query_string: Selects the corresponding strategy based on the target field type to process the query string.
Field TypeQuery StringBehavior Description
textapple bananaEquivalent to apple OR banana, matches if any term is found
keyword"apple" "banana"Must use double quotes to explicitly search for multiple complete strings, or use apple OR banana
keywordapple bananaNo results, as it is treated as a single complete string match
Numeric/Date/Boolean"123" "456"Multi-value search must explicitly specify values with double quotes, or use 123 OR 456
Numeric/Date/Boolean123 456No results, as query_string does not automatically split multiple complete values

JSON Multi-term Query Example (using escaped double quotes):

json
{
  "query": {
    "query_string": {
      "query": "\"apple\" \"banana\""
    }
  }
}

Exact phrase search (using double quotes):

text
"red apple"

2. Boolean Operators

AND operator (both terms must exist):

text
apple AND banana

OR operator (at least one term must exist):

text
apple OR banana

NOT operator (excludes documents containing the term):

text
apple NOT banana

Plus sign (must include this term):

text
+apple banana

Minus sign (must exclude this term):

text
apple -banana

WARNING

English operators must be in all caps.

3. Field-Specific Queries

Search in a specific field:

text
title:apple

Search across multiple fields:

text
title:apple AND content:banana

Multi-value query (OR condition):

text
user_id:(1234 OR 5678)

Field existence query:

text
_exists_:email        // Query documents where the email field exists (not null)
NOT _exists_:phone    // Query documents where the phone field does not exist (is null)

Field specification priority: Explicit field in query string > fields parameter > default_field parameter.

4. Range Queries and Comparison Operators

Range Queries:

text
price:[10 TO 20]    // Closed interval, includes 10 and 20
price:{10 TO 20}    // Open interval, excludes 10 and 20
price:[10 TO *]     // Greater than or equal to 10
price:[* TO 20]     // Less than or equal to 20

Comparison Operators:

text
price:>10           // Greater than 10
price:>=10          // Greater than or equal to 10
price:<20           // Less than 20
price:<=20          // Less than or equal to 20

These two writing styles are functionally similar, and you can choose based on the context. However, testing shows that range query syntax has better compatibility across various parameter combinations. Here are test examples:

Limitations on using comparison operators:

Comparison operators (>, >=, <, <=) cause errors when used with the fields array parameter, but work correctly when using default_field or specifying the field directly in the query string.

Working syntax:

json
// Method 1: No field specified
{
  "query": {
    "query_string": {
      "query": ">=10"
    }
  }
}

// Method 2: Specify field in query string
{
  "query": {
    "query_string": {
      "query": "price:>=10"
    }
  }
}

// Method 3: Use default_field parameter
{
  "query": {
    "query_string": {
      "query": ">=10",
      "default_field": "price"
    }
  }
}

Syntax that causes errors:

json
// Using the fields array causes the query to fail
{
  "query": {
    "query_string": {
      "query": ">=10",
      "fields": ["price"]
    }
  }
}

Solution: Use range query syntax instead

Range query syntax ([x TO y]) is compatible with the fields parameter and works correctly:

json
{
  "query": {
    "query_string": {
      "query": "[10 TO *]",
      "fields": ["price"]
    }
  }
}

Wildcard search:

text
te?t      // Question mark represents one character
test*     // Asterisk represents zero or more characters

6. Date and Time Queries

Basic Date Range Queries

Use range syntax for date queries:

text
timestamp:[2023-01-01 TO 2023-01-31]

You can also use relative time for queries:

text
timestamp:>now-1d  // Query data from the past 24 hours

Common Relative Time Expressions:

  • now-1h: One hour ago
  • now-1d: One day ago
  • now-1w: One week ago
  • now/d: Start of today (00:00:00)
  • now/w: Start of this week
  • now/M: Start of this month

Date and Time Formats

The Elasticsearch date type defaults to millisecond precision; if nanosecond precision is needed, use the date_nanos type.

Supported Formats:

  • Standard format: yyyy-MM-ddTHH:mm:ss.SSSZ (e.g., 2023-01-15T08:30:00.000Z)
  • Simplified format: yyyy-MM-dd (e.g., 2023-01-15)
  • Default timezone is UTC.

Format Behavior Differences:

Different date precision formats have different behaviors during queries. Key differences:

json
// Querying by "year" matches the first second of that year
{
  "query": {
    "query_string": {
      "query": "timestamp:2023"  // Equivalent to ="2023-01-01T00:00:00Z"
    }
  }
}

// Querying by "month" matches the first second of that month
{
  "query": {
    "query_string": {
      "query": "timestamp:2023-02"  // Equivalent to ="2023-02-01T00:00:00Z"
    }
  }
}

// When precision reaches "day", behavior changes
// This queries the entire range of that date
{
  "query": {
    "query_string": {
      "query": "timestamp:2023-03-01"  // Queries >="2023-03-01T00:00:00Z" <"2023-03-02T00:00:00Z"
    }
  }
}

// When precision reaches "hour", it queries the entire range of that hour
{
  "query": {
    "query_string": {
      "query": "timestamp:2023-03-01T08"  // Queries >="2023-03-01T08:00:00Z" <"2023-03-01T09:00:00Z"
    }
  }
}

// When precision reaches "minute", double quotes are required (due to the colon)
{
  "query": {
    "query_string": {
      "query": "timestamp:\"2023-03-01T08:00\""  // Queries >="2023-03-01T08:00:00Z" <"2023-03-01T08:01:00Z"
    }
  }
}

Usage Notes

1. Standard format requires double quotes

Because the standard format contains a colon :, it is treated as a special character in QueryString and must be wrapped in double quotes:

Incorrect syntax (causes parsing error):

json
{
  "query": {
    "query_string": {
      "query": "timestamp:2023-01-15T08:30:00Z"
    }
  }
}

Correct syntax:

json
// Method 1: Wrap in double quotes
{
  "query": {
    "query_string": {
      "query": "timestamp:\"2023-01-15T08:30:00Z\""
    }
  }
}

// Method 2: Use simplified format (no colon)
{
  "query": {
    "query_string": {
      "query": "timestamp:2023-01-15"
    }
  }
}

2. Date queries do not support comparison operators

When using comparison operators to query dates, the query will fail or produce an error:

json
// This query actually searches for the date in "all fields"
// instead of performing a comparison on the timestamp field
{
  "query": {
    "query_string": {
      "query": "timestamp:>=\"2023-01-15T08:30:00Z\""
    }
  }
}

// If you use default_field or fields, it will directly cause a parsing error
{
  "query": {
    "query_string": {
      "query": ">=\"2023-01-15T08:30:00Z\"",
      "fields": ["timestamp"]  // ❌ Will throw an error
    }
  }
}

{
  "query": {
    "query_string": {
      "query": ">=\"2023-01-15T08:30:00Z\"",
      "default_field": "timestamp"  // ❌ Will throw an error
    }
  }
}

Correct approach: Use range query syntax

json
{
  "query": {
    "query_string": {
      "query": "timestamp:[2023-01-15T08:30:00Z TO *]"
    }
  }
}

3. Impact of custom formats

When a date type field has a specified format (other than the default strict_date_optional_time||epoch_millis), the query format must exactly match the specified format:

json
// Assuming the field is defined as follows
"timestamp": {
    "type": "date",
    "format": "yyyy-MM-dd'T'HH:mm:ss'Z'"
}

Using the wrong format leads to different results:

json
// Specifying the field directly in the query: No results (silent failure)
{
  "query": {
    "query_string": {
      "query": "timestamp:[2023-01-15T08:30 TO *]"  // Format mismatch
    }
  }
}

// Using default_field or fields: Produces an error message
{
  "query": {
    "query_string": {
      "query": "[2023-01-15T08:30 TO *]",
      "default_field": "timestamp"  // Will throw error: failed to parse date field
    }
  }
}

Error message example:

text
failed to parse date field [2023-01-15T08:30] with format [yyyy-MM-dd'T'HH:mm:ss'Z']

Other Parameters

default_field vs fields:

  • default_field: Specifies a single field for default searching.
  • fields: Specifies multiple search fields and their weights.
json
// Using default_field
{
  "query_string": {
    "query": "apple",
    "default_field": "content"
  }
}

// Using fields
{
  "query_string": {
    "query": "apple",
    "fields": ["title^2", "content", "tags"]
  }
}

2. analyzer

Specifies how query_string processes and analyzes the query string. Elasticsearch provides various built-in analyzers; for a detailed list, refer to the official documentation.

json
{
  "query_string": {
    "query": "The Quick Brown Fox",
    "analyzer": "standard"
  }
}

Important Concept: Index-time vs. Query-time Analyzers

Elasticsearch uses analyzers in two stages:

1. Index-time Analyzer

  • Processes data being stored in the index.
  • Example: text field stores "Wing Chou" → tokenized as ["wing", "chou"].

2. Query-time Analyzer

  • Processes the query string.
  • The analyzer parameter of query_string controls this stage.

Example Explanation

Assuming the text field has stored "Wing Chou" and the index-time tokens are ["wing", "chou"]:

json
// ✅ Using standard analyzer
// Query string is analyzed as ["wing", "chou"], and the default operator is OR, so it matches the data regardless of whether it's wing or chou
{
  "query_string": {
    "query": "Wing Chou",
    "analyzer": "standard"
  }
}

// ❌ Using keyword analyzer, incorrect query condition
// Query string remains the complete "Wing Chou", which cannot match the tokenized results
{
  "query_string": {
    "query": "Wing Chou",
    "analyzer": "keyword"
  }
}

// Query string remains "Wing" (uppercase), cannot match "wing" (lowercase) in the data
{
  "query_string": {
    "query": "Wing",
    "analyzer": "keyword"
  }
}

// ✅ Using keyword analyzer, correct query condition
{
  "query_string": {
    "query": "wing",
    "analyzer": "keyword"
  }
}

{
  "query_string": {
    "query": "wing OR chou",
    "analyzer": "keyword"
  }
}

3. analyze_wildcard

Controls whether wildcard expressions are analyzed. The official documentation states:

(Optional, boolean) If true, the query attempts to analyze wildcard terms. Defaults to false.

Note that even if set to true, only queries ending in * are fully analyzed. Queries containing * at the beginning or in the middle are only normalized.

json
{
  "query_string": {
    "query": "Te*",
    "analyze_wildcard": true,
    "analyzer": "standard"
  }
}

Parameter Value Explanation

  • analyze_wildcard: false (default): Wildcard expressions are not processed by the analyzer.
  • analyze_wildcard: true: Wildcard expressions are processed by the analyzer, but the method depends on the wildcard position:
    • Trailing wildcard (e.g., Te*): Fully analyzed.
    • Leading or middle wildcard (e.g., *Te*, T*e): Only normalized.

Observations and Questions from Testing

In actual testing, I found that the behavior of this parameter has some inconsistencies or is difficult to understand compared to the official documentation:

1. Behavior of Text fields

Regardless of whether analyze_wildcard is set to true or false, the query is case-insensitive:

json
// The following two queries yield the same results and both match the data
{
  "query_string": {
    "query": "name:Te*",
    "analyze_wildcard": true
  }
}

{
  "query_string": {
    "query": "name:Te*",
    "analyze_wildcard": false
  }
}

2. Keyword fields without an analyzer specified

Regardless of whether analyze_wildcard is set to true or false, the query is case-sensitive:

json
// The following two queries yield the same results and both only match exact case
{
  "query_string": {
    "query": "email:Te*",
    "analyze_wildcard": true
  }
}

{
  "query_string": {
    "query": "email:Te*",
    "analyze_wildcard": false
  }
}

3. Contradictory behavior when an analyzer is specified for Keyword fields

When analyzer: "standard" is specified for a keyword field, a phenomenon inconsistent with the official documentation appears (or perhaps my understanding of "full analysis" vs. "normalization" is incorrect):

json
// Leading or middle wildcard: Converted to lowercase (matches "normalization" description)
// Actual query: *te*
{
  "query_string": {
    "query": "email:(*Te*)",
    "analyze_wildcard": true,
    "analyzer": "standard"
  }
}

// Trailing wildcard: Not converted to lowercase (contradicts "full analysis" description)
// Actual query: Te*
{
  "query_string": {
    "query": "email:(Te*)",
    "analyze_wildcard": true,
    "analyzer": "standard"
  }
}

According to the official documentation, trailing wildcards should be "fully analyzed," and theoretically Te* should be converted to te* by the standard analyzer, but the test result shows it remains Te*.

Processing Differences by Wildcard Position

Wildcard PatternOfficial DescriptionKeyword + standard analyzer test
Te*Full analysisNot converted to lowercase (Te*)
*Te*Normalization onlyConverted to lowercase (*te*)
*TeNormalization onlyConverted to lowercase (*te)
T*eNormalization onlyConverted to lowercase (t*e)

4. auto_generate_synonyms_phrase_query

Controls how synonyms are handled:

  • true (default): Automatically generates phrase queries for synonyms, maintaining word order and adjacency.
  • false: Generates only standard synonym queries, ignoring word order.

For example, if "ny" and "new york" are defined as synonyms:

json
{
  "query_string": {
    "query": "ny restaurants",
    "auto_generate_synonyms_phrase_query": true
  }
}
  • true: Matches "ny restaurants" or "new york restaurants" ("new york" as a whole phrase).
  • false: Matches "ny restaurants" or "new restaurants" or "york restaurants".

5. Weight Control: boost and Field Weighting

Elasticsearch provides two levels of weight control mechanisms:

5.1 Field Boost

Use the ^ syntax to adjust the weight of specific fields.

json
{
  "query_string": {
    "query": "apple iphone",
    "fields": ["title^3", "description^2", "content"]
  }
}
  • Function: Controls the relative importance of different fields in the same query.
  • Mechanism:
    • Each field independently calculates a base score (based on the BM25 algorithm).
    • The score for the title field is multiplied by 3.
    • The score for the description field is multiplied by 2.
    • The score for the content field is multiplied by 1 (default).
    • Finally, all weighted field scores are added together to get the final score.
  • Important Note: title^3 does not mean the final score of title is 3 times that of content; it means the base score of the title field is amplified by 3 before being combined with other fields. The final score is also affected by factors like term frequency, inverse document frequency, and document length.
  • Scenario: When searching across multiple fields, some fields (like titles) represent the document topic better than others (like content).

Calculation Example (base scores are hypothetical):

Assuming a search for "apple", the base scores for each field are as follows:

FieldBase ScoreWeighted Score
title2.02.0 × 3 = 6.0
description1.51.5 × 2 = 3.0
content2.52.5 × 1 = 2.5
Final Score11.5

Note: The above base scores are hypothetical examples used to explain the calculation logic. Actual scores will vary based on index state, document content, term frequency, etc. You must use the _explain API to view the actual scoring process.


5.2 Query-level Boost

Adjusts the weight of the entire query clause.

json
{
  "query_string": {
    "query": "apple iphone",
    "boost": 2.0
  }
}
  • Function: Adjusts the importance of the entire query clause in a compound query (like a bool query).
  • Important Limitation: If used alone, the boost parameter has no effect on sorting (all document scores are multiplied by the same factor, so the relative order does not change).
  • Primary Use:
    • In the should clause of a bool query, adjust the relative importance of different query conditions.
    • Make certain matching conditions have a greater impact on the final sorting than others.
    • Reflect the importance differences of different search dimensions in business logic.
  • Scenario: Combining multiple query methods (text search, exact match, range query, etc.) where you need to adjust their impact weight on the final sorting.

5.3 Combining Both

Example

json
{
  "query": {
    "bool": {
      "should": [
        {
          "query_string": {
            "query": "apple",
            "fields": ["title^2"],
            "boost": 2.0
          }
        },
        {
          "query_string": {
            "query": "iphone",
            "fields": ["description"],
            "boost": 4.0
          }
        }
      ]
    }
  }
}

Calculation Explanation (base scores are hypothetical):

Assuming there are three documents:

Documenttitle contains "apple"description contains "iphone"Score Calculation
Doc A✅ (Base 3.0)✅ (Base 2.0)(3.0 × 2 × 2.0) + (2.0 × 1 × 4.0) = 20.0
Doc B✅ (Base 2.5)(2.5 × 2 × 2.0) + 0 = 10.0
Doc C✅ (Base 2.5)0 + (2.5 × 1 × 4.0) = 10.0

Sorting Result: Doc A (20.0) > Doc B (10.0) = Doc C (10.0)

Analysis:

  • Doc A matches both conditions and gets the highest score.
  • Doc B only matches "apple" in the title: Base score 2.5 × Field boost 2 × Query boost 2.0 = 10.0.
  • Doc C only matches "iphone" in the description: Base score 2.5 × Field boost 1 × Query boost 4.0 = 10.0.

Even though Doc C's query boost (4.0) is higher than Doc B's (2.0), because Doc B has a field boost (^2), the actual result still depends on the base scores.

Note: The above base scores are hypothetical; actual scores must be viewed using the _explain API.


5.4 Key Differences

FeatureField Boost (^)Query Boost (boost)
ScopeDifferent fields within a single queryDifferent query clauses in a compound query
Control ObjectRelative weight between fieldsRelative weight between query clauses
LocationIn the fields parameterTop-level parameter of the query clause
Used AloneEffective (affects field score combination)Ineffective (does not change sorting)
Typical ScenarioTitle is more important than contentUser search is more important than filter conditions

6. Fuzzy Search (fuzziness and phrase_slop)

The tilde ~ has two different uses in QueryString, depending on its position:

6.1 Fuzzy Search (Tilde after a word)

text
apple~    // Tilde after a word indicates fuzzy search
apple~2   // Specifies fuzziness as 2
  • Corresponding parameter: fuzziness.
  • Function: Handles spelling errors in a single word based on edit distance.
  • Identification: Used after a word.

Fuzziness Value Limits:

  • Valid values: 0, 1, 2, or "AUTO".
  • Values greater than 2 will cause an error: Valid edit distances are [0, 1, 2].

Conditions for fuzziness parameter to take effect:

The fuzziness parameter only takes effect when a word in the query string uses ~ (but does not specify a number):

json
// ✅ Word has ~, no number, no fuzziness set: default fuzziness is 1
{
  "query_string": {
    "query": "apple~"  // apple uses fuzziness 1 (default)
  }
}

// ✅ Word has ~, no number: uses fuzziness parameter
{
  "query_string": {
    "query": "apple~",
    "fuzziness": 2  // Takes effect, apple uses fuzziness 2
  }
}

// ❌ Word has no ~: does not take effect
{
  "query_string": {
    "query": "apple banana",
    "fuzziness": 1  // Does not take effect, both words are exact matches
  }
}

// ❌ Word has ~N (explicit number): uses the number in the query string
{
  "query_string": {
    "query": "apple~2",
    "fuzziness": 1  // Does not take effect, uses the 2 from the query string
  }
}

// ⚠️ Mixed case: only words with ~ apply fuzzy search
{
  "query_string": {
    "query": "apple~2 banana",
    "fuzziness": 1  // Does not take effect, apple uses 2, banana is exact match
  }
}

{
  "query_string": {
    "query": "apple~ banana",
    "fuzziness": 1  // Only takes effect for apple (fuzziness 1), banana is exact match
  }
}

Priority Rules:

  1. ~N in the query string (explicit number): Uses that number directly.
  2. ~ in the query string (no number) + fuzziness parameter: Uses the fuzziness parameter value.
  3. ~ in the query string (no number) + no fuzziness set: Default fuzziness is 1.
  4. No ~ in the query string: The word is an exact match; the fuzziness parameter does not take effect.

⚠️ Important Note:

The fuzziness parameter must be used with ~ in the query string to take effect. If there is no ~ after the term, the term is treated as an exact match, regardless of the fuzziness parameter setting.

Fuzziness Setting Differences:

  • "fuzziness": "AUTO": Automatically adjusts based on word length. The default behavior is equivalent to AUTO:3,6, with the following rules:
    • Word length 0-2 characters: fuzziness = 0 (must be an exact match)
    • Word length 3-5 characters: fuzziness = 1
    • Word length 6+ characters: fuzziness = 2
  • "fuzziness": "AUTO:[low],[high]": AUTO mode with custom thresholds. For example, AUTO:4,7 means:
    • Word length 0-3 characters: fuzziness = 0
    • Word length 4-6 characters: fuzziness = 1
    • Word length 7+ characters: fuzziness = 2
  • "fuzziness": 1 or "fuzziness": 2: Fixed maximum allowed edit distance.

For detailed information, please refer to the official documentation.

Example:

  • apple~1 can match spelling errors like "aple", "appla", etc.

6.2 Proximity Search (Tilde after a phrase)

text
"apple banana"~5  // Tilde after a phrase indicates proximity search
  • Corresponding parameter: phrase_slop
  • Function: Handles the order and distance of words in a phrase.
  • Identification: Used after a phrase enclosed in quotes.

Slop Value Calculation Logic:

Slop represents the minimum number of steps words need to "move," which can handle:

  1. Other words interspersed between the terms.
  2. Incorrect word order (swapped).

Calculation Explanation:

Assuming the index contains "quick brown fox jumps"

text
// Example 1: Interspersed words
Query: "quick fox"
Index: "quick brown fox"
→ "fox" needs to move 1 step to the left, skipping "brown"
→ slop = 1

// Example 2: Adjacent word order swapped
Query: "brown quick"
Index: "quick brown"
→ The two words swap positions
→ slop = 2 (Official documentation: swap cost is 2)

// Example 3: Non-adjacent word order swapped + interspersed
Query: "fox quick"
Index: "quick brown fox"
→ "fox" needs to move in front of "quick," crossing 2 positions
→ slop = 3

Actual Example:

json
// slop = 0: Must match exactly
{
  "query_string": {
    "query": "\"quick brown\""
  }
}

// ✅ Matches: "quick brown fox"
// slop = 1: Allows 1 word in between
{
  "query_string": {
    "query": "\"quick fox\"~1"
  }
}

// slop = 2: Allows adjacent words to be swapped
// ✅ Matches: "quick brown fox"
// ❌ Does not match: "quick fox"
{
  "query_string": {
    "query": "\"brown quick\"~2"
  }
}

// slop = 3: Allows more complex movement
// ✅ Matches: "quick brown fox"
{
  "query_string": {
    "query": "\"fox quick\"~3"
  }
}

Setting in API:

json
{
  "query_string": {
    "query": "apple~2 \"quick fox\"~3",
    "fuzziness": "AUTO",  // Global setting, overridden by specific values in the query
    "phrase_slop": 2      // Global setting, overridden by specific values in the query
  }
}

Priority:

Values set using ~ in the query string override the global settings in the API parameters.

Pagination and Sorting

Pagination Parameters

json
{
  "query": {
    "query_string": {
      "query": "apple"
    }
  },
  "from": 0,   // Starting position, defaults to 0
  "size": 10   // Number of items per page, defaults to 10
}
  • from: Specifies the starting result index (0 represents the first result).
  • size: Specifies how many results to return.

Notes:

  • Deep pagination (from + size too large) affects performance.
  • Elasticsearch defaults to limiting from + size to no more than 10,000.
  • For processing large amounts of data, it is recommended to use the Search After or Scroll API.

Sorting Parameters

The sort parameter accepts an array, allowing you to specify multiple sorting conditions. The sorting priority is determined by the order in the array.

Sorting Syntax

Method 1: Simple field name (defaults to ascending)

json
{
  "sort": ["price"]  // Sort by price ascending
}

Method 2: Field + sort direction object

json
{
  "sort": [
    { "price": "asc" }   // Sort by price ascending
  ]
}

Method 3: Full configuration object

json
{
  "sort": [
    {
      "price": {
        "order": "desc",           // Sort direction: asc (ascending) or desc (descending)
        "missing": "_last"         // Documents missing this field are placed at the end
      }
    }
  ]
}

Multi-field Sorting

json
{
  "query": {
    "query_string": {
      "query": "apple"
    }
  },
  "sort": [
    { "price": "asc" },      // First priority: sort by price ascending
    { "created_at": "desc" }, // Second priority: if price is the same, sort by created_at descending
    "_score"                  // Third priority: if other conditions are the same, sort by relevance score
  ]
}

Special Sorting Values

  • "_score": Sort by query relevance score (defaults to descending).
  • "_doc": Sort by the internal order of the document (fastest, but the order is not fixed).

Sorting Direction

  • "asc": Ascending - from smallest to largest.
  • "desc": Descending - from largest to smallest.

Handling Missing Values

Use the missing parameter to specify where documents missing the sorting field should be placed:

json
{
  "sort": [
    {
      "price": {
        "order": "asc",
        "missing": "_last"   // Options: _first (place at start), _last (place at end), or specify a default value
      }
    }
  ]
}

Full Example

json
{
  "query": {
    "query_string": {
      "query": "laptop"
    }
  },
  "from": 0,
  "size": 20,
  "sort": [
    { "price": { "order": "asc", "missing": "_last" } },
    { "rating": "desc" },
    "_score"
  ]
}

Sorting Logic Explanation:

  1. Sort by price ascending first (those without a price are placed at the end).
  2. If prices are the same, sort by rating descending.
  3. If prices and ratings are both the same, sort by relevance score.

Special Character Escaping

Many special characters in QueryString have specific meanings. If you want to use them as ordinary characters, you need to escape them with a backslash \:

text
\+ \- \= \&\& \|\| \> \< \! \( \) \{ \} \[ \] \^ \" \~ \* \? \: \\ \/

For example:

  • Search for documents containing a plus sign: title:\+1.
  • Search for documents containing parentheses: content:\(sample\).
  • Search for documents containing quotes: description:\"quoted text\".

Changelog

  • 2025-04-13 Initial document created.
  • 2025-10-03
    • Refined phrasing and terminology.
    • Corrected explanation of date query range syntax (date fields do not support comparison operators; range query syntax should be used).
    • Corrected the weight calculation to include the missing field base score component.
    • Added technical details.