<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="ru">
	<id>https://bard-wiki.net/index.php?action=history&amp;feed=atom&amp;title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C%3AWDBackend</id>
	<title>Модуль:WDBackend - История изменений</title>
	<link rel="self" type="application/atom+xml" href="https://bard-wiki.net/index.php?action=history&amp;feed=atom&amp;title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C%3AWDBackend"/>
	<link rel="alternate" type="text/html" href="https://bard-wiki.net/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:WDBackend&amp;action=history"/>
	<updated>2026-10-28T23:07:48Z</updated>
	<subtitle>История изменений этой страницы в вики</subtitle>
	<generator>MediaWiki 1.43.9</generator>
	<entry>
		<id>https://bard-wiki.net/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:WDBackend&amp;diff=6692&amp;oldid=prev</id>
		<title>Admin: 1 версия импортирована</title>
		<link rel="alternate" type="text/html" href="https://bard-wiki.net/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:WDBackend&amp;diff=6692&amp;oldid=prev"/>
		<updated>2023-06-21T15:36:54Z</updated>

		<summary type="html">&lt;p&gt;1 версия импортирована&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;ru&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Предыдущая версия&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Версия от 18:36, 21 июня 2023&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;ru&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(нет различий)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>Admin</name></author>
	</entry>
	<entry>
		<id>https://bard-wiki.net/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:WDBackend&amp;diff=6691&amp;oldid=prev</id>
		<title>ru&gt;D6194c-1cc: Проверка элементов массива на nil на случай пропусков в упорядоченных утверждениях Викиданных</title>
		<link rel="alternate" type="text/html" href="https://bard-wiki.net/index.php?title=%D0%9C%D0%BE%D0%B4%D1%83%D0%BB%D1%8C:WDBackend&amp;diff=6691&amp;oldid=prev"/>
		<updated>2023-05-28T08:30:39Z</updated>

		<summary type="html">&lt;p&gt;Проверка элементов массива на nil на случай пропусков в упорядоченных утверждениях Викиданных&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Новая страница&lt;/b&gt;&lt;/p&gt;&lt;div&gt;require(&amp;#039;strict&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
local base = require(&amp;#039;Модуль:WDBase&amp;#039;)&lt;br /&gt;
&lt;br /&gt;
local Backend = {}&lt;br /&gt;
&lt;br /&gt;
function Backend:new(lang)&lt;br /&gt;
	local obj = { lang=lang }&lt;br /&gt;
	setmetatable(obj, self)&lt;br /&gt;
	self.__index = self&lt;br /&gt;
	&lt;br /&gt;
	return obj&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Backend:safeField(source, fieldName, parentField)&lt;br /&gt;
	if not parentField then&lt;br /&gt;
		parentField = source&lt;br /&gt;
	end&lt;br /&gt;
	local info = parentField[fieldName]&lt;br /&gt;
	if not info then&lt;br /&gt;
		info = {}&lt;br /&gt;
	end&lt;br /&gt;
	return info&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Backend:trySetField(source, fieldName, info, parentField)&lt;br /&gt;
	if info.value or info.entity then&lt;br /&gt;
		if not parentField then&lt;br /&gt;
			parentField = source&lt;br /&gt;
		end&lt;br /&gt;
		parentField[fieldName] = info&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function inArray(value, array)&lt;br /&gt;
	for _, currValue in ipairs(array) do&lt;br /&gt;
		if currValue == value then&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function Backend:fetchFieldsByQualifiers(source, fieldMap, qualifiers, parentField)&lt;br /&gt;
	for _, map in ipairs(fieldMap) do&lt;br /&gt;
		local qualifier = qualifiers[map.property]&lt;br /&gt;
		if qualifier then&lt;br /&gt;
			if map.filter then&lt;br /&gt;
				qualifier = map.filter(qualifier, self.lang)&lt;br /&gt;
			end&lt;br /&gt;
			local currParentField = parentField&lt;br /&gt;
			if map.isLocal then&lt;br /&gt;
				currParentField = parentField.components or {}&lt;br /&gt;
			end&lt;br /&gt;
			self:fetchFieldByMap(source, map, qualifier, base.dataBySnak, currParentField)&lt;br /&gt;
			if map.isLocal and next(currParentField) ~= nil then&lt;br /&gt;
				parentField.components = currParentField&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Backend:fetchFieldItem(source, map, statementOrSnak, getData)&lt;br /&gt;
	local item = getData(statementOrSnak, self.lang)&lt;br /&gt;
	if item then&lt;br /&gt;
		item.retrieved = true&lt;br /&gt;
	else&lt;br /&gt;
		item = {}&lt;br /&gt;
	end&lt;br /&gt;
	if map.mapEntity then&lt;br /&gt;
		local entity = map.mapEntity[item.entity]&lt;br /&gt;
		if not entity then&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
		item = base.dataByEntity(entity)&lt;br /&gt;
		if item then&lt;br /&gt;
			item.retrieved = true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if map.getValue then&lt;br /&gt;
		item.value = map.getValue(item.entity, self.lang)&lt;br /&gt;
	end&lt;br /&gt;
	if map.allowedEntities and item.entity and not inArray(item.entity, map.allowedEntities) then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if map.defaultUnit and not item.unitEntity then&lt;br /&gt;
		item.unitEntity = map.defaultUnit&lt;br /&gt;
	end&lt;br /&gt;
	if map.allowedUnits then&lt;br /&gt;
		if not item.unitEntity or not inArray(item.unitEntity, map.allowedUnits) then&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not item.value and not item.entity then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if map.exact then&lt;br /&gt;
		item.exact = true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	return item&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function skipGetIf(item, cond)&lt;br /&gt;
	if not cond then&lt;br /&gt;
		return false&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	for key, value in pairs(cond) do&lt;br /&gt;
		if item[key] == value then&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return false&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Backend:fetchFieldByMap(source, map, statementsOrSnaks, getData, parentField)&lt;br /&gt;
	local fieldName = map.name&lt;br /&gt;
	local currParentField = source&lt;br /&gt;
	if map.isLocal or not fieldName then&lt;br /&gt;
		currParentField = parentField&lt;br /&gt;
	end&lt;br /&gt;
	if fieldName then&lt;br /&gt;
		local fieldTable = self:safeField(source, fieldName, currParentField)&lt;br /&gt;
		if map.match and getData == base.dataByStatement and fieldTable.value then&lt;br /&gt;
			local statement = base.searchStatementByValue(statementsOrSnaks, fieldTable.value)&lt;br /&gt;
			if not statement then&lt;br /&gt;
				return&lt;br /&gt;
			end&lt;br /&gt;
			local item = self:fetchFieldItem(source, map, statement, getData)&lt;br /&gt;
			if item then&lt;br /&gt;
				currParentField[fieldName] = item&lt;br /&gt;
				if map.qualifiers and statement.qualifiers then&lt;br /&gt;
					self:fetchFieldsByQualifiers(source, map.qualifiers, statement.qualifiers, item)&lt;br /&gt;
				end&lt;br /&gt;
				if map.get and not skipGetIf(item, map.skipGetIf) then&lt;br /&gt;
					self:fetchFieldsByMap(source, item.entity, map.get, currParentField)&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				if map.elseGet then&lt;br /&gt;
					self:fetchFieldsByMap(source, parentField.entity, map.elseGet, parentField)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			return&lt;br /&gt;
		end&lt;br /&gt;
		if fieldTable.value and not map.overwrite then&lt;br /&gt;
			return&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local count = table.getn(statementsOrSnaks)&lt;br /&gt;
	if map.max then&lt;br /&gt;
		if count &amp;gt; map.max then&lt;br /&gt;
			count = map.max&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local items = {}&lt;br /&gt;
	local indices = {}&lt;br /&gt;
	for i=1, count do&lt;br /&gt;
		local statementOrSnak = statementsOrSnaks[i]&lt;br /&gt;
		if statementOrSnak ~= nil then&lt;br /&gt;
			local item = self:fetchFieldItem(source, map, statementOrSnak, getData)&lt;br /&gt;
			if item then&lt;br /&gt;
				table.insert(items, item)&lt;br /&gt;
				indices[i] = table.getn(items)&lt;br /&gt;
			else&lt;br /&gt;
				indices[i] = 0&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local triggerElseGet = false&lt;br /&gt;
	if fieldName then&lt;br /&gt;
		if table.getn(items) == 1 and not map.isArray then&lt;br /&gt;
			currParentField[fieldName] = items[1]&lt;br /&gt;
		elseif next(items) ~= nil then&lt;br /&gt;
			currParentField[fieldName] = items&lt;br /&gt;
		else&lt;br /&gt;
			triggerElseGet = true&lt;br /&gt;
		end&lt;br /&gt;
	elseif next(items) ~= nil then&lt;br /&gt;
		if map.overwriteValue then&lt;br /&gt;
			currParentField.value = items[1].value&lt;br /&gt;
			currParentField.exact = map.exact&lt;br /&gt;
		end&lt;br /&gt;
		if map.overwriteEntity then&lt;br /&gt;
			currParentField.entity = items[1].entity&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		triggerElseGet = true&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	for i=1, count do&lt;br /&gt;
		local statementOrSnak = statementsOrSnaks[i]&lt;br /&gt;
		local item = items[indices[i]]&lt;br /&gt;
		if map.qualifiers and statementOrSnak.qualifiers then&lt;br /&gt;
			self:fetchFieldsByQualifiers(source, map.qualifiers, statementOrSnak.qualifiers, item)&lt;br /&gt;
		end				&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if fieldName then&lt;br /&gt;
		for j, item in ipairs(items) do&lt;br /&gt;
			if map.get and not skipGetIf(item, map.skipGetIf) then&lt;br /&gt;
				local components = {}&lt;br /&gt;
				self:fetchFieldsByMap(source, item.entity, map.get, components)&lt;br /&gt;
				if next(components) ~= nil then&lt;br /&gt;
					item.components = components&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		for j, item in ipairs(items) do&lt;br /&gt;
			if map.get and not skipGetIf(item, map.skipGetIf) then&lt;br /&gt;
				self:fetchFieldsByMap(source, item.entity, map.get, currParentField)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if triggerElseGet and map.elseGet then&lt;br /&gt;
		self:fetchFieldsByMap(source, parentField.entity, map.elseGet, parentField)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Backend:fetchFieldByCustomFunc(source, entity, map, parentField)&lt;br /&gt;
	local currParentField = source&lt;br /&gt;
	if map.isLocal then&lt;br /&gt;
		currParentField = parentField&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local fieldTable = self:safeField(source, map.name, currParentField)&lt;br /&gt;
	if fieldTable.value and not map.overwrite then&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	fieldTable.value = map.getValue(entity, self.lang)&lt;br /&gt;
	self:trySetField(source, map.name, fieldTable, currParentField)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getPropertyByPath(source, propertyPath)&lt;br /&gt;
	local currField = source&lt;br /&gt;
	for _, pathEntry in ipairs(propertyPath) do&lt;br /&gt;
		currField = currField[pathEntry]&lt;br /&gt;
		if not currField then&lt;br /&gt;
			return nil&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return currField&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function filterStatementsByPropertiesAndValues(statements, properties)&lt;br /&gt;
	local filtered = {}&lt;br /&gt;
	local propsCount = table.getn(properties)&lt;br /&gt;
	for _, statement in ipairs(statements) do&lt;br /&gt;
		local matched = 0&lt;br /&gt;
		local qualifiers = statement.qualifiers&lt;br /&gt;
		if qualifiers then&lt;br /&gt;
			for _, propInfo in ipairs(properties) do&lt;br /&gt;
				local propQualifiers = qualifiers[propInfo.property]&lt;br /&gt;
				if propQualifiers then&lt;br /&gt;
					for _, propQualifier in ipairs(propQualifiers) do&lt;br /&gt;
						if propInfo.value then&lt;br /&gt;
							local value = base.valueBySnak(propQualifier)&lt;br /&gt;
							if value == propInfo.value then&lt;br /&gt;
								matched = matched + 1&lt;br /&gt;
							else&lt;br /&gt;
								break&lt;br /&gt;
							end&lt;br /&gt;
						else&lt;br /&gt;
							matched = matched + 1&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		if matched == propsCount then&lt;br /&gt;
			table.insert(filtered, statement)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	if not next(filtered) then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	return filtered&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function statementsByCache(entity, property, cache)&lt;br /&gt;
	if cache then&lt;br /&gt;
		local entityCache = cache[entity]&lt;br /&gt;
		if entityCache then&lt;br /&gt;
			local statements = entityCache[property]&lt;br /&gt;
			if statements then&lt;br /&gt;
				return statements&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return base.statements(entity, property)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Backend:fetchFieldsByMap(source, entity, fieldMap, parentField)&lt;br /&gt;
	for _, map in ipairs(fieldMap) do&lt;br /&gt;
		local currEntity = entity&lt;br /&gt;
		if map.entity then&lt;br /&gt;
			currEntity = map.entity&lt;br /&gt;
		end&lt;br /&gt;
		if currEntity then&lt;br /&gt;
			local propertySpecified = false&lt;br /&gt;
			local statements&lt;br /&gt;
&lt;br /&gt;
			if map.property then&lt;br /&gt;
				statements = statementsByCache(currEntity, map.property, map.cache)&lt;br /&gt;
				propertySpecified = true&lt;br /&gt;
			elseif map.properties then&lt;br /&gt;
				statements = base.statementsByProperties(currEntity, map.properties)&lt;br /&gt;
				propertySpecified = true&lt;br /&gt;
			elseif map.propertyPath then&lt;br /&gt;
				local property = getPropertyByPath(source, map.propertyPath)&lt;br /&gt;
				if property then&lt;br /&gt;
					statements = statementsByCache(currEntity, property, map.cache)&lt;br /&gt;
				end&lt;br /&gt;
				propertySpecified = true&lt;br /&gt;
			end&lt;br /&gt;
&lt;br /&gt;
			if propertySpecified then&lt;br /&gt;
				if statements then&lt;br /&gt;
					if map.filter then&lt;br /&gt;
						statements = map.filter(statements, self.lang)&lt;br /&gt;
					end&lt;br /&gt;
					if map.has then&lt;br /&gt;
						statements = filterStatementsByPropertiesAndValues(statements, map.has)&lt;br /&gt;
					end&lt;br /&gt;
					self:fetchFieldByMap(source, map, statements, base.dataByStatement, parentField)&lt;br /&gt;
				else&lt;br /&gt;
					if map.elseGet then&lt;br /&gt;
						self:fetchFieldsByMap(source, currEntity, map.elseGet, parentField)&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				if not propertySpecified and map.getValue then&lt;br /&gt;
					self:fetchFieldByCustomFunc(source, currEntity, map, parentField)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Backend:fetch(source, fieldsMap)&lt;br /&gt;
	for _, map in ipairs(fieldsMap) do&lt;br /&gt;
		local fieldTable = self:safeField(source, map.name)&lt;br /&gt;
		if fieldTable then&lt;br /&gt;
			self:fetchFieldsByMap(source, fieldTable.entity, map.get, source)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Backend:fetchEntity(source, entity, fieldMap)&lt;br /&gt;
	self:fetchFieldsByMap(source, entity, fieldMap)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Backend:ensureLang()&lt;br /&gt;
	if self.lang then&lt;br /&gt;
		return&lt;br /&gt;
	end&lt;br /&gt;
&lt;br /&gt;
	local defaultLangObj = mw.getContentLanguage()&lt;br /&gt;
	local defaultLang = defaultLangObj:getCode()&lt;br /&gt;
	self.lang = defaultLang&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function Backend:assertLang()&lt;br /&gt;
	assert(self.lang, &amp;#039;No language selected.&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.new(lang)&lt;br /&gt;
	return Backend:new(lang)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>ru&gt;D6194c-1cc</name></author>
	</entry>
</feed>