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
Basic Search
Simple Text Search
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" }]
}
Faceted Search
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
- Use keyword fields for exact matches: Use
.keywordsuffix for term filters (e.g.,category.keyword) - Use sortable fields for sorting: Use
.sortableor.exactsuffixes for date/number sorting - Configure default date ranges: Set up
defaultDateRangesin config for consistent date facet behavior - Limit facet sizes: Use reasonable
sizevalues for facets to avoid performance issues - Use source filtering: Only request fields you need to reduce response size
- Monitor query performance: Use
tookandqueryInfoto debug slow queries - Test with status endpoint: Use
esStatusquery to verify connectivity before running searches