Zelda Wiki

Want to contribute to this wiki?
Sign up for an account, and get started!

Come join the Zelda Wiki community Discord server!

READ MORE

Zelda Wiki
Advertisement

This module is a wrapper over mw.ext.cargo. It also provides utilities for constructing queries.

Using this module instead of using mw.ext.cargo directly allows us to see via Special:WhatLinksHere what pages are running Cargo queries.

This module exports the following functions.

mw.ext.cargo.query wrapper

query

query(tables, fields, [args])

Parameters

Returns

  • An array of the query results. Throws an error when query syntax is invalid.

Examples

#InputOutputResult
1
query(
  "Games",
  "code, shortName",
  {
    orderBy = "releaseDate",
    where = "type='main'",
    limit = 3,
  }
)
{
  {
    shortName = "The Legend of Zelda",
    code = "TLoZ",
  },
  {
    shortName = "The Adventure of Link",
    code = "TAoL",
  },
  {
    shortName = "A Link to the Past",
    code = "ALttP",
  },
}
Green check


Query builders

allOf

allOf(...)

Parameters

Returns

  • A WHERE clause with ANDed conditions and escaped quotation marks.

Examples

#InputOutputStatus
2
allOf(
  {
    remakeNum = 2,
    game = "Link's Awakening",
  },
  "foo HOLDS 'bar'",
  "baz LIKE '%quux%'"
)
"remakeNum='2' AND game='Link\\'s Awakening' AND foo HOLDS 'bar' AND baz LIKE '%quux%'"
Green check

holdsAll

holdsAll(field, values)

Parameters

Returns

  • A query string of and'ed HOLDS clauses.

Examples

#InputOutputStatus
3
holdsAll("game", {"Link's Awakening"})
"game HOLDS 'Link\\'s Awakening'"
Green check
As a workaround to a Cargo issue, multiple HOLDS statements are converted to an equivalent regex-based syntax.
4
holdsAll("game", {"OoT", "TP"})
"game__full REGEXP '(^|\\\\|)OoT($|\\\\|)' AND game__full REGEXP '(^|\\\\|)TP($|\\\\|)'"
Green check

holdsAny

holdsAny(field, values)

Parameters

Returns

  • A query string of or'ed HOLDS clauses.

Examples

#InputOutputStatus
5
holdsAny("game", {"Link's Awakening"})
"game HOLDS 'Link\\'s Awakening'"
Green check
6
holdsAny("game", {"OoT", "TP"})
"game HOLDS 'OoT' OR game HOLDS 'TP'"
Green check

IN

IN(field, values)

Parameters

Returns

  • A where clause using the SQL IN keyword.

Examples

#InputOutputStatus
7
IN("_pageName", {"Link's Shadow", "Zelda", "Ganon"})
"_pageName IN ('Link\\'s Shadow', 'Zelda', 'Ganon')"
Green check
8
IN("_pageName", {})
"_pageName IN ('')"
Green check

local p = {}
local h = {}

local frame = mw.getCurrentFrame()

function p.query(tables, fields, args)
	return mw.ext.cargo.query(tables, fields, args)
end

local function escape(str)
	return string.gsub(str, "'", "\\'")
end

function p.allOf(...)
	local query
	for i, v in ipairs({...}) do
		if type(v) == "table" then
			for k, v in pairs(v) do
				v = escape(v)
				query = h.andClause(query, string.format("%s='%s'", k, v))
			end
		else
			query = h.andClause(query, v)
		end
	end
	return query
end

function p.holdsAll(field, values)
	local query
	for i, value in ipairs(values) do
		value = escape(value)
		if #values > 1 then
			-- Workaround for Cargo bug: https://phabricator.wikimedia.org/T267498
			-- __full lists all the categories as a string separated by pipes.
			-- Specific regex is needed to account for categories which are at the start or end of the string, which will be missing a pipe.
			-- (^|\\|) matches the beginning
			-- ($|\\|) matches the end
			query = h.andClause(query, field..[[__full REGEXP '(^|\\|)]]..value..[[($|\\|)']])
		else
			query = h.andClause(query, string.format("%s HOLDS '%s'", field, value))
		end
	end
	return query
end

function p.holdsAny(field, values)
	local query
	for i, value in ipairs(values) do
		value = escape(value)
		query = h.orClause(query, string.format("%s HOLDS '%s'", field, value))
	end
	return query
end

function p.IN(field, values)
	local inValues = {}
	for i, value in ipairs(values) do
		value = escape(value)
		value = string.format("'%s'", value)
		inValues[i] = value
	end
	inValues = table.concat(inValues, ", ")
	if inValues == "" then
		inValues = "''"
	end
	return string.format("%s IN (%s)", field, inValues)
end

function h.andClause(query, clause)
	return h.addClause("AND", query, clause)
end

function h.orClause(query, clause)
	return h.addClause("OR", query, clause)
end

function h.addClause(operator, query, clause)
	if not query or query == "" then
		return clause
	end
	return table.concat({query, operator, clause}, " ")
end

function p.Schemas()
	return {
		query = {
			tables = {
				type = "string",
				required = true,
			},
			fields = {
				type = "string",
				required = true,
			},
			args = {
				type = "record",
				properties = {
					{
						name = "where",
						type = "string",
					},	
					{
						name = "join",
						type = "string",
					},
					{
						name = "groupBy",
						type = "string",
					},
					{
						name = "having",
						type = "string",
					},
					{
						name = "orderBy",
						type = "string",
					},
					{
						name = "limit",
						type = "number",
						default = 100,
					},
					{
						name = "offset",
						type = "number",
						default = 0,
					},
				}
			},
		},
		allOf = {
			["..."] = {
				type = "array",
				required = true,
				items = {
					oneOf = {
						{
							type = "string",
						},
						{
							type = "map",
							keys = { type = "string" },
							values = { type = "string" },
						},
					},
				}
			},
		},
		holdsAll = {
			field = {
				type = "string",
				required = true,
			},
			values = {
				type = "array",
				required = true,
				items = { type = "string" },
			},
		},
		holdsAny = {
			field = {
				type = "string",
				required = true,
			},
			values = {
				type = "array",
				required = true,
				items = { type = "string" },
			}
		},
		IN = {
			field = {
				type = "string",
				required = true,
			},
			values = {
				type = "array",
				required = true,
				items = { type = "string" },
			},
		}
	}
end

function p.Documentation()
	return {
		sections = {
			{
				heading = "<code>mw.ext.cargo.query</code> wrapper",
				section = {
					query = {
						params = {"tables", "fields", "args"},
						returns = "An array of the query results. Throws an error when query syntax is invalid.",
						cases = {
							{
								args = {
									"Games",
									"code, shortName",
									{
										where = "type='main'",
										orderBy = "releaseDate",
										limit = 3,
									},
								},
								expect = {
									{ code = "TLoZ", shortName = "The Legend of Zelda"},
									{ code = "TAoL", shortName = "The Adventure of Link"},
									{ code = "ALttP", shortName = "A Link to the Past"},
								},
							},
						}
					},
				}
			},
			{
				heading = "Query builders",
				section = {
					allOf = {
						params = {"..."},
						returns = "A WHERE clause with ANDed conditions and escaped quotation marks.",
						cases = {
							outputOnly = true,
							{
								args = {
									{
										game = "Link's Awakening",
										remakeNum = 2,
									},
									"foo HOLDS 'bar'",
									"baz LIKE '%quux%'",
								},
								expect = [[remakeNum='2' AND game='Link\'s Awakening' AND foo HOLDS 'bar' AND baz LIKE '%quux%']]
							}
						},
					},
					holdsAll = {
						params = {"field", "values"},
						returns = "A query string of and'ed HOLDS clauses.",
						cases = {
							outputOnly = true,
							{
								args = {"game", {"Link's Awakening"}},
								expect = "game HOLDS 'Link\\'s Awakening'",
							},
							{
								desc = "As a workaround to a [https://phabricator.wikimedia.org/T267498 Cargo issue], multiple HOLDS statements are converted to an equivalent regex-based syntax.",
								args = {"game", {"OoT", "TP"}},
								expect = "game__full REGEXP '(^|\\\\|)OoT($|\\\\|)' AND game__full REGEXP '(^|\\\\|)TP($|\\\\|)'",
							}
						},
					},
					holdsAny = {
						params = {"field", "values"},
						returns = "A query string of or'ed HOLDS clauses.",
						cases = {
							outputOnly = true,
							{
								args = {"game", {"Link's Awakening"}},
								expect = "game HOLDS 'Link\\'s Awakening'",
							},
							{
								args = {"game", {"OoT", "TP"}},
								expect = "game HOLDS 'OoT' OR game HOLDS 'TP'",
							}
						}
					},
					IN = {
						params = {"field", "values"},
						returns = "A where clause using the SQL IN keyword.",
						cases = {
							outputOnly = true,
							{
								args = {"_pageName", {"Link's Shadow", "Zelda", "Ganon"}},
								expect = [[_pageName IN ('Link\'s Shadow', 'Zelda', 'Ganon')]],
							},
							{
								args = {"_pageName", {}},
								expect = [[_pageName IN ('')]],
							}
						}
					},
				}
			}
		}
	}
end

return p
Advertisement