2014-07-21 4 views
3

J'ai une application de ruby ​​on rails en utilisant Mongoid et MongoDB v2.4.6.Requête Mongo Documents incorporés avec une taille

J'ai la structure MongoDB suivante, un record qui embeds_many fragments:

{ 
    "_id" : "76561198045636214", 
    "fragments" : [ 
    { 
     "id" : 76561198045636215, 
     "source_id" : "source1" 
    }, 
    { 
     "id" : 76561198045636216, 
     "source_id" : "source2" 
    }, 
    { 
     "id" : 76561198045636217, 
     "source_id" : "source2" 
    } 
    ] 
} 

Je suis en train de trouver tous les enregistrements de la base de données qui contiennent des fragments avec source_ids en double. Je suis assez sûr que j'ai besoin d'utiliser $ elemMatch car j'ai besoin d'interroger des documents incorporés.

J'ai essayé

Record.elem_match(fragments: {source_id: 'source2'}) 

qui fonctionne, mais ne limite pas les doublons.

J'ai ensuite essayé

Record.elem_match(fragments: {source_id: 'source2', :source_id.with_size => 2}) 

qui ne retourne aucun résultat (mais est une requête valide). La requête Mongoid produit est:

selector: {"fragments"=>{"$elemMatch"=>{:source_id=>"source2", "source_id"=>{"$size"=>2}}}} 

Une fois cela fonctionne, je dois le mettre à jour pour que la taille soit> 1.

Est-ce possible? J'ai l'impression d'être très proche. Ceci est une opération de nettoyage unique, donc la performance de la requête n'est pas trop un problème (cependant nous avons des millions d'enregistrements à mettre à jour!)

Toute aide est très appréciée!

J'ai été en mesure d'atteindre le résultat souhaité, mais lors des tests, c'est beaucoup trop lent (cela prendra plusieurs semaines à travers notre système de production). Le problème est double requête par enregistrement (nous avons ~ 30 millions d'enregistrements en production).

Record.where('fragments.source_id' => 'source2').each do |record| 
    query = record.fragments.where(source_id: 'source2') 
    if query.count > 1 
    # contains duplicates, delete all but latest 
    query.desc(:updated_at).skip(1).delete_all 
    end 
    # needed to trigger after_save filters 
    record.save! 
end 

Répondre

1

Le problème avec l'approche actuelle ici est que les formulaires de requête standard MongoDB ne « filtre » pas réellement des documents les tableaux imbriqués de quelque façon. C'est essentiellement ce dont vous avez besoin pour "trouver les doublons" dans vos documents ici. Pour cela, MongoDB fournit le cadre d'agrégation comme étant probablement la meilleure approche pour trouver ceci. Il n'y a pas d'approche directe de type "mongoïde" pour les requêtes car celles-ci sont orientées vers le style "rails" existant pour traiter les documents relationnels.

Vous pouvez accéder au « cyclomoteur » forme que par l'accesseur .collection sur votre modèle de classe:

Record.collection.aggregate([ 

    # Find arrays two elements or more as possibles 
    { "$match" => { 
     "$and" => [ 
      { "fragments" => { "$not" => { "$size" => 0 } } }, 
      { "fragments" => { "$not" => { "$size" => 1 } } } 
     ] 
    }}, 

    # Unwind the arrays to "de-normalize" as documents 
    { "$unwind" => "$fragments" }, 

    # Group back and get counts of the "key" values 
    { "$group" => { 
     "_id" => { "_id" => "$_id", "source_id" => "$fragments.source_id" }, 
     "fragments" => { "$push" => "$fragments.id" }, 
     "count" => { "$sum" => 1 } 
    }}, 

    # Match the keys found more than once 
    { "$match" => { "count" => { "$gte" => 2 } } } 
]) 

qui vous retourner des résultats comme celui-ci:

{ 
    "_id" : { "_id": "76561198045636214", "source_id": "source2" }, 
    "fragments": ["76561198045636216","76561198045636217"], 
    "count": 2 
} 

Ce au moins vous donne quelque chose travailler avec sur la façon de traiter les "doublons" ici

+0

Wow chouette travail Neil, je ne l'aurais pas fait tout seul! Merci travaille avec brio;) – daveharris

Questions connexes