Hallo websitemannen. Ik wil jullie om wat inzichten vragen ...
Ik ben een grote nieuwssite aan het bouwen die wellicht nog best wat teweeg gaat brengen. Onder de artikelen die op de site staan, kunnen mensen reacties (comments) posten. Gewoon een commenting-systeem dus.
Nou wil ik dat hele commenting-systeem, treaded oftewel hiërarchisch maken ... zodat mensen kunnen reageren op reacties. Dus dan is het niet zomaar een lijst van reacties in chronologische volgorde, maar echt een boom van gestructureerde reacties; net als op Youtube.
Da's simpel: je toont gewoon bij elke reactie een "reageer" knop.
En in de database in de tabel van reacties, komt er een kolom bij, genaamd `parent_reactie`, om de relaties tussen de reacties op te slaan. In die kolom staat gewoon voor elke reactie, de ID van de reactie waar het een reactie op is, of een 0 als het een top-level-reactie is.
Zijn we d'r nog? :-)
De PHP-code om de reacties onder een artikel te tonen, kan ik ook wel schrijven ... maar nu het volgende:
Hoe kan je voor zo'n threaded/hiërarchisch commenting-systeem nou de paginering verzorgen?
Ik wil bijvoorbeeld maximaal 200 reacties per pagina. Als er meer dan 200 reacties zijn, zijn er dus meerdere pagina's ...
Je zou bijvoorbeeld kunnen zeggen: we tellen alleen de top-level-reacties, en die limiteren we tot zoveel per pagina. Maar de ene keer wordt het gesprek gevoerd met alleen maar top-level-reacties, en de andere keer ontstaat er een lang gesprek als zijtak op een enkele reactie.
Dus alleen de top-level-reacties tellen dat is niet goed, we willen echt alle zichtbare reacties op de pagina tellen.
(Het is niet erg als een volgende pagina begint met een niet-top-level-reactie.)
Dus de vraag is: wat is nou een slimme manier om zo'n boom van reacties te verdelen in pagina's. Met de performance van de server in gedachten ... dus niet zomaar van alles querieen en dan met PHP gaan tellen ... Het mooiste zou zijn om middels de LIMIT-clause (sql) alleen de juiste reacties te laden.
Een extra kolom maken in de tabel van reacties in de database; genaamd `order`, van het type floating point. (Gaaf, hoe vaak komt dat nou voor, dat je floating points gebruikt bij web development.)
De allereerste reactie krijgt `order` = 0.0.
Elke nieuwe top-level-reactie krijgt `order` = {de waarde van de vorige top-level-reactie} + 1.0.
Elke nieuwe reactie op een bestaande reactie, krijgt `order` = het gemiddelde tussen de reactie erboven en de reactie eronder.
[0.0000] de eerste top-level-reactie
[0.5000] eerste reactie op eerste reactie
[0.7500] tweede reactie op eerste reactie
[0.8750] reactie op reactie op reactie
[1.0000] de tweede top-level-reactie
[1.5000] nietes
[1.7500] welles
[1.8750] nietes
< ... einde van de pagina >
[0.0000] de eerste top-level-reactie
[0.5000] eerste reactie op eerste reactie
[0.7500] tweede reactie op eerste reactie
[0.8750] reactie op reactie op reactie
[1.0000] de tweede top-level-reactie
[1.5000] nietes
[1.7500] welles
[1.8750] nietes
<... einde van de pagina >
En als er dan later nog gereageerd wordt op "eerste reactie op eerste reactie":
[0.0000] de eerste top-level-reactie
[0.5000] eerste reactie op eerste reactie
[0.6250] * deze is later er bij geplaatst
[0.6875] * en nog later deze ook
[0.7500] tweede reactie op eerste reactie
[0.8750] reactie op reactie op reactie
[1.0000] de tweede top-level-reactie
[1.5000] nietes
< ... einde van de pagina >
[0.0000] de eerste top-level-reactie
[0.5000] eerste reactie op eerste reactie
[0.6250]* deze is later er bij geplaatst
[0.6875]* en nog later deze ook
[0.7500] tweede reactie op eerste reactie
[0.8750] reactie op reactie op reactie
[1.0000] de tweede top-level-reactie
[1.5000] nietes
<... einde van de pagina >
Het grote voordeel is nu dat de database sorteert op de `order` kolom, en pagina's kan selecteren middels de LIMIT-clause.
Het aantal reacties en de maximale diepte wordt slechts beperkt door de nauwkeurigheid van de floating-points.
Het lastige is misschien om bij een nieuwe reactie uit te zoeken welke reactie erboven en eronder staat.
Een ander idee:
ook met een `order` kolom, maar dan van het type varchar (een string).
Ditmaal met een beperkt aantal top-level-reacties (zeg 1000) en een beperkt aantal reacties per reactie (zeg ook 1000).
['000'] de eerste top-level-reactie
['000:000'] eerste reactie op eerste reactie
['000:000:000'] * deze is later er bij geplaatst
['000:000:001'] * en nog later deze ook
['000:001'] tweede reactie op eerste reactie
['000:001:000'] reactie op reactie op reactie
['001'] de tweede top-level-reactie
['001:001'] nietes
['001:002'] welles
['001:003'] nietes
< ... einde van de pagina >
['000'] de eerste top-level-reactie
['000:000'] eerste reactie op eerste reactie
['000:000:000']* deze is later er bij geplaatst
['000:000:001']* en nog later deze ook
['000:001'] tweede reactie op eerste reactie
['000:001:000'] reactie op reactie op reactie
['001'] de tweede top-level-reactie
['001:001'] nietes
['001:002'] welles
['001:003'] nietes
<... einde van de pagina >
Die dubbele punten kunnen weg; staan er alleen voor de duidelijkheid.
Wel moet het een vast aantal cijfers (of hexadecimale cijfers, of bits of bytes ... ) zijn per "niveau".
Want het gaat er om dat de database kan sorteren op die `order` kolom.
Deze aanpak is makkelijker te implementeren dan die andere aanpak, omdat je bij een nieuwe reactie niet hoeft te kijken naar de reactie die er onder staat.
En de kolom `parent_reactie` is nu zelfs overbodig.
Het nadeel is dat je een compromis moet sluiten tussen verspilling van opslagruimte en een beperkt aantal reacties per tak.
Er moet toch een perfecte oplossing zijn? Hoe zouden jullie dit aanpakken ...
(En ik verwacht hier geen lappen uitleg terug hoor, als iemand zegt van "ja die tweede manier lijkt me wel goed" dan ben ik al blij )
Yes, via die kolom kan je dan de relaties tussen de reacties bijhouden.
TomJansen schreef:
En in de database in de tabel van reacties, komt er een kolom bij, genaamd `parent_reactie`, om de relaties tussen de reacties op te slaan. In die kolom staat gewoon voor elke reactie, de ID van de reactie waar het een reactie op is, of een 0 als het een top-level-reactie is.
Maar hoe kan je dan de paginering verzorgen? Wat is een slimme manier om zo'n "boom" van reacties te verdelen in pagina's? Als je b.v. 200 reacties per pagina wilt laten zien.
Ik kan wel alle reacties van het artikel querieen en dan met PHP gaan tellen en selecteren, maar dat lijkt me niet efficient ...
Toen zat ik te denken aan die `order` kolom. De database moet daar dan op kunnen sorteren; en dan kan je gewoon LIMIT gebruiken om de juiste pagina te pakken.
Het moet wel mogelijk zijn om nog ergens een reactie tussen te plaatsen; vandaar dat idee van die floating-point en dat idee van die strings.
Met die laatste aanpak - met string kolom `order` - wordt `parent_id` of `parent_reactie` zelfs overbodig! Want de lengte van de string geeft aan hoe ver de reactie moet inspringen / indenten.
Als je gegevens opvraagt met de query krijg je de volgende set terug:
reactieid | parentid | inhoud
1 | 1 | bla
2 | 1 | blabla
4 | 1 | blablabla
3 | 3 | iets anders
"Wanneer een reactie toplevel is geef je hem als parentid zijn eigen reactieid mee."
En dan "ORDER BY parentid, reactieid"
Dat is gewoon de oplossing! En ik maar moeilijk doen met floating points en gekke strings ... :-)
In de bovenstaande query moet ORDER BY parentid, reactieid
worden vervangen door: ORDER BY r2.parentid, r2.reactieid
anders klaagt MySQL dat het ambigious is.
Maar ik moet eerlijk zeggen dat ik die hele JOIN niet echt begrijp.
(Alle join's behalve LEFT en RIGHT vind ik ingewikkeld :shame: )
Volgens mij is die JOIN niet nodig, en levert de volgende query hetzelfde resultaat op: