Skip to content

Let Elasticsearch sort by Byte and Short keys - #5081

Merged
porunov merged 1 commit into
JanusGraph:masterfrom
porunov:es-order-by-byte-and-short
Oct 17, 2026
Merged

porunov merged 1 commit into
JanusGraph:masterfrom
porunov:es-order-by-byte-and-short

Conversation

@porunov

@porunov porunov commented Oct 15, 2026 •

Copy link
Copy Markdown
Member

Fixes #5080

A graph query which a mixed index on Elasticsearch answered in the order of a Byte or a Short key failed with Could not call index, and an index query ordered by such a key failed too: ElasticSearchIndex gave the sort an unmapped_type of null, which Elasticsearch and OpenSearch reject with a 400 ([field_sort] unmapped_type doesn't support values of type: VALUE_NULL), as convertToEsDataType had no type for these keys. This PR gives byte and short, the types which pushMapping maps them to. It also gives keyword in place of string for a string key, a type which Elasticsearch 5 replaced: Elasticsearch 8 and 9 rejected such a sort where the index had no mapping of the key.

Changes

  • ElasticSearchIndex.convertToEsDataType, which gives the unmapped_type of a sort by a key, the type which Elasticsearch sorts by where an index has no mapping of the field:
    • gives short for a Short key and byte for a Byte key, the types which pushMapping maps them to. It returned null for them, and the request went out with "unmapped_type": null, which Elasticsearch rejects while it parses the request, whether the field is mapped or not;
    • gives keyword for every string key. An unmapped_type applies only to an index without a mapping of the field, whose documents all sort as having no value, so it needs only a type which Elasticsearch can sort strings by, whatever the key's mapping: pushMapping maps the field which such a sort reads as keyword, or as text where the key has a custom string analyzer. It gave string, which Elasticsearch 5 replaced with text and keyword: where the index had no mapping of the field, Elasticsearch 8 and 9 rejected the sort (No mapper found for type [string]), and Elasticsearch 6 and 7 sorted by keyword in its place, with a deprecation warning, [unmapped_type:string] should be replaced with [unmapped_type:keyword].
  • Changelog: a section, "Elasticsearch sorts by Byte and Short keys".

Of the built-in types which ElasticSearchIndex supports, every one now has an unmapped type, with the PR which compares UUIDs as their string forms (branch order-uuids-as-the-indexes-do): it adds a branch for a UUID key, keyword, after the Geoshape one. The two merge cleanly in either order.

What the versions accept, sending the sorts by hand to a container of each, with an index which maps a keyword, a byte and a short field, and a field which it doesn't map:

Server, by the unmapped_type of the sort null byte, short, keyword string, mapped field string, unmapped field
Elasticsearch 6.6.0 400 accepted accepted accepted, with a deprecation warning
Elasticsearch 7.17.8 400 accepted accepted accepted, with a deprecation warning
Elasticsearch 8.15.3 400 accepted accepted 400, No mapper found for type [string]
Elasticsearch 9.5.4 400 accepted accepted 400, No mapper found for type [string]
OpenSearch 2.19.6 400 accepted accepted accepted
OpenSearch 3.9.0 400 accepted accepted accepted

The 400 for null is [field_sort] unmapped_type doesn't support values of type: VALUE_NULL, for a mapped field as for an unmapped one.

Tests

  • JanusGraphIndexTest.testMixedIndexOrdersByBytesAndShorts (every index backend; the mixed index is named mixed, which the Solr test container has a collection for). A mixed index on tag, a Byte key small and a Short key medium; four vertices with the least value of each, 0, the greatest and -1. For each of the two keys, the graph query g.V().has('tag', 't').order().by(key) comes in the order of the values, ascending, descending and ascending with a limit of two, and so does the index query indexQuery('mixed', 'v.tag:t').orderBy(key, desc).
  • ElasticsearchConfigTest.testOrderByStringKeyWhichTheIndexHasNotMapped. Under an external dynamic mapping (index.[X].elasticsearch.create.use-external-mappings) without index.[X].elasticsearch.create.allow-mapping-update, a string key registered with the index, and a document without it: the index has no mapping of the key, which the test checks, and a query ordered by the key returns the document.

Without the fix, on Elasticsearch 9.5.4:

  • Without the byte and short cases: testMixedIndexOrdersByBytesAndShorts fails at its first query ordered by small, with Could not call index from the 400 for the unmapped_type of null.
  • Without the short case alone: it fails at the first query ordered by medium, after those by small passed.
  • With string for a string key: testOrderByStringKeyWhichTheIndexHasNotMapped fails with PermanentBackendException: Unknown exception while executing index operation, from the 400 No mapper found for type [string]. Elasticsearch 6 and 7 and OpenSearch accept string for a field without a mapping (see the table above), so there the test passes either way.
  • Each of the two tests passes with the other's mutation.

Runs, locally, Java 11, on master dd4f0f845:

  • BerkeleyElasticsearchTest (118, 1 skipped), ElasticsearchIndexTest (306) and ElasticsearchConfigTest (13) pass against Elasticsearch 9.5.4 in a container, with checkstyle.
  • The two new tests pass against Elasticsearch 6.6.0, 7.17.8 and 8.15.3 and OpenSearch 2.19.6 and 3.9.0 in containers.
  • janusgraph-driver (130), janusgraph-core (228), janusgraph-inmemory (652, 85 skipped), janusgraph-berkeleyje (350, 9 skipped) and janusgraph-lucene (394, 148 skipped) pass, with checkstyle.
  • The Solr test container doesn't start on this machine (a 404 from /solr/admin/collections while it starts), so BerkeleySolrTest's configuration ran in the JVM against a MiniSolrCloudCluster (Solr 9.10.1) with the test schema: testMixedIndexOrdersByBytesAndShorts passes. The rest of the Solr suite is left to CI.

Notes

  • Left out:
    • A custom attribute type which is a Number other than the six built-in ones: ElasticSearchIndex supports it, but pushMapping maps it to no type, so a sort by it still goes out with an unmapped_type of null. Any numeric type would do there, as it only applies where no document of the index holds the field; it is left to a change which maps such types.
    • Character keys, which ElasticSearchIndex doesn't support.
  • OpenSearch accepts string, and keyword as well.

🤖 Generated with Claude Code

@porunov
porunov requested a balanced review from Copilot October 15, 2026 15:59

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

3 open findings
What changed in this PR

Fix Elasticsearch/OpenSearch sorting failures when ordering by Byte, Short, or unmapped string keys by ensuring unmapped_type is set to a valid ES datatype.

Changes:

  • Update ElasticSearchIndex.convertToEsDataType to return byte, short, and keyword instead of null/string for relevant key types.
  • Add regression tests covering mixed-index ordering by Byte/Short and ordering by an unmapped string field under external dynamic mappings.
  • Document the behavior change in the changelog.
File Description
janusgraph-es/​src/​main/​java/​org/​janusgraph/​diskstorage/​es/​ElasticSearchIndex.java Fixes ES unmapped_type derivation for Byte/Short and modernizes string type to keyword.
janusgraph-es/​src/​test/​java/​org/​janusgraph/​diskstorage/​es/​ElasticsearchConfigTest.java Adds regression test ensuring ordering works when the index has no mapping for a string key.
janusgraph-backend-testutils/​src/​main/​java/​org/​janusgraph/​graphdb/​JanusGraphIndexTest.java Adds mixed-index regression test for ordering by Byte and Short keys.
docs/​changelog.md Documents the fix and relevant Elasticsearch version behavior.

🧠 Review effort: Lite


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread janusgraph-es/src/main/java/org/janusgraph/diskstorage/es/ElasticSearchIndex.java Outdated
@porunov
porunov force-pushed the es-order-by-byte-and-short branch from b5e5bb5 to 5b3bb3f Compare October 15, 2026 17:15
@porunov
porunov requested a balanced review from Copilot October 15, 2026 17:17

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread janusgraph-es/src/main/java/org/janusgraph/diskstorage/es/ElasticSearchIndex.java Outdated

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment thread janusgraph-es/src/main/java/org/janusgraph/diskstorage/es/ElasticSearchIndex.java Outdated
Fixes JanusGraph#5080

ElasticSearchIndex gives each sort the unmapped_type of its key, the
type which Elasticsearch sorts by where an index has no mapping of the
field. It had none for a Byte or a Short key and sent null, which
Elasticsearch and OpenSearch reject with a 400, so a graph query or an
index query ordered by such a key failed. It now gives byte and short,
the types which JanusGraph maps such keys to.

For a string key it gave string, which Elasticsearch 5 replaced with
text and keyword: where the index had no mapping of the key, as under
an external dynamic mapping before any document held it, Elasticsearch
8 and 9 rejected the sort, and Elasticsearch 6 and 7 sorted by keyword
in its place, with a deprecation warning. It now gives keyword.

Signed-off-by: Oleksandr Porunov <alexandr.porunov@gmail.com>
Co-authored-by: Oleksandr Porunov <alexandr.porunov@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@porunov
porunov force-pushed the es-order-by-byte-and-short branch from 5b3bb3f to b4e973d Compare October 16, 2026 13:17
@porunov
porunov requested a balanced review from Copilot October 16, 2026 13:19

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Warning

Copilot couldn't run its full agentic review because it didn't start before the timeout. Make sure your repository has a runner available, or add a copilot-code-review.yml file specifying one with the runs-on attribute. See the docs for more details.

Copilot review overview

2 open findings
3 resolved since last review

🧠 Review effort: Lite

@porunov porunov added this to the 1.2.0 milestone Oct 17, 2026
@porunov
porunov merged commit c8d0566 into JanusGraph:master Oct 17, 2026
216 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Graph queries ordered by a Byte or Short key fail on Elasticsearch

2 participants