web-dev-qa-db-fra.com

Tri des vues CouchDB par valeur

Je teste CounchDB pour voir comment il pourrait gérer la journalisation des résultats de la recherche. Ce que je voudrais faire, c'est produire une vue où je peux produire les premières requêtes des résultats. Pour le moment, j'ai quelque chose comme ça:

Exemple de portion de document

{
  "query": "+dangerous +dogs",
  "hits": "123"
}

Fonction de la carte (pas exactement ce dont j'ai besoin/veulent, mais c'est assez bon pour tester)

function(doc) {
  if (doc.query) {
    var split = doc.query.split(" ");
    for (var i in split) {
      emit(split[i], 1);
    }
  }
}

Réduire la fonction

function (key, values, rereduce) {
  return sum(values);
}

Maintenant, cela me donnera des résultats dans un format où un terme de requête est la clé et le compte pour ce terme à droite, ce qui est génial. Mais j'aimerais que cela soit commandé par la valeur, pas la clé. Du des sons de celui-ci, cela n'est pas encore possible avec Couchdb.

Est-ce que quelqu'un a des idées sur la manière dont je peux avoir une vue où j'ai une version ordonnée des termes de la requête et de leurs comptes liés? Je suis très nouveau à Couchdb et je ne peux pas penser à la façon dont j'écrirais les fonctions nécessaires.

38
Lee Theobald

Il est vrai qu'il n'y a pas de réponse simple morte. Il y a cependant plusieurs modèles.

  1. http://wiki.apache.org/couchdb/view_snippets#retrieve_the_top_n_tags . Je n'aime pas personnellement cela parce qu'ils reconnaissent que c'est une solution fragile, et le code n'est pas relaxant.

  2. La réponse d'AVI, qui doit trier la mémoire dans votre application.

  3. Couchdb-Lucene qu'il semble que tout le monde se trouve avoir finalement besoin de finir!

  4. Ce que j'aime, c'est ce que Chris a dit dans la citation d'Avi. Relaxer. Dans COUCHDB, les bases de données sont légères et Excel à vous donner une perspective unique de vos données. Ces jours-ci, le buzz est tout sur la réplication filtrée qui consiste à affiner les sous-ensembles de vos données à mettre dans une DB distincte.

    Quoi qu'il en soit, les bases sont simples. Vous prenez votre .rows À partir de la sortie de la vue et vous l'insérez dans une DB distincte qui émet simplement de la saisie sur le compte. Un astuce supplémentaire est d'écrire un très simple _list fonction. Les listes "rendent" la sortie du canapé brut en différents formats. Ton _list fonction devrait produire

    { "docs":
        [ {..view row1...},
          {..view row2...},
          {..etc...}
        ]
    }
    

    Ce que cela va faire est de formater la sortie de vue exactement la façon dont le _bulk_docs API l'exige. Maintenant, vous pouvez faire du tuyau en boucle directement dans une autre boucle:

    curl Host:5984/db/_design/myapp/_list/bulkdocs_formatter/query_popularity \
     | curl -X POST Host:5984/popularity_sorter/_design/myapp/_view/by_count
    
  5. En fait, si votre fonction de liste peut gérer tous les documents, vous pouvez simplement le faire trier lui-même et les renvoyer au client triché.

25
JasonSmith

Ceci est ven sur la liste de diffusion utilisateur CouchDB et Chris Anderson, l'un des principaux développeurs, a écrit:

Il s'agit d'une demande commune, mais non prise en charge directement par Couchdb's Views - de le faire, vous devez copier la requête de groupe-Réduire dans une autre base de données et créer une vue pour trier par la valeur.

Il s'agit d'un compromis que nous faisons en faveur des requêtes de la gamme dynamique et des indices incrémentiels.

J'avais besoin de le faire récemment et j'ai fini par le faire dans mon niveau d'application. Ceci est facile à faire en JavaScript:

db.view('mydesigndoc', 'myview', {'group':true}, function(err, data) {

    if (err) throw new Error(JSON.stringify(err));

    data.rows.sort(function(a, b) {
        return a.value - b.value;
    });

    data.rows.reverse(); // optional, depending on your needs

    // do something with the data…
});

Cet exemple fonctionne dans nœud.js et utilise node-couchdb , mais il pourrait facilement être adapté pour fonctionner dans un navigateur ou un autre environnement JavaScript. Et bien sûr, le concept est portable à tout langage/environnement de programmation.

!

13
Avi Flax

C'est une ancienne question mais je pense que cela mérite toujours une réponse décente (j'ai passé au moins 20 minutes à chercher la bonne réponse ...)

Je désapprouve les autres suggestions dans les réponses ici et estimez qu'elles ne sont pas satisfaisantes. Surtout, je n'aime pas la suggestion de trier les lignes de la couche applicative, car elle n'allage pas bien et ne traite pas d'une affaire dans laquelle vous devez limiter le résultat défini dans la DB.

La meilleure approche que j'ai rencontrée est suggérée dans ce fil et cela pose que si vous devez trier les valeurs de la requête, vous devez les ajouter à la touche, puis interroger la clé à l'aide d'une plage - Spécification d'une clé souhaitée et relâchement de la plage de valeurs. Par exemple, si votre clé est composée de pays, d'état et de ville:

emit([doc.address.country,doc.address.state, doc.address.city], doc);

Ensuite, vous interrogez juste le pays et obtenez le tri libre sur le reste des composants clés:

startkey=["US"]&endkey=["US",{}] 

Si vous devez également inverser la commande - Notez que la définition simple descending: true ne suffira pas. Vous devez réellement inverser l'ordre de départ et de fin de la fin, c'est-à-dire

startkey=["US",{}]&endkey=["US"]

Voir plus de référence à cette excellente source .

4
roy650

Je ne suis pas sûr de ce que vous avez comme votre résultat retourné, mais je suis certain que cela devrait faire l'affaire:

emit([doc.hits, split[i]], 1);

Les règles de tri sont définis dans les documents.

2

Basé sur la réponse d'AVI, j'ai proposé cette fonction de liste de Couchdb qui a fonctionné pour mes besoins, ce qui est simplement un rapport d'événements les plus populaires (clé = nom d'événement, valeur = participants).

 ddoc.lists.eventPopularity = fonction (req, res) {
 Démarrer ({en-têtes: {"Type de contenu": "Texte/plaine"}}); [.____] Data = [] 
 Bien que (rangée = getrowow ()) {
 données.push (rangée); 
} 
 données.sort (fonction (A, B ) {
 Renvoie A.Value - B.Value; [.____]}). Inverser (); [.____] pour (i in données) {[. ] .value + ':' + données [i] .key + "\ n"); 
} [.____]} [.____]

Pour référence, voici la fonction de vue correspondante:

 ddoc.views.eventpopularity = {
 Carte: fonction (doc) {[.____] si (doc.type == 'utilisateur') {[.____] doc.events) {
 Emit (doc.events [i] .Event_name, 1); 
} [ '_compter'
}

Et la sortie de la fonction de liste (coupée):

[.____] 165: Innovation axée sur la conception: comment les concepteurs facilitent la boîte de dialogue [.____] 165: Vos clients sont-ils une foule ou une communauté? [ N'ayez pas peur de la créativité! Tout peut arriver [.____] 159: Les agences doivent-elles penser comme des sociétés de logiciels? [.____] 158: Expérience client: Tendances et idées futures [.____] 156: L'écrivain accidentel: une excellente copie Web pour tout le monde [ .____] 155: Pourquoi tout est incroyable mais personne n'est heureux 
2
user599515

Le lien récupération_the_top_n_tags semble être brisé, mais j'ai trouvé une autre solution ici .

Citant le dev qui a écrit cette solution:

plutôt que de renvoyer les résultats clés par la balise dans la carte de la carte, j'émettais chaque occurrence de chaque étiquette à la place. Ensuite, dans la mesure de réduction, je calculerais les valeurs d'agrégation regroupées par balise à l'aide d'un hachage, transformez-la en une matrice, triez-la et choisissez le TOP 3.

Comme indiqué dans les commentaires, le seul problème serait en cas de longue queue:

Problème est que vous devez faire attention au nombre de balises que vous obtenez; Si le résultat est plus grand que 500 octets, vous aurez CouchDB en se plaignant, car "Réduire doit réduire efficacement". 3 ou 6 ou même 20 tags ne devraient pas être un problème, cependant.

Cela a fonctionné parfaitement pour moi, vérifiez le lien pour voir le code!

0
edelans

Chaque solution ci-dessus va briser les performances de CouchDB, je pense. Je suis très nouveau dans cette base de données. Comme je sais que CaouchDB, les vues préparent des résultats avant qu'il ne soit interrogé. Il semble que nous ayons besoin de préparer des résultats manuellement. Par exemple, chaque terme de recherche résidera dans la base de données avec des chiffres de frappe. Et quand les recherches de quelqu'un, ses termes de recherche seront recherchés et incrustions le nombre de résultats. Lorsque nous voulons voir la popularité du terme de recherche, il émettra une paire (Hitcount, ServerTerm).

0
Melug