[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>
This commit is contained in:
@@ -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;
|
||||
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);
|
||||
// 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;
|
||||
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']];
|
||||
}
|
||||
if (!field.autocreate) {
|
||||
continue;
|
||||
|
||||
for (const field of Object.values(Model.fields)) {
|
||||
if (field.compute || field.related) {
|
||||
// new record should always invoke computed fields.
|
||||
this.registerToComputeField(record, field);
|
||||
}
|
||||
}
|
||||
data2[field.fieldName] = [['create']];
|
||||
|
||||
this.update(proxifiedRecord, data2);
|
||||
|
||||
return proxifiedRecord;
|
||||
});
|
||||
if (!isMulti) {
|
||||
return res[0];
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
return proxifiedRecord;
|
||||
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
|
||||
|
||||
@@ -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]] });
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user