Context
Today HolySheets does not expose a Prisma-like case-insensitive mode for string filters.
Current behavior:
- Core local filtering (
WhereService + whereFilters) is case-sensitive for equals, not, contains, startsWith, endsWith.
search in core is currently case-insensitive by implementation detail (RegExp(..., 'i')).
- CLI (public mode) builds GViz queries and also has no explicit case mode option.
- Google Visualization Query Language does not provide a native
mode parameter; case-insensitive behavior must be implemented via expression strategy (e.g. lower(column) patterns).
Reference and product direction:
- We want a Prisma-like API shape where case mode is defined per string filter, with explicit
mode: 'insensitive'.
Goal
Implement a consistent, Prisma-inspired case-sensitivity API across:
- Core API (
where object)
- CLI (
--where-* flags)
with predictable behavior and docs/tests.
Proposed API (Prisma-like)
Core
Allow per-filter object with value + mode for string operators.
Example:
await reader.findMany({
where: {
name: { contains: { value: 'saur', mode: 'insensitive' } },
type: { equals: { value: 'GRASS', mode: 'insensitive' } },
city: { startsWith: { value: 'san', mode: 'default' } }
}
})
Suggested mode values:
default (or omitted): current behavior
insensitive
Backwards compatibility:
- Existing syntax must keep working:
{ name: { contains: 'saur' } }
{ name: 'Pikachu' }
CLI
Add --where-mode <default|insensitive> scoped to the current --where-field block.
Example:
holysheets read find-many \
--spreadsheet-id <ID> \
--sheet pokemon \
--where-field name --where-op contains --where-value saur --where-mode insensitive \
--where-field type --where-op equals --where-value GRASS --where-mode insensitive
Grouping rule:
--where-field starts a block
--where-op, --where-value, and new --where-mode apply to latest block
Default:
- If omitted, mode is
default
Implementation Notes
Core filtering engine
- Extend where types to support string filter object payloads:
- Current:
contains: string
- New:
contains: string | { value: string; mode?: 'default' | 'insensitive' }
- Apply normalization when
mode === 'insensitive' (string comparisons via normalized casing)
- Keep numeric and array operators unchanged (
lt, in, etc.)
- Decide and document
search behavior explicitly:
- Option A: support mode as well (
search: { value: '...regex...', mode: ... })
- Option B: keep current behavior and disallow
mode for search in v1
CLI + normalization
- Parse and validate new
--where-mode
- Include mode in normalized where representation
- Validate only allowed values
- Keep old CLI syntax valid
GViz query builder
- For string operators with
mode=insensitive, generate expressions using lowercasing strategy, e.g.:
lower(A) contains 'saur'
lower(A) starts with 'pi'
lower(A) = 'grass'
- Ensure escaping still works
- Confirm behavior for operators unsupported with lowercasing and document decisions
Docs to update
docs/en/concepts/where-filters.md
docs/en/cli/filters-and-select.md
docs/en/cli/complete-guide.md
README.md
- Any type/reference docs that describe
WhereClause
Include examples in both:
- Core usage (TS)
- CLI usage (
--where-mode insensitive)
Tests
Add/adjust tests for:
- Core where filter behavior with
mode: 'insensitive'
- Backward compatibility with current syntax
- CLI parse/normalize for
--where-mode
- GViz query builder output for insensitive mode
- Validation errors for invalid modes
Acceptance Criteria
Open Questions
- Should
search receive mode in v1, or keep current semantics?
- Should simple string shorthand (
where: { name: 'Pikachu' }) ever support insensitive mode, or stay strictly default-sensitive?
- Exact naming: use
default + insensitive (closest to Prisma), or only insensitive boolean-like semantics?
Context
Today HolySheets does not expose a Prisma-like case-insensitive mode for string filters.
Current behavior:
WhereService+whereFilters) is case-sensitive forequals,not,contains,startsWith,endsWith.searchin core is currently case-insensitive by implementation detail (RegExp(..., 'i')).modeparameter; case-insensitive behavior must be implemented via expression strategy (e.g.lower(column)patterns).Reference and product direction:
mode: 'insensitive'.Goal
Implement a consistent, Prisma-inspired case-sensitivity API across:
whereobject)--where-*flags)with predictable behavior and docs/tests.
Proposed API (Prisma-like)
Core
Allow per-filter object with
value+modefor string operators.Example:
Suggested mode values:
default(or omitted): current behaviorinsensitiveBackwards compatibility:
{ name: { contains: 'saur' } }{ name: 'Pikachu' }CLI
Add
--where-mode <default|insensitive>scoped to the current--where-fieldblock.Example:
Grouping rule:
--where-fieldstarts a block--where-op,--where-value, and new--where-modeapply to latest blockDefault:
defaultImplementation Notes
Core filtering engine
contains: stringcontains: string | { value: string; mode?: 'default' | 'insensitive' }mode === 'insensitive'(string comparisons via normalized casing)lt,in, etc.)searchbehavior explicitly:search: { value: '...regex...', mode: ... })modeforsearchin v1CLI + normalization
--where-modeGViz query builder
mode=insensitive, generate expressions using lowercasing strategy, e.g.:lower(A) contains 'saur'lower(A) starts with 'pi'lower(A) = 'grass'Docs to update
docs/en/concepts/where-filters.mddocs/en/cli/filters-and-select.mddocs/en/cli/complete-guide.mdREADME.mdWhereClauseInclude examples in both:
--where-mode insensitive)Tests
Add/adjust tests for:
mode: 'insensitive'--where-modeAcceptance Criteria
mode: 'insensitive'for string operators--where-modeper where blockOpen Questions
searchreceivemodein v1, or keep current semantics?where: { name: 'Pikachu' }) ever support insensitive mode, or stay strictly default-sensitive?default+insensitive(closest to Prisma), or onlyinsensitiveboolean-like semantics?