Skip to content
This repository was archived by the owner on Sep 2, 2024. It is now read-only.

Commit bd9a9b9

Browse files
committed
Merge branch 'feature_add_bulk_document_update' of github.com:rostikts/core into rostikts-feature_add_bulk_document_update
2 parents 91dd037 + 047de14 commit bd9a9b9

File tree

10 files changed

+331
-45
lines changed

10 files changed

+331
-45
lines changed

database/memory/base.go

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -82,43 +82,7 @@ func (m *Memory) QueryDocuments(auth internal.Auth, dbName, col string, filter m
8282

8383
list = secureRead(auth, col, list)
8484

85-
var filtered []map[string]any
86-
for _, doc := range list {
87-
matches := 0
88-
for k, v := range filter {
89-
op, field := extractOperatorAndValue(k)
90-
switch op {
91-
case "=":
92-
if equal(doc[field], v) {
93-
matches++
94-
}
95-
case "!=":
96-
if notEqual(doc[field], v) {
97-
matches++
98-
}
99-
case ">":
100-
if greater(doc[field], v) {
101-
matches++
102-
}
103-
case "<":
104-
if lower(doc[field], v) {
105-
matches++
106-
}
107-
case ">=":
108-
if greaterThanEqual(doc[field], v) {
109-
matches++
110-
}
111-
case "<=":
112-
if lowerThanEqual(doc[field], v) {
113-
matches++
114-
}
115-
}
116-
}
117-
118-
if matches == len(filter) {
119-
filtered = append(filtered, doc)
120-
}
121-
}
85+
filtered := filterByClauses(list, filter)
12286

12387
start := (params.Page - 1) * params.Size
12488
end := start + params.Size - 1
@@ -156,9 +120,7 @@ func (m *Memory) UpdateDocument(auth internal.Auth, dbName, col, id string, doc
156120
return
157121
}
158122

159-
delete(doc, FieldID)
160-
delete(doc, FieldAccountID)
161-
delete(doc, FieldOwnerID)
123+
removeNotEditableFields(doc)
162124

163125
for k, v := range doc {
164126
exists[k] = v
@@ -168,6 +130,28 @@ func (m *Memory) UpdateDocument(auth internal.Auth, dbName, col, id string, doc
168130
return
169131
}
170132

133+
func (m *Memory) UpdateDocuments(auth internal.Auth, dbName, col string, filter map[string]interface{}, updateFields map[string]interface{}) (n int64, err error) {
134+
list, err := all[map[string]any](m, dbName, col)
135+
136+
if err != nil {
137+
return
138+
}
139+
list = secureRead(auth, col, list)
140+
141+
removeNotEditableFields(updateFields)
142+
filtered := filterByClauses(list, filter)
143+
144+
for _, v := range filtered {
145+
_, err := m.UpdateDocument(auth, dbName, col, v[FieldID].(string), updateFields)
146+
if err != nil {
147+
return n, err
148+
}
149+
n++
150+
}
151+
152+
return
153+
}
154+
171155
func (m *Memory) IncrementValue(auth internal.Auth, dbName, col, id, field string, n int) error {
172156
doc, err := m.GetDocumentByID(auth, dbName, col, id)
173157
if err != nil {
@@ -239,6 +223,12 @@ func extractOperatorAndValue(s string) (op string, field string) {
239223
return
240224
}
241225

226+
func removeNotEditableFields(m map[string]any) {
227+
delete(m, FieldID)
228+
delete(m, FieldAccountID)
229+
delete(m, FieldOwnerID)
230+
}
231+
242232
func equal(v any, val any) bool {
243233
return fmt.Sprintf("%v", v) == fmt.Sprintf("%v", val)
244234
}

database/memory/base_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,46 @@ func TestUpdateDocument(t *testing.T) {
208208
}
209209
}
210210

211+
func TestUpdateDocuments(t *testing.T) {
212+
task1 := newTask("should be completed", false)
213+
task2 := newTask("should be completed", false)
214+
215+
var many []interface{}
216+
many = append(many, task1)
217+
many = append(many, task2)
218+
219+
if err := datastore.BulkCreateDocument(adminAuth, confDBName, colName, many); err != nil {
220+
t.Fatal(err)
221+
}
222+
var clauses [][]interface{}
223+
clauses = append(clauses, []interface{}{"title", "=", "should be completed"})
224+
225+
filters, err := datastore.ParseQuery(clauses)
226+
if err != nil {
227+
t.Fatal(err)
228+
}
229+
230+
updateFields := map[string]any{"done": true}
231+
n, err := datastore.UpdateDocuments(adminAuth, confDBName, colName, filters, updateFields)
232+
if err != nil {
233+
t.Errorf("The documents are not updated because of an error\nExpected err = nil\nActual err: %s", err.Error())
234+
}
235+
if n != int64(len(many)) {
236+
t.Errorf("The incorrect number of documents are updated\nExpected: %v\nActual: %v", len(many), n)
237+
}
238+
239+
docs, err := datastore.QueryDocuments(adminAuth, confDBName, colName, filters, internal.ListParams{Page: 1, Size: 5})
240+
if err != nil {
241+
t.Fatal(err)
242+
}
243+
for _, v := range docs.Results {
244+
got := dec(v)
245+
if !got.Done {
246+
t.Errorf("The '%s' task is not updated; It should be completed (done=true)", got.Title)
247+
}
248+
}
249+
}
250+
211251
func TestIncrementValue(t *testing.T) {
212252
task1 := newTask("incr", false)
213253
m, err := datastore.CreateDocument(adminAuth, confDBName, colName, task1)

database/memory/memory.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,46 @@ func filter[T any](list []T, fn func(x T) bool) []T {
132132
return results
133133
}
134134

135+
func filterByClauses(list []map[string]any, filter map[string]any) (filtered []map[string]any) {
136+
for _, doc := range list {
137+
matches := 0
138+
for k, v := range filter {
139+
op, field := extractOperatorAndValue(k)
140+
switch op {
141+
case "=":
142+
if equal(doc[field], v) {
143+
matches++
144+
}
145+
case "!=":
146+
if notEqual(doc[field], v) {
147+
matches++
148+
}
149+
case ">":
150+
if greater(doc[field], v) {
151+
matches++
152+
}
153+
case "<":
154+
if lower(doc[field], v) {
155+
matches++
156+
}
157+
case ">=":
158+
if greaterThanEqual(doc[field], v) {
159+
matches++
160+
}
161+
case "<=":
162+
if lowerThanEqual(doc[field], v) {
163+
matches++
164+
}
165+
}
166+
}
167+
168+
if matches == len(filter) {
169+
filtered = append(filtered, doc)
170+
}
171+
}
172+
return
173+
}
174+
135175
func sortSlice[T any](list []T, fn func(a, b T) bool) []T {
136176
sort.Slice(list, func(i, j int) bool {
137177
return fn(list[i], list[j])

database/mongo/base.go

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -330,10 +330,7 @@ func (mg *Mongo) UpdateDocument(auth internal.Auth, dbName, col, id string, doc
330330
return nil, err
331331
}
332332

333-
delete(doc, "id")
334-
delete(doc, FieldID)
335-
delete(doc, FieldAccountID)
336-
delete(doc, FieldOwnerID)
333+
removeNotEditableFields(doc)
337334

338335
filter := bson.M{FieldID: oid}
339336

@@ -366,6 +363,31 @@ func (mg *Mongo) UpdateDocument(auth internal.Auth, dbName, col, id string, doc
366363
return result, nil
367364
}
368365

366+
func (mg *Mongo) UpdateDocuments(auth internal.Auth, dbName, col string, filters map[string]interface{}, updateFields map[string]interface{}) (n int64, err error) {
367+
db := mg.Client.Database(dbName)
368+
369+
acctID, userID, err := parseObjectID(auth)
370+
if err != nil {
371+
return 0, err
372+
}
373+
374+
secureWrite(acctID, userID, auth.Role, col, filters)
375+
removeNotEditableFields(updateFields)
376+
377+
newProps := bson.M{}
378+
for k, v := range updateFields {
379+
newProps[k] = v
380+
}
381+
382+
update := bson.M{"$set": newProps}
383+
384+
res, err := db.Collection(internal.CleanCollectionName(col)).UpdateMany(mg.Ctx, filters, update)
385+
if err != nil {
386+
return 0, err
387+
}
388+
return res.ModifiedCount, err
389+
}
390+
369391
func (mg *Mongo) IncrementValue(auth internal.Auth, dbName, col, id, field string, n int) error {
370392
db := mg.Client.Database(dbName)
371393

@@ -477,3 +499,10 @@ func cleanMap(m map[string]interface{}) {
477499

478500
delete(m, FieldOwnerID)
479501
}
502+
503+
func removeNotEditableFields(m map[string]any) {
504+
delete(m, "id")
505+
delete(m, FieldID)
506+
delete(m, FieldAccountID)
507+
delete(m, FieldOwnerID)
508+
}

database/mongo/base_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,46 @@ func TestUpdateDocument(t *testing.T) {
208208
}
209209
}
210210

211+
func TestUpdateDocuments(t *testing.T) {
212+
task1 := newTask("should be completed", false)
213+
task2 := newTask("should be completed", false)
214+
215+
var many []interface{}
216+
many = append(many, task1)
217+
many = append(many, task2)
218+
219+
if err := datastore.BulkCreateDocument(adminAuth, confDBName, colName, many); err != nil {
220+
t.Fatal(err)
221+
}
222+
var clauses [][]interface{}
223+
clauses = append(clauses, []interface{}{"title", "=", "should be completed"})
224+
225+
filters, err := datastore.ParseQuery(clauses)
226+
if err != nil {
227+
t.Fatal(err)
228+
}
229+
230+
updateFields := map[string]any{"done": true}
231+
n, err := datastore.UpdateDocuments(adminAuth, confDBName, colName, filters, updateFields)
232+
if err != nil {
233+
t.Errorf("The documents are not updated because of an error\nExpected err = nil\nActual err: %s", err.Error())
234+
}
235+
if n != int64(len(many)) {
236+
t.Errorf("The incorrect number of documents are updated\nExpected: %v\nActual: %v", len(many), n)
237+
}
238+
239+
docs, err := datastore.QueryDocuments(adminAuth, confDBName, colName, filters, internal.ListParams{Page: 1, Size: 5})
240+
if err != nil {
241+
t.Fatal(err)
242+
}
243+
for _, v := range docs.Results {
244+
got := dec(v)
245+
if !got.Done {
246+
t.Errorf("The '%s' task is not updated; It should be completed (done=true)", got.Title)
247+
}
248+
}
249+
}
250+
211251
func TestIncrementValue(t *testing.T) {
212252
task1 := newTask("incr", false)
213253
m, err := datastore.CreateDocument(adminAuth, confDBName, colName, task1)

database/postgresql/base.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,31 @@ func (pg *PostgreSQL) UpdateDocument(auth internal.Auth, dbName, col, id string,
250250
return updated, nil
251251
}
252252

253+
func (pg *PostgreSQL) UpdateDocuments(auth internal.Auth, dbName, col string, filters map[string]interface{}, updateFields map[string]interface{}) (n int64, err error) {
254+
where := secureWrite(auth, col)
255+
where = applyFilter(where, filters)
256+
257+
qry := fmt.Sprintf(`
258+
UPDATE %s.%s SET
259+
data = data || $3
260+
%s
261+
`, dbName, internal.CleanCollectionName(col), where)
262+
263+
b, err := json.Marshal(updateFields)
264+
if err != nil {
265+
return 0, err
266+
}
267+
res, err := pg.DB.Exec(qry, auth.AccountID, auth.UserID, b)
268+
if err != nil {
269+
return 0, err
270+
}
271+
n, err = res.RowsAffected()
272+
if err != nil {
273+
return 0, err
274+
}
275+
return
276+
}
277+
253278
func (pg *PostgreSQL) IncrementValue(auth internal.Auth, dbName, col, id, field string, n int) error {
254279
where := secureWrite(auth, col)
255280

database/postgresql/base_test.go

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,46 @@ func TestUpdateDocument(t *testing.T) {
208208
}
209209
}
210210

211+
func TestUpdateDocuments(t *testing.T) {
212+
task1 := newTask("should be completed", false)
213+
task2 := newTask("should be completed", false)
214+
215+
var many []interface{}
216+
many = append(many, task1)
217+
many = append(many, task2)
218+
219+
if err := datastore.BulkCreateDocument(adminAuth, confDBName, colName, many); err != nil {
220+
t.Fatal(err)
221+
}
222+
var clauses [][]interface{}
223+
clauses = append(clauses, []interface{}{"title", "=", "should be completed"})
224+
225+
filters, err := datastore.ParseQuery(clauses)
226+
if err != nil {
227+
t.Fatal(err)
228+
}
229+
230+
updateFields := map[string]any{"done": true}
231+
n, err := datastore.UpdateDocuments(adminAuth, confDBName, colName, filters, updateFields)
232+
if err != nil {
233+
t.Errorf("The documents are not updated because of an error\nExpected err = nil\nActual err: %s", err.Error())
234+
}
235+
if n != int64(len(many)) {
236+
t.Errorf("The incorrect number of documents are updated\nExpected: %v\nActual: %v", len(many), n)
237+
}
238+
239+
docs, err := datastore.QueryDocuments(adminAuth, confDBName, colName, filters, internal.ListParams{Page: 1, Size: 5})
240+
if err != nil {
241+
t.Fatal(err)
242+
}
243+
for _, v := range docs.Results {
244+
got := dec(v)
245+
if !got.Done {
246+
t.Errorf("The '%s' task is not updated; It should be completed (done=true)", got.Title)
247+
}
248+
}
249+
}
250+
211251
func TestIncrementValue(t *testing.T) {
212252
task1 := newTask("incr", false)
213253
m, err := datastore.CreateDocument(adminAuth, confDBName, colName, task1)

0 commit comments

Comments
 (0)