2017-04-04 1 views
0

Dans ma base de données que j'ai des images comme celle-ci:Comment trouver des documents non référencés dans Cloudant

{ 
    "id": "image-1", 
    "type": "image", 
    "href": "..." 
} 

Et les livres qui contiennent des images:

{ 
    "id": "book-1", 
    "type": "book", 
    "images": [ 
    "image-1", 
    "image-33" 
    ] 
} 

Ceci est un many-to-many parce que la La même image peut apparaître dans plusieurs livres, modélisés à l'aide du modèle List of Keys. Je peux utiliser une vue pour traverser la relation dans l'autre direction et trouver les livres dans lesquels une image donnée apparaît.

Ma question est: Comment puis-je trouver les images qui n'apparaissent dans aucun livre?

Les images occupent de l'espace et peuvent avoir d'autres coûts associés, ce qui serait un bon candidat pour la suppression.

Ma première pensée était d'utiliser une vue qui compte le nombre de références à chaque image:

"map": function (doc) { 
    if (doc.type == "book") { 
    for (i = 0; i < doc.images.length; i++) { 
     emit(doc.images[i], null); 
    } 
    } 
    else if (doc.type == "image") { 
    emit(doc._id, null); 
    } 
}, 
"reduce": _count 

Si je dis que une des références d'image alors lui-même je peux être sûr que toutes les images existent dans la vue et J'ai juste besoin de sélectionner ceux qui sont seulement référencés par eux-mêmes, c'est-à-dire où le compte est 1. Mais c'est là que je suis bloqué parce que le mécanisme de vue ne me laisse filtrer que sur la clé.

J'ai également envisagé de créer un index par rapport au membre book.images puis de rechercher les images qui n'apparaissent pas dans cet index, mais je ne trouve aucun exemple le long de ces lignes. Je suis conscient que mes deux solutions potentielles nécessitent une recherche linéaire sur toutes les images, mais je suis d'accord avec cela parce que c'est une opération d'entretien qui s'exécute occasionnellement et n'est pas critique. À ce point, ma première option fonctionne assez bien, cela signifie simplement que je dois ramener toute la vue au client et y chercher quand je préférerais appliquer le filtre sur le serveur.

Je suis également conscient que si je devais changer le modèle et d'inverser la relation de sorte qu'une image contenait une liste de livres:

{ 
    "id": "image-1", 
    "type": "image", 
    "href": "...", 
    "books": [ 
    "book-1", 
    "book-12" 
    ] 
} 

Alors je pouvais encore trouver les images dans un livre en utilisant une vue , mais je peux aussi indexer le membre image.books et trouver rapidement ceux dont la longueur est 0. Cependant, cela impose un fardeau à l'application car l'application édite des livres, et cela signifie que chaque fois qu'un utilisateur modifie les images dans un livre, l'application doit modifient également les images, ce qui introduit des problèmes de cohérence car deux utilisateurs travaillant avec des livres différents peuvent finir par modifier la même image.

+0

Qu'en est-il d'y aller avec votre première pensée, puis en interrogeant la vue avec "? group = true"? Cela retournera chaque image et comptera. Vous parcourez tous les résultats, en trouvez un en comptant 1, et supprimez ces images ... – markwatsonatx

+0

@markwatsonatx Oui, merci, c'est ce que je fais - désolé si je n'ai pas été clair. Et cela fonctionne, donc ma question est vraiment: dois-je tirer la vue entière (groupée) vers le client et la filtrer là? Ou existe-t-il un moyen d'inclure le filtre dans la requête pour réduire la charge sur mon application? Ou une autre approche entièrement. –

+0

Je ne peux pas penser à un excellent moyen de le faire sans mettre plus de fardeau sur l'application. J'ai suggéré une alternative ci-dessous, mais je sais que cela ne résout pas vraiment votre problème. – markwatsonatx

Répondre

0

Une autre méthode consiste à utiliser une recherche à facettes et à ne renvoyer que les comptes. Il retournera toujours chaque image.Payload ressemblerait à ceci:

{ 
    "total_rows": 4, 
    "bookmark": "g2o", 
    "rows": [ ], 
    "counts": { 
    "image": { 
     "image-1": 3.0, 
     "image-2": 1.0, 
     "image-33": 1.0 
    } 
    }  
} 

design doc ressemblerait à ceci:

{ 
    "_id": "_design/bookSearch", 
    "_rev": "xxx", 
    "views": {}, 
    "language": "javascript", 
    "indexes": { 
    "search": { 
     "index": "function (doc) {\n if (doc.type == \"image\") {\n index(\"image\", doc.id, {\"facet\": true});\n }\n else if (doc.type == \"book\") {\n for (var i=0; i<doc.images.length; i++) {\n  index(\"image\", doc.images[i], {\"facet\": true});\n }\n }\n}\n", 
     "analyzer": "standard" 
    } 
    } 
} 

Fonction ressemble à ceci:

function (doc) { 
    if (doc.type == "image") { 
    index("image", doc.id, {"facet": true}); 
    } 
    else if (doc.type == "book") { 
    for (var i=0; i<doc.images.length; i++) { 
     index("image", doc.images[i], {"facet": true}); 
    } 
    } 
} 

la requête ressemblerait à ceci:

https://xxx.cloudant.com/db/_design/bookSearch/_search/search 
?q=*:*&limit=0&counts=["image"] 

Vous ne savez pas s'il existe un ny réel avantage de le faire de cette façon si ...