Efficent Subgroup Removal by Subkey: Double Maps

pallets/double-map Try on playground View on GitHub

For some runtimes, it may be necessary to remove a subset of values in a key-value mapping. If the subset maintain an associated identifier type, this can be done in a clean way with the double_map via the remove_prefix api.

pub type GroupIndex = u32; // this is Encode (which is necessary for double_map)

#[pallet::storage]
#[pallet::getter(fn member_score)]
pub(super) type MemberScore<T: Config> = StorageDoubleMap<
	_,
	Blake2_128Concat,
	GroupIndex,
	Blake2_128Concat,
	T::AccountId,
	u32,
	ValueQuery,
>;

#[pallet::storage]
#[pallet::getter(fn group_membership)]
pub(super) type GroupMembership<T: Config> =
		StorageMap<_, Blake2_128Concat, T::AccountId, GroupIndex, ValueQuery>;

#[pallet::storage]
#[pallet::getter(fn all_members)]
pub(super) type AllMembers<T: Config> = StorageValue<_, Vec<T::AccountId>, ValueQuery>;

For the purposes of this example, store the scores of each member in a map that associates this u32 value with two keys: (1) a GroupIndex identifier, and (2) the member's AccountId. This allows for efficient removal of all values associated with a specific GroupIndex identifier.

#[pallet::weight(10_000)]
pub fn remove_group_score(
	origin: OriginFor<T>,
	group: GroupIndex,
	) -> DispatchResultWithPostInfo {
		let member = ensure_signed(origin)?;

		let group_id = <GroupMembership<T>>::get(member);
		// check that the member is in the group
		ensure!(
			group_id == group,
			"member isn't in the group, can't remove it"
		);

		// remove all group members from MemberScore at once
		<MemberScore<T>>::remove_prefix(&group_id);

		Self::deposit_event(Event::RemoveGroup(group_id));
		Ok(().into())
}