summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorSébastien Theys <seb@odoo.com>2020-07-31 13:48:31 +0000
committerSébastien Theys <seb@odoo.com>2020-07-31 14:43:15 +0000
commit0d4cb52b9dcd56cb463f2168eaea7a0e8fc2d9a8 (patch)
tree109758dce03461b2cc8e0327ab833f0dc19790cf /addons
parent7a1bfa793a6e0d47b1574cd9cc44f6dd00d910f2 (diff)
[FIX] mail: batch create/insert of JS model for performance
PR: #55261 Part of task-2308312 closes odoo/odoo#55266 X-original-commit: eded5f6372d4f1cffd1a361bd8fa7fd8f1f8ae9e Signed-off-by: Alexandre Kühn (aku) <aku@odoo.com>
Diffstat (limited to 'addons')
-rw-r--r--addons/mail/static/src/model/model_manager.js105
-rw-r--r--addons/mail/static/src/models/chatter/chatter.js10
-rw-r--r--addons/mail/static/src/models/messaging_initializer/messaging_initializer.js17
-rw-r--r--addons/mail/static/src/models/model/model.js10
-rw-r--r--addons/mail/static/src/models/notification_group_manager/notification_group_manager.js1
-rw-r--r--addons/mail/static/src/models/partner/partner.js8
-rw-r--r--addons/mail/static/src/models/thread/thread.js13
-rw-r--r--addons/mail/static/src/models/thread_cache/thread_cache.js8
8 files changed, 92 insertions, 80 deletions
diff --git a/addons/mail/static/src/model/model_manager.js b/addons/mail/static/src/model/model_manager.js
index fb3319a6fb3..815a3c05409 100644
--- a/addons/mail/static/src/model/model_manager.js
+++ b/addons/mail/static/src/model/model_manager.js
@@ -115,47 +115,56 @@ class ModelManager {
* from this record creation.
*
* @param {mail.model} Model class
- * @param {Object} [data={}]
- * @returns {mail.model} newly created record
+ * @param {Object|Object[]} [data={}]
+ * If data is an iterable, multiple records will be created.
+ * @returns {mail.model|mail.model[]} newly created record(s)
*/
create(Model, data = {}) {
return this._updateCycle(() => {
- const record = new Model({ valid: true });
- Object.defineProperty(record, 'env', { get: () => Model.env });
- record.localId = record._createRecordLocalId(data);
- // Contains field values of record.
- record.__values = {};
- // Contains revNumber of record for checking record update in useStore.
- record.__state = 0;
-
- // Make proxified record, so that access to field redirects
- // to field getter.
- const proxifiedRecord = this._makeProxifiedRecord(record);
- this._records[record.localId] = proxifiedRecord;
- proxifiedRecord.init();
- this._makeDefaults(proxifiedRecord);
-
- const data2 = Object.assign({}, data);
- for (const field of Object.values(Model.fields)) {
- if (field.fieldType !== 'relation') {
- continue;
- }
- if (!field.autocreate) {
- continue;
+ const isMulti = typeof data[Symbol.iterator] === 'function';
+ const dataList = isMulti ? data : [data];
+ const res = dataList.map(data => {
+ const record = new Model({ valid: true });
+ Object.defineProperty(record, 'env', { get: () => Model.env });
+ record.localId = record._createRecordLocalId(data);
+ // Contains field values of record.
+ record.__values = {};
+ // Contains revNumber of record for checking record update in useStore.
+ record.__state = 0;
+
+ // Make proxified record, so that access to field redirects
+ // to field getter.
+ const proxifiedRecord = this._makeProxifiedRecord(record);
+ this._records[record.localId] = proxifiedRecord;
+ proxifiedRecord.init();
+ this._makeDefaults(proxifiedRecord);
+
+ const data2 = Object.assign({}, data);
+ for (const field of Object.values(Model.fields)) {
+ if (field.fieldType !== 'relation') {
+ continue;
+ }
+ if (!field.autocreate) {
+ continue;
+ }
+ data2[field.fieldName] = [['create']];
}
- data2[field.fieldName] = [['create']];
- }
- for (const field of Object.values(Model.fields)) {
- if (field.compute || field.related) {
- // new record should always invoke computed fields.
- this.registerToComputeField(record, field);
+ for (const field of Object.values(Model.fields)) {
+ if (field.compute || field.related) {
+ // new record should always invoke computed fields.
+ this.registerToComputeField(record, field);
+ }
}
- }
- this.update(proxifiedRecord, data2);
+ this.update(proxifiedRecord, data2);
- return proxifiedRecord;
+ return proxifiedRecord;
+ });
+ if (!isMulti) {
+ return res[0];
+ }
+ return res;
});
}
@@ -259,18 +268,27 @@ class ModelManager {
* per "unique find" criteria from data on Model.
*
* @param {mail.model} Model class
- * @param {Object} data
- * @returns {mail.model} created or updated record.
+ * @param {Object|Object[]} data
+ * If data is an iterable, multiple records will be created/updated.
+ * @returns {mail.model|mail.model[]} created or updated record(s).
*/
insert(Model, data) {
return this._updateCycle(() => {
- let record = Model.find(Model._findFunctionFromData(data));
- if (!record) {
- record = Model.create(data);
- } else {
- record.update(data);
+ const isMulti = typeof data[Symbol.iterator] === 'function';
+ const dataList = isMulti ? data : [data];
+ const res = dataList.map(data => {
+ let record = Model.find(Model._findFunctionFromData(data));
+ if (!record) {
+ record = Model.create(data);
+ } else {
+ record.update(data);
+ }
+ return record;
+ });
+ if (!isMulti) {
+ return res[0];
}
- return record;
+ return res;
});
}
@@ -721,7 +739,10 @@ class ModelManager {
if (k === 'constructor') {
return target[k];
}
- const field = target.constructor.fields[k];
+ const fields = target.constructor.fields;
+ const field = Object.prototype.hasOwnProperty.call(fields, k)
+ ? fields[k]
+ : undefined;
if (!field) {
// No crash, we allow these reads due to patch()
// implementation details that read on `this._super` even
diff --git a/addons/mail/static/src/models/chatter/chatter.js b/addons/mail/static/src/models/chatter/chatter.js
index eb1081ac1b5..4df875a0898 100644
--- a/addons/mail/static/src/models/chatter/chatter.js
+++ b/addons/mail/static/src/models/chatter/chatter.js
@@ -61,13 +61,9 @@ function factory(dependencies) {
method: 'activity_format',
args: [newActivityIds]
}));
- const activities = [];
- for (const activityData of activitiesData) {
- const activity = this.env.models['mail.activity'].insert(
- this.env.models['mail.activity'].convertData(activityData)
- );
- activities.push(activity);
- }
+ const activities = this.env.models['mail.activity'].insert(activitiesData.map(
+ activityData => this.env.models['mail.activity'].convertData(activityData)
+ ));
this.update({ activities: [['replace', activities]] });
}
diff --git a/addons/mail/static/src/models/messaging_initializer/messaging_initializer.js b/addons/mail/static/src/models/messaging_initializer/messaging_initializer.js
index 5bcebff0af5..6da45ac49ca 100644
--- a/addons/mail/static/src/models/messaging_initializer/messaging_initializer.js
+++ b/addons/mail/static/src/models/messaging_initializer/messaging_initializer.js
@@ -143,11 +143,10 @@ function factory(dependencies) {
channel_direct_message = [],
channel_private_group = [],
} = {}) {
- for (const data of channel_channel.concat(channel_direct_message, channel_private_group)) {
- this.env.models['mail.thread'].insert(
- this.env.models['mail.thread'].convertData(data),
- );
- }
+ const channelsData = channel_channel.concat(channel_direct_message, channel_private_group);
+ this.env.models['mail.thread'].insert(channelsData.map(
+ channelData => this.env.models['mail.thread'].convertData(channelData)
+ ));
}
/**
@@ -203,10 +202,10 @@ function factory(dependencies) {
* @param {Object} mailFailuresData
*/
_initMailFailures(mailFailuresData) {
- for (const messageData of mailFailuresData) {
- const message = this.env.models['mail.message'].insert(
- this.env.models['mail.message'].convertData(messageData)
- );
+ const messages = this.env.models['mail.message'].insert(mailFailuresData.map(
+ messageData => this.env.models['mail.message'].convertData(messageData)
+ ));
+ for (const message of messages) {
// implicit: failures are sent by the server at initialization
// only if the current partner is author of the message
if (!message.author && this.messaging.currentPartner) {
diff --git a/addons/mail/static/src/models/model/model.js b/addons/mail/static/src/models/model/model.js
index 15647acaf49..6ee5125426d 100644
--- a/addons/mail/static/src/models/model/model.js
+++ b/addons/mail/static/src/models/model/model.js
@@ -61,8 +61,9 @@ function factory() {
* function, otherwise the record will not be registered.
*
* @static
- * @param {Object} [data] data object with initial data, including relations.
- * @returns {mail.model} newly created record
+ * @param {Object|Object[]} [data] data object with initial data, including relations.
+ * If data is an iterable, multiple records will be created.
+ * @returns {mail.model|mail.model[]} newly created record(s)
*/
static create(data) {
return this.env.modelManager.create(this, data);
@@ -99,8 +100,9 @@ function factory() {
* on provided data.
*
* @static
- * @param {Object} data
- * @returns {mail.model} created or updated record.
+ * @param {Object|Object[]} data
+ * If data is an iterable, multiple records will be created/updated.
+ * @returns {mail.model|mail.model[]} created or updated record(s).
*/
static insert(data) {
return this.env.modelManager.insert(this, data);
diff --git a/addons/mail/static/src/models/notification_group_manager/notification_group_manager.js b/addons/mail/static/src/models/notification_group_manager/notification_group_manager.js
index be7bef8352a..9c7c38ef52b 100644
--- a/addons/mail/static/src/models/notification_group_manager/notification_group_manager.js
+++ b/addons/mail/static/src/models/notification_group_manager/notification_group_manager.js
@@ -17,6 +17,7 @@ function factory(dependencies) {
group.delete();
}
const groups = [];
+ // TODO batch insert, better logic task-2258605
this.env.messaging.currentPartner.failureNotifications.forEach(notification => {
const thread = notification.message.originThread;
// Notifications are grouped by model and notification_type.
diff --git a/addons/mail/static/src/models/partner/partner.js b/addons/mail/static/src/models/partner/partner.js
index 646e3bdd1f7..32b02e6e60c 100644
--- a/addons/mail/static/src/models/partner/partner.js
+++ b/addons/mail/static/src/models/partner/partner.js
@@ -109,10 +109,10 @@ function factory(dependencies) {
},
{ shadow: true }
);
- for (const data of partnersData) {
- const partner = this.insert(this.convertData(data));
- partners.push(partner);
- }
+ const newPartners = this.insert(partnersData.map(
+ partnerData => this.convertData(partnerData)
+ ));
+ partners.push(...newPartners);
}
callback(partners);
}
diff --git a/addons/mail/static/src/models/thread/thread.js b/addons/mail/static/src/models/thread/thread.js
index cf3ce78b955..ceb4a366ec3 100644
--- a/addons/mail/static/src/models/thread/thread.js
+++ b/addons/mail/static/src/models/thread/thread.js
@@ -363,19 +363,14 @@ function factory(dependencies) {
}
return list;
}, []);
- const messagePreviews = await this.env.services.rpc({
+ const channelPreviews = await this.env.services.rpc({
model: 'mail.channel',
method: 'channel_fetch_preview',
args: [channelIds],
}, { shadow: true });
- for (const preview of messagePreviews) {
- const messageData = preview.last_message;
- if (messageData) {
- this.env.models['mail.message'].insert(
- this.env.models['mail.message'].convertData(messageData)
- );
- }
- }
+ this.env.models['mail.message'].insert(channelPreviews.filter(p => p.last_message).map(
+ channelPreview => this.env.models['mail.message'].convertData(channelPreview.last_message)
+ ));
}
/**
diff --git a/addons/mail/static/src/models/thread_cache/thread_cache.js b/addons/mail/static/src/models/thread_cache/thread_cache.js
index 81027d665ba..b4fe3ca28c3 100644
--- a/addons/mail/static/src/models/thread_cache/thread_cache.js
+++ b/addons/mail/static/src/models/thread_cache/thread_cache.js
@@ -266,11 +266,9 @@ function factory(dependencies) {
* @param {Object[]} messageData
*/
_handleMessagesLoaded(messagesData) {
- const messages = messagesData.map(data =>
- this.env.models['mail.message'].insert(
- this.env.models['mail.message'].convertData(data)
- )
- );
+ const messages = this.env.models['mail.message'].insert(messagesData.map(
+ messageData => this.env.models['mail.message'].convertData(messageData)
+ ));
if (!this.thread) {
return;