Skip to content

Commit

Permalink
Update state docs with new QLC examples
Browse files Browse the repository at this point in the history
Updates state docs with the previously agreed upon QLC examples in issue

Additionally, provides an example of how to evaluate a QLC query and
return the result to use within Elixir.

Closes #552
  • Loading branch information
jb3 committed May 3, 2024
1 parent 0f8b1f2 commit 6c1fb5c
Showing 1 changed file with 50 additions and 9 deletions.
59 changes: 50 additions & 9 deletions guides/functionality/state.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,19 +23,60 @@ nostrum's built-in functions to query the cache should be sufficient to cover
common use cases. If you need more involved queries, it is recommended to use
nostrum's [qlc](https://www.erlang.org/doc/man/qlc.html) support.

As an example, Nosedrum has a function to find a guild member by username and
discriminator. This is internally implemented with the following query:
### Examples

Below you can find some example queries using QLC.

```erl
find_by(RequestedGuildId, Name, Discriminator, MemberCache, UserCache) ->
qlc:q([Member || {{GuildId, MemberId}, Member} <- MemberCache:query_handle(),
GuildId =:= RequestedGuildId,
{UserId, User} <- UserCache:query_handle(),
MemberId =:= UserId,
map_get(username, User) =:= Name,
map_get(discriminator, User) =:= Discriminator]).
% src/nostrum_queries.erl

-module(nostrum_queries).
-export([find_role_users/4, find_large_communities/2]).

-include_lib("stdlib/include/qlc.hrl").

% Find the Nostrum.Struct.User and Member objects of all members in a specific guild role.
find_role_users(RequestedGuildId, RoleId, MemberCache, UserCache) ->
qlc:q([{User, Member} || {{GuildId, MemberId}, Member} <- MemberCache:query_handle(),
% Filter to member objects of the selected guild
GuildId =:= RequestedGuildId,
% Filter to members of the provided role
lists:member(RoleId, map_get(roles, Member)),
% Get a handle on the UserCache table
{UserId, User} <- UserCache:query_handle(),
% Find the User struct that matches the found Member
MemberId =:= UserId]).

% Find all communities in the Guild cache with the COMMUNITY guild feature
% that are over a certain threshold in user size
find_large_communities(Threshold, GuildCache) ->
qlc:q([Guild || {_, Guild} <- GuildCache:query_handle(),
% Filter for guilds that are over the provided size
map_get(member_count, Guild) > Threshold,
% Filter for guilds that have COMMUNITY in the features field
lists:member(<<"COMMUNITY">>, map_get(features, Guild))]).
```

Query 1 fetches all users in the specified guild (`RequestedGuildId`) with the
role `RoleId`. The code is annotated, but step-by-step the flow is: the member cache
is filtered down to all members in the guild, then using `lists:member/2` we check
for role membership, and finally we join against the user cache to return full
user objects in the result.

Query 2 fetches all guilds in the guild cache that meet the criteria of having
at least `Threshold` members *and* having the `COMMUNITY` guild feature set
to true in the Discord UI. It is easy to follow the flow of this query by the
annotations, with only some minor things to note such as needing to use `<<"bitstring">>`
bit syntax to represent the strings, which is implicit in Elixir.

In Elixir, you can call these queries like so using `:qlc.eval/1`:

```elixir
matching_guilds = :qlc.eval(:nostrum_queries.find_large_communities(50, Nostrum.Cache.GuildCache))
```

### Implementing your own queries and caches

By [implementing a QLC
table](https://www.erlang.org/doc/man/qlc.html#implementing_a_qlc_table), all
read operations from nostrum will be performed over your QLC table
Expand Down

0 comments on commit 6c1fb5c

Please # to comment.