Gå til innhold

Begrense antall stemmer til en per dag


Anbefalte innlegg

Jeg har et system hvor brukere kan stemme på flere tusen objekter. Det er veldig viktig at hver bruker ikke kan stemme på samme objekt mer enn en gang per dag. Hvordan løser jeg dette på en praktisk og ytelsesvennlig måte?

 

Alle brukere har hver sin unike ID, noe hvert object også har. Jeg ønsker hovedsaklig en løsning som ikke er avhengig av sessions og cookies, da dette ikke er sikkert nok. Noen innspill? :)

Lenke til kommentar
Videoannonse
Annonse

Har du innlogging? Om du har det må du jo også ha en form for sessions eller cookies.

 

Men det du kan gjøre er at du lager en database (blir ganske stor, så kreves vedlikehold) hvor du har fire rader. id bare for å ha noe unikt i alle radene, bruker-id, objekt-id, og tid. Her kan du velge to forskjellige tider: Enten time() hvor du så må regne ut om det er en dag senere eller ikke, eller du kan bruke date("z"). Ganske likegyldig hvilke du bruker.

Lenke til kommentar

Har innlogging ja, og benytter da selvfølgelig både sessions og cookies. Grunnen til at jeg ikke ønsker å benytte sessions og cookies til denne funksjonen, er at brukeren lett kan fjerne både sessions og cookies.

 

Ideen om en egen tabell i databasen har jeg tenkt på, men tenkte også at siden det er snakk om veldig mange bilder og brukere, vil datamengden bli stor, og mye resurser vil gå med til å sjekke om brukeren allerede har stemt på bildet samme dagen (men kanskje jeg tar feil?).

 

Si at det er snakk om alt fra 5.000 til 1.000.000 brukere. Vil løsningen din da holde mål?

Lenke til kommentar

Det viktigste om du bruker en løsning som det er at du må sørge for å vedlikeholde databasen. Viktig at du tar med dato/tid og sletter innlegg som er for gamle. Satt opp en database nå med godt over 600 000 innlegg, og tok 0.7 sekunder og hente 1000 tilfeldige innlegg. (Med en ny spørring pr. innlegg).

 

Men er det 1 000 000 som stemmer samme dag? Eller totalt?

Lenke til kommentar

Har allerede et script som rydder diverse tabeller, de fleste to ganger per døgn. Å fjerne rader som i dette tilfellet er over et døgn, er en selvfølge. Men hvor mye en database/server klarer vet jeg veldig lite om.

 

Jeg tenkte på at om det finnes 1.000.000 registrerte brukere, og 30 % av disse avlegger i gjennomsnitt 10 stemmer hvert døgn, må løsningen takle dette. Det er kun et tenkt eksempel, men systemet må som sagt takle det.

 

Vil en løsningen med et innlegg per stemme være aktuellt?

 

EDIT: Og forresten, gratulerer med dagen NorskFirefox ;)

Endret av simenss
Lenke til kommentar

Du kan legge inn et serialisert array i et felt som tilhører bildet.

Indeksen på arrayet kan være brukeriden, så setter du timestamp på når han stemte som en av verdiene.

feks:

Array
{
 [123] => array =>
              [timestamp] => idag
}

$arr = serialize( array( 
                      $userid => array('timestamp' => time(), 
                       ) ) );

Så sjekker du med in_array (etter unserialize) om brukeren finnes i arrayet, og/eller har stemt i løpet av siste 24 timer.

Så kan man bruke array_merge() for å legge til nye brukere, hvor du igjen bruker serialize() før du oppdaterer databasen.

Endret av M4rTiN
Lenke til kommentar

Dette burde vel være ganske straight forward med en koblingstabell mellom bruker og objekt hvor man også legger til tidspunkt for siste stemme. For å holde antall rader i tabellen nede kan man passe på å sette primærnøkkel til brukerid og objektid, og iallfall i MySQL kan man så kjøre REPLACE-spørringer slik at det aldri blir mer enn en rad pr. bruker-objekt-kombinasjon.

 

Edit: REPLACE blir et dumt valg. Bruk heller INSERT ... ON DUPLICATE KEY UPDATE

Endret av Ernie
Lenke til kommentar
Dette burde vel være ganske straight forward med en koblingstabell mellom bruker og objekt hvor man også legger til tidspunkt for siste stemme. For å holde antall rader i tabellen nede kan man passe på å sette primærnøkkel til brukerid og objektid, og iallfall i MySQL kan man så kjøre REPLACE-spørringer slik at det aldri blir mer enn en rad pr. bruker-objekt-kombinasjon.

 

Edit: REPLACE blir et dumt valg. Bruk heller INSERT ... ON DUPLICATE KEY UPDATE

9478197[/snapback]

Har gått for løsningen med en egen tabell for stemmene. For å forklare problemet under bruker jeg følgende struktur på tabellen:

 

vote_user = ID-en til brukeren som har gitt stemmen

vote_object = ID-en til objektet brukeren har stemt på

vote_datetime = stemmens dato og klokkelsett

 

...og følgende variabler:

 

$user = brukeren som prøver å stemme

$object = objektet brukeren ønsker å stemme på

 

Har støtt på et problem når jeg benytter meg av INSERT ... ON DUPLICATE KEY UPDATE. MySQL skal kun oppdatere vote_datetime dersom $user allerede har gitt en stemme til $object. Om ikke, må en ny rad settes inn i tabellen.

 

Det jeg må få til er derfor noe som:

 

INSERT INTO `votes` (`vote_user`, `vote_object`, `vote_datetime`) 
VALUES ('$user', '$object', NOW()) 
ON DUPLICATE KEY UPDATE `vote_datetime` = NOW()

 

MySQL må altså "oppfatte" at det blir en duplikat dersom det allerede finnes en rad hvor både vote_user = $user OG vote_object = $object.

 

Noen tips?

Endret av simenss
Lenke til kommentar
Hva er primærnøkkelen? Den bør i dette tilfellet være satt til vote_user og vote_object. Ev. kan de være unique.

9509105[/snapback]

Det er ikke mulig å definere to primærnøkler, og ingen av de kan være unike da en bruker kan stemme på flere objekter, og et objekt kan blir stemt på av flere brukere.

 

EDIT: Var litt rask her. Man kan ikke definere to primærnøkler, men primærnøkkelen kan bestå av to rader. Problemet er løst :)

Endret av simenss
Lenke til kommentar

Kom over et nytt problem: Hvordan henter jeg ut de stemmene som er eldre enn et døgn og tilhører brukeren som ønsker å stemme, eller de objektene som brukeren ikke har stemt på ennå?

 

Spørringen min ser foreløpig slik ut:

SELECT objects.object_id, 
objects.object_user, 
objects.object_title, 
votes.vote_user_id, 
votes.vote_object_id, 
votes.vote_time, 
users.user_verification
FROM `objects` LEFT JOIN `votes` ON (objects.object_id = votes.vote_object_id)
RIGHT JOIN `users` ON (objects.object_user = users.user_id)
WHERE `user_verification` = '1' AND `user_id` != '$user' AND DATE_FORMAT(NOW(), '%Y%m%d%H%i%S') - DATE_FORMAT(vote_time, '%Y%m%d%H%i%S') >= 1000000
GROUP BY `object_id`
ORDER BY RAND() 
LIMIT 1

 

 

Jeg har tabellen users (tabell med alle brukere), objects (alle objektene som kan stemmes på) og votes (alle stemmer som gis).

 

Problemet her er at spørringen ikke klarer hente ut de objektene fra objects som brukeren ikke har stemt på (det finnes ingen rad i votes som inneholder både vote_user_id = bruker som prøver og stemme, og vote_object_id = objektet som brukeren prøver å stemme på).

 

GROUP BY og ORDER BY er lagt til for å prøve å spre stemmene mest mulig ut over de forskjellige objektene.

 

Kanskje noe vanskelig forklart?

Lenke til kommentar

Tror jeg nå har funnet riktig metode, men løsningen er jeg usikker på. Spørringen i forrige innlegg må ekskludere alle stemmer som er en stemme på det samme objektet. Altså, om bruker B ønsker å stemme på objekt X1, men bruker A allerede har stemt på objektet X1, må spørringen ekskludere stemmen (raden) som A har gitt X1. Med andre ord fjerne duplikater, men beholde stemmen (raden) for aktuell brukers (B) stemme.

 

Dette innlegget ble tungt...

Endret av simenss
Lenke til kommentar

Kanskje jeg ikke helt forstår hvordan jeg kan bruke spørringen din, men får det ikke til å fungere. Skal prøve å forklare litt bedre:

 

Si at jeg har to tabeller: votes og objects

 

votes:

 

object_id    user_id    time

      1000    101          9487

      1001    101          13544

      1003    202          41546

      1005    202          1542

objects:

 

object_id

      1000

      1001

      1002

      1003

      1004

      1005

 

 

Jeg skal nå hente ut alle objekter som bruker_id 202 ikke har stemt på (gyldige objekter er 1000, 1001, 1002, 1004), og de objektene som bruker_id 202 har stemt på men hvor time <= 10000 (gyldige objekter er 1005). De objektene jeg skal sitte igjen med er dermed 1000, 1001, 1002, 1004 og 1005.

 

 

EDIT: Med din spørring roac, klarer jeg kun å hente ut de objektene bruker_id 202 ikke har stemt på.

Endret av simenss
Lenke til kommentar

Du kan ikkje hente ut ein mengde av ein mengde som brukeren har stemt på av dei han ikkje har stemt på.

 

Men ein annen tankegang er å plukke opp dei som følger de kriteriene

logikken blir vell ala:

WHERE NOT EXISTS ( SELECT alle objekter bruker id 202 har stemt på) AND EXISTS ( SELECT alle objekter brukeren har stemt på som er <=10000)

Lenke til kommentar
Kjapt før jeg går hjem, fyll inn det som mangler:

 

select
 ...
from
 objects o 
   left outer join votes v ... 
   left outer join users u ...
where
 (u.user_name ... and v.time ...) or
 v.user_id is null

9527765[/snapback]

Jeg ender da opp med følgende spørring:

SELECT votes.vote_user_id, votes.vote_time, user_objects.user_object_id, user_objects.user_object_user, user_objects.user_object_title, votes.vote_object_id, votes.vote_time, users.user_gender, users.user_username, users.user_verification

FROM `user_objects` left outer join `votes` ON (user_objects.user_object_id = votes.vote_object_id)
  left outer join `users` ON (user_objects.user_object_user = users.user_id)

WHERE (users.user_id = '$user' AND DATE_FORMAT( NOW( ) , '%Y%m%d%H%i%S' ) - DATE_FORMAT( vote_time, '%Y%m%d%H%i%S' ) >=1000000) OR votes.vote_user_id IS NULL

 

Spørringen henter ut de objektene brukeren selv ikke har stemt på (eller tiden er >= 1000000), men den klarer ikke hente ut de objektene som andre brukere har stemt på men som aktuell bruker ikke har stemt på. Spørringen klarer altså ikke å skille aktuell brukers stemmer og andre brukeres stemmer. Det er dette problemet jeg har hatt hele veien.

Endret av simenss
Lenke til kommentar

Nå begynner jeg å ramle av her. Mulig jeg har oversett noe, men jeg trodde du ville hente ut de som brukeren ikke har stemt på, eller det er en gitt tid siden han har stemt på.

 

Uansett mener jeg at du ved å bruke fremgangsmåten i spørringene vil kunne klare å få ut det du vil. Med håndtering av nulls og bruk av outer joins kan du ganske effektivt få ut akkurat det du måtte ønske.

Lenke til kommentar

Det er akkurat det jeg vil. Har formulert det slik tidligere:

 

Hvordan henter jeg ut de stemmene som er eldre enn et døgn og tilhører brukeren som ønsker å stemme, eller de objektene som brukeren ikke har stemt på ennå?

 

Prøver å forklare med noen skjermbilder fra phpMyAdmin:

 

100000008 er aktuell bruker i eksemplene som følger

 

objects.jpg

 

votes.jpg

 

Når jeg kjører følgende spørring mot databasen:

SELECT votes.vote_user_id, votes.vote_time, objects.object_id, objects.object_user, votes.vote_object_id, votes.vote_time
FROM `objects` LEFT OUTER JOIN `votes` ON ( objects.object_id = votes.vote_object_id )
LEFT OUTER JOIN `users` ON ( objects.object_user = users.user_id )
WHERE (
votes.vote_user_id = '100000008' AND DATE_FORMAT( NOW( ) , '%Y%m%d%H%i%S' ) - DATE_FORMAT( vote_time, '%Y%m%d%H%i%S' ) >=1000000
) OR votes.vote_user_id IS NULL

...får jeg opp følgende resultat:

res1.jpg

 

Om du ser på votes-tabellen ser du at objectet med ID 100000003 også skulle vært blant resultatene, siden aktuell bruker (100000008) ikke har stemt på objektet. Men som du også ser har en annen bruker (100000000) stemt på objektet med ID 100000003. Om jeg fjerner raden fra votes hvor vote_user_id = 100000000 og vote_object_id = 100000003, kommer også objektet 100000003 opp under resultatene fra spørringen over.

 

Om jeg kjører samme spørringen uten WHERE får jeg opp følgende resultat:

res2.jpg

 

Her ser du at objektet med ID 100000003 er stemt på av en annen bruker enn 100000008 (som i denne posten er aktuell bruker). Derfor skriver jeg at spørringen ikke klarer å skille mellom aktuell brukers stemmer og andre brukeres stemmer.

Lenke til kommentar
Her ser du at objektet med ID 100000003 er stemt på av en annen bruker enn 100000008 (som i denne posten er aktuell bruker). Derfor skriver jeg at spørringen ikke klarer å skille mellom aktuell brukers stemmer og andre brukeres stemmer.

9528388[/snapback]

Prøve å bytte ut

LEFT OUTER JOIN `users` ON ( objects.object_user = users.user_id)

med

LEFT OUTER JOIN `users` ON ( votes.vote_user_id = users.user_id )

Lenke til kommentar

Opprett en konto eller logg inn for å kommentere

Du må være et medlem for å kunne skrive en kommentar

Opprett konto

Det er enkelt å melde seg inn for å starte en ny konto!

Start en konto

Logg inn

Har du allerede en konto? Logg inn her.

Logg inn nå
  • Hvem er aktive   0 medlemmer

    • Ingen innloggede medlemmer aktive
×
×
  • Opprett ny...