feat: PostgreSQL-backed search/document storage as ElasticSearch alternative#700
feat: PostgreSQL-backed search/document storage as ElasticSearch alternative#700pandor4u wants to merge 4 commits into
Conversation
|
Here is some feedback from GLM-5.1 PR #700 EvaluationFeature: Replaces ElasticSearch/OpenSearch with a PostgreSQL-backed search backend (JSONB + tsvector) via the existing What's in scope (appropriate for upstream)
What does NOT belong upstream
Key technical concerns
Overall assessmentThe architecture is solid — clean Strategy pattern, backward-compatible, good SQL injection prevention, proper parameterized queries. The main issue is that this PR mixes the legitimate framework feature with a lot of fork-specific baggage (3 identical AI instruction files, fork-specific gitignore entries, fork-specific docker compose). If you're evaluating this for upstream merge, those should be stripped out first, the integration test suite issue should be fixed, and the duplicated SQL should be consolidated. |
…rnative Simplified PR addressing review feedback: - Consolidated duplicated INSERT...ON CONFLICT SQL into shared constant - Removed fork-specific files (AGENTS.md, CLAUDE.md, GEMINI.md, docker compose, .gitignore) - Moved PostgreSQL tests to separate opt-in suite (MoquiSuite untouched) - Updated ElasticRequestLogFilter to use ElasticClient interface type - 83 tests: 46 unit (query translation + SQL injection) + 37 integration New files: - PostgresElasticClient.groovy: Full ElasticClient impl using JSONB/tsvector - ElasticQueryTranslator.groovy: ES Query DSL to PostgreSQL SQL - PostgresSearchLogger.groovy: Log4j2 appender for PostgreSQL - SearchEntities.xml: Entity definitions for search tables - PostgresSearchSuite.groovy: Separate JUnit test suite - PostgresSearchTranslatorTests.groovy: Unit tests - PostgresElasticClientTests.groovy: Integration tests Modified files: - ElasticFacadeImpl.groovy: type=postgres routing - ElasticRequestLogFilter.groovy: Interface type usage - MoquiDefaultConf.xml: Postgres config + entity load - moqui-conf-3.xsd: type attribute with elastic/postgres enum - build.gradle: Test dependencies and suite include
1d1ebb4 to
2553448
Compare
…ne, parameterized queries - Route HTTP logs to dedicated moqui_http_log table with typed columns instead of generic moqui_document JSONB storage - Route deleteByQuery to dedicated tables (moqui_http_log, moqui_logs) with proper timestamp range extraction for nightly cleanup jobs - Replace Java regex highlights with PostgreSQL ts_headline() for accurate, index-aware snippet generation - Fix update() to read-merge-extract pattern so content_text stays consistent with extractContentText() used by index() - Add searchHttpLogTable() for searching against dedicated HTTP log table - Improve guessCastType() to inspect actual values (epoch millis, decimals, ISO dates) when field name heuristics are ambiguous - Parameterize exists query (use ?? operator) to prevent SQL injection - Handle indexExists/createIndex/count for dedicated table names
|
@schue Thanks for the thorough review and for running it through GLM-5.1 — the feedback was very helpful. I've addressed the concerns across two commits. Here's a breakdown: Fork-specific baggage — removed ✅The simplified PR (commit
Technical concerns — addressed1. Fragile 2. Duplicated SQL ✅ — The 3. 4. Highlight support ✅ — Replaced the Java regex stub with PostgreSQL's native 5. Integration tests ✅ — 6. 7. Raw REST API methods — These ( Additional improvements in the second commitBeyond the review feedback, I identified and fixed several issues from reading through the Moqui codebase more carefully:
All changes compile cleanly ( |
|
I ran Qwen 3.6 on it looking for problems and it made the following suggestions. I checked the testing and getPitId issues and they seem to be real.
|
|
Can you make it generate more detailed issues and code suggestions?
Collaboration on the branch to harden it before pushing upstream.
…On Fri, Jul 17, 2026 at 11:12 AM Ean Schuessler ***@***.***> wrote:
*schue* left a comment (moqui/moqui-framework#700)
<#700 (comment)>
I ran Qwen 3.6 on it looking for problems and it made the following
suggestions. I checked the testing and getPitId issues and they seem to be
real.
# Issue Effort
1 Resolve entity definition vs. initSchema column type conflicts High —
needs entity type extension or sync exclusion
2 Make FTS language configurable per-cluster / per-index Medium
3 Handle logging datasource group transition cleanly Medium — runtime
detection or config migration
4 Implement deep merge in update() Medium
5 Harden SQL injection surface (nested paths, cast types) Medium — add
sanitization to all interpolation points
6 Add partitioning or cleanup strategy for log tables High
7 Move DDL out of transaction or use IF NOT EXISTS per-statement with
error recovery Medium
8 Reconsider jsonb_path_ops GIN index for large documents Medium —
selective indexing or remove
9 Improve guessCastType() with mapping-based type detection Medium
10 Remove or reconsider @CompileStatic Low
11 Document aggregation limitation clearly Low
12 Make trackTotal configurable (default to true for compat) Low
13 Implement real PIT with MVCC snapshot or serializable isolation High
14 Gate Postgres test suite behind a system property or profile Low
—
Reply to this email directly, view it on GitHub
<#700?email_source=notifications&email_token=AFK5KLVC4456R4QKDUWSYM35FKJDDA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMBQG42TCNZQHE4KM4TFMFZW63VKON2WE43DOJUWEZLEUVSXMZLOOSWGM33PORSXEX3DNRUWG2Y#issuecomment-5007517098>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AFK5KLQZ3J4YK3OKGFPT2N35FKJDDAVCNFSNUABEKJSXA33TNF2G64TZHM2TAMRWGM2TMMJ3JFZXG5LFHM2DAMBTGM4TKNZVGKQXMAQ>
.
Triage notifications, keep track of coding agent tasks and review pull
requests on the go with GitHub Mobile for iOS
<https://github.com/notifications/mobile/ios/AFK5KLVRIE7CP2AQWQYQ2IT5FKJDDA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMBQG42TCNZQHE4KM4TFMFZW63VKON2WE43DOJUWEZLEUVSXMZLOOSVGM33PORSXEX3JN5ZQ>
and Android
<https://github.com/notifications/mobile/android/AFK5KLRJRXKNP4SC76VZMHT5FKJDDA5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMBQG42TCNZQHE4KM4TFMFZW63VKON2WE43DOJUWEZLEUVSXMZLOOSXGM33PORSXEX3BNZSHE33JMQ>.
Download it today!
You are receiving this because you are subscribed to this thread.Message
ID: ***@***.***>
--
Guru Dharam Singh Khalsa
|
Hardens the type=postgres ElasticClient in response to the PR moqui#700 review. Each item maps to PROBLEMS_RESOLUTION_PLAN.md: - moqui#1 Schema/initSchema type conflict: ensureColumnType() heals document/content_tsv/mdc/thrown columns to JSONB/TSVECTOR regardless of entity-sync ordering; failed casts now log at .warn and the resulting type is verified. Schema-ownership note added to SearchEntities.xml. - moqui#2 Hardcoded 'english' FTS language: new text-config cluster attribute (validated safe-identifier, default english) threaded through to_tsvector/websearch_to_tsquery/ts_headline; declared in moqui-conf-3.xsd. - moqui#4 Shallow merge in update(): recursive deepMerge() preserves nested sibling keys (ES _update semantics). - moqui#5 SQL-injection hardening: ALLOWED_CAST_TYPES whitelist assertion on guessCastType() output. - moqui#6 Unbounded log deletes: batchedDelete() chunks retention deletes via ctid+LIMIT. - moqui#7 DDL-in-transaction: execRequired()/execOptional() so optional steps (pg_trgm, sequence heal) no longer abort init; fatal path logs the failing statement. - moqui#8 jsonb_path_ops GIN index now optional via enable-jsonb-path-index cluster attribute (declared in moqui-conf-3.xsd). - moqui#9 guessCastType() prefers declared ES mapping types (per-cluster fieldTypeMapCache) over name/value heuristics. - moqui#11 search() logs a one-time warn when aggs/aggregations are requested (unsupported). - moqui#12 Single-query total via count(*) OVER() (falls back to standalone COUNT for keyset cursor / empty page). - moqui#13 PIT: real ES-style search_after keyset pagination (updated_stamp,doc_id) with documented snapshot limitation. - moqui#14 PostgresSearchSuite gated behind -PincludePostgresSearch / MOQUI_PG_SEARCH=true (out of default test run). - moqui#3, moqui#10 doc/comment corrections (logging datasource group; @CompileStatic intent). Tests: PostgresElasticClientTests (40) + PostgresSearchTranslatorTests (51), all passing.
- FieldInfo.setPreparedStatementValue(): when a String/CharSequence value is bound to a numeric field (int/long/float/double/BigDecimal), parse and bind it as the actual number type. PostgreSQL (unlike H2/MySQL) does not implicitly cast a varchar bind param to numeric and fails with 'operator does not exist: numeric = character varying'. Re-enables the previously-disabled EntityFindTests 'testNumberInteger | "4321"' case. - Make the default :framework:test (MoquiSuite) suite repeatable against the persistent local PostgreSQL test DB: move order-dependent fixture cleanup to the correct consumer/cleanupSpec in EntityFindTests (ArtifactAuthz), EntityNoSqlCrud (200 BULK* rows), and ToolsScreenRenderTests (TestEntity/UserAccount/DbView* rows).
|
Please review the updates and let me know if further changes are needed.
…On Fri, Jul 17, 2026 at 4:20 PM Ean Schuessler ***@***.***> wrote:
*schue* left a comment (moqui/moqui-framework#700)
<#700 (comment)>
PROBLEMS.md
<https://github.com/user-attachments/files/30145023/PROBLEMS.md>
—
Reply to this email directly, view it on GitHub
<#700?email_source=notifications&email_token=AYZI4FVI4QS3AU2SO3WDWML5FLNH7A5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMBQHE2DINBSGE2KM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#issuecomment-5009444214>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AYZI4FW5XXM2MFA73MXTK2D5FLNH7AVCNFSNUABEKJSXA33TNF2G64TZHM2TAMRWGM2TMMJ3JFZXG5LFHM2DAMBTGM4TKNZVGKQXMAQ>
.
You are receiving this because you authored the thread.Message ID:
***@***.***>
|
|
And these are separate changes. Not sure if they're relevant or not.
https://github.com/pandor4u/moqui-framework/tree/wip/java17-tooling-and-dev-scaffold
On Sat, Jul 18, 2026 at 11:54 AM Pandor Photos ***@***.***>
wrote:
… Please review the updates and let me know if further changes are needed.
On Fri, Jul 17, 2026 at 4:20 PM Ean Schuessler ***@***.***>
wrote:
> *schue* left a comment (moqui/moqui-framework#700)
> <#700 (comment)>
>
> PROBLEMS.md
> <https://github.com/user-attachments/files/30145023/PROBLEMS.md>
>
> —
> Reply to this email directly, view it on GitHub
> <#700?email_source=notifications&email_token=AYZI4FVI4QS3AU2SO3WDWML5FLNH7A5CNFSNUABFM5UWIORPF5TWS5BNNB2WEL2JONZXKZKDN5WW2ZLOOQXTKMBQHE2DINBSGE2KM4TFMFZW63VGMF2XI2DPOKSWK5TFNZ2KYZTPN52GK4S7MNWGSY3L#issuecomment-5009444214>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AYZI4FW5XXM2MFA73MXTK2D5FLNH7AVCNFSNUABEKJSXA33TNF2G64TZHM2TAMRWGM2TMMJ3JFZXG5LFHM2DAMBTGM4TKNZVGKQXMAQ>
> .
> You are receiving this because you authored the thread.Message ID:
> ***@***.***>
>
|
PostgreSQL-Backed Search & Document Storage
This PR adds a new
postgresElasticClient type that implements the fullElasticFacade.ElasticClientinterface using PostgreSQL — eliminating the need for a separate ElasticSearch/OpenSearch cluster.Motivation
Many Moqui deployments already run PostgreSQL. Requiring a separate ElasticSearch/OpenSearch cluster adds operational complexity, memory overhead, and cost — especially for small-to-medium deployments. This provides a zero-dependency alternative that uses PostgreSQL's native JSONB, tsvector full-text search, and GIN indexes.
Changes
New Files
PostgresElasticClient.groovy— FullElasticClientimplementation: index/get/update/delete/bulk/search/count operations backed by PostgreSQL tablesElasticQueryTranslator.groovy— Translates ElasticSearch Query DSL (bool,term,terms,range,nested,exists,match_all,query_string,ids) into parameterized PostgreSQL SQLPostgresSearchLogger.groovy— Log4j2 appender that writes structured logs to PostgreSQL instead of ESSearchEntities.xml— Moqui entity definitions formoqui_search_index,moqui_document,moqui_logs,moqui_http_logwith JSONB columns, tsvector, and GIN/BRIN indexesmoqui-postgres-only-compose.yml— Docker Compose for postgres-only deploymentModified Files
ElasticFacadeImpl.groovy— Addedtype="postgres"client instantiation pathElasticRequestLogFilter.groovy— Updated to work with postgres clientMoquiDefaultConf.xml— Default configuration for postgres searchmoqui-conf-3.xsd— Schema update fortypeattribute onelastic-clientbuild.gradle— PostgreSQL JDBC driver dependencyMoquiSuite.groovy— Test suite registrationTests (83 tests, all passing)
PostgresSearchTranslatorTests.groovy— 46 unit tests for query translation including 13 SQL injection prevention testsPostgresElasticClientTests.groovy— 37 integration tests for CRUD, bulk, search, count, delete-by-queryPostgresSearchSuite.groovy— JUnit test suiteSecurity
^[a-zA-Z0-9_@][a-zA-Z0-9_.\-]*$regex before SQL interpolation--) SQL comments explicitly blockedConfiguration
Set
type="postgres"on anyelastic-clientelement in your Moqui XML configuration. The client will use thetransactionalentity group's database connection.Testing
All 166 Postgres-related tests pass (83 per suite × 2 suite runs). No regressions in existing framework tests.