rimecms 0.23.26 → 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,6 +100,13 @@ 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];
@@ -109,31 +116,114 @@ export const buildWhereParam = ({ query, slug, db, locale, tables, configCtx })
109
116
  // strict equality semantics (the relation set must equal the provided value(s)).
110
117
  if (fieldConfig.many && operator === 'equals') {
111
118
  // Accept array inputs, repeated params, or CSV values for equality checks
112
- let values = Array.isArray(rawValue)
113
- ? rawValue
114
- : typeof rawValue === 'string' && rawValue.includes(',')
115
- ? rawValue.split(',')
116
- : [value];
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
+ })();
117
128
  // Ensure values are unique
118
129
  values = Array.from(new Set(values));
119
130
  // Owners with total relation count equal to values.length
120
131
  const ownersWithTotalCount = db
121
132
  .select({ id: relationTable.ownerId })
122
133
  .from(relationTable)
123
- .where(and(...(localized ? [eq(relationTable.locale, locale)] : [])))
134
+ .where(and(eq(relationTable.path, column), ...(localized ? [eq(relationTable.locale, locale)] : [])))
124
135
  .groupBy(relationTable.ownerId)
125
136
  .having(drizzleORM.eq(drizzleORM.count(relationTable.id), values.length));
126
137
  // Owners with matching relations count equal to values.length
127
138
  const ownersWithMatchingCount = db
128
139
  .select({ id: relationTable.ownerId })
129
140
  .from(relationTable)
130
- .where(and(drizzleORM.inArray(relationTable[`${to}Id`], values), ...(localized ? [eq(relationTable.locale, locale)] : [])))
141
+ .where(and(drizzleORM.inArray(relationTable[`${to}Id`], values), eq(relationTable.path, column), ...(localized ? [eq(relationTable.locale, locale)] : [])))
131
142
  .groupBy(relationTable.ownerId)
132
143
  .having(drizzleORM.eq(drizzleORM.count(relationTable.id), values.length));
133
144
  return and(inArray(table.id, ownersWithTotalCount), inArray(table.id, ownersWithMatchingCount));
134
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
+ }
135
225
  // Default behavior (membership checks with in_array etc.)
136
- const conditions = [fn(relationTable[`${to}Id`], value)];
226
+ const conditions = [eq(relationTable.path, column), fn(relationTable[`${to}Id`], value)];
137
227
  if (localized) {
138
228
  conditions.push(eq(relationTable.locale, locale));
139
229
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rimecms",
3
- "version": "0.23.26",
3
+ "version": "0.23.27",
4
4
  "homepage": "https://github.com/bienbiendev/rime",
5
5
  "scripts": {
6
6
  "dev": "vite dev",