Mon modèle de produit contient des éléments
Product.first
=> #<Product id: 10, name: "Blue jeans" >
J'importe maintenant certains paramètres de produit d'un autre ensemble de données, mais il y a des incohérences dans l'orthographe des noms. Par exemple, dans l'autre jeu de données, Blue jeans
pourrait être orthographié Blue Jeans
.
Je le voulais Product.find_or_create_by_name("Blue Jeans")
, mais cela va créer un nouveau produit, presque identique au premier. Quelles sont mes options si je veux trouver et comparer le nom en minuscule.
Les problèmes de performances ne sont pas vraiment importants ici: il n'y a que 100 à 200 produits, et je veux exécuter cela comme une migration qui importe les données.
Des idées?
ruby-on-rails
activerecord
case-insensitive
Jesper Rønn-Jensen
la source
la source
"$##"
et'$##'
. Le premier est interpolé (guillemets doubles). Le second ne l'est pas. L'entrée utilisateur n'est jamais interpolée.find(:first)
c'est obsolète, et l'option est maintenant d'utiliser#first
. Ainsi,Product.first(conditions: [ "lower(name) = ?", name.downcase ])
model = Product.where('lower(name) = ?', name.downcase).first_or_create
after_create
rappel dans leProduct
modèle et à l'intérieur du rappel, nous avons unewhere
clause, par exempleproducts = Product.where(country: 'us')
. Dans ce cas, leswhere
clauses sont chaînées lorsque les rappels s'exécutent dans le contexte de la portée. Juste FYI.Ceci est une configuration complète dans Rails, pour ma propre référence. Je suis content si ça vous aide aussi.
la requête:
le validateur:
l'index (réponse de l' index unique insensible à la casse dans Rails / ActiveRecord? ):
Je souhaite qu'il y ait une plus belle façon de faire la première et la dernière, mais là encore, Rails et ActiveRecord sont open source, nous ne devrions pas nous plaindre - nous pouvons l'implémenter nous-mêmes et envoyer une demande de tirage.
la source
find(:first, ...)
c'est désormais obsolète, je pense que c'est la réponse la plus appropriée.Product.where("lower(name) = ?", name).first
Si vous utilisez Postegres et Rails 4+, vous avez la possibilité d'utiliser le type de colonne CITEXT, qui permettra des requêtes insensibles à la casse sans avoir à écrire la logique de requête.
La migration:
Et pour le tester, vous devez vous attendre à ce qui suit:
la source
Vous pouvez utiliser les éléments suivants:
Veuillez noter que par défaut, le paramètre est: case_sensitive => false, vous n'avez donc même pas besoin d'écrire cette option si vous n'avez pas changé d'autres façons.
Plus d'informations sur: http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMethods.html#method-i-validates_uniqueness_of
la source
En postgres:
la source
Plusieurs commentaires se réfèrent à Arel, sans donner d'exemple.
Voici un exemple Arel d'une recherche insensible à la casse:
L'avantage de ce type de solution est qu'elle est indépendante de la base de données - elle utilisera les commandes SQL correctes pour votre adaptateur actuel (
matches
utiliseraILIKE
pour Postgres etLIKE
pour tout le reste).la source
Citant de la documentation SQLite :
... que je ne connaissais pas, mais ça marche:
Vous pouvez donc faire quelque chose comme ceci:
Non
#find_or_create
, je sais, et ce n'est peut-être pas très convivial pour les bases de données, mais ça vaut le coup d'être examiné?la source
Une autre approche que personne n'a mentionnée consiste à ajouter des chercheurs insensibles à la casse dans ActiveRecord :: Base. Les détails peuvent être trouvés ici . L'avantage de cette approche est que vous n'avez pas à modifier chaque modèle et que vous n'avez pas à ajouter la
lower()
clause à toutes vos requêtes insensibles à la casse, vous utilisez simplement une méthode de recherche différente à la place.la source
Les lettres majuscules et minuscules ne diffèrent que par un seul bit. Le moyen le plus efficace de les rechercher est d'ignorer ce bit, de ne pas convertir en bas ou en haut, etc. Voir les mots-clés
COLLATION
pour MSSQL, voirNLS_SORT=BINARY_CI
si vous utilisez Oracle, etc.la source
Find_or_create est maintenant obsolète, vous devez utiliser une relation AR à la place plus first_or_create, comme ceci:
Cela renverra le premier objet correspondant ou en créera un pour vous s'il n'en existe aucun.
la source
La recherche insensible à la casse est intégrée à Rails. Il tient compte des différences dans les implémentations de bases de données. Utilisez soit la bibliothèque Arel intégrée, soit une gemme comme Squeel .
la source
Il y a beaucoup de bonnes réponses ici, en particulier chez @ oma. Mais une autre chose que vous pouvez essayer est d'utiliser la sérialisation de colonne personnalisée. Si cela ne vous dérange pas que tout soit stocké en minuscules dans votre base de données, vous pouvez créer:
Puis dans votre modèle:
L'avantage de cette approche est que vous pouvez toujours utiliser tous les moteurs de recherche standard (y compris
find_or_create_by
) sans utiliser d'étendues, de fonctions personnalisées oulower(name) = ?
dans vos requêtes.L'inconvénient est que vous perdez les informations de boîtier dans la base de données.
la source
Similaire à Andrews qui est n ° 1:
Quelque chose qui a fonctionné pour moi est:
Cela élimine le besoin de faire un
#where
et#first
dans la même requête. J'espère que cela t'aides!la source
Vous pouvez également utiliser des étendues comme celle-ci ci-dessous et les mettre en danger et les inclure dans les modèles dont vous pourriez avoir besoin:
scope :ci_find, lambda { |column, value| where("lower(#{column}) = ?", value.downcase).first }
Ensuite, utilisez comme ceci:
Model.ci_find('column', 'value')
la source
En supposant que vous utilisez mysql, vous pouvez utiliser des champs qui ne respectent pas la casse: http://dev.mysql.com/doc/refman/5.0/en/case-sensitivity.html
la source
la source
TypeError: Cannot visit Regexp
Certaines personnes montrent qu'elles utilisent LIKE ou ILIKE, mais celles-ci autorisent les recherches d'expression régulière. De plus, vous n'avez pas besoin de downcase en Ruby. Vous pouvez laisser la base de données le faire pour vous. Je pense que cela pourrait être plus rapide.
first_or_create
Peut également être utilisé aprèswhere
.la source
Une alternative peut être
la source
Jusqu'à présent, j'ai fait une solution en utilisant Ruby. Placez-le dans le modèle de produit:
Cela me donnera le premier produit où les noms correspondent. Ou rien.
la source