Reputation: 1481
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
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