1. Graphql
  2. Elasticsearch

Graphql

Elasticsearch

IdentityHub provides comprehensive Elasticsearch search functionality through its GraphQL API, allowing you to search documents, apply filters, compute facets, and retrieve highlighted results—all through a unified GraphQL interface.

Overview

The Elasticsearch integration provides:

  • Full-Text Search: Multi-field search with boost values and relevance scoring
  • Advanced Filtering: Boolean logic (AND/OR), term filters, range filters, prefix filters, and exists/not-exists filters
  • Faceted Search: Dynamic aggregations for building filter UIs (terms, date ranges, prefixes)
  • Result Highlighting: Configurable highlighting of search matches in result fields
  • Cursor-Based Pagination: Relay-style cursor pagination with page number information
  • Flexible Sorting: Sort by relevance, custom fields, or multiple fields
  • Source Filtering: Control which fields are returned in search results
  • Query Debugging: Complete query information for troubleshooting

Configuration

Configure Elasticsearch in your config file:

[elasticsearch]
# Elasticsearch endpoint URL
endpoint = "http://localhost:9200"

# Request timeout in milliseconds
timeout = 30000

# Authentication (optional)
auth = { username = "elastic", password = "changeme" }

# TLS configuration (optional)
tls = { rejectUnauthorized = false }

# Default fields to search (if not specified in query)
fields = ["title", "content", "category"]

# Highlight configuration
highlights = { enabled = true }

sortFields = {
  # Sort field mappings (GraphQL field name -> Elasticsearch field name)

}

"title" = "title.exact"
"date" = "date.sortable"
"relevance" = "_score"
defaultDateRanges = [
  { from = "now-1y", to = "now", key = "P1Y" }
  { from = "now-1M", to = "now", key = "P1M" }
  { from = "now-1w", to = "now", key = "P1W" }
  { from = "now-3d", to = "now", key = "P3D" }
]# Default date ranges for DATE_RANGE facets
query SearchDocuments {
	esSearch(q: "identity management") {
		nodes {
			id
			score
			source
		}
		pageInfo {
			hasNextPage
			currentPage
			totalPages
		}
	}
}

Search with Filters

query SearchWithFilters {
	esSearch(
		q: "user authentication"
		filters: {
			terms: [
				{ field: "category.keyword", value: "documentation" }
				{ field: "status.keyword", value: "published" }
			]
			ranges: [{ field: "date.standard", from: "2025-01-01", to: "now" }]
		}
	) {
		nodes {
			id
			score
			source
		}
	}
}

Advanced Filtering

Boolean Logic (AND/OR)

query ComplexFilters {
	esSearch(
		filters: {
			or: [
				{ terms: [{ field: "category.keyword", value: "news" }] }
				{
					and: [
						{ terms: [{ field: "category.keyword", value: "blog" }] }
						{ ranges: [{ field: "date.standard", from: "2025-01-01" }] }
					]
				}
			]
		}
	) {
		nodes {
			id
			source
		}
	}
}

Filter Types

Term Filters (exact matches):

filters: {
  terms: [
    { field: "category.keyword", value: "technology" }
    { field: "author.keyword", value: "John Doe" }
  ]
}

Range Filters (dates and numbers):

filters: {
  ranges: [
    { field: "date.standard", from: "2025-01-01", to: "2025-12-31" }
    { field: "price", from: "10", to: "100" }
  ]
}

Prefix Filters:

filters: {
  prefix: [
    { field: "index", value: "ad-2000-" }
  ]
}

Exists/Not Exists Filters:

filters: {
  exists: [{ field: "publishedDate.standard" }]
  notExists: [{ field: "deletedAt.standard" }]
}

Facets provide aggregation results for building filter UIs:

Terms Facet

query SearchWithFacets {
	esSearch(
		q: "identity"
		facets: [{ name: "categories", type: TERMS, field: "category.keyword", size: 10 }]
	) {
		nodes {
			id
			source
		}
		facets {
			name
			buckets {
				key
				docCount
			}
		}
	}
}

Date Range Facet

query SearchWithDateFacets {
  esSearch(
    facets: [
      {
        name: "dateRanges"
        type: DATE_RANGE
        field: "date.standard"
        ranges: [
          { from: "now-1y", to: "now", key: "P1Y" }
          { from: "now-1M", to: "now", key = "P1M" }
          { from: "now-1w", to: "now", key = "P1W" }
        ]
      }
    ]
  ) {
    facets {
      name
      buckets {
        key
        docCount
      }
    }
  }
}

Facets with Pre-filters

query FacetsWithFilters {
	esSearch(
		facets: [
			{
				name: "filteredCategories"
				type: TERMS
				field: "category.keyword"
				filters: { ranges: [{ field: "date.standard", from: "2025-01-01" }] }
			}
		]
	) {
		facets {
			name
			buckets {
				key
				docCount
			}
		}
	}
}

Result Highlighting

Highlight search matches in result fields:

query SearchWithHighlighting {
	esSearch(
		q: "identity management"
		highlight: {
			fields: ["title", "content"]
			preTag: "<mark>"
			postTag: "</mark>"
			fragmentSize: 150
			numberOfFragments: 3
		}
	) {
		nodes {
			id
			source
			highlight
		}
	}
}

Pagination

Use cursor-based pagination following the Relay specification:

Forward Pagination

query FirstPage {
	esSearch(q: "identity", paging: { first: 10 }) {
		edges {
			node {
				id
				source
			}
			cursor
		}
		pageInfo {
			hasNextPage
			hasPreviousPage
			startCursor
			endCursor
			currentPage
			totalPages
			pageSize
		}
	}
}

Next Page

query NextPage {
	esSearch(q: "identity", paging: { first: 10, after: "eyJvZmZzZXQiOjEwfQ==" }) {
		edges {
			node {
				id
				source
			}
			cursor
		}
		pageInfo {
			hasNextPage
			currentPage
			totalPages
		}
	}
}

Backward Pagination

query PreviousPage {
	esSearch(q: "identity", paging: { last: 10, before: "eyJvZmZzZXQiOjIwfQ==" }) {
		edges {
			node {
				id
				source
			}
			cursor
		}
		pageInfo {
			hasPreviousPage
			currentPage
			totalPages
		}
	}
}

Sorting

Sort results by relevance or custom fields:

Sort by Relevance

query SortByRelevance {
	esSearch(q: "identity management", sort: { by: [relevance] }) {
		nodes {
			id
			score
			source
		}
	}
}

Sort by Custom Field

query SortByDate {
	esSearch(sort: { by: [date], direction: DESCENDING }) {
		nodes {
			id
			source
		}
	}
}

Multi-Field Sorting

query MultiFieldSort {
	esSearch(sort: { by: [date, title], direction: DESCENDING }) {
		nodes {
			id
			source
		}
	}
}

Field Selection

Control which fields are returned:

Include Specific Fields

query SelectFields {
	esSearch(sourceFields: { include: ["title", "summary", "category"] }) {
		nodes {
			id
			source
		}
	}
}

Exclude Fields

query ExcludeFields {
	esSearch(sourceFields: { exclude: ["content", "fullText"] }) {
		nodes {
			id
			source
		}
	}
}

Status Check

Check Elasticsearch connection and cluster status:

query CheckStatus {
	esStatus {
		connected
		clusterName
		version
		clusterHealth
		numberOfNodes
		numberOfDataNodes
		responseTime
		error
	}
}

Query Debugging

Enable query debugging to see the exact Elasticsearch query:

query DebugQuery {
	esSearch(q: "identity", filters: { terms: [{ field: "category.keyword", value: "docs" }] }) {
		queryInfo {
			parsedQuery
			esVersion
		}
	}
}

Index Selection

Search specific indices:

query SearchSpecificIndices {
	esSearch(indices: ["documents", "articles"], q: "identity") {
		nodes {
			id
			index
			source
		}
	}
}

Complete Example

A complete example combining multiple features:

query CompleteSearch {
	esSearch(
		q: "identity management"
		indices: ["documents", "articles"]
		filters: {
			terms: [{ field: "category.keyword", value: "documentation" }]
			ranges: [{ field: "date.standard", from: "2025-01-01", to: "now" }]
			exists: [{ field: "publishedDate.standard" }]
		}
		facets: [
			{ name: "categories", type: TERMS, field: "category.keyword", size: 10 }
			{
				name: "dateRanges"
				type: DATE_RANGE
				field: "date.standard"
				ranges: [
					{ from: "now-1y", to: "now", key: "P1Y" }
					{ from: "now-1M", to: "now", key: "P1M" }
				]
			}
		]
		highlight: { fields: ["title", "content"], preTag: "<mark>", postTag: "</mark>" }
		sort: { by: [date], direction: DESCENDING }
		sourceFields: { include: ["title", "summary", "category", "date"] }
		paging: { first: 20 }
	) {
		nodes {
			id
			score
			source
			highlight
			index
		}
		edges {
			node {
				id
				source
			}
			cursor
		}
		facets {
			name
			buckets {
				key
				docCount
				subFacets {
					name
					buckets {
						key
						docCount
					}
				}
			}
			sumOtherDocCount
		}
		pageInfo {
			hasNextPage
			hasPreviousPage
			startCursor
			endCursor
			currentPage
			totalPages
			pageSize
		}
		estimatedSize
		took
		maxScore
		queryInfo {
			parsedQuery
			esVersion
		}
	}
}

Compatibility

The Elasticsearch integration currently supports Elasticsearch 2.x syntax for compatibility with existing infrastructure. Support for modern Elasticsearch versions (8.x/9.x) is planned for future releases.

Best Practices

  1. Use keyword fields for exact matches: Use .keyword suffix for term filters (e.g., category.keyword)
  2. Use sortable fields for sorting: Use .sortable or .exact suffixes for date/number sorting
  3. Configure default date ranges: Set up defaultDateRanges in config for consistent date facet behavior
  4. Limit facet sizes: Use reasonable size values for facets to avoid performance issues
  5. Use source filtering: Only request fields you need to reduce response size
  6. Monitor query performance: Use took and queryInfo to debug slow queries
  7. Test with status endpoint: Use esStatus query to verify connectivity before running searches