Professional Documents
Culture Documents
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.