রেল মডেলের ক্ষেত্রে সংবেদনশীল অনুসন্ধান


211

আমার পণ্যের মডেলটিতে কিছু আইটেম রয়েছে

 Product.first
 => #<Product id: 10, name: "Blue jeans" >

আমি এখন অন্য ডেটাसेट থেকে কিছু পণ্য প্যারামিটার আমদানি করছি, তবে নামের বানানটিতে অসঙ্গতি রয়েছে। উদাহরণস্বরূপ, অন্যান্য ডেটাসেটে, Blue jeansবানান করা যেতে পারে Blue Jeans

আমি চেয়েছিলাম Product.find_or_create_by_name("Blue Jeans"), তবে এটি একটি নতুন পণ্য তৈরি করবে, প্রায় প্রথমটির মতোই ical আমি যদি নিম্নের নামটি খুঁজতে এবং তুলনা করতে চাই তবে আমার বিকল্পগুলি কী।

পারফরম্যান্সের সমস্যাগুলি এখানে এখানে গুরুত্বপূর্ণ নয়: কেবলমাত্র 100-200 পণ্য রয়েছে এবং আমি এটি একটি মাইগ্রেশন হিসাবে চালাতে চাই যা ডেটা আমদানি করে।

কোন ধারনা?

উত্তর:


368

আপনাকে সম্ভবত এখানে আরও ভারবস হতে হবে

name = "Blue Jeans"
model = Product.where('lower(name) = ?', name.downcase).first 
model ||= Product.create(:name => name)

5
@ বটবটের মন্তব্য ব্যবহারকারী ইনপুট থেকে স্ট্রিংগুলিতে প্রযোজ্য নয়। "# $$" হ'ল রুবি স্ট্রিং ইন্টারপোলেশন সহ গ্লোবাল ভেরিয়েবলগুলি পালানোর জন্য একটি স্বল্প পরিচিত শর্টকাট। এটি "# {$$}" এর সমতুল্য। তবে স্ট্রিং ইন্টারপোলেশন ব্যবহারকারী-ইনপুট স্ট্রিংগুলিতে ঘটে না। পার্থক্যটি দেখতে ইরবগুলিতে এগুলি ব্যবহার করে দেখুন: "$##"এবং '$##'। প্রথমটি ইন্টারপোল্টেড (ডাবল-কোট)। দ্বিতীয়টি হয় না। ব্যবহারকারীর ইনপুট কখনই বিরক্ত হয় না।
ব্রায়ান মোরের্তি

5
কেবল find(:first)তা অবহিত করা হয়েছে তা লক্ষ করুন এবং এখন বিকল্পটি ব্যবহার করা #first। এইভাবে,Product.first(conditions: [ "lower(name) = ?", name.downcase ])
লুস রামালহো

2
আপনার এই সমস্ত কাজ করার দরকার নেই। ব্যবহার করুন বিল্ট ইন Arel লাইব্রেরি বা Squeel
Dogweather

17
4 রেলগুলিতে আপনি এখন করতে পারেনmodel = Product.where('lower(name) = ?', name.downcase).first_or_create
ডেরেক লুকাস

1
@ ডেরেকলুস যদিও রেল ৪ এ এটি করা সম্ভব, এই পদ্ধতিটি একটি অপ্রত্যাশিত আচরণের কারণ হতে পারে। ধরুন আমরা আছে after_createমধ্যে কলব্যাক Productমডেল এবং কলব্যাক ভিতরে, আমরা আছে whereদফা যেমন, products = Product.where(country: 'us')। এই ক্ষেত্রে, whereকলজগুলি সুযোগের প্রেক্ষাপটে সম্পাদনা করার সাথে সাথে ক্লজগুলি বেঁধে রাখা হয়েছে। শুধু এফওয়াইআই।
এলকুইমিস্ট

100

এটি আমার নিজস্ব রেফারেন্সের জন্য রেলগুলিতে একটি সম্পূর্ণ সেটআপ। এটি আপনাকে যদি সহায়তা করে তবে আমি খুশি।

ক্যোয়ারী:

Product.where("lower(name) = ?", name.downcase).first

বৈধতা প্রদানকারী:

validates :name, presence: true, uniqueness: {case_sensitive: false}

সূচি ( রেলস / অ্যাক্টিভেকর্ডে কেস-সংবেদনশীল অনন্য সূচকের উত্তর ? ):

execute "CREATE UNIQUE INDEX index_products_on_lower_name ON products USING btree (lower(name));"

আমি চাই যে প্রথম এবং শেষটি করার আরও সুন্দর উপায় ছিল তবে তারপরে আবারও, রেলস এবং অ্যাক্টিভেকর্ডটি ওপেন সোর্স, আমাদের অভিযোগ করা উচিত নয় - আমরা এটি নিজেরাই প্রয়োগ করতে পারি এবং পুল অনুরোধ পাঠাতে পারি।


6
PostgreSQL এ কেস-সংবেদনশীল সূচক তৈরির জন্য কৃতিত্বের জন্য ধন্যবাদ। কীভাবে এটি রেলগুলিতে ব্যবহার করবেন তা দেখানোর জন্য আপনাকে ক্রেডিট! একটি অতিরিক্ত নোট: আপনি যদি কোনও স্ট্যান্ডার্ড সন্ধানকারী ব্যবহার করেন, যেমন: find_by_name, তবে এটি এখনও একটি সঠিক ম্যাচ করে। উপরের আপনার "ক্যোয়ারী" লাইনের অনুরূপ আপনাকে কাস্টম অনুসন্ধানকারী লিখতে হবে, যদি আপনি চান যে আপনার অনুসন্ধান কেস-সংবেদনশীল হোক।
মার্ক বেরি

find(:first, ...)এখন অবহেলিত বিবেচনা করে , আমি মনে করি এটি সবচেয়ে সঠিক উত্তর।
ব্যবহারকারী

নাম.ডাউনস দরকার? এটির সাথে কাজ করছে বলে মনে হচ্ছেProduct.where("lower(name) = ?", name).first
জর্ডান

1
@ জোর্ডান আপনি কি নামের চেষ্টা করেছেন যে বড় বড় অক্ষর রয়েছে?
ওমা

1
@ জর্দান, সম্ভবত খুব গুরুত্বপূর্ণ নয়, তবে আমরা অন্যদের সাহায্য করার কারণে এসও সম্পর্কে যথাযথতার জন্য প্রচেষ্টা করা উচিত :)
ওমা

28

যদি আপনি পোস্তেগ্রেস এবং রেলস 4+ ব্যবহার করেন তবে আপনার কাছে কলাম টাইপ সিআইটিএসএক্স্ট ব্যবহার করার বিকল্প রয়েছে যা কোয়েরি যুক্তিটি না লিখে ক্ষেত্রে সংবেদনশীল প্রশ্নের সমাধান করবে।

স্থানান্তর:

def change
  enable_extension :citext
  change_column :products, :name, :citext
  add_index :products, :name, unique: true # If you want to index the product names
end

এবং এটি পরীক্ষা করার জন্য আপনার নিম্নলিখিতটি আশা করা উচিত:

Product.create! name: 'jOgGers'
=> #<Product id: 1, name: "jOgGers">

Product.find_by(name: 'joggers')
=> #<Product id: 1, name: "jOgGers">

Product.find_by(name: 'JOGGERS')
=> #<Product id: 1, name: "jOgGers">

21

আপনি নিম্নলিখিত ব্যবহার করতে পারেন:

validates_uniqueness_of :name, :case_sensitive => false

দয়া করে নোট করুন যে ডিফল্টরূপে সেটিংসটি হ'ল কেস_সেন্সিটিভ => মিথ্যা, সুতরাং যদি আপনি অন্য উপায় পরিবর্তন না করে থাকেন তবে আপনাকে এই বিকল্পটি লেখার দরকার নেই।

এখানে আরও সন্ধান করুন: http://api.rubyonrails.org/class/AtivetiveRecord/Validations/ClassMethods.html# মেমোডি- আই- লায়েলিটি_ ইউনিকনেস_


5
আমার অভিজ্ঞতার সাথে ডকুমেন্টেশনের বিপরীতে কেস_সেটিসিটি ডিফল্টরূপে সত্য। আমি পোস্টগ্র্যাস্কল এ আচরণটি দেখেছি এবং অন্যরা মাইএসকিএলে একই প্রতিবেদন করেছে।
ট্রয়

1
সুতরাং পোস্টগ্রিজ দিয়ে আমি এটি চেষ্টা করছি, এবং এটি কার্যকর হয় না। সন্ধান_বি_এক্স নির্বিশেষে কেস সংবেদনশীল ...
লুই

এই বৈধতাটি কেবলমাত্র মডেলটি তৈরি করার সময়। সুতরাং আপনার যদি আপনার ডাটাবেসে 'এইচএএমএল' থাকে এবং আপনি 'এইচএমএল' যুক্ত করার চেষ্টা করেন তবে এটি বৈধতা পাস করবে না।
ডুডো

14

পোস্টগ্রিসে:

 user = User.find(:first, :conditions => ['username ~* ?', "regedarek"])

1
হেরোকুতে রেলস, তাই পোস্টগ্রিস ব্যবহার করে… ILIKE উজ্জ্বল। ধন্যবাদ!
FeifanZ

অবশ্যই পোস্টগ্রিজ এসকিউএল-এ IliKE ব্যবহার করছে।
ডোম

12

বেশ কয়েকটি মন্তব্য উদাহরণ সরবরাহ না করেই আরেলকে উল্লেখ করে।

কেস-সংবেদনশীল অনুসন্ধানের একটি আরেল উদাহরণ এখানে:

Product.where(Product.arel_table[:name].matches('Blue Jeans'))

এই ধরণের সমাধানের সুবিধাটি হ'ল এটি ডাটাবেস-অজোনস্টিক - এটি আপনার বর্তমান অ্যাডাপ্টারের জন্য সঠিক এসকিউএল কমান্ডগুলি ব্যবহার matchesকরবে ( ILIKEপোস্টগ্রিসের LIKEজন্য এবং অন্য কিছুর জন্য ব্যবহার করবে )।


9

এসকিউএল ডকুমেন্টেশন থেকে উদ্ধৃতি :

অন্য কোনও অক্ষর নিজের বা তার নিম্ন / উচ্চতর ক্ষেত্রে সমতুল্য (যেমন কেস-সংবেদনশীল মিল) মিলে যায়

... যা আমি জানতাম না ut তবে এটি কাজ করে:

sqlite> create table products (name string);
sqlite> insert into products values ("Blue jeans");
sqlite> select * from products where name = 'Blue Jeans';
sqlite> select * from products where name like 'Blue Jeans';
Blue jeans

সুতরাং আপনি এই মত কিছু করতে পারে:

name = 'Blue jeans'
if prod = Product.find(:conditions => ['name LIKE ?', name])
    # update product or whatever
else
    prod = Product.create(:name => name)
end

না #find_or_create, আমি জানি, এবং এটি খুব ক্রস-ডাটাবেস বন্ধুত্বপূর্ণ নাও হতে পারে, তবে দেখার মতো?


1
যেমনটি মাইএসকিএলে ক্ষেত্রে সংবেদনশীল তবে পোস্টগ্রিস্কিএলে নয়। আমি ওরাকল বা ডিবি 2 সম্পর্কে নিশ্চিত নই। মূল কথাটি, আপনি এটির উপর নির্ভর করতে পারবেন না এবং আপনি যদি এটি ব্যবহার করেন এবং আপনার বস যদি আপনার অন্তর্নিহিত ডিবি পরিবর্তন করে তবে আপনি কোনও স্পষ্ট কারণ ছাড়াই "নিখোঁজ" রেকর্ডগুলি শুরু করবেন। @ নিউট্রিনোর নিম্ন (নাম) পরামর্শ সম্ভবত এটির সমাধানের সেরা উপায়।
মাসুকোমি

6

অ্যাক্টিভেকর্ড :: বেসে কেস সংবেদনশীল অনুসন্ধানকারীদের যুক্ত করা ছাড়াও অন্য কোনও পদ্ধতির উল্লেখ নেই। বিশদ এখানে পাওয়া যাবে । এই পদ্ধতির সুবিধাটি হ'ল আপনাকে প্রতিটি মডেলকে সংশোধন করতে হবে না এবং আপনার lower()সমস্ত ক্ষেত্রে সংবেদনশীল প্রশ্নের সাথে আপনাকে এই ধারাটি যুক্ত করতে হবে না, আপনি কেবল পরিবর্তে একটি আলাদা অনুসন্ধানকারী পদ্ধতি ব্যবহার করুন।


আপনার লিঙ্ক করা পৃষ্ঠাটি মারা গেলে আপনার উত্তরটিও দেয়।
অ্যান্টনি

@ অ্যান্থনি যেমন ভবিষ্যদ্বাণী করেছে, তেমনটি ঘটেছে। লিঙ্কটি মারা গেছে।
এক্সপি 84

3
@ এক্সপি 84 আমি জানি না এটি আর কতটা প্রাসঙ্গিক, তবে আমি লিঙ্কটি ঠিক করেছি।
অ্যালেক্স কোরবান

6

উচ্চ এবং নিম্নতর অক্ষরের অক্ষর কেবল একটি বিট দ্বারা পৃথক হয়। তাদের সন্ধান করার সর্বাধিক দক্ষ উপায় হ'ল এই বিটটিকে উপেক্ষা করা, নিম্ন বা উচ্চতর রূপান্তর না করা ইত্যাদি COLLATIONM এমএসএসকিউএল-এর কীওয়ার্ড দেখুন , ওরেकल NLS_SORT=BINARY_CIব্যবহার করছেন কিনা দেখুন see


4

Find_or_create এখন অবহিত করা হয়েছে, আপনার পরিবর্তে একটি এআর রিলেশন ব্যবহার করা উচিত প্লাস first_or_create এর মতো:

TombolaEntry.where("lower(name) = ?", self.name.downcase).first_or_create(name: self.name)

এটি প্রথম মিলিত বস্তুটি ফিরিয়ে দেবে, বা যদি কিছু না থাকে তবে এটি আপনার জন্য তৈরি করবে।


2

কেস-সংবেদনশীল অনুসন্ধানের সাথে বিলগুলি অন্তর্ভুক্ত রয়েছে ails এটি ডাটাবেস বাস্তবায়নে পার্থক্যের জন্য অ্যাকাউন্ট করে। হয় বিল্ট-ইন আরেল লাইব্রেরি, বা স্কেলের মতো মণি ব্যবহার করুন


2

এখানে প্রচুর দুর্দান্ত উত্তর রয়েছে, বিশেষত @ ওমা। তবে আর একটি জিনিস যা আপনি চেষ্টা করতে পারেন তা হ'ল কাস্টম কলাম সিরিয়ালাইজেশন ব্যবহার করা। আপনি যদি আপনার ডিবিতে ছোট হাতের সঞ্চিত সমস্ত কিছু মনে না করেন তবে আপনি তৈরি করতে পারেন:

# lib/serializers/downcasing_string_serializer.rb
module Serializers
  class DowncasingStringSerializer
    def self.load(value)
      value
    end

    def self.dump(value)
      value.downcase
    end
  end
end

তারপরে আপনার মডেলটিতে:

# app/models/my_model.rb
serialize :name, Serializers::DowncasingStringSerializer
validates_uniqueness_of :name, :case_sensitive => false

এই পদ্ধতির সুবিধাটি হ'ল আপনি এখনও find_or_create_byকাস্টম স্কোপ, ফাংশন এবং না ব্যবহার করে নিয়মিত সকল অনুসন্ধানকারী (সহ ) ব্যবহার করতে পারেনlower(name) = ? আপনার অনুসন্ধানগুলি ।

খারাপ দিকটি হ'ল আপনি ডাটাবেসে কেসিং তথ্য হারাবেন।


2

অ্যান্ড্রুজের অনুরূপ যা # 1:

আমার পক্ষে কাজ করা কিছু হ'ল:

name = "Blue Jeans"
Product.find_by("lower(name) = ?", name.downcase)

এটি একটি #whereএবং #firstএকই ক্যোয়ারিতে করার প্রয়োজনকে দূর করে । আশাকরি এটা সাহায্য করবে!


1

আপনি নীচে এর মতো স্কোপগুলি ব্যবহার করতে পারেন এবং এগুলিকে উদ্বেগের সাথে রাখতে পারেন এবং আপনার প্রয়োজন হতে পারে এমন মডেলগুলিতে অন্তর্ভুক্ত করতে পারেন:

scope :ci_find, lambda { |column, value| where("lower(#{column}) = ?", value.downcase).first }

তারপরে এটি ব্যবহার করুন: Model.ci_find('column', 'value')



0
user = Product.where(email: /^#{email}$/i).first

TypeError: Cannot visit Regexp
ডোরিয়ান

@ শিলভক ধন্যবাদ আমি ঠিক এটিই খুঁজছিলাম। এবং এটা গৃহীত উত্তর চেয়ে চেয়ে দেখলাম stackoverflow.com/a/2220595/1380867
MZaragoza

আমি এই সমাধানটি পছন্দ করি, তবে কীভাবে আপনি "রেজিপেক্সে যেতে পারবেন না" ত্রুটিটি পেরিয়ে গেলেন? আমিও তা দেখছি।
গেইল

0

কিছু লোক LIKE বা IliKE ব্যবহার করে দেখায়, তবে যারা রেজেক্স অনুসন্ধানের অনুমতি দেয়। এছাড়াও আপনার রুবিতে ডাউনসেস দরকার নেই। আপনি আপনার জন্য এটি ডাটাবেসটি করতে দিতে পারেন। আমি মনে করি এটি আরও দ্রুত হতে পারে। এছাড়াও first_or_createপর ব্যবহার করা যেতে পারে where

# app/models/product.rb
class Product < ActiveRecord::Base

  # case insensitive name
  def self.ci_name(text)
    where("lower(name) = lower(?)", text)
  end
end

# first_or_create can be used after a where clause
Product.ci_name("Blue Jeans").first_or_create
# Product Load (1.2ms)  SELECT  "products".* FROM "products"  WHERE (lower(name) = lower('Blue Jeans'))  ORDER BY "products"."id" ASC LIMIT 1
# => #<Product id: 1, name: "Blue jeans", created_at: "2016-03-27 01:41:45", updated_at: "2016-03-27 01:41:45"> 


-9

এতক্ষণ, আমি রুবি ব্যবহার করে একটি সমাধান করেছি। এটি পণ্য মডেলের ভিতরে রাখুন:

  #return first of matching products (id only to minimize memory consumption)
  def self.custom_find_by_name(product_name)
    @@product_names ||= Product.all(:select=>'id, name')
    @@product_names.select{|p| p.name.downcase == product_name.downcase}.first
  end

  #remember a way to flush finder cache in case you run this from console
  def self.flush_custom_finder_cache!
    @@product_names = nil
  end

এটি আমাকে প্রথম পণ্যটি দেবে যেখানে নাম মেলে। বা শূন্য।

>> Product.create(:name => "Blue jeans")
=> #<Product id: 303, name: "Blue jeans">

>> Product.custom_find_by_name("Blue Jeans")
=> nil

>> Product.flush_custom_finder_cache!
=> nil

>> Product.custom_find_by_name("Blue Jeans")
=> #<Product id: 303, name: "Blue jeans">
>>
>> #SUCCESS! I found you :)

2
এটি বৃহত্তর ডেটা সেটের জন্য অত্যন্ত অদক্ষ, কারণ এটি পুরো জিনিসটিকে মেমোরিতে লোড করতে হয়। যদিও আপনার কয়েকশত এন্ট্রি নিয়ে আপনার সমস্যা নেই, এটি ভাল অনুশীলন নয়।
lambshaanxy
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.