You are on page 1of 5

Go Further Exercise - Tidying the

Likes functionality using a Custom


Query to the Umbraco Database

So far we have refactored the likes to use Relations: now a like is recorded as a relation
between the individual Status Update Document and the Member object of the person
who liked it. However our list of Status Updates is still displaying the number of likes
from our label property on the Status Update Document Type. This requires the Status
Update to be republished after each Like is made, to display the updated Likes count in
uWaterCooler. Instead we need to display this number of likes directly from the
Umbraco Likes relation we setup earlier.

Buthowwillwedothisefficiently?

Wecould,asweloopthrougheachstatusupdateobject,usetherelationsapitopullbackalist
ofalltherelationsforthatparticularstatusupdateandcountthem,thenwriteoutthiscountat
thesametimeasrenderingthestatusupdate.Thisishowever,goingtoslowdownthe
renderingofourpage.Why?Anunderlyingdatabaserequestwillbemadeforeachstatus
update,(imagine20statusupdatesononepage,thats20databaserequests)andnothingwill
bedisplayeduntilalltheserequestshavecompleted.

Sohowaboutwedothisasynchronously?

WiththisapproachwerendertheStatusUpdatescontentquicklyfromtheUmbracoCache,and
thenforeachonefireoffanasynchronousrequesttoaWebAPIendpoint,thatusesthe
relationsAPItopullbacktherelationsforthestatusupdate,countthemandupdatethehtml
withthecalculatedlikesvalue.Butthisisstill20databaserequests..butatleasttheir
asynchronousnaturewontdelaytherenderingofthecontentofthestatusupdates.Consider
however100peopleviewingthepage,that's100x20asynchronousrequests,triggering2000
databaserequests.

IdeallywewouldonlytriggeronedatabaserequesttoretrievealltheLikecountsfortheStatus
Updatesthatweredisplayedonthepage,viaonesingleasynchronousrequest.

iecreateasingleendpointtotakemultipleStatusUpdateIdsasaparameter:

GetStatusUpdateLikesCount
(
stringids
)

CanweusetheRelationsapitopassacommadelimitedstringofStatusUpdateIds,and
receivebackanIEnumerableofIRelationobjectswhichwecancount?N
o
,thisoverload
doesntexistandanywaywedontreallywantallthoserowsofdatareturnedforeachrelation,
weareonlyinterestedinthecount,andwedontwanttohavetodothecountinmemoryeither
sohowcanweefficientlyretrievethisdatafromthedatabase?

Letsstartfleshingoutthesolution.

First,in
Level2.js
letsaddsomejavascriptthatwillfireafterthepagehasloaded,thatwillfind
theidsofalltheStatusUpdatesthathavebeendisplayed,andpassthislistofIdstoanew
endpointthatwewillcreate:

addthefollowingtotheexistingjquerydocumentreadyfunction:

varstatusIds=[]
$
(
'#streama[datastatusid]'
)
.
each
(
function

(
index
)

//readtheirids

statusIds.
push
(
$
(
this
)
.
data
(
"statusid"
))

})

//passalltheidstoanendpoint,

if

(
statusIds.
length

>

0
)

//endpointqueriesdatabaseandreturnsjsonofstatusidtolikecount

$.
get
(
"/umbraco/api/likes/getstatusupdatelikescount",

{
ids
:
statusIds.
join
(
','
)

}
,

function

(
data
)

//dosomethingwiththedata

})

Youwillseethisisgettingtheids,andmakingarequesttoanewendpoint:
/umbraco/api/likes/getstatusupdatelikescount

NowletsupdateourLikesControllerandaddthisnewWebApiendpoint:

[
System.
Web
.
Http
.
HttpGet
]

public
IEnumerable
<
StatusLikeCount>

GetStatusUpdateLikesCount(

string
ids
)
{
}


HangonwhatisthisStatusLikeCountclasswearereturninganIEnumerableof?

Wellthisisjustaplainc#classthattheWebApiwillserializetoJsonforus,andaswereonly
interestedintheidofthestatusupdateandthecountofitslikes,letscreateitlikeso:

public

class
StatusLikeCount

public

int
ParentId
{

get

set

public

int
LikeCount
{

get

set

Sohowarewegoingtoretrievethecountofthelikes?

WellsometimeswhenyouareintegratinganapplicationwithUmbraco,youmighthavea
customtableintheUmbracodatabase,orinthiscaseneedtoqueryUmbracotablesina
customway,whenunfortunatelyanapimethoddoesnotexist,orwontbeefficientforthecrazy
adhocthingyouaretryingtodo.

UmbracousesPetaPocoasadataaccesslayer:

WhatisPetaPoco?
http://www.toptensoftware.com/petapoco/
(
PetaPoco is a tiny, fast,
single-file micro-ORM for .NET and Mono)

PetaPocoallowsyoutosendcustomSQLstatementstoadatabaseandmapthereturned
resultstoplainoldc#objects(poco),whichsoundsjustlikethesortofthingweneedtodohere,
(ifyoucanrememberhowtowriteSQL?)

RelationsinUmbracoarestoredinan
umbracoRelation
table:
https://our.umbraco.org/wiki/reference/database/umbracorelationtable/

Soinsteadofreturningalltherelationsforthestatusupdatesandcountingtheminmemoryin
c#,wecanleveragethepowerofSQLsg
roupby
toreturnstraightfromthedbthecountofthe
relationsforeachstatusupdate:

TheSQL:
SELECT
parentid
,

COUNT
(
parentid
)

AS
likecount
FROM
umbracoRelation
WHERE
parentid
IN

(
1001
,
1002
,
1003
)

GROUP

BY
parentid

SohowdowesendthisSqlstatementtotheUmbracodatabaseandretrieveourPOCO
objects?

[
System.
Web
.
Http
.
HttpGet
]

public
IEnumerable
<
StatusLikeCount>

GetStatusUpdateLikesCount(

string
ids
)

//ConnecttotheUmbracoDB

var
db
=
ApplicationContext
.
DatabaseContext
.
Database

//sqltogroupbyparentid,andreturncountforeachstatusupdatewhoseidisin

thepassedlistofids

var
getCountSql
=

"Selectparentid,Count(parentid)aslikecountfromumbracoRelation
whereparentidin("

+
ids
+

")groupbyparentid"

//usepetapocotoreturntheresultsofthesqlinapocoobject(seebelow)

return
db
.
Query
<
StatusLikeCount>
(
getCountSql
)

Whatsgoingonhere?

WecanreferenceUmbracosPetaPocodatabasecontextfromthecurrentApplicationContext

var
db
=
ApplicationContext.
DatabaseContext.

Database

andusePetaPocosstronglytyped.Query()methodtosendtheSQLcommandandmapthe
resultstoanIEnumerableof<StatusLikeCount>type,whichisethenreturnedasourJson
payload.

var
getCountSql
=

"Selectparentid,Count(parentid)aslikecountfrom
umbracoRelationwhereparentidin("

+
ids
+

")groupbyparentid"

return
db
.
Query
<
StatusLikeCount>
(
getCountSql
)

NB:YoucanusethisapproachtoquerydataincustomtablesinsidetheUmbracodatabase

Receivingthedata

Nowletsupdatethejavascriptinlevel2.js,toloopthroughthisreturnedJsondataandupdate
thedisplayedstatusupdatesasynchronously:

$.
get
(
"/umbraco/api/likes/getstatusupdatelikescount",

{
ids
:
statusIds.
join
(
','
)

}
,

function

(
data
)

//loopthroughreturnedjson

$.
each
(
data
,

function
(i

ndex,
element)

//updateeachlikecountvalue

$
(
'#status'

+
element.
ParentId

'span.likes')
.

text
(
element.L
ikeCount)

})

})

AndfinallyIfweupdateourliststatusupdate.cshtmlpartial(andmacropartial)todefaultto0
likes

replace:
<
span

class
=
"likes"
>
@
(
statusUpdate.
GetPropertyValue

<
int
>(
"likes")
)
<
/
span
>
with:

<
span

class
=
"likes"
>
0
<
/
span
>

Wearedone,thestreampageshouldload,withalllikessetto0,thentheasynchronous
requestismade,andtheupdatesarepopulatedwiththeircorrectlikestotals.

Summary

Likesarenolongerstoredonthestatusupdateslabelproperty,andtheyareefficiently
retrievedasynchronouslywithasingledatabaserequest.

WehaveintroducedamethodforinteractingwiththeUmbracoDatabaseshouldyouhave
customtablesforyourwebsiteapplicationinsidetheUmbracoDatabase,orneedtoquerythe
umbracotablesinawaynotcoveredbytheAPIs.

You might also like