Skip to content

Instantly share code, notes, and snippets.

@russelldb
Last active December 26, 2015 18:38
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save russelldb/5da7d895cebc77dd38b8 to your computer and use it in GitHub Desktop.
Save russelldb/5da7d895cebc77dd38b8 to your computer and use it in GitHub Desktop.
Imagine modelling this is Riak pre2.0? Look at user.erl, where are the merge functions? There aren't any.
{
"profile": {
"guid": "12345",
"created": "2008-08-26T23:35:16Z",
"familyName": "Edgerton",
"givenName": "Samantha",
"image": {
"height": 225,
"url": "http://img.avatars.yahoo.com/users/1YfXUc4vMAAEB9IFDbJ_vk45UmUYE==.large.png",
"width": 150
},
"Interests": {
"Hobbies": [
"Pottery",
"Tennis",
"Skiing",
"Hiking",
"Travel",
"picnics"
],
"Films": [
"Ratatouille",
"TheIncredibles"
],
"Books": []
},
"nickname": "Sam"
},
"searches": 100,
"views": 10,
"liked": 8,
"active": true
}
Eshell V5.10.3 (abort with ^G)
1> f(Pid), {ok, Pid} = riakc_pb_socket:start_link("localhost", 8087).
{ok,<0.34.0>}
2> Key = <<"0987">>.
<<"0987">>
3> {ok, U} = user:create_user(Pid, Key, <<"Bob">>, <<"Dobalina">>, <<"bodo">>).
{ok,{map,[{{<<"active">>,flag},true},
{{<<"likes">>,counter},0},
{{<<"profile">>,map},
[{{<<"nickName">>,register},<<"bodo">>},
{{<<"interests">>,map},
[{{<<"hobbies">>,set},[]},
{{<<"films">>,set},[]},
{{<<"books">>,set},[]}]},
{{<<"image">>,map},
[{{<<"width">>,register},<<>>},
{{<<"url">>,register},<<>>},
{{<<"height">>,register},<<>>}]},
{{<<"guid">>,register},<<"0987">>},
{{<<"givenName">>,register},<<"Bob">>},
{{<<"familyName">>,register},<<"Dobalina">>}]},
{{<<"searches">>,counter},0},
{{<<"views">>,counter},0}],
[],[],[],
<<77,1,0,0,0,113,131,108,0,0,0,5,104,2,100,0,14,114,105,
97,107,...>>}}
4> user:add_image(Pid, Key, <<"125">>, <<"150">>, <<"http://not.reallybobd.info/bodb.png">>).
{ok,{map,[{{<<"active">>,flag},true},
{{<<"likes">>,counter},0},
{{<<"profile">>,map},
[{{<<"nickName">>,register},<<"bodo">>},
{{<<"interests">>,map},
[{{<<"hobbies">>,set},[]},
{{<<"films">>,set},[]},
{{<<"books">>,set},[]}]},
{{<<"image">>,map},
[{{<<"width">>,register},<<"150">>},
{{<<"url">>,register},
<<"http://not.reallybobd.info/bodb.png">>},
{{<<"height">>,register},<<"125">>}]},
{{<<"guid">>,register},<<"0987">>},
{{<<"givenName">>,register},<<"Bob">>},
{{<<"familyName">>,register},<<"Dobalina">>}]},
{{<<"searches">>,counter},0},
{{<<"views">>,counter},0}],
[],[],[],
<<77,1,0,0,0,113,131,108,0,0,0,5,104,2,100,0,14,114,105,
97,107,...>>}}
5> user:add_hobbies(Pid, Key, [<<"fishing">>, <<"skiing">>, <<"cycling">>, <<"knitting">>]).
{ok,{map,[{{<<"active">>,flag},true},
{{<<"likes">>,counter},0},
{{<<"profile">>,map},
[{{<<"nickName">>,register},<<"bodo">>},
{{<<"interests">>,map},
[{{<<"hobbies">>,set},
[<<"cycling">>,<<"fishing">>,<<"knitting">>,<<"skiing">>]},
{{<<"films">>,set},[]},
{{<<"books">>,set},[]}]},
{{<<"image">>,map},
[{{<<"width">>,register},<<"150">>},
{{<<"url">>,register},
<<"http://not.reallybobd.info/bodb.png">>},
{{<<"height">>,register},<<"125">>}]},
{{<<"guid">>,register},<<"0987">>},
{{<<"givenName">>,register},<<"Bob">>},
{{<<"familyName">>,register},<<"Dobalina">>}]},
{{<<"searches">>,counter},0},
{{<<"views">>,counter},0}],
[],[],[],
<<77,1,0,0,0,113,131,108,0,0,0,5,104,2,100,0,14,114,105,
97,107,...>>}}
6> user:add_films(Pid, Key, [<<"bambi">>, <<"The Incredibles">>, <<"Enter The Dragon">>, <<"Cocolat">>]).
{ok,{map,[{{<<"active">>,flag},true},
{{<<"likes">>,counter},0},
{{<<"profile">>,map},
[{{<<"nickName">>,register},<<"bodo">>},
{{<<"interests">>,map},
[{{<<"hobbies">>,set},
[<<"cycling">>,<<"fishing">>,<<"knitting">>,<<"skiing">>]},
{{<<"films">>,set},
[<<"Cocolat">>,<<"Enter The Dragon">>,<<"The Incredibles">>,
<<"bambi">>]},
{{<<"books">>,set},[]}]},
{{<<"image">>,map},
[{{<<"width">>,register},<<"150">>},
{{<<"url">>,register},
<<"http://not.reallybobd.info/bodb.png">>},
{{<<"height">>,register},<<"125">>}]},
{{<<"guid">>,register},<<"0987">>},
{{<<"givenName">>,register},<<"Bob">>},
{{<<"familyName">>,register},<<"Dobalina">>}]},
{{<<"searches">>,counter},0},
{{<<"views">>,counter},0}],
[],[],[],
<<77,1,0,0,0,113,131,108,0,0,0,5,104,2,100,0,14,114,105,
97,107,...>>}}
7> {ok, U2} = user:remove_films(Pid, Key, [<<"bambi">>]).
{ok,{map,[{{<<"active">>,flag},true},
{{<<"likes">>,counter},0},
{{<<"profile">>,map},
[{{<<"nickName">>,register},<<"bodo">>},
{{<<"interests">>,map},
[{{<<"hobbies">>,set},
[<<"cycling">>,<<"fishing">>,<<"knitting">>,<<"skiing">>]},
{{<<"films">>,set},
[<<"Cocolat">>,<<"Enter The Dragon">>,
<<"The Incredibles">>]},
{{<<"books">>,set},[]}]},
{{<<"image">>,map},
[{{<<"width">>,register},<<"150">>},
{{<<"url">>,register},
<<"http://not.reallybobd.info/bodb.png">>},
{{<<"height">>,register},<<"125">>}]},
{{<<"guid">>,register},<<"0987">>},
{{<<"givenName">>,register},<<"Bob">>},
{{<<"familyName">>,register},<<"Dobalina">>}]},
{{<<"searches">>,counter},0},
{{<<"views">>,counter},0}],
[],[],[],
<<77,1,0,0,0,113,131,108,0,0,0,5,104,2,100,0,14,114,105,
97,107,...>>}}
8> riakc_map:value(U2).
[{{<<"active">>,flag},true},
{{<<"likes">>,counter},0},
{{<<"profile">>,map},
[{{<<"nickName">>,register},<<"bodo">>},
{{<<"interests">>,map},
[{{<<"hobbies">>,set},
[<<"cycling">>,<<"fishing">>,<<"knitting">>,<<"skiing">>]},
{{<<"films">>,set},
[<<"Cocolat">>,<<"Enter The Dragon">>,
<<"The Incredibles">>]},
{{<<"books">>,set},[]}]},
{{<<"image">>,map},
[{{<<"width">>,register},<<"150">>},
{{<<"url">>,register},
<<"http://not.reallybobd.info/bodb.png">>},
{{<<"height">>,register},<<"125">>}]},
{{<<"guid">>,register},<<"0987">>},
{{<<"givenName">>,register},<<"Bob">>},
{{<<"familyName">>,register},<<"Dobalina">>}]},
{{<<"searches">>,counter},0},
{{<<"views">>,counter},0}]
9>
%%%-------------------------------------------------------------------
%%% @author Russell Brown <russelldb@basho.com>
%%% @copyright (C) 2013, Russell Brown
%%% @doc
%%%
%%% @end
%%% Created : 28 Oct 2013 by Russell Brown <russelldb@basho.com>
%%%-------------------------------------------------------------------
-module('user').
%% API
-compile([export_all]).
-define(USERS_BUCKET, {<<"maps">>, <<"user">>}).
%% Field names
-define(PROFILE, <<"profile">>).
-define(GUID, <<"guid">>).
-define(GIVEN_NAME, <<"givenName">>).
-define(FAMILY_NAME, <<"familyName">>).
-define(NICK, <<"nickName">>).
-define(IMAGE, <<"image">>).
-define(INTERESTS, <<"interests">>).
-define(HOBBIES, <<"hobbies">>).
-define(FILMS, <<"films">>).
-define(BOOKS, <<"books">>).
-define(URL, <<"url">>).
-define(HEIGHT, <<"height">>).
-define(WIDTH, <<"width">>).
%%%===================================================================
%%% API
%%%===================================================================
%%--------------------------------------------------------------------
%% @doc
%%
%% @end
%%--------------------------------------------------------------------
create_user(Pid, Key, GivenName, FamilyName, Nickname) ->
riakc_pb_socket:modify_type(Pid, fun(Map) -> create(Map, Key, GivenName, FamilyName, Nickname) end,
?USERS_BUCKET, Key, [create, return_body]).
%% No need to fetch before just adding to a set
add_hobbies(Pid, Key, Hobbies) ->
add_interests(Pid, Key, ?HOBBIES, Hobbies).
%% Ensure the context is included by doing a fetch, update, send
remove_hobbies(Pid, Key, Hobbies) ->
remove_interests(Pid, Key, ?HOBBIES, Hobbies).
add_films(Pid, Key, Films) ->
add_interests(Pid, Key, ?FILMS, Films).
remove_films(Pid, Key, Films) ->
remove_interests(Pid, Key, ?FILMS, Films).
add_books(Pid, Key, Books) ->
add_interests(Pid, Key, ?BOOKS, Books).
remove_books(Pid, Key, Books) ->
remove_interests(Pid, Key, ?BOOKS, Books).
%% No need to fetch before updating a bunch of registers
add_image(Pid, Key, Height, Width, Url) ->
Image = lists:foldl(fun({Name, Val}, Map) ->
riakc_map:update({Name, register},
fun(Current) ->
riakc_register:set(Val, Current) end,
Map) end,
riakc_map:new(),
[{?URL, Url},
{?HEIGHT, Height},
{?WIDTH, Width}]),
Profile = riakc_map:update({?IMAGE, map}, fun(_) -> Image end, riakc_map:new()),
User = riakc_map:update({?PROFILE, map}, fun(_N) -> Profile end, riakc_map:new()),
riakc_pb_socket:update_type(Pid, ?USERS_BUCKET, Key, riakc_map:to_op(User), [return_body]).
%% Just send the minimal op, no fetch needed
update_counter(Pid, Key, Counter, Amt) ->
User = riakc_map:update({Counter, counter}, fun(C) ->
riakc_counter:increment(Amt, C) end,
riakc_map:new()),
riakc_pb_socket:update_type(Pid, ?USERS_BUCKET, Key, riakc_map:to_op(User), [return_body]).
%%%===================================================================
%%% Internal functions
%%%===================================================================
create(User, UID, GivenName, FamilyName, NickName) ->
CreateProfile = fun(Profile) ->
Prof = lists:foldl(fun({Name, Val}, Map) ->
riakc_map:update({Name, register},
fun(Current) ->
riakc_register:set(Val, Current) end,
Map) end,
Profile,
[{?GUID, UID},
{?GIVEN_NAME, GivenName},
{?FAMILY_NAME, FamilyName},
{?NICK, NickName}]),
ProfImage = riakc_map:update({?IMAGE, map}, fun create_image/1, Prof),
riakc_map:update({?INTERESTS, map}, fun create_interests/1, ProfImage)
end,
%% Add profile map
UserProf = riakc_map:update({?PROFILE, map}, CreateProfile, User),
%% Add counters
User1 = lists:foldl(fun(Name, Map) -> riakc_map:add({Name, counter}, Map) end,
UserProf,
[<<"searches">>, <<"views">>, <<"likes">>]),
%% set active
riakc_map:update({<<"active">>, flag}, fun(F) -> riakc_flag:enable(F) end, User1).
create_image(Image) ->
lists:foldl(fun(Name, Map) ->
riakc_map:add({Name, register}, Map) end,
Image,
[?HEIGHT, ?WIDTH, ?URL]).
create_interests(Interests) ->
lists:foldl(fun(Name, Map) ->
riakc_map:add({Name, set}, Map) end,
Interests,
[?HOBBIES, <<"books">>, <<"films">>]).
add_interests(Pid, Key, Interest, Interests) ->
Interests1 = lists:foldl(fun(I, Set) ->
riakc_set:add_element(I, Set) end,
riakc_set:new(),
Interests),
Interests2 = riakc_map:update({Interest, set}, fun(_S) -> Interests1 end, riakc_map:new()),
Profile = riakc_map:update({?INTERESTS, map}, fun(_) -> Interests2 end, riakc_map:new()),
User = riakc_map:update({?PROFILE, map}, fun(_N) -> Profile end, riakc_map:new()),
riakc_pb_socket:update_type(Pid, ?USERS_BUCKET, Key, riakc_map:to_op(User), [return_body]).
%% Ensure the context is included by doing a fetch, update, send
remove_interests(Pid, Key, Interest, Interests) ->
riakc_pb_socket:modify_type(Pid, fun(Map) ->
remove_interests(Map, Interest, Interests) end,
?USERS_BUCKET,
Key,
[return_body]).
%% OK, this looks pretty bad, but we're working with sets within maps within maps within maps
%% There are other ways, grab the map and context, generate the ops
%% and ship them back
remove_interests(Map, Interest, Interests) ->
riakc_map:update({?PROFILE, map},
fun(P) ->
riakc_map:update({?INTERESTS, map},
fun(I) ->
riakc_map:update({Interest, set},
fun(S) ->
lists:foldl(fun(ToRem, Set) ->
riakc_set:del_element(ToRem, Set) end,
S,
Interests) end,
I) end,
P) end,
Map).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment