rimecms 0.23.25 → 0.23.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -100,12 +100,130 @@ export const buildWhereParam = ({ query, slug, db, locale, tables, configCtx })
100
100
  // Return a condition that will always be false
101
101
  return eq(table.id, '-1'); // No document will have ID = -1, so this will always be false
102
102
  }
103
+ // Unsupported operator for multi-valued relations
104
+ const supportedRelationManyOperators = ['equals', 'not_equals', 'in_array', 'not_in_array'];
105
+ if (fieldConfig.many && !supportedRelationManyOperators.includes(operator)) {
106
+ logger.warn(`the operator "${operator}" is not supported for multi-valued relation field "${column}" in ${documentConfig.slug} document`);
107
+ // Return a condition that will always be false
108
+ return eq(table.id, '-1'); // No document will have ID = -1, so this will always be false
109
+ }
103
110
  // Only compare with the relation ID for now
104
111
  // @TODO handle relation props ex: author.email
105
112
  const [to, localized] = [fieldConfig.relationTo, fieldConfig.localized];
106
113
  const relationTableName = `${slug}Rels`;
107
114
  const relationTable = getTable(relationTableName);
108
- const conditions = [fn(relationTable[`${to}Id`], value)];
115
+ // Handle multi-valued relations specially when operator is `equals` to provide
116
+ // strict equality semantics (the relation set must equal the provided value(s)).
117
+ if (fieldConfig.many && operator === 'equals') {
118
+ // Accept array inputs, repeated params, or CSV values for equality checks
119
+ let values = (() => {
120
+ if (Array.isArray(rawValue))
121
+ return rawValue;
122
+ if (typeof rawValue === 'string' && rawValue.includes(','))
123
+ return rawValue.split(',');
124
+ if (Array.isArray(value))
125
+ return value;
126
+ return [value];
127
+ })();
128
+ // Ensure values are unique
129
+ values = Array.from(new Set(values));
130
+ // Owners with total relation count equal to values.length
131
+ const ownersWithTotalCount = db
132
+ .select({ id: relationTable.ownerId })
133
+ .from(relationTable)
134
+ .where(and(eq(relationTable.path, column), ...(localized ? [eq(relationTable.locale, locale)] : [])))
135
+ .groupBy(relationTable.ownerId)
136
+ .having(drizzleORM.eq(drizzleORM.count(relationTable.id), values.length));
137
+ // Owners with matching relations count equal to values.length
138
+ const ownersWithMatchingCount = db
139
+ .select({ id: relationTable.ownerId })
140
+ .from(relationTable)
141
+ .where(and(drizzleORM.inArray(relationTable[`${to}Id`], values), eq(relationTable.path, column), ...(localized ? [eq(relationTable.locale, locale)] : [])))
142
+ .groupBy(relationTable.ownerId)
143
+ .having(drizzleORM.eq(drizzleORM.count(relationTable.id), values.length));
144
+ return and(inArray(table.id, ownersWithTotalCount), inArray(table.id, ownersWithMatchingCount));
145
+ }
146
+ // For multi-valued relations, allow `in_array` to act as a subset check:
147
+ // The provided values must contain ALL relation values of the document.
148
+ if (fieldConfig.many && operator === 'in_array') {
149
+ // Accept array inputs, repeated params, or CSV values for in_array checks
150
+ let values = (() => {
151
+ if (Array.isArray(rawValue))
152
+ return rawValue;
153
+ if (typeof rawValue === 'string' && rawValue.includes(','))
154
+ return rawValue.split(',');
155
+ if (Array.isArray(value))
156
+ return value;
157
+ return [value];
158
+ })();
159
+ // Ensure values are unique
160
+ values = Array.from(new Set(values));
161
+ // Owners that have at least one relation row not included in the provided set
162
+ const ownersWithNonMatching = db
163
+ .select({ id: relationTable.ownerId })
164
+ .from(relationTable)
165
+ .where(and(drizzleORM.notInArray(relationTable[`${to}Id`], values), eq(relationTable.path, column), ...(localized ? [eq(relationTable.locale, locale)] : [])))
166
+ .groupBy(relationTable.ownerId)
167
+ .having(drizzleORM.gt(drizzleORM.count(relationTable.id), 0));
168
+ // Owners that have any relation rows (to exclude docs with no relations)
169
+ const ownersWithRelations = db
170
+ .select({ id: relationTable.ownerId })
171
+ .from(relationTable)
172
+ .where(and(eq(relationTable.path, column), ...(localized ? [eq(relationTable.locale, locale)] : [])))
173
+ .groupBy(relationTable.ownerId)
174
+ .having(drizzleORM.gt(drizzleORM.count(relationTable.id), 0));
175
+ // Match documents that have relations and do NOT have any non-matching relation rows
176
+ return and(drizzleORM.notInArray(table.id, ownersWithNonMatching), inArray(table.id, ownersWithRelations));
177
+ }
178
+ // For multi-valued relations, `not_in_array` should match documents where the provided
179
+ // set does NOT contain all of the document's relation values (inverse of `in_array`).
180
+ if (fieldConfig.many && operator === 'not_in_array') {
181
+ let values = (() => {
182
+ if (Array.isArray(rawValue))
183
+ return rawValue;
184
+ if (typeof rawValue === 'string' && rawValue.includes(','))
185
+ return rawValue.split(',');
186
+ if (Array.isArray(value))
187
+ return value;
188
+ return [value];
189
+ })();
190
+ values = Array.from(new Set(values));
191
+ const ownersWithNonMatching = db
192
+ .select({ id: relationTable.ownerId })
193
+ .from(relationTable)
194
+ .where(and(drizzleORM.notInArray(relationTable[`${to}Id`], values), eq(relationTable.path, column), ...(localized ? [eq(relationTable.locale, locale)] : [])))
195
+ .groupBy(relationTable.ownerId)
196
+ .having(drizzleORM.gt(drizzleORM.count(relationTable.id), 0));
197
+ return inArray(table.id, ownersWithNonMatching);
198
+ }
199
+ // For multi-valued relations, `not_equals` is the inverse of `equals` (exact-set inequality)
200
+ if (fieldConfig.many && operator === 'not_equals') {
201
+ let values = (() => {
202
+ if (Array.isArray(rawValue))
203
+ return rawValue;
204
+ if (typeof rawValue === 'string' && rawValue.includes(','))
205
+ return rawValue.split(',');
206
+ if (Array.isArray(value))
207
+ return value;
208
+ return [value];
209
+ })();
210
+ values = Array.from(new Set(values));
211
+ const ownersWithTotalCount = db
212
+ .select({ id: relationTable.ownerId })
213
+ .from(relationTable)
214
+ .where(and(eq(relationTable.path, column), ...(localized ? [eq(relationTable.locale, locale)] : [])))
215
+ .groupBy(relationTable.ownerId)
216
+ .having(drizzleORM.eq(drizzleORM.count(relationTable.id), values.length));
217
+ const ownersWithMatchingCount = db
218
+ .select({ id: relationTable.ownerId })
219
+ .from(relationTable)
220
+ .where(and(inArray(relationTable[`${to}Id`], values), eq(relationTable.path, column), ...(localized ? [eq(relationTable.locale, locale)] : [])))
221
+ .groupBy(relationTable.ownerId)
222
+ .having(drizzleORM.eq(drizzleORM.count(relationTable.id), values.length));
223
+ return or(drizzleORM.notInArray(table.id, ownersWithTotalCount), drizzleORM.notInArray(table.id, ownersWithMatchingCount));
224
+ }
225
+ // Default behavior (membership checks with in_array etc.)
226
+ const conditions = [eq(relationTable.path, column), fn(relationTable[`${to}Id`], value)];
109
227
  if (localized) {
110
228
  conditions.push(eq(relationTable.locale, locale));
111
229
  }
@@ -1,4 +1,3 @@
1
- // /Users/ai/Dev/rime/src/lib/fields/rich-text/core/features/bold.ts
2
1
  import { Text } from '@lucide/svelte';
3
2
  import Paragraph from '@tiptap/extension-paragraph';
4
3
  const paragraphFeatureNode = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rimecms",
3
- "version": "0.23.25",
3
+ "version": "0.23.27",
4
4
  "homepage": "https://github.com/bienbiendev/rime",
5
5
  "scripts": {
6
6
  "dev": "vite dev",