Как сделать агрегирующий EntityShemaQuery запрос join по двум таблицам на клиенте js?

Как сделать агрегатирующий EntityShemaQuery запрос join по двум таблицам на клиенте (JavaScript)?

Нравится

10 комментариев

Пример который выводит в консоль количество типов для контрагентов, имя которых содержит “111”

var select = Ext.create("Terrasoft.EntitySchemaQuery", {
                rootSchemaName: "Account"   
});
select.addColumn("Name");
select.addAggregationSchemaColumn("Type.Id", Terrasoft.AggregationType.COUNT, "Count");
select.filters.add(select.createColumnFilterWithParameter(
                Terrasoft.ComparisonType.CONTAIN, "Name", "111", Terrasoft.DataValueType.TEXT));
select.getEntityCollection(function(result) {
                if (result.success) {
                               var collection = result.collection;
                               collection.each(function(item) {
console.log(item.get("Name"));
                                               console.log(item.get("Count"));
                               }, this);
                }
}, this);

Здравствуйте,

Скажите, это правильное поведение, что агрегирующий MAX запрос возвращает более одной записи?

Объясню бизнес-логику. Есть раздел "Vacancies", на ней есть деталь "Vacancy candidates", куда можно добавить 0 или более кандидатов на эту вакансию. У каждого кандидата есть лукап "Candidate status". В процессе работы с кандидатом он проходит определённые стадии в определённом порядке, поэтому каждая запись из соответствующей таблицы UsrCandStat содержит целочисленную колонку "Value", где хранится число, определяющее эту стадию, и "StatusName". Допустим, 10 стадий, 0 - нашли кандидата, 1 - связались с ним, 2 - кандидат послал своё резюме, ..., 9 - кандидат успешно прошел интервью с заказчиком, 10 - кандидат взят на работу. В разделе "Vacancies" есть вычисляемое виртуальное поле, которое автоматически должно заполнятся "StatusName" того кандидата из списка кандидатов детали "Vacancy candidates", который прошел в своём нелёгком пути наиболее далеко, т.е. с максимальным значением "Value".

Допустим, в детали добавили 3 кандидата и у ВСЕХ заполнили поле "Candidate status". В этом случае всё более менее работает - возвращаются 3 записи, отсортированные по максимальному значению Value, т.е. можно взять элемент с индексом 0 и это и будет максимум. Хотя, опять же вопрос - почему 3, а не одна максимальная?

Ситуация похуже. В детали 3 кандидата, у 2х заполнили "Candidate status", а у третьего оставили пустым, допустим, поле необязательное. В итоге возвращаются 3 записи, при этом под индексом 0 будет запись с Value равным 0 и пустым "StatusName". Это корректное поведение? Как мне теперь найти запись с максимальным индексом? Искать Макс среди того, что вернул Макс?

Записал видео это всего:
youtube video

setUsrMostFarthestCandStatusVirtualValue: function ()
{
	var vacancyId = this.get("Id");;
	var esqCand = this.Ext.create("Terrasoft.EntitySchemaQuery", { rootSchemaName: "UsrVacancyCandidates" });
	esqCand.addAggregationSchemaColumn("UsrCandStat.Value", Terrasoft.AggregationType.MAX, "Value");
	esqCand.addColumn("UsrCandStat.Name", "CandidateStatusName");
	esqCand.filters.add("ByUsrVacanciesFilter", this.Terrasoft.createColumnInFilterWithParameters("UsrVacancies", [ vacancyId ]));
	esqCand.getEntityCollection(function(response)
	{
		if (response.success)
		{
			var recruitmentStageNameWithMaxValue = "";
			var collection = response.collection;
			if (collection && collection.getCount() > 0)
			{
				Terrasoft.each(collection.getItems(), function(item) {
					console.log(item.get("CandidateStatusName") + ': ' + item.get("Value"));
				}, this);
				var recruitmentStageNameWithMaxValue = collection.getByIndex(0).get("CandidateStatusName");
			}
			this.set("UsrMostFarthestCandStatusVirtual", recruitmentStageNameWithMaxValue);
		}
	}, this);
},

Заранее благодарю.

Потому, что Вы формируете неправильный агрегированный запрос.
У Вас получается в итоге что-то на подобии этого:

Select MAX(Value), CandidateStatusName from 

А надо просто Select MAX(Value) from

Спасибо, Вы правы, протупил. Еще вопросы:
1. Если у Кандидата не выбран статус, то в "Value" (числовой тип) содержится 0, хотя я ожидаю, допустим, null или undefined, а 0 уже используется как индекс определённой стадии. Как это побороть? Хотя, как вариант, можно начинать индексацию стадий с 1, а значение 0 использовать как индикацию, что ничего не выбрано. Но всё же.
2. Почему запрос функция срабатывает несколько раз? Это сказывается на производительности. Как это исправить? Я вызываю её так, как Вы советовали

updateDetail: function(config) {
   config.reloadAll = true;
   this.callParent(arguments);
   this.setUsrMostFarthestCandStatusVirtualValue();
},

3. Я сделал виртуальное поле, куда записываю вычисленное значение. Как мне его сделать readonly?

Добавляю сокращенный код модуля / последовательность действий, пожалуйста, посмотрите, нельзя ли сделать оптимальней:

1. Добавил виртуальную (вычисляемую) колонку. Вопрос - Как сделать её readonly?

attributes:
{
	"UsrMostFarthestCandStatusVirtual":
	{
		type: Terrasoft.ViewModelColumnTypeVIRTUAL_COLUMN,
		referenceSchemaName: "UsrVacancies",
		dataValueType: Terrasoft.DataValueTypeTEXT,
		customConfig:
		{
			readonly: true // не работает
		},
	}
}

2. Связал с diff (разметка?). Вопрос - Как сделать её readonly? Кстати, колонка отобразилась в визуальном редакторе страницы на нужном месте, но, когда выделяешь её, пропадает кнопка "Edit". Так и надо?

{
	"operation": "insert",
	"name": "UsrMostFarthestCandStatusVirtual",
	"values":
	{
		"layout":
		{
			"column": 0,
			"row": 0,
			"colSpan": 12,
			"rowSpan": 1
		},
		"bindTo": "UsrMostFarthestCandStatusVirtual",
		"caption":
		{
			"bindTo": "Resources.Strings.UsrMostFarthestCandStatusVirtualCaption"
		},
		"textSize": 0,
		"contentType": 3,
		"labelConfig":
		{
			"visible": true
		},
		"enabled": true
	},
	"parentName": "group_gridLayout",
	"propertyName": "items",
	"index": 0
}

3. Создал функцию, которая вычисляет значение виртуальной колонки. Пришлось делать 2 EntitySchemaQuery запроса. Один - для получения макс значения Value, второй - для получения Name по уже известному макс значению Value. Вопросы - можно ли обойтись 1-м запросом? Почему метод вызывается несколько раз (см ниже)? Между переходом к другой вакансии и появлением значения в поле виртуальной колонки приходит около 2 секунд, что много.

setUsrMostFarthestCandStatusVirtualValue: function()
{
	var vacancyId = this.get("Id");;
	var esqCand = this.Ext.create("Terrasoft.EntitySchemaQuery",
	{
		rootSchemaName: "UsrVacancyCandidates"
	});
	esqCand.addAggregationSchemaColumn("UsrCandStat.Value", Terrasoft.AggregationType.MAX, "Value");
	esqCand.filters.add("ByUsrVacanciesFilter", this.Terrasoft.createColumnInFilterWithParameters("UsrVacancies", [vacancyId]));
	esqCand.getEntityCollection(function(response)
	{
		if (response.success)
		{
			var collection = response.collection;
			if (collection && collection.getCount() > 0)
			{
				var maxStageValue = collection.getByIndex(0).get("Value");
				if (maxStageValue === 0)
				{
					this.set("UsrMostFarthestCandStatusVirtual", "");
					return;
				}
				var esqCandStatName = this.Ext.create("Terrasoft.EntitySchemaQuery",
				{
					rootSchemaName: "UsrCandStat"
				});
				esqCandStatName.addColumn("Name", "StatusName");
				esqCandStatName.filters.add("ByValueFilter", this.Terrasoft.createColumnInFilterWithParameters("Value", [maxStageValue]));
				esqCandStatName.getEntityCollection(function(responseCandStatus)
				{
					var recruitmentStageNameWithMaxValue = "";
					if (responseCandStatus.success)
					{
						var candStatCollection = responseCandStatus.collection;
						if (candStatCollection && candStatCollection.getCount() > 0)
						{
							recruitmentStageNameWithMaxValue = candStatCollection.getByIndex(0).get("StatusName");
						}
						this.set("UsrMostFarthestCandStatusVirtual", recruitmentStageNameWithMaxValue);
					}
				}, this)
			}
		}
	}, this);
}

4. Дёргаю функцию на updateDetail событие, как вы и советовали. Почему-то она дергается несколько раз. Я вижу это в дебагере и в закладке Network инспектора. Как исправить?

updateDetail: function(config)
{
	config.reloadAll = true;
	this.callParent(arguments);
	this.setUsrMostFarthestCandStatusVirtualValue();
},

2. мастер разделов (если вы об этом) не рассчитан на редактирование нестандартных полей и не умеет это делать.
запрет редактирования поля

{
        "operation": "insert",
        "name": "UsrMostFarthestCandStatusVirtual",
        "values":
        {
                ...
                "enabled": false
        }
...

3. Т.к. вам важна скорость обновления значения (а основное время уходит не на SQL-запрос, а на общение между браузером, сервером и ядром) то рекомендую минимизировать запрос, для этого лучше всего имхо подойдет представление в БД (с Id вакансии и теми колонками, что вам нужны - я так понимаю это Имя кандидата) с использованием (на вскидку) cross join

Спасибо, звучит разумно. Правда нет ответа на вопрос, почему функция дёргается несколько раз, и нормально ли это.

"trickbz" написал:функция дёргается несколько раз, и нормально ли это.

с помощью метода updateDetail обновляются все детали (если не ошибаюсь, то в т.ч. и при их инициализации):

this.updateDetail({detail: "Название детали из блока details, например, Files"});

таким образом вам надо сделать так:

updateDetail: function(config) {
	if (config.detail && config.detail === "VacancyCandidates") { //проверьте имя
		config.reloadAll = true;
		this.callParent(arguments);
		this.setUsrMostFarthestCandStatusVirtualValue();
	} else {
		this.callParent(arguments);
	}
}

Спасибо большое за наводку, проверю! Мог бы сам догадаться добавить параметр в функцию и проинспектировать, или arguments в консоле написатЬ, но как то мозги отключаются по началу знакомства с BPM :)

Здравствуйте,

Хочется забить последний гвоздь. Мне хочется, чтобы значение вышесозданного виртуального поля обновлялось, когда добавляется / изменяется новый кандидат в деталь, или меняется значение Candidate Status одного из кандидатов детали. Сейчас, чтобы увидеть изменения, нужно перезайти на страницу редактирования детали.

Заранее благодарю.

Вам необходимо добавить обработчик ниже приведенного сообщения на вашей странице.

/**
* @message DetailChanged
* Срабатывает когда изменились данные на детали
*/
"DetailChanged": {
mode: this.Terrasoft.MessageMode.PTP,
direction: this.Terrasoft.MessageDirectionType.SUBSCRIBE
},

Показать все комментарии