9
votes

Interrogation pour N Records aléatoires sur Appengine Datastore

J'essaie d'écrire une requête GQL qui renvoie n enregistrements aléatoires d'un type spécifique. Ma mise en œuvre actuelle fonctionne mais nécessite n appels dans le magasin de données. Je voudrais le faire 1 appel au magasin de données si possible.

J'aime actuellement un nombre aléatoire à chaque type que j'ai mis dans le magasin de données. Lorsque j'intervoque pour un enregistrement aléatoire, je génère un autre nombre aléatoire et requête pour les enregistrements> Commande Rand par ASC Limite 1.

Cela fonctionne, cependant, il ne renvoie que 1 enregistrement pour que je dois faire n requêtes. Des idées sur la façon de faire cette question? Merci.


1 commentaires

J'ai créé un problème pour cela, vous pouvez l'intégrer pour vous aider à l'obtenir corrigé: code.google.com/p/googleAppengine/issues/detail?id=9044


6 Réponses :


5
votes

"Sous la capuche" Un seul appel de requête de recherche ne peut renvoyer qu'un ensemble de lignes consécutives de certains index. C'est pourquoi certaines requêtes GQL, y compris toute utilisation de! =, Développez plusieurs appels de données de données.

n Les sélections aléatoires uniformes indépendantes ne sont pas (en général) consécutives dans n'importe quel index.

QED.

Vous pouvez probablement utiliser MemCache pour stocker les entités et réduire le coût de la saisie de N d'entre eux. Ou si cela ne vous dérange pas que les sélections "aléatoires" se rapprochent dans l'index, sélectionnez un bloc de choisi par hasard (par exemple) 100 dans une requête, puis choisissez n au hasard parmi ceux-ci. Depuis que vous avez un champ déjà randomisé, il ne sera pas immédiatement évident pour un étranger que les n articles sont liés. Au moins, pas avant de regarder beaucoup d'échantillons et de noter que les articles A et Z n'apparaissent jamais dans le même groupe, car ils sont plus de 100 personnes à l'intérieur de l'indice randomisé. Et si le rendement le permet, vous pouvez ré-aléatoire vos entités de temps à autre.


5 commentaires

Merci - j'ai vraiment besoin de résultats randomisés, donc je suppose que je devrai utiliser plusieurs appels de datastore. Je vais essayer de minimiser N autant que je peux deviner.


Ce n'est pas vrai. Opérations de lot et le dans opérateur de requête peut retourner non consécutive entités.


@Ryan: Même avec ! = . Les deux et dans sont mis en œuvre comme un nombre limité de sous-requêtes. Les opérations par lots ne sont pas vraiment pertinentes pour la question, mais oui, il est vrai que certaines opérations agissent sur des entités qui ne sont pas contiguës dans un index. Juste pas des recherches.


bon point! Désolé, j'ai raté la partie "Query" de votre réponse. Vous avez vraiment raison en général. Il y a une exception près: Fusionner Rejoignez les requêtes Retourner des entités non contiguës. Ils n'étaient utilisés que pour une petite classe de questions au lancement, mais ils ont récemment augmenté avec les capacités de requête avancées Alfred décrit dans le article .


@Ryan: En fait, j'ai édité ma réponse en réponse à votre commentaire, je ne pense donc pas que tu as manqué quoi que ce soit d'abord. Auparavant, je n'étais pas clair que les "appels de données", je fais référence à des requêtes. Intéressant sur le zigzag Fusion Rejoignez - Je pense que je pourrais probablement sauver ma "preuve" avec certains lemmes que les requêtes qu'il effectue équivalent à des requêtes que sont contiguës dans un indice qui aurait pu être construit mais réellement n'était pas. Mais je ne suis pas sûr, je ne vais pas essayer. J'ai commenté avant que chaque fois que j'essaie de répondre à une question GAE dans le négatif, vous ajoutez des fonctionnalités :-)



3
votes

On dirait que la seule méthode consiste à stocker la valeur entière aléatoire dans la propriété spéciale de chaque entité et en interrogant à ce sujet. Cela peut être fait assez automatiquement si vous ajoutez simplement une propriété initialisée automatiquement.

Malheureusement, cela nécessitera une fois le traitement de toutes les entités une fois si votre magasin de données est déjà rempli.

C'est bizarre, je sais.


1 commentaires

Je pense que c'est une excellente approche et correspond au modèle NOSQL de faire le travail sur l'écriture plutôt que de lire. Bien sûr, cela ne serait pas entièrement aléatoire - si vous avez toujours reçu N entrées séquentielles à ce moment-là, un utilisateur verrait occasionnellement les mêmes enregistrements à côté de l'autre. Mais cela pourrait être assez aléatoire pour l'OP. (Vous pouvez également créer plusieurs centaines de propriétés de même - des nombres aléatoires différents et faire pivoter quel index vous tiré.)



5
votes

Quel genre de compromis recherchez-vous? Si vous êtes prêt à supporter une petite performance sur l'insertion de ces entités, vous pouvez créer une solution pour obtenir N d'entre eux très rapidement.

Voici ce que vous devez faire:

Lorsque vous insérez vos entités, spécifiez la clé. Vous voulez donner des clés à vos entités dans l'ordre, en commençant par 1 et monte à partir de là. (Cela nécessitera des efforts, car l'App moteur n'a pas d'autocrampe (), vous devez donc garder une trace de la dernière carte d'identité que vous avez utilisée dans une autre entité, appelons-le un iDGenerator)

Maintenant, lorsque vous avez besoin de n entités aléatoires, générez n nombres aléatoires compris entre 1 et quel que soit le dernier identifiant que vous avez généré, c'était (votre IDGenerator saura cela). Vous pouvez ensuite faire un lot get d'une clé à l'aide des n touches, qui n'exigeront qu'un voyage dans le magasin de données et sera également plus rapide qu'une requête, car la clé est généralement plus rapide que les requêtes, AFAIK.

Cette méthode nécessite de traiter quelques détails gênants:

  1. Votre IDGenerator pourrait devenir un goulot d'étranglement si vous insérez beaucoup de ces éléments à la volée (plus de quelques-uns une seconde), ce qui nécessiterait une sorte de mise en œuvre de l'IDGenerator Shaffed. Si toutes ces données sont préchargées ou non de volume élevé, vous l'avez facile.
  2. Vous trouverez peut-être que certains identifiants ne possèdent plus une entité qui y est associée, car vous l'avez supprimé ou parce qu'un put () a échoué quelque part. Si cela s'est produit, vous devez saisir une autre entité aléatoire. (Si vous vouliez avoir envie de vous fier et de réduire les chances de cela, vous pouvez rendre cet ID à la disposition de l'IDGenerator pour réutiliser pour «remplir les trous»)

    La question revient donc à la rapidité avec laquelle vous avez besoin de ces n items vs à quelle fréquence vous y ajouterez et de les supprimer, et si une petite complexité supplémentaire vaut cette performance.


3 commentaires

Vous pouvez plus ou moins implémenter cela à l'aide de la numérotation intégrée de l'IDS intégrée du moteur - Si vous connaissez l'ID maximum, vous pouvez choisir un peu de manière uniforme au hasard. Certains n'existeront pas, alors réessayez-les avec de nouveaux identifiants aléatoires, et ainsi de suite. Si votre espace d'identification est dense, cela fonctionnera bien.


doux. Je ne savais pas que nous pourrions compter sur la numérotation intégrée pour commencer à 1 et monter 1 d'ici 1 à partir de là.


Vous ne pouvez pas - mais cela allouera dans des blocs, et tant que les blocs deviennent surtout utilisés, vos tentatives devraient être suffisamment petites pour être gérables.



0
votes

J'avais juste le même problème. J'ai décidé de ne pas attribuer des identifiants à mes entrées déjà existantes dans DataStore et j'ai fait cela, car j'ai déjà eu le totalCompte à partir d'un compteur loué.

Ceci sélectionne des entrées "Compter" des entrées "TotalCount", triés par Key .

    # select $count from the complete set
    numberlist = random.sample(range(0,totalcount),count)
    numberlist.sort()

    pagesize=1000

    #initbuckets
    buckets = [ [] for i in xrange(int(max(numberlist)/pagesize)+1) ]
    for k in numberlist:
        thisb = int(k/pagesize)
        buckets[thisb].append(k-(thisb*pagesize))
    logging.debug("Numbers: %s. Buckets %s",numberlist,buckets)

    #page through results.

    result = []
    baseq =  db.Query(MyEntries,keys_only=True).order("__key__")
    for b,l in enumerate(buckets):
        if len(l) > 0: 
            result += [ wq.fetch(limit=1,offset=e)[0] for e in l ]

        if b < len(buckets)-1: # not the last bucket
            lastkey  = wq.fetch(1,pagesize-1)[0]
            wq = baseq.filter("__key__ >",lastkey)


0 commentaires

1
votes

J'accepte la réponse de Steve, il n'existe pas de telle façon de récupérer n rangées aléatoires dans une requête.

Cependant, même la méthode de récupération d'une seule entité ne fonctionne généralement pas de telle sorte que la marchandabilité des résultats renvoyées soit uniformément distribuée. La probabilité de retourner une entité donnée dépend de l'écart de son numéro assigné de manière aléatoire et du prochain nombre aléatoire supérieur. Par exemple. Si des nombres aléatoires 1,2 et 10 ont été attribués (et aucun des chiffres 3-9), l'algorithme retournera "2" 8 fois plus souvent que "1".

J'ai réparé cela de manière légèrement plus d'expension. Si quelqu'un est intéressé, je suis heureux de partager


0 commentaires

-1
votes

Si je comprends correctement, vous devez récupérer N instance aléatoire.

C'est facile. Il suffit de faire une requête avec seulement des clés. Et faire aléatoire.choice n fois sur la liste des touches. Ensuite, obtenez des résultats en récupérant les touches. xxx


2 commentaires

Et si vous avez une million d'entités dans votre magasin de données? Chargez toutes les clés du magasin de données - semble mauvaise ...


@aloo Si vous avez tellement d'instances, vous pouvez garder le nombre total total d'entre eux dans DataStore et MemCache, alors vous venez de faire aléatoire.choice sur la plage de chiffres entre 0 et le nombre total. Et après avoir simplement itération des clés avec index, que vous avez généré. Ou simplement utiliser la limite et le décalage.