正在加载,请稍候…

Elasticsearch Full-Text Search: Mappings, Queries, and Relevance Tuning

Build powerful search with Elasticsearch — index mappings, query DSL, bool queries, aggregations, highlighting, fuzzy search, and performance optimization.

Elasticsearch Core Concepts

  • Index: Collection of documents (like a table)
  • Document: JSON object (like a row)
  • Mapping: Schema definition
  • Shard: Horizontal partition for scaling
  • Replica: Copy of shard for HA

Index Mapping

PUT /products
{
  "settings": {
    "number_of_shards": 3,
    "number_of_replicas": 1,
    "analysis": {
      "analyzer": {
        "product_analyzer": {
          "type": "custom",
          "tokenizer": "standard",
          "filter": ["lowercase", "stop", "snowball"]
        }
      }
    }
  },
  "mappings": {
    "properties": {
      "id": { "type": "keyword" },
      "name": {
        "type": "text",
        "analyzer": "product_analyzer",
        "fields": {
          "keyword": { "type": "keyword" },
          "suggest": { "type": "completion" }
        }
      },
      "description": { "type": "text", "analyzer": "product_analyzer" },
      "price": { "type": "double" },
      "category": { "type": "keyword" },
      "tags": { "type": "keyword" },
      "rating": { "type": "float" },
      "in_stock": { "type": "boolean" },
      "created_at": { "type": "date" }
    }
  }
}

Search Queries

const { Client } = require('@elastic/elasticsearch')
const client = new Client({ node: 'http://localhost:9200' })

// Full-text search with filters
const result = await client.search({
  index: 'products',
  body: {
    query: {
      bool: {
        must: [
          {
            multi_match: {
              query: 'wireless headphones',
              fields: ['name^3', 'description'],  // name weighted 3x
              type: 'best_fields',
              fuzziness: 'AUTO',
            },
          },
        ],
        filter: [
          { term: { in_stock: true } },
          { range: { price: { gte: 20, lte: 200 } } },
          { terms: { category: ['electronics', 'audio'] } },
        ],
        should: [
          { range: { rating: { gte: 4.0, boost: 2 } } },
        ],
      },
    },
    sort: [
      { _score: 'desc' },
      { rating: 'desc' },
    ],
    highlight: {
      fields: {
        name: { pre_tags: ['<mark>'], post_tags: ['</mark>'] },
        description: { fragment_size: 150, number_of_fragments: 2 },
      },
    },
    aggs: {
      categories: { terms: { field: 'category', size: 10 } },
      price_ranges: {
        range: {
          field: 'price',
          ranges: [
            { to: 25 }, { from: 25, to: 50 },
            { from: 50, to: 100 }, { from: 100 },
          ],
        },
      },
      avg_rating: { avg: { field: 'rating' } },
    },
    from: 0,
    size: 20,
  },
})

Autocomplete with Completion Suggester

// Index a document with suggest field
await client.index({
  index: 'products',
  body: {
    name: 'Wireless Headphones',
    'name.suggest': {
      input: ['Wireless Headphones', 'Headphones Wireless', 'BT Headphones'],
      weight: 10,
    },
  },
})

// Query autocomplete
const suggestions = await client.search({
  index: 'products',
  body: {
    suggest: {
      product_suggest: {
        prefix: 'wire',
        completion: {
          field: 'name.suggest',
          size: 5,
          fuzzy: { fuzziness: 1 },
        },
      },
    },
  },
})

Aggregations for Faceted Search

// Get facets for search results
const facets = await client.search({
  index: 'products',
  body: {
    size: 0,  // Don't need documents, just aggregations
    aggs: {
      categories: { terms: { field: 'category', size: 20 } },
      brands: { terms: { field: 'brand', size: 20 } },
      price_stats: { stats: { field: 'price' } },
      rating_histogram: {
        histogram: { field: 'rating', interval: 0.5 },
      },
    },
  },
})

Index Templates and Aliases

// Template for time-series indices
PUT /_index_template/logs
{
  "index_patterns": ["logs-*"],
  "template": {
    "settings": { "number_of_shards": 1 },
    "mappings": {
      "properties": {
        "@timestamp": { "type": "date" },
        "level": { "type": "keyword" },
        "message": { "type": "text" },
        "service": { "type": "keyword" }
      }
    }
  }
}

// Write to alias, switch indices without downtime
POST /_aliases
{
  "actions": [
    { "add": { "index": "products-v2", "alias": "products" } },
    { "remove": { "index": "products-v1", "alias": "products" } }
  ]
}

Performance Tips

  • Set "doc_values": false on text fields not used for aggregations
  • Use filter context over query for non-scoring checks
  • Use keyword type for IDs, categories (exact match)
  • Set refresh_interval: -1 during bulk indexing, then restore
  • Use bulk API for indexing (not individual requests)