24-05-2012, 12:00 AM
Bon quelques bouts de ma solution après plusieurs jours de recherche, si ça intéresse toujours ^^
Sur CouchDB deux databases :
- achievements
- attribution
Exemple de document dans achievement :
Dans attribution
Apache :
Décommenter les extensions mod_proxy, rajoute un ProxyPass vers localhost:5984 pour palier à la non gestion des CORS
Client :
Pour le log j'ai fait grosso modo comme pour la page d'avant.
Pour vérifier un succès à débloquer :
Vous noterez l'utilisation de eval(), fonction très controversée mais bien utile si utilisée sciemment finalement... Pour les paramètres passés en argument, j'utilise le petit trick de passer un object {} afin de pouvoir utiliser des paramètres optionnels sans me soucier de l'ordre.
Dans la méthode unlock() j'ai une logique de présentation, puis :
On POST vers CouchDB la nouvelle entrée. Bon c'est une fonction ajax() maison, mais avec JQuery ça se présenterait de tête :
On note déjà que le scénario en écriture via requête HTTP c'est pas la panacée si y'a beaucoup d'écritures, comme l'a dit Sephi-Chan.
Pour récupérer les données, il faut créer une vue sous CouchDB
Pour récupérer tous les achievements
Pour récupérer les attributions
Notez la clé doc.pseudo qui va être très importante
Le système de requête par vue est pas super intuitif, j'ai mis du temps à faire ce que je voulais faire, surtout par rapport à ce j'ai pu testé sur MongoDB par exemple. Après cet avis n'engage que moi...
Retour côté client
Voilà pour le feedback.
En conclusion, à mon avis, CouchDB c'est vraiment top quand on veut faire une appli côté client, avec une vue et un model uniquement.
Pour un scénario d'écriture, les requêtes HTTP ça va vite surcharger.
Ca me permet aussi de sortir un peu de mes habitudes, et de découvrir le NoSQL. Ca m'a aussi permis de me rendre compte que j'aurais finalement mieux fait d'adopter MongoDB dès le début. Car pour ce que j'en ai lu, ça serait plus rapide que MySQL. De plus je pourrais sortir du JSON, plutôt que de sortir des lignes MySQL et de remplir un objet avec ensuite... D'autant plus que je n'ai jamais besoin de jointures de dingue, et que j'ai rarement plus de 2 clés étrangères. Javascript, Node.JS, MongoDB, Socket.IO, c'est vraiment un éco-système qui se complète avec un point commun : le JSON.
Sur CouchDB deux databases :
- achievements
- attribution
Exemple de document dans achievement :
{
"_id": "5",
"_rev": "2-e6ef82dc01b1a4b6e2fd2cf2075ccc68",
"name": "Ce n'est pas la taille qui compte...",
"description": "Vous avez battu un bot pour le moins imposant",
"unlock": {
"1": "(target.pv <= 0) && (target.isBot() && target.isBig())"
}
}
{
"_id": "1",
"_rev": "4-b2346d1f821165b754bb4db337a67a9b",
"name": "Bourrin !",
"description": "Vous avez attaqué 10 fois de suite",
"unlock": {
"1": "attaquer",
"2": "attaquer",
"3": "attaquer",
"4": "attaquer",
"5": "attaquer",
"6": "attaquer",
"7": "attaquer",
"8": "attaquer",
"9": "attaquer",
"10": "attaquer"
}
}
Dans attribution
{
"_id": "4b1ec22a351847fd4789a86fb500508a",
"_rev": "1-d8df72aeff2d0d0c0605acfca4bfb9d5",
"pseudo": "Maks",
"idAchievement": "2"
}
Apache :
Décommenter les extensions mod_proxy, rajoute un ProxyPass vers localhost:5984 pour palier à la non gestion des CORS
Client :
Pour le log j'ai fait grosso modo comme pour la page d'avant.
Pour vérifier un succès à débloquer :
/*
* On boucle dans les tous succès
*/
for (var id in this.achievements) {
/*
* On récupère l'objet qui décrit le cheminement pour débloquer le succès
*/
var unlock = this.achievements[id].unlock;
/*
* La variable 'stepCompleted' contient le nombre d'étapes complétées pour débloquer le succès
*/
var stepCompleted = 0;
var i = 0;
/*
* On boucle les étapes dans l'objet 'unlock'
*/
for (var step in unlock) {
/*
* Fix : si le log ne contient pas assez d'action pour boucler jusqu'au bout des étapes (cas où on a commencé à jouer)
* Alors on sort de la boucle
*/
if (!this.queue[i]) {
break;
}
/*
* On récupère l'étape
*/
var unlockStep = unlock[step];
/*
* On récupère les variables loggées qui permettront d'effectuer les tests
*
* Note : on donne une valeur par défaut pour les paramètres non renseignés
*/
var action = this.queue[i].action;
var degats = this.queue[i].params.degats || 0;
var target = this.queue[i].params.target || '';
var run = this.queue[i].params.run || 0;
var soin = this.queue[i].params.soin || 0;
/*
* Si l'action contient un opérateur (=, !, <, >, ect..) alors il faut évaluer grâce à eval() la chaîne de caractère pour savoir si l'étape est complétée
*/
var test1 = unlockStep.indexOf('=');
var test2 = unlockStep.indexOf('!');
var test3 = unlockStep.indexOf('>');
var test4 = unlockStep.indexOf('<');
var test5 = unlockStep.indexOf('&');
var test6 = unlockStep.indexOf('|');
var test7 = unlockStep.indexOf('+');
var test8 = unlockStep.indexOf('-');
var test9 = unlockStep.indexOf('/');
var test10 = unlockStep.indexOf('*');
var test11 = unlockStep.indexOf('%');
/*
* Si l'un des tests est rempli et que l'évalution de l'étape vaut vrai
* Ou que l'étape à effectuer est strictement égale à l'action loggée
* On incrémente la variable 'stepCompleted'
*/
if ((( test1 > -1 || test2 > -1 || test3 > -1 || test4 > -1 || test5 > -1 || test6 > -1
|| test7 > -1 || test8 > -1 || test9 > -1 || test10 > -1 || test11 > -1) && eval(unlockStep))
|| action === unlockStep) {
++stepCompleted
}
++i;
}
/*
* Si le nombre d'étapes complétées est égale aux nombre d'étapes totales 'step'
*
* Note : step est de type String
*/
if (stepCompleted === parseInt(step)) {
this.unlock(id);
}
}
Vous noterez l'utilisation de eval(), fonction très controversée mais bien utile si utilisée sciemment finalement... Pour les paramètres passés en argument, j'utilise le petit trick de passer un object {} afin de pouvoir utiliser des paramètres optionnels sans me soucier de l'ordre.
Dans la méthode unlock() j'ai une logique de présentation, puis :
/*
* On supprime le succès de la liste des achievements
*/
delete this.achievements[id];
/*
* On met à jour CouchDB en ajoutant dans la table attribution un doc contenant le pseudo et l'id de l'achievement
*/
ajax('POST', '/couchDB/attribution', JSON.stringify({ 'pseudo' : joueur.pseudo, 'idAchievement' : id }), { 'Content-type' : 'application/json' });
On POST vers CouchDB la nouvelle entrée. Bon c'est une fonction ajax() maison, mais avec JQuery ça se présenterait de tête :
$.ajax({ 'method' : POST,
'url' : '/couchDB/attribution'
'data' : JSON.stringify({ 'pseudo' : joueur.pseudo, 'idAchievement' : id }),
'headers' : { 'Content-type' : 'application/json' },
'async' : true
});
On note déjà que le scénario en écriture via requête HTTP c'est pas la panacée si y'a beaucoup d'écritures, comme l'a dit Sephi-Chan.
Pour récupérer les données, il faut créer une vue sous CouchDB
Pour récupérer tous les achievements
{
"_id": "_design/get",
"_rev": "1-c3aadc7c4354a3a71d3c87177830f18b",
"views": {
"all": {
"map": "function(doc){ emit(null, doc) }"
}
}
}
Pour récupérer les attributions
{
"_id": "_design/get",
"_rev": "14-31c39a656b122e7a56490f278b77e4e7",
"views": {
"all": {
"map": "function(doc) { emit(doc.pseudo, doc) }"
}
}
}
Notez la clé doc.pseudo qui va être très importante
Le système de requête par vue est pas super intuitif, j'ai mis du temps à faire ce que je voulais faire, surtout par rapport à ce j'ai pu testé sur MongoDB par exemple. Après cet avis n'engage que moi...
Retour côté client
/*
* On récupère la vue contenant les docs de la bdd achievements dans CouchDB
*/
var achievements = JSON.parse(ajax('GET', '/couchDB/achievements/_design/get/_view/all'));
/*
* On boucle selon le nombre d'achievements
*/
for (var i = 0, l = achievements.rows.length; i < l; ++i) {
/*
* L'object est contenu dans la clé 'rows', puis clé 'value'
*/
var achievement = achievements.rows[i].value;
/*
* On supprime les clés reservées à CouchDB
*/
delete achievement._id;
delete achievement._rev;
/*
* On ajoute l'achievement à l'objet 'achievements'
*
* Note : la clé est i + 1 car l'ID commence à 1
*/
this.achievements[i + 1] = achievement;
}
/*
* On récupère la vue contenant les docs de la bdd attribution dans CouchDB en ne récupérant que les clés concernant le joueur (?key="joueur.pseudo")
*/
var attributions = JSON.parse(ajax('GET', '/couchDB/attribution/_design/get/_view/all?key="' + joueur.pseudo + '"'));
/*
* On boucle selon le nombre d'attributions
*/
for (var i = 0, l = attributions.rows.length; i < l; ++i) {
/*
* L'object est contenu dans la clé 'rows', puis clé 'value'
*/
var attribution = attributions.rows[i].value;
/*
* Si l'id de l'achievement est déjà attribué, on le supprime de l'objet 'achievements'
*/
if (attribution.idAchievement in this.achievements) {
delete this.achievements[attribution.idAchievement];
}
}
Voilà pour le feedback.
En conclusion, à mon avis, CouchDB c'est vraiment top quand on veut faire une appli côté client, avec une vue et un model uniquement.
Pour un scénario d'écriture, les requêtes HTTP ça va vite surcharger.
Ca me permet aussi de sortir un peu de mes habitudes, et de découvrir le NoSQL. Ca m'a aussi permis de me rendre compte que j'aurais finalement mieux fait d'adopter MongoDB dès le début. Car pour ce que j'en ai lu, ça serait plus rapide que MySQL. De plus je pourrais sortir du JSON, plutôt que de sortir des lignes MySQL et de remplir un objet avec ensuite... D'autant plus que je n'ai jamais besoin de jointures de dingue, et que j'ai rarement plus de 2 clés étrangères. Javascript, Node.JS, MongoDB, Socket.IO, c'est vraiment un éco-système qui se complète avec un point commun : le JSON.