此模块的文档可以在模块:CustomSearch/battlenet/doc创建

local p = {}
local getArgs = require('Module:Arguments').getArgs
local Util = require('Module:Util')
local Linq = require('Module:Linq')
local Metadata = require('Module:Metadata')
local ListTable = require('Module:Card/output').listTable
-----------------------------------------------------------------------------
-- ----------------------筛选条件的schema------------------------------------
-----------------------------------------------------------------------------
local FILTER_LIST = {
    { name = '卡牌名称/随从身材',  id='name',        type='text',		placeholder='如“207”、“末日”或“Doom”' },
    { name = '卡牌文本',  id='text',        type='text',		placeholder='如“随机对两个敌方随从”' },
	{ name = '费用',      id='manaCost',    type='key-value' },
    { name = '关键字',    id='keywordIds',  type='key-value' },
	{ name = '职业',      id='classId',     type='key-value' },
	{ name = '卡牌类型',  id='cardTypeId',  type='key-value' },
	{ name = '种族',	  id='minionTypeId',type='key-value',	precondition = {cardTypeId = 4}}, --需要是随从(暂不实装)
	{ name = '稀有度',    id='rarityId',    type='key-value' },
	{ name = '来源',      id='cardSetId',   type='key-value' },
	{ name = '收集',      id='collectible', type='key-value' },
	{ name = '模式',      id='mode',		type='key-value' },
}
------------------------------------------------------------------------------
function p.page()
	local html = mw.html.create()

	-- 初始化div
	local div = html:tag('div'):addClass('search_loading hs_block'):cssText('min-height: 400px;text-align: center;')

	div
		:tag('div'):addClass('hs_loading'):cssText('margin-top: 150px;'):done()
		:tag('div'):wikitext('正在初始化,请稍候……')

	-- 正式div
	local top_div = html:tag('div'):addClass('item_search hs_input'):cssText('display:none;'):attr('data_version', 'DEFAULT')

	local filter_div = top_div:tag('div'):addClass('hs_search_filter hs_flex_list')
	for _, filter in ipairs(FILTER_LIST) do
		div = filter_div:tag('div'):addClass('hs_search_filter_item filter_type_'..filter.type)
		div:tag('div'):addClass('hs_search_filter_item_name'):wikitext(filter.name)
		local input = div:tag('div'):addClass('hs_search_filter_item_input')
			:attr('data-input-type', filter.type)
			:attr('data-input-id', filter.id)
		if filter.placeholder then
			input:attr('data-placeholder', filter.placeholder)
		end
	end

	----------- 提交按钮
	div = filter_div:tag('div'):addClass('hs_search_filter_item hs_button filter_type_submit')
	div:tag('div'):addClass('hs_search_filter_item_name'):wikitext(' ')
    div:tag('div'):addClass('hs_search_filter_item_input')
    :attr('data-input-type', 'button')
    :attr('data-input-name', '搜索')
    :attr('data-input-class', 'submit')
    :attr('data-input-icon', 'search')

	----------- 重置按钮
	div = filter_div:tag('div'):addClass('hs_search_filter_item hs_button filter_type_submit')
	div:tag('div'):addClass('hs_search_filter_item_name'):wikitext(' ')
    div:tag('div'):addClass('hs_search_filter_item_input')
    :attr('data-input-type', 'button')
    :attr('data-input-name', '重置')
    :attr('data-input-class', 'reset')
    :attr('data-input-icon', 'refresh')

    top_div:tag('div'):addClass('hs_search_result')

    ---------- 把js需要的数据上传---------------------------
	local function getFilterMap(list, key, isArray)
		-- 对该字段的所有值进行统计,只保留至少出现过一次的值
		local pipeline = {
		   { ['$match']= { _source= 'battlenet' } }, --筛选
		   { ['$project'] = {[key]=1}}, --移除无用数据
		   { ['$group']= {_id= "$"..key, cnt= { ['$sum']= 1 } } } --按值分组,统计每组元素数量
		}
		if isArray then
			table.insert(pipeline, 3, {['$unwind'] = "$"..key}) --对于数组类型,将数组中的每个值映射成一条记录
		end
		local cnt = {}
		for _,v in ipairs(mw.huiji.db.aggregate(pipeline)) do
			cnt[v._id or '_'] = v.cnt
		end
		return Linq(list)
		:Select(function(t) return {
			key = tostring(t.id),
			name = t.name,
			cnt = cnt[t.id] or 0
		} end)
		:Where(function(t) return t.cnt>0 and t.name and t.name~='' end)
		:Done()
	end
	local js_data_base = {
		keywordIds = getFilterMap(Metadata.keywords, 'keywordIds', true),
		classId = getFilterMap(Metadata.classes, 'classId'),
		cardTypeId = getFilterMap(Metadata.types, 'cardTypeId'),
		minionTypeId = getFilterMap(Metadata.minionTypes, 'minionTypeId'),
		rarityId = getFilterMap(Metadata.rarities, 'rarityId'),
		cardSetId = getFilterMap(Metadata.sets, 'cardSetId'),
		collectible = {
			{ key = '1',    name = '可收集' },
			{ key = '0',    name = '不可收集'},
		},
		mode = {
			--{ key = 'constructed',  	name = '对战模式'},
			{ key = 'battlegrounds',    name = '酒馆战棋'},
			{ key = 'duels',    		name = '对决模式'},
		},
		manaCost = Linq.Range(10):Append('11+'):Select(function(v) return {
			key = tostring(v),
			name = tostring(v),
		} end):Done()
	}
	--mw.logObject(js_data_base)
	mw.huiji.expose('CustomSearch', js_data_base)
	return tostring(html)
end
-----------------------------------------------------------------------------
-- 查询
-----------------------------------------------------------------------------
local function Exists(v)
	return v and v~=''
end
local function NoRegex(s)
	for _,v in ipairs{'\\','.','+','*','(',')','[',']','^','$','{','}'} do
		s = s:gsub('%'..v, '\\'..v)
	end
	return s
end
----------------------------------
-- 查询数据库
----------------------------------
local function Query(args)
	local query = {}
	local query_cond_count = 0     ---条件count
	local and_list = {{['_source']	= 'battlenet'}}
    -------------------------------------------
	--------------职业-------------------------
	local value = tonumber(args['classId'])
    if value then
    	table.insert(and_list, {
    		['$or'] = {
    			{['classId'] = value}, --单职业
    			{['multiClassIds'] = {
					['$elemMatch'] = {['$eq'] = value} --双职业 数组中任一匹配
				}}
    		}
    	})
        query_cond_count = query_cond_count + 1
	end
	----------类型、稀有度、来源、可收集、种族---------
	for _, key in ipairs{'cardTypeId', 'rarityId','cardSetId','collectible','minionTypeId'} do
		value = tonumber(args[key])
		if value then
			query[key] = value
			query_cond_count = query_cond_count + 1
		end
	end
	---------------费用------------------------
	value = args['manaCost']
	if value then
		local v,op = value:match('^(%d+)(%D?)$')
		v = tonumber(v)
		if v then
			if op == '' then
				query['manaCost'] = v
			elseif op == '+' then
				query['manaCost'] = {['$gte'] = v}
			elseif op == '-' then
				query['manaCost'] = {['$lte'] = v}
			end
		end
		if query['manaCost']==nil then
			error('错误的条件:manaCost = '..tostring(value))
		end
		query_cond_count = query_cond_count + 1
	end
	---------------关键词------------------------
	value = tonumber(args['keywordIds'])
	if value then
		query['keywordIds'] = {['$elemMatch'] = {['$eq'] = value}}
		query_cond_count = query_cond_count + 1
	end
	---------------模式------------------------
	value = args['mode']
	if value then
		if value == 'battlegrounds' then
			table.insert(and_list, {['$or'] = {
				{battlegrounds = {['$exists'] = 1}},
				{cardSetId = 1453}
			}})
		elseif value == 'duels' then
			--table.insert(and_list, {['duels.relevant'] = true})
			table.insert(and_list, {['duels.constructed'] = true})
		end
		query_cond_count = query_cond_count + 1
	end
	---------------名称------------------------
	value = args['name']
	if Exists(value) then
		local or_list = {
			{ ['name'] = { ["$regex"] = value } },
			{ ['name_enUS'] = { ["$regex"] = value , ['$options'] = 'i' } },
			{ ['name_jaJP'] = { ["$regex"] = value } },
			{ ['name_zhTW'] = { ["$regex"] = value } },
		}
		table.insert(and_list, {['$or'] = or_list})
		if #value>=3 and value:match('^%d+$') then --按5106这样的三围查询
			-- 测试用例:=p.search_result{name='5106',cardClass='0',rarity='0',type='0',set='0',mechanic='0'}
			local length = #value
			-- 枚举所有可能的划分方式
			for i=1,length-2 do
				for j=i+1,length-1 do
					local triplet = {value:sub(1,i), value:sub(i+1,j), value:sub(j+1,length)}
					local valid = true
					for _,v in ipairs(triplet) do
						if #v>1 and v:sub(1,1)=='0' then -- 排除'5|1|06'这样的划分方式
							valid = false
						end
					end
					if valid then
						table.insert(or_list, {
							manaCost = tonumber(triplet[1]),
							attack = tonumber(triplet[2]),
							health = tonumber(triplet[3])
						})
					end
				end
			end
		end
		query_cond_count = query_cond_count + 1
	end
	--------------文本------------------------
	value = args['text']
	if Exists(value) then
		query['text'] = { ['$regex'] = NoRegex(value) }
		query_cond_count = query_cond_count + 1
	end
---------------------------------------------------------------
	query['$and'] = and_list
	-- 计算最大页数
	local result_total = mw.huiji.db.count(query)

	if result_total == 0 then
		return 0, {}
	end
	local limit = tonumber(args['limit']) or 60
	local max_page_number = math.ceil(result_total/limit)
	if max_page_number == 0 then
		max_page_number = 1
	end

	args['page'] = tonumber(args['page']) or 1
	if args['page'] < 1 then
		args['page'] = 1
	elseif args['page'] > max_page_number then
		args['page'] = max_page_number
	end
    if query_cond_count == 0 then
        query['collectible'] = 1
		result_total = mw.huiji.db.count(query)
	end
	-- 按上传时间,从新到旧排序(默认排序为从旧到新)
	local skip = math.max(result_total - (args['page']) * limit, 0)
	local options = {
		limit = limit,
		skip = skip
	}
	local result = Linq(mw.huiji.db.find(query, options)):Reverse():Done()
	
	return result_total, result, query_cond_count
end
-----------------------------
-- 入口
-----------------------------
function p.get(frame)
	local args = getArgs(frame)
	args['page'] = tonumber(args['page']) or 1
	if args['page'] > 100 then
		args['page'] = 100
	end

	local one_page_limit = 60  -- 每页显示几个结果
	local page_number_limit = 100   -- 最多显示多少页
	args['limit'] = one_page_limit
--------------------------------------------------------------
	local item_count, data, query_cond_count= Query(args)
--------------------------------------------------------------
	local html = mw.html.create()

	-- 计算最大页数
	local max_page_number = math.ceil(item_count / one_page_limit)  -- 最大页码
	if max_page_number > page_number_limit then
		max_page_number = page_number_limit
	end

	-- 设置提示文本
	local count_text ='<div class="search_notice">'
	local count_text_2 = '请指定查询条件,可以同时筛选多个条件查询...'
	if query_cond_count == 0 then
		count_text_2 = '请指定查询条件,可以同时筛选多个条件查询,默认显示<span class="text-primary">'..item_count..'</span>张可收集卡牌。'
		else
		count_text_2 = '共有<span class="text-primary"> '..item_count..' </span>张符合条件的卡牌。'
	end
	count_text = count_text..count_text_2

	if item_count == 0 then
		count_text = count_text..'没有找到符合条件的卡牌。'
		--..mw.dumpObject(args)
	elseif item_count > one_page_limit * page_number_limit then
		count_text = count_text..'最多提供 100 页的查询结果,如未能找到需要的卡牌,请缩小条件重新搜索。'
	end
    count_text = count_text..'</div>'
	html:tag('p'):wikitext(count_text)

	if item_count == 0 then
		return tostring(html)
	end
------------------------------------------------------------------------------
	local show_page_count = 2  -- 前后各显示多少页,写2表示前后各2页
	local page_div = mw.html.create():tag('div'):addClass('hs_item_list_page_selector')
	if item_count > one_page_limit then  -- 不足一页时不显示页码
		-- 第一页按钮
		if args['page'] - show_page_count > 1 then
			page_div:tag('div'):addClass('hs_item_list_page_button'):wikitext(1):attr('data-page', 1)
		end
		-- 如果紧接着就是第二页的话,中间就不需要显示省略号,否则需要显示
		if args['page'] - show_page_count > 2 then
			page_div:tag('div'):addClass('hs_item_list_page_ellipsis'):wikitext('...')
		end
		-- 当前页之前的页码
		for minus_number = show_page_count, 1, -1 do
			local page_number = args['page'] - minus_number
			if page_number > 0 then
				page_div:tag('div'):addClass('hs_item_list_page_button'):wikitext(page_number):attr('data-page', page_number)
			end
		end
		-- 当前页
		page_div:tag('div'):addClass('hs_item_list_page_button current'):wikitext(args['page'])
		-- 当前页后面的页码
		for minus_number = 1, show_page_count do
			local page_number = args['page'] + minus_number
			if page_number <= max_page_number then
				page_div:tag('div'):addClass('hs_item_list_page_button'):wikitext(page_number):attr('data-page', page_number)
			end
		end
		-- 如果紧接着就是倒数第二页的话,中间就不需要显示省略号,否则需要显示
		if args['page'] + show_page_count < max_page_number - 1 then
			page_div:tag('div'):addClass('hs_item_list_page_ellipsis'):wikitext('...')
		end
		-- 最后一页按钮
		if args['page'] + show_page_count < max_page_number then
			page_div:tag('div'):addClass('hs_item_list_page_button'):wikitext(max_page_number):attr('data-page', max_page_number)
		end
	end
	-- 列表前翻页
	html:node(page_div)
	-- 列表
		html:node(ListTable(data, args))
	-- 列表后翻页
	html:node(page_div)

    return tostring(html)
end
return p
0.0
0人评价
avatar