2017-10-02 2 views
1

Après avoir creusé google et SO pendant une semaine, j'ai fini par poser la question ici. Supposons qu'il y ait deux collections,mongodb vérifier regex sur les champs d'une collection à tous les champs de l'autre collection

UsersCollection:

[ 
{... 
    name:"James" 
    userregex: "a|regex|str|here" 
}, 
{... 
    name:"James" 
    userregex: "another|regex|string|there" 
}, 
... 
] 

PostCollection:

[ 
{... 
    title:"a string here ..." 
}, 
{... 
    title: "another string here ..." 
}, 
... 
] 

J'ai besoin de tous les utilisateurs dont userregex correspond à tout post.title (Besoin user_id, des groupes post_id ou quelque chose de similaire) . Ce que j'ai essayé jusqu'ici:
1. Obtenez tous les utilisateurs dans la collection, exécutez regex sur tous les produits, fonctionne mais trop sale! il faudra exécuter une requête pour chaque utilisateur 2. Comme ci-dessus, mais en utilisant une requête foreach dans Mongo, c'est la même chose que ci-dessus mais seulement la couche de base de données à la place de la couche d'application

J'ai cherché beaucoup de méthodes disponibles telles que comme agrégations, au près, etc. sans chance.
Alors, est-ce possible de le faire dans Mongo? Devrais-je changer mon type de base de données? Si oui quel type serait bon? la performance est ma première priorité. Merci

+0

Depuis cela semble être quelque chose que vous n'avez pas besoin de faire pour un utilisateur spécifique, peut-être faire le coût e calcul (que vous ne pouvez pas contourner de toute façon) et cache le résultat en mémoire. Il semble que cela pourrait être un problème AB. –

+0

@ IngoBürk Je ne peux pas mettre en cache le résultat, les nouvelles données viennent dans PostCollection et UsersCollection est également mis à jour. Peut-être utiliser des drapeaux pour travailler sur de nouveaux documents modifiés mais c'est trop sale –

+0

L'ajout de messages ne changera pas le résultat mis en cache. Seulement en supprimant les, et ce que vous pourriez expliquer en mémorisant quel poste a été apparié pour un utilisateur (si c'est le supprimé, recalculer pour les utilisateurs concernés). Et en ajoutant des utilisateurs, vous n'avez qu'à calculer les informations pour un seul utilisateur et l'ajouter au cache. –

Répondre

1

Il n'est pas possible de référencer le champ regex stocké dans le document dans l'opérateur regex dans l'expression de correspondance. Par conséquent, il ne peut pas être fait du côté mongo avec la structure actuelle.

$lookup fonctionne bien avec les conditions d'égalité. Donc une alternative (similaire à ce que Nic a suggéré) serait de mettre à jour votre collection de post pour inclure un champ supplémentaire appelé keywords (tableau de valeurs de mots-clés, il peut être recherché sur) pour chaque titre.

db.users.aggregate([ 
    {$lookup: { 
      from: "posts", 
      localField: "userregex", 
      foreignField: "keywords", 
      as: "posts" 
     } 
    } 
]) 

La requête ci-dessus fera quelque chose comme ça (fonctionne à partir de 3.4).

keywords: { $in: [ userregex.elem1, userregex.elem2, ... ] }. 

De la documentation

Si le champ contient un tableau, puis $ dans l'opérateur sélectionne les documents dont le champ contient un tableau qui contient au moins un élément qui correspond à une valeur dans la tableau spécifié (par exemple, , etc.)

Il ressemble à des versions antérieures (testé sur 3.2) ne correspondra si le tableau ont même ordre, les valeurs et la longueur de arra ys est la même chose.

Exemple d'entrée:

utilisateurs

db.users.insertMany([ 
    { 
    "name": "James", 
    "userregex": [ 
     "another", 
     "here" 
    ] 
    }, 
    { 
    "name": "John", 
    "userregex": [ 
     "another", 
     "string" 
    ] 
    } 
]) 

Messages

db.posts.insertMany([ 
    { 
    "title": "a string here", 
    "keyword": [ 
     "here" 
    ] 
    }, 
    { 
    "title": "another string here", 
    "keywords": [ 
     "another", 
     "here" 
    ] 
    }, 
    { 
    "title": "one string here", 
    "keywords": [ 
     "string" 
    ] 
    } 
]) 

Exemple de sortie:

[ 
    { 
    "name": "James", 
    "userregex": [ 
     "another", 
     "here" 
    ], 
    "posts": [ 
     { 
     "title": "another string here", 
     "keywords": [ 
      "another", 
      "here" 
     ] 
     }, 
     { 
     "title": "a string here", 
     "keywords": [ 
      "here" 
     ] 
     } 
    ] 
    }, 
    { 
    "name": "John", 
    "userregex": [ 
     "another", 
     "string" 
    ], 
    "posts": [ 
     { 
     "title": "another string here", 
     "keywords": [ 
      "another", 
      "here" 
     ] 
     }, 
     { 
     "title": "one string here", 
     "keywords": [ 
      "string" 
     ] 
     } 
    ] 
    } 
] 
1

MongoDB est bon pour votre cas d'utilisation, mais vous devez utiliser une approche différente de celle en cours. Puisque vous n'êtes concerné que par un titre correspondant à un message, vous pouvez stocker les derniers résultats d'une telle correspondance. Ci-dessous un exemple de code

db.users.find({last_post_id: {$exists: 0}}).forEach(
    function(row) { 
     var regex = new RegExp(row['userregex']); 
     var found = db.post_collection.findOne({title: regex}); 
     if (found) { 
      post_id = found["post_id"]; 
      db.users.updateOne({ 
       user_id: row["user_id"] 
       }, { 
        $set :{ last_post_id: post_id} 
        }); 
     } 
    } 
) 

Ce qu'il fait est que les utilisateurs ne filtre que qui n'ont pas last_post_id ensemble, recherche des dossiers de poste pour cela et définit si un enregistrement se trouve le last_post_id. Donc, après l'exécution, vous pouvez retourner les résultats comme

db.users.find({last_post_id: {$exists: 1}}, {user_id:1, last_post_id:1, _id:0}) 

La seule chose que vous devez être préoccupé est de modifier/supprimer un poste existant. Donc, après chaque modification/suppression, vous devriez simplement exécuter ci-dessous, de sorte que toutes les correspondances pour cet id de poste soient réexécutées.

post_id_changed = 1 
db.users.updateMany({last_post_id: post_id_changed}, {$unset: {last_post_id: 1}}) 

Ceci garantira que la prochaine fois que vous exécuterez la mise à jour, ces utilisateurs seront à nouveau traités. L'approche a un inconvénient que pour chaque utilisateur sans titre correspondant, la requête pour de tels utilisateurs serait exécutée encore et encore. Bien que vous pouvez contourner ce en utilisant des horodateurs ou post compte des contrôles

Aussi, vous devriez faire pour vous de mettre l'index sur post_collection.title

1

Je pensais que si vous pré-tokenisé vos titres de poste comme celui-ci:

{ 
    "_id": ... 
    "title": "Another string there", 
    "keywords": [ 
    "another", 
    "string", 
    "there" 
    ] 
} 

mais malheureusement $lookup exige que foreignField est un seul élément, donc mon idée de quelque chose comme cela pas travail :(mais peut-être vous donner une autre idée?

db.Post.aggregate([ 
    {$lookup: { 
      from: "Users", 
      localField: "keywords", 
      foreignField: "keywords", 
      as: "users" 
     } 
    }, 
]))