Received: from malur.postgresql.org ([217.196.149.56]) by arkaria.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vvVB0-0059v6-2L for pgsql-hackers@arkaria.postgresql.org; Thu, 26 Feb 2026 06:48:46 +0000 Received: from localhost ([127.0.0.1] helo=malur.postgresql.org) by malur.postgresql.org with esmtp (Exim 4.96) (envelope-from ) id 1vvVAy-00AP52-0z for pgsql-hackers@arkaria.postgresql.org; Thu, 26 Feb 2026 06:48:44 +0000 Received: from makus.postgresql.org ([2001:4800:3e1:1::229]) by malur.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.96) (envelope-from ) id 1vvVAy-00AP4t-03 for pgsql-hackers@lists.postgresql.org; Thu, 26 Feb 2026 06:48:44 +0000 Received: from mail-pf1-x431.google.com ([2607:f8b0:4864:20::431]) by makus.postgresql.org with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 (Exim 4.98.2) (envelope-from ) id 1vvVAu-00000001FV1-3dlb for pgsql-hackers@lists.postgresql.org; Thu, 26 Feb 2026 06:48:43 +0000 Received: by mail-pf1-x431.google.com with SMTP id d2e1a72fcca58-823c56765fdso298633b3a.1 for ; Wed, 25 Feb 2026 22:48:41 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1772088521; x=1772693321; darn=lists.postgresql.org; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:from:to:cc:subject:date :message-id:reply-to; bh=+RakTKd0SWoHspoRd9997TImmwD/YY25pg0TcLWCPRM=; b=Ry0kRAa0ucqcnp2eGNohWi3pwSC+L1thrOkn3RXvyP1GWvX6mon+0L3QJhwB0bxPFF R9YzMb2ZqlEUiX4q/QW3b3+ZDJhVBr/sis1jdCiJ3yeV6mTfZZiOjEVuX61r7gM6u598 +U+Tm+cfbFAqj/ds+jROAbN001gIQ4oucD4YYAzj4KuD9I2Xf13fxELRK52yob+8M/Fm xjidpbFy4D6/Pd4XAut/jE0Pf1/Mf18sstHr6vDg9gaf7Wv/jILuvbDyUb4nT8mwWNqL qQ8GkTAD0F3gD6Jkfo/id4G9h5pMuc7bInKW3omxHXlLzTxqfq6iwGxrs0MCFt3O/RaU 1s3A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1772088521; x=1772693321; h=to:references:message-id:content-transfer-encoding:cc:date :in-reply-to:from:subject:mime-version:x-gm-gg:x-gm-message-state :from:to:cc:subject:date:message-id:reply-to; bh=+RakTKd0SWoHspoRd9997TImmwD/YY25pg0TcLWCPRM=; b=ETN0vm96AUsUmtLXfxpn7c6QMzCyCTmpjfjnEuPa5C1zcHUd8BxSjZrIRY9ooVQ/7d ifIh3ak4r9x/r92scJe8vi4fFehUM+nIJ9Z21pxxrMoHanbCT7u9mlh1anjaIE1nv8ja tk0yR2Sx6JP2cDkwCP0QK9A6IxmAYT/ihQdOCc7aFwgPLrIBhfQ0w1w1zxzNf/vfV/1j fxFW0TvSbgOwrZIpJieos36opC5E6+zpyBTC8AbsloRpPXMSrgu8wqMdPDO3jalmh1J/ timQ3GZ7Bx/GjMg3Oc1sN8F+RxjpwuhoJ9J/SdIO9ptyrj1Ynw59b1RTNV+GcVvMn9FF +a5A== X-Gm-Message-State: AOJu0YzbjkCWCTlPwzsIX9yRASeuNz/ka3Bt8gws/aQfllyG+bXEWcJR RvwaVt1vwx5UD2rG9tCR+i3VVSfAB8yAPljgiZjBa074kcwQ8+loLJbO X-Gm-Gg: ATEYQzzfOWBO2Z84LFWqXfG+D9SIUupnZEoV0DB8q5sJOorZFTQ2M5GODaV6kfXX68d 4RupXQmrvLXEzTydFyy5yUK7N0UnridjAxxNjhUCwnJtcgrMNDY7DbvUSPgMpNes595rLIxTJp+ UtN/AdGNVS+mj2O7ML2D8yX77jHboF9PYTTkZjYb61fCo2ynRqpGXAMyvGFXOP53NYmGHY82J8N Eo8eT7KS6QiSD7PdQdRpMDXEXqcWtJFgLJg4VJI5voYYLXcZB6xOfeDAHj8FaxLBOzR+EFnuzS+ Z/oDif8lKU37+L2tDopbodSj8OjXCsmkZka4I/+UIVpoOPFL3EcWkzGuClPjYT6DRovT/jZoDBv dMqLow9miXozKa+ejzRw0qt2isRxI0SKP+i3zw2vwnev/Wop4V8/cG+PagJLMKjcluJ27vrr+xS LhQwwevkWI6t3GWHacHU5lnDMkW6VbtA== X-Received: by 2002:a05:6300:85:b0:35d:6b4e:91f1 with SMTP id adf61e73a8af0-395b48d5cacmr1439064637.34.1772088520962; Wed, 25 Feb 2026 22:48:40 -0800 (PST) Received: from smtpclient.apple ([203.10.98.27]) by smtp.gmail.com with ESMTPSA id 98e67ed59e1d1-3593ddd103asm1000319a91.14.2026.02.25.22.48.39 (version=TLS1_2 cipher=ECDHE-ECDSA-AES128-GCM-SHA256 bits=128/128); Wed, 25 Feb 2026 22:48:40 -0800 (PST) Content-Type: text/plain; charset=utf-8 Mime-Version: 1.0 (Mac OS X Mail 16.0 \(3864.400.21\)) Subject: Re: Patch: Add tsmatch JSONPath operator for granular Full Text Search From: Chao Li In-Reply-To: Date: Thu, 26 Feb 2026 14:48:06 +0800 Cc: pgsql-hackers Content-Transfer-Encoding: quoted-printable Message-Id: References: To: Florents Tselai X-Mailer: Apple Mail (2.3864.400.21) List-Id: List-Help: List-Subscribe: List-Post: List-Owner: List-Archive: Archived-At: Precedence: bulk > On Feb 1, 2026, at 19:02, Florents Tselai = wrote: >=20 >=20 >=20 >=20 > On Mon, Jan 26, 2026 at 7:22=E2=80=AFPM Florents Tselai = wrote: > Hi, >=20 > in real-life I work a lot with json & fts search, here's a feature = I've always wished I had, > but never tackle it. Until yesterday that is. >=20 > SELECT jsonb_path_query(doc, '$.comments[*] ? (@.user =3D=3D "Alice" = && @.body tsmatch "performance")'); >=20 > This patch introduces a tsmatch boolean operator to the JSONPath = engine. > By integrating FTS natively into path expressions, > this operator allows for high-precision filtering of nested JSONB = structures=E2=80=94 > solving issues with structural ambiguity and query complexity. >=20 > Currently, users must choose between two suboptimal paths for FTS-ing = nested JSON: > - Imprecise Global Indexing > jsonb_to_tsvector aggregates text into a flat vector. > This ignores JSON boundaries, leading to false positives when the same = key (e.g., "body") > appears in different contexts (e.g., a "Product Description" vs. a = "Customer Review"). >=20 > - Complex SQL Workarounds > Achieving 100% precision requires unnesting the document via = jsonb_array_elements and LATERAL joins. > This leads to verbose SQL and high memory overhead from generating = intermediate heap tuples. >=20 > One of the most significant advantages of tsmatch is its ability to = participate in multi-condition predicates > within the same JSON object - something jsonb_to_tsvector cannot do. >=20 > SELECT jsonb_path_query(doc, '$.comments[*] ? (@.user =3D=3D "Alice" = && @.body tsmatch "performance")'); >=20 > In a flat vector, the association between "Alice" and "performance" is = lost. > tsmatch preserves this link by evaluating the FTS predicate in-place = during path traversal. >=20 > While the SQL/JSON standard (ISO/IEC 9075-2) does not explicitly = define an FTS operator, > tsmatch is architecturally modeled after the standard-defined = like_regex. >=20 > The implementation follows the like_regex precedent: > it is a non-indexable predicate that relies on GIN path-matching for = pruning and heap re-checks for precision. > Caching is scoped to the JsonPathExecContext, > ensuring 'compile-once' efficiency per execution without violating the = stability requirements of prepared statements. >=20 > This initial implementation uses plainto_tsquery. > However, the grammar is designed to support a "mode" flag (similar to = like_regex flags) > in future iterations to toggle between to_tsquery, = websearch_to_tsquery, and phraseto_tsquery. >=20 > Here's a v2, that implements the tsqparser clause=20 >=20 > So this should now work too=20 >=20 > select jsonb_path_query_array('["fast car", "slow car", "fast and = furious"]', '$[*] ? (@ tsmatch "fast car" tsqparser "w") = Hi Florents, Grant pinged me about this. I can review it in coming days. Can you = please rebase it? I failed to apply to current master. Also, the CF = reported a failure test case, please take a look. Best regards, -- Chao Li (Evan) HighGo Software Co., Ltd. https://www.highgo.com/