Conversation
This commit adds the ability to cast JSONB arrays directly into vector and halfvec vector types, for example: SELECT '[1,2,3]'::jsonb::vector(3); SELECT '[1,2,3]'::jsonb::halfvec(3);
Member
|
Thanks @jkatz. I'm hesitant about including a direct cast for this, since I don't think it's super common and the existing cast is fairly performant. |
|
wqess |
sagitrs
suggested changes
Jun 14, 2026
helmize1978
approved these changes
Aug 28, 2026
oliness
added a commit
to oliness/pgvector
that referenced
this pull request
Sep 21, 2026
Applies pgvector#944 by jkatz, which adds jsonb_to_vector and jsonb_to_halfvec, rebased onto current master with these changes: - Added break after the ereport in the jbvNull case, which fell through to default and produced two -Wimplicit-fallthrough warnings - Converted each element with numeric_out and strtof instead of numeric_float4, which wraps those same two steps in another fmgr call and an input function call. Values are unchanged; the range error now names vector or halfvec rather than real, matching vector_in - Added the functions and casts to the 0.8.6--0.8.7 upgrade script, so an upgraded install gets them and not only a fresh one - Added COMMENT ON FUNCTION for each, as every other cast function has - Fixed the indentation and trailing whitespace of jsonb_to_halfvec - Added tests for a boolean element, out of range values and the dimension limit jsonb stores numbers as numeric, so casting from it still converts every element to text and parses it back. That cannot be avoided: NumericData is opaque and numeric.h exposes no binary conversion, and jsonb::text::vector pays the same cost inside jsonb_out. json keeps the original text, so this also adds casts both ways that skip numeric entirely: - json_to_vector and json_to_halfvec pass the stored text to vector_in and halfvec_in, which already parse "[1,2,3]" with strtof - vector_to_json and halfvec_to_json return the text form, which is already a JSON array bench/json_casts.sql times every path. Medians of five runs at 1536 dimensions over 3000 rows: 494 ms for jsonb::text::vector, 437 ms for jsonb to vector, 366 ms for json to vector, and 183 ms for vector to json against 623 ms for to_jsonb(v::real[]). At 128 dimensions over 20000 rows: 289, 246, 248 and 102 ms against 368 ms. The direct casts beat the existing routes in all five runs at both sizes, and all values match them exactly. README has a JSON section covering both directions, indexing a JSON column, and the difference between json and jsonb.
oliness
added a commit
to oliness/pgvector
that referenced
this pull request
Sep 22, 2026
Applies pgvector#944 by jkatz, which adds jsonb_to_vector and jsonb_to_halfvec, rebased onto current master with these changes: - Added break after the ereport in the jbvNull case, which fell through to default and produced two -Wimplicit-fallthrough warnings - Converted each element with numeric_out and strtof instead of numeric_float4, which wraps those same two steps in another fmgr call and an input function call. Values are unchanged; the range error now names vector or halfvec rather than real, matching vector_in - Left each element string to the memory context rather than freeing it per element, as JsonbToCString does, which is worth about 6% - Added the functions and casts to the 0.8.6--0.8.7 upgrade script, so an upgraded install gets them and not only a fresh one - Added COMMENT ON FUNCTION for each, as every other cast function has - Fixed the indentation and trailing whitespace of jsonb_to_halfvec - Added tests for a boolean element, out of range values and the dimension limit jsonb stores numbers as numeric, so casting from it still converts every element to text and parses it back. That cannot be avoided: NumericData is opaque and numeric.h exposes no binary conversion, and jsonb::text::vector pays the same cost inside jsonb_out. json keeps the original text, so this also adds casts both ways that skip numeric entirely: - json_to_vector and json_to_halfvec pass the stored text to vector_in and halfvec_in, which already parse "[1,2,3]" with strtof - vector_to_json and halfvec_to_json return the text form, which is already a JSON array bench/json_casts.sql times every path. At 1536 dimensions over 3000 rows: 478 ms for jsonb::text::vector, 393 ms for jsonb to vector and 358 ms for json to vector, while vector to json takes 183 ms against 623 ms for to_jsonb(v::real[]). The direct casts beat the existing routes in every run, and all values match them exactly. README has a JSON section covering both directions, indexing a JSON column, and the difference between json and jsonb.
oliness
added a commit
to oliness/pgvector
that referenced
this pull request
Sep 22, 2026
Applies pgvector#944 by jkatz, which adds jsonb_to_vector and jsonb_to_halfvec, rebased onto current master with these changes: - Added break after the ereport in the jbvNull case, which fell through to default and produced two -Wimplicit-fallthrough warnings - Converted each element with numeric_out and strtof instead of numeric_float4, which wraps those same two steps in another fmgr call and an input function call. Values are unchanged; the range error now names vector or halfvec rather than real, matching vector_in - Left each element string to the memory context rather than freeing it per element, as JsonbToCString does, which is worth about 6% - Added the functions and casts to the 0.8.6--0.8.7 upgrade script, so an upgraded install gets them and not only a fresh one - Added COMMENT ON FUNCTION for each, as every other cast function has - Fixed the indentation and trailing whitespace of jsonb_to_halfvec - Added tests for a boolean element, out of range values and the dimension limit jsonb stores numbers as numeric, so casting from it still converts every element to text and parses it back. That cannot be avoided: NumericData is opaque and numeric.h exposes no binary conversion, and jsonb::text::vector pays the same cost inside jsonb_out. json keeps the original text, so this also adds casts both ways that skip numeric entirely: - json_to_vector and json_to_halfvec pass the stored text to vector_in and halfvec_in, which already parse "[1,2,3]" with strtof - vector_to_json and halfvec_to_json return the text form, which is already a JSON array bench/json_casts.sql times every path, including ingest, and takes a digits parameter for the precision of the values. At 1536 dimensions over 3000 rows: 478 ms for jsonb::text::vector, 393 ms for jsonb to vector and 358 ms for json to vector, while vector to json takes 183 ms against 623 ms for to_jsonb(v::real[]). The direct casts beat the existing routes in every run, and all values match them exactly. With the full precision of a float the json text doubles in size and the json and jsonb casts converge. README has a JSON section covering both directions, indexing a JSON column, and the difference between json and jsonb.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This commit adds the ability to cast JSONB arrays directly into vector and halfvec vector types, for example:
There are several TODOs if the overall idea of the patch is acceptable:
sparsevec(and possibly bit vectors, but maybe a separate discussion givenbitis a core type).vectortojsonb, one must cast to an array (to_jsonb(vector::real[])) otherwise the value is stored as a string. It may be helpful to have avector_to_jsonbfunction similar to how PostgreSQL hasarray_to_json.Motivation
There are several vector data sources that come from JSON documents, whether the embeddings are contained in them or they're being transformed from other sources, such as from data lake files. Additionally, some cases prefer not to duplicate the vector data between the JSON file and a separate vector column, though are fine to use the vector as an expression in a HNSW/IVFFlat index. The previous discussion concluded that the cast from
jsonb::text::vectorwould work, but for cases with bulk imports or transformations, this adds nontrivial overhead.Testing
The tests show how the
jsonb_to_vectorfunctions performs compared tojsonb::text::vectorcast. This was executed ask=10exact-nearest neighbor queries on a dataset of 100,000 vectors that all fit into memory. Each test was run until 50 or 500 transactions was completed, and the average of these transactions taken. The times are in milliseconds.I went through a few different variations of the tests including:
The below show the results from the last two tests
jsonb::text::vectorjsonb_to_vector(similar forjsonb_to_halfvec)Results
Most tests showed a direct jsonb to vector cast had close to a 20% speedup over the
jsonb::text::vectormethod.vector
jsonb::text::vectorjsonb_to_vectorhalfvec