2010-07-27 7 views
9

Objectif: Utilisation d'une tâche CRON (ou d'un autre événement planifié) pour mettre à jour la base de données avec l'exportation nocturne des données d'un système existant.Comment écrire une tâche Rake pour importer des données dans l'application Rails?

Toutes les données sont créées/mises à jour/supprimées dans un système existant. Le site Web ne s'intègre pas directement à ce système, l'application rails doit simplement refléter les mises à jour apparaissant dans l'exportation de données.

Je dispose d'un fichier .txt de ~ 5000 produits qui ressemble à ceci:

"1234":"product name":"attr 1":"attr 2":"ABC Manufacturing":"2222" 
"A134":"another product":"attr 1":"attr 2":"Foobar World":"2447" 
... 

Toutes les valeurs sont des chaînes entre guillemets (") qui sont séparés par des virgules (:)

Les champs sont :

  • id: identifiant unique; alphanumérique
  • name: nom du produit; tout caractère
  • colonnes d'attributs: chaînes; n'importe quel caractère (par exemple, taille, poids, couleur, dimension)
  • vendor_name: chaîne; n'importe quel caractère
  • vendor_id: ID fournisseur unique; Numérique

Les informations du fournisseur ne sont pas normalisées dans le système actuel.

Quelles sont les meilleures pratiques ici? Est-il acceptable de supprimer les tables des produits et des vendeurs et de les réécrire avec les nouvelles données à chaque cycle? Ou est-il préférable de n'ajouter que de nouvelles lignes et de mettre à jour celles qui existent déjà?

Notes:

  1. Ces données seront utilisées pour générer Orders qui persistera dans l'importation de base de données de nuit. OrderItems devra être connecté aux ID de produit spécifiés dans le fichier de données, de sorte que nous ne pouvons pas compter sur une clé primaire auto-incrémentée pour être la même pour chaque importation; l'identifiant alphanumérique unique devra être utilisé pour joindre products à order_items.
  2. Idéalement, je voudrais l'importateur de normaliser les données des fournisseurs
  3. Je ne peux pas utiliser des instructions SQL à la vanille, donc je suppose que je vais devoir écrire une tâche rake afin d'utiliser Product.create(...) et Vendor.create(...) syntaxe de style.
  4. Ce sera mis en œuvre sur EngineYard

Répondre

14

Je ne supprime pas les produits et les fournisseurs de tables à chaque cycle. Est-ce une application rails? Si c'est le cas, il y a de très jolis assistants ActiveRecord qui vous seraient utiles.

Si vous avez un modèle d'enregistrement actif de produit, vous pouvez faire:

p = Product.find_or_initialize_by_identifier(<id you get from file>) 
p.name = <name from file> 
p.size = <size from file> 
etc... 
p.save! 

Le find_or_initialize va rechercher le produit dans la base de données par l'ID que vous spécifiez, et si elle ne peut pas le trouver, il en créer un nouveau.La manière très pratique de le faire de cette façon est qu'ActiveRecord enregistre uniquement dans la base de données si l'une des données a changé, et met automatiquement à jour les champs d'horodatage que vous avez dans la table (updated_at) en conséquence. Encore une chose, puisque vous rechercheriez des enregistrements par l'identifiant (id du fichier), je m'assurerais d'ajouter un index sur ce champ dans la base de données. Pour faire une tâche de rake pour accomplir ceci, j'ajouterais un rake dans le répertoire lib/tasks de votre application rails. Nous l'appellerons data.rake.

intérieur data.rake, il ressemblerait à quelque chose comme ceci:

namespace :data do 
    desc "import data from files to database" 
    task :import => :environment do 
    file = File.open(<file to import>) 
    file.each do |line| 
     attrs = line.split(":") 
     p = Product.find_or_initialize_by_identifier(attrs[0]) 
     p.name = attrs[1] 
     etc... 
     p.save! 
    end 
    end 
end 

que d'appeler la tâche de coupe, utilisez "données de rake: import" de la ligne de commande.

+0

J'ai essayé, mais je reçois l'erreur 'variable locale non définie ou méthode 'données' pour principal: Object'. Des pensées pour lesquelles cela pourrait se produire? – Nick

+0

Le problème était que 'namespace data do' devait être changé en' namespace: data do'. – Nick

0

Puisque les produits ne changent pas vraiment souvent, la meilleure façon de voir est de mettre à jour uniquement les enregistrements qui changent.

  1. Obtenez tous les deltas
  2. mise à jour de masse en utilisant une seule instruction SQL

Si vous avez votre code de normalisation dans les modèles, vous pouvez utiliser Product.create et Vendor.create ou bien elle serait juste une exagération. En outre, regardez dans l'insertion de plusieurs enregistrements dans une seule transaction SQL, c'est beaucoup plus rapide.

+0

comme indiqué dans ma question, je __cannot__ utiliser une déclaration SQL vanille. –

0
  • Créer une tâche de râteau qui est importateur cronned
  • la ligne de Parse fichier en ligne en utilisant CSV ou plus rapide via le rubis de vanille comme:

file.each do | ligne | products_array = line.split (":") fin

  • Fendez chaque ligne sur le ":" et pousser dans un hachage
  • Utilisez un find_or_initialize pour peupler votre base de données telles que:

    Product.find_or_initialize_by_name_and_vendor_id ("foo", 111)

+0

Pourquoi utilisez-vous 'find_or_initialize_by_name_and_vendor_id'? Cela suggère-t-il le produit 'accepts_nested_attributes_for: vendor'? –