uno
uno

Reputation: 1481

Searching by multiple possible attributes for one search field

How can I have a search field that can search for multiple different attributess. For example, be able to search for an associated models attribute and the self attribute in one search field?

I want to allow the user to search by what they want to search by, as most websites have this feature...

I have:

Model:

  def self.user_search(search)
    joins(:shipments).where(shipments: { id: Shipment.where(shipping_label: ShippingLabel.where(tracking_number: "#{search.downcase}")) })
    joins(:shipping_address).where(shipping_addresses: { id: ShippingAddress.where(shipping_address_final: ShippingAddressFinal.where(address1: "#{search.downcase}")) })
  end

This doesn't work. each joins works on it's own, but together only the last one will ever work.

How can I combine these to work?

Overall, I will be implementing many more possible search attributes but starting with just these 2 will lead me in the correct way.

I have tried using ||

This only allows the first join to work.

Using:

def self.user_search(search)
  tracking_number = joins(:shipments).where(shipments: { id: Shipment.where(shipping_label: ShippingLabel.where(tracking_number: "#{search.downcase}")) })
  address1 = joins(:shipping_address).where(shipping_addresses: { id: ShippingAddress.where(shipping_address_final: ShippingAddressFinal.where(address1: "#{search.downcase}")) })
  tracking_number.or(address1)
end

gives an error upon search:

ArgumentError (Relation passed to #or must be structurally compatible. Incompatible values: [:joins]):

Upvotes: 0

Views: 234

Answers (1)

3limin4t0r
3limin4t0r

Reputation: 21130

Your or approach is a step in the right direction. The reason you get the error is because the structures are incompatible. Meaning that they should generate the same SQL FROM statement. Currently one query is joining shipments while the other is joining shipping_address.

# statement #1 (tracking_number)
FROM model INNER JOIN shipments ON ...

# statement #2 (address1)
FROM model INNER JOIN shipping_addresses ON ...

You should do the joins first. Both statements should have both joins to make them compatible.

scope = joins(:shipments).joins(:shipping_address)
tracking_number = scope.where(shipments: { id: Shipment.where(shipping_label: ShippingLabel.where(tracking_number: "#{search.downcase}")) })
address1 = scope.where(shipping_addresses: { id: ShippingAddress.where(shipping_address_final: ShippingAddressFinal.where(address1: "#{search.downcase}")) })
tracking_number.or(address1)

The only thing changed in the above statement is that both your queries now join both tables, making them compatible since they generate the same SQL FROM statement.

Resulting in:

# statement #1 (tracking_number)
FROM model 
  INNER JOIN shipments ON ...
  INNER JOIN shipping_addresses ON ...

# statement #2 (address1)
FROM model 
  INNER JOIN shipments ON ...
  INNER JOIN shipping_addresses ON ...

Upvotes: 1

Related Questions