如何解决搜索多个模型 searchkick
预期结果:
我想要两件事:
- 在我的聚合链接列表中,我希望能够添加物业特征(例如,带阳台的物业)、物业类型(例如,别墅、公寓、单间公寓等)和物业状况(例如,新的、新装修、在建等)。
- 在我的搜索表单中,我想要一个选择框/单选按钮,用于特征、属性类型和条件,它 (1) 显示数据库中可用项目的集合,以及 (2) 当我选择一个项目并搜索时具有该特征/属性类型/条件的所有属性。
实际结果:
选择属性类型(如villa),无论属性类型如何,我都得到了所有属性。条件和特征相同。
使用 searchkick gem 的聚合功能,我也无法显示属性类型、条件或特征的聚合。
在 searchkick 的文档中,他们谈到它能够搜索多个模型,但是当我在控制器中尝试时,我得到的是错误:
配置
我有一个 rails 应用程序 (v6.1.0),它有一个 Property
(属于 property_type、condition 并且有很多特征到characteristic_properties)、PropertyType
(has_many 属性)、Condition
(has_many properties) 和 Characteristic
(has_many properties thrucharacteristic_properties) 模型,我已经通过 searchkick gem 实现了弹性搜索。
从那时起,我在属性属性的 seachkick 聚合的帮助下添加了内容,并添加了一个 Google 地图,该地图显示了属性在地图上的位置。最后,我用 Pagy gem 添加了分页。考虑到这一点,这就是 #index
在 properties_controller.rb
def index
args = {}
args[:surface_plot] = {}
args[:surface_plot][:gte] = params[:surface_plot_min] if params[:surface_plot_min].present?
args[:surface_plot][:lte] = params[:surface_plot_max] if params[:surface_plot_max].present?
args[:q_bathrooms] = {}
args[:q_bathrooms][:gte] = params[:q_bathrooms_min] if params[:q_bathrooms_min].present?
args[:q_bathrooms][:lte] = params[:q_bathrooms_max] if params[:q_bathrooms_max].present?
args[:q_bedrooms] = {}
args[:q_bedrooms][:gte] = params[:q_bedrooms_min] if params[:q_bedrooms_min].present?
args[:q_bedrooms][:lte] = params[:q_bedrooms_max] if params[:q_bedrooms_max].present?
args[:q_bedrooms] = params[:q_bedrooms] if params[:q_bedrooms].present?
args[:region] = params[:region] if params[:region].present?
args[:characteristics] = params[:characteristics] if params[:characteristics].present?
args[:buy] = params[:buy] if params[:buy].present?
args[:rent] = params[:rent] if params[:rent].present?
args[:share] = params[:share] if params[:share].present?
args[:floor] = {}
args[:floor][:gte] = params[:floor_min] if params[:floor_min].present?
args[:floor][:lte] = params[:floor_max] if params[:floor_max].present?
args[:floor] = params[:floor] if params[:floor].present?
args[:surface_interior] = {}
args[:surface_interior][:gte] = params[:surface_interior_min] if params[:surface_interior_min].present?
args[:surface_interior][:lte] = params[:surface_interior_max] if params[:surface_interior_max].present?
args[:sell_price] = {}
args[:sell_price][:gte] = params[:sell_price_from] if params[:sell_price_from].present?
args[:sell_price][:lte] = params[:sell_price_to] if params[:sell_price_to].present?
args[:sell_price][:gte] = params[:sell_price_min] if params[:sell_price_min].present?
args[:sell_price][:lte] = params[:sell_price_max] if params[:sell_price_max].present?
price_ranges = [{ to: 150000 },{from: 150000,to: 300000},{from: 300000,to: 500000},{from: 500000,to: 1000000},{from: 1000000}]
@properties = if params[:l]
sw_lat,sw_lng,ne_lat,ne_lng = params[:l].split(",")
Property.search("*",page: params[:page],per_page: 9,where: {
location: { top_left: {
lat: ne_lat,lon: sw_lng
},bottom_right: {
lat: sw_lat,lon: ne_lng
}
}
},aggs: {
region: {},buy: {},rent: {},share: {},sell_price: { ranges: price_ranges },q_bedrooms: {},surface_interior: {},floor: {},characteristics: {},surface_plot: {},q_bathrooms: {}
})
elsif params[:near]
# Property.near(params[:near]).page(params[:page]).per(9)
location = Geocoder.search(params[:near]).first
Property.search "*",where: {
location: {
near: {
lat: location.latitude,lon: location.longitude
},within: "10mi"
}
},aggs: {
region: {},q_bathrooms: {}
}
else
# byebug
@query = params[:q].presence || "*"
@properties = Searchkick.search @query,models: [Property,PropertyType],where: args,aggs: {
region: {},q_bathrooms: {}
}
end
@pagy = Pagy.new_from_searchkick(@properties,link_extra: 'data-remote="true"')
end
这就是property.rb
的样子
class Property < ApplicationRecord
searchkick locations: [:location]
Pagy::SEARCHKICK
geocoded_by :address
after_validation :geocode,if: :address_changed?
has_many_attached :photos
belongs_to :property_type
belongs_to :condition
has_many :characteristic_properties
has_many :characteristics,through: :characteristic_properties
has_many :viewings,dependent: :destroy
has_many :users,through: :viewings
belongs_to :user
def address
[street,city,zip_code].compact.join(",")
end
def address_changed?
street_changed? || city_changed? || zip_code_changed?
end
def search_data
attributes.merge location: { lat: latitude,lon: longitude }
end
end
<%= form_with url: properties_path,method: :get do |f| %>
<!-- Price -->
<div data-controller="menu" class="relative inline-block text-left p-0.5">
<div data-action="click->menu#toggle">
<button type="button" class="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500" id="options-menu" aria-expanded="true" aria-haspopup="true">
Price
<!-- Heroicon name: solid/chevron-down -->
<svg class="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
</div>
<!--
Dropdown menu,show/hide based on menu state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
-->
<div data-menu-target="toggleable" class="hidden z-10 origin-top-left absolute left-0 mt-2 max-w-sm w-64 py-4 px-2 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
<div class="py-1" role="none">
<div class="flex flex-col">
<div>
<%= f.label :sell_price_min,"Price Range",class: "mx-1" %>
</div>
<div class="flex">
<%= f.select :sell_price_min,[["No Min",""],["100mil€","100000"],["130mil€","130000"],["150mil€","150000"],["200mil€","200000"],["250mil€","250000"],["300mil€","300000"],["350mil€","350000"],["400mil€","400000"],["450mil€","450000"],["500mil€","500000"],["550mil€","550000"],["600mil€","600000"],["650mil€","650000"],["700mil€","700000"],["750mil€","750000"],["800mil€","800000"],["850mil€","850000"],["900mil€","900000"],["1M€","1000000"],["1.25M€","1250000"],["1.5M€","1500000"],["1.75M€","1750000"],["2M€","2000000"],["2.25M€","2250000"],["2.5M€","2500000"],["2.75M€","2750000"],["3M€","3000000"],["3.5M€","3500000"],["4M€","4000000"],["5M€","5000000"],["10M€","10000000"],["20M€","20000000"]],{},{ class: "mx-1 mt-1 block w-full pl-3 pr-10 py-2 text-base border-gray-300 focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 sm:text-sm rounded-md",onchange: "this.form.dispatchEvent(new Event('submit',{bubbles: true}))" } %>
<%= f.select :sell_price_max,[["No Max",{bubbles: true}))" } %>
</div>
</div>
</div>
</div>
</div>
<!-- bedrooms -->
<div data-controller="menu" class="relative inline-block text-left p-0.5">
<div data-action="click->menu#toggle">
<button type="button" class="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500" id="options-menu" aria-expanded="true" aria-haspopup="true">
bedrooms
<!-- Heroicon name: solid/chevron-down -->
<svg class="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
</div>
<!--
Dropdown menu,show/hide based on menu state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
-->
<div data-menu-target="toggleable" class="hidden z-10 origin-top-left absolute left-0 mt-2 max-w-sm w-64 py-4 px-2 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
<div class="py-1" role="none">
<div class="flex flex-col">
<div>
<%= f.label :q_bedrooms_min,"bedrooms",class: "mx-1" %>
</div>
<div class="flex">
<%= f.select :q_bedrooms_min,[["All beds",["1+","1"],["2+","2"],["3+","3"],["4+","4"],["5+","5"]],{bubbles: true}))" } %>
</div>
</div>
</div>
</div>
</div>
<!-- Region -->
<div data-controller="menu" class="relative inline-block text-left p-0.5">
<div data-action="click->menu#toggle">
<button type="button" class="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500" id="options-menu" aria-expanded="true" aria-haspopup="true">
Region
<!-- Heroicon name: solid/chevron-down -->
<svg class="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
</div>
<!--
Dropdown menu,show/hide based on menu state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
-->
<div data-menu-target="toggleable" class="hidden z-10 origin-top-left absolute left-0 mt-2 max-w-sm w-64 py-4 px-2 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
<div class="py-1" role="none">
<div class="flex flex-col">
<div>
<%= f.label :region,"Region",class: "mx-1" %>
</div>
<div class="flex">
<%= f.select :region,@properties.aggs["region"]["buckets"].sort_by { |b| b["key"] }.collect { |bucket| bucket["key"] },{ include_blank: "All Regions",selected: "All Regions" },{bubbles: true}))" } %>
</div>
</div>
</div>
</div>
</div>
<!-- More -->
<div data-controller="menu" class="relative inline-block text-left p-0.5">
<div data-action="click->menu#toggle">
<button type="button" class="inline-flex justify-center w-full rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-gray-100 focus:ring-indigo-500" id="options-menu" aria-expanded="true" aria-haspopup="true">
More
<!-- Heroicon name: solid/chevron-down -->
<svg class="-mr-1 ml-2 h-5 w-5" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
</svg>
</button>
</div>
<!--
Dropdown menu,show/hide based on menu state.
Entering: "transition ease-out duration-100"
From: "transform opacity-0 scale-95"
To: "transform opacity-100 scale-100"
Leaving: "transition ease-in duration-75"
From: "transform opacity-100 scale-100"
To: "transform opacity-0 scale-95"
-->
<div data-menu-target="toggleable" class="hidden z-10 origin-top-left absolute left-0 mt-2 max-w-sm w-96 py-4 px-2 rounded-md shadow-lg bg-white ring-1 ring-black ring-opacity-5 focus:outline-none" role="menu" aria-orientation="vertical" aria-labelledby="options-menu">
<div class="py-1" role="none">
<div class="flex flex-col">
<div class="py-2">
<div class="flex justify-center py-2">
<!-- radio_button(object_name,method,tag_value,options = {}) public -->
<!-- This example requires Tailwind CSS v2.0+ -->
<div class="flex justify-center">
<span class="relative z-0 inline-flex shadow-sm rounded-md">
<% if params[:buy] == "true" %>
<%= link_to "Buy",request.params.except(:buy),id: "type",class: "relative inline-flex items-center px-3 py-2 rounded-l-md border border-gray-300 text-sm font-medium focus:z-10 focus:outline-none focus:border-white focus:bg-blue-500 focus:text-white hover:no-underline text-white bg-blue-500 duration-300 ease-in-out" %>
<% else %>
<%= link_to "Buy",request.params.merge(buy: true),class: "relative inline-flex items-center px-3 py-2 rounded-l-md border border-gray-300 text-sm font-medium focus:z-10 focus:outline-none focus:border-white focus:bg-blue-500 focus:text-white hover:no-underline hover:text-white hover:bg-blue-500 duration-300 ease-in-out" %>
<% end %>
<% if params[:rent] == "true" %>
<%= link_to "Rent",request.params.except(:rent),class: "-ml-px relative inline-flex items-center px-3 py-2 border border-gray-300 text-sm font-medium focus:z-10 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 hover:no-underline text-white bg-blue-500 duration-300 ease-in-out" %>
<% else %>
<%= link_to "Rent",request.params.merge(rent: true),class: "-ml-px relative inline-flex items-center px-3 py-2 border border-gray-300 text-sm font-medium focus:z-10 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 hover:no-underline hover:text-white hover:bg-blue-500 duration-300 ease-in-out" %>
<% end %>
<% if params[:share] == "true" %>
<%= link_to "Share",request.params.except(:share),class: "-ml-px relative inline-flex items-center px-3 py-2 rounded-r-md border border-gray-300 text-sm font-medium focus:z-10 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 hover:no-underline text-white bg-blue-500 duration-300 ease-in-out" %>
<% else %>
<%= link_to "Share",request.params.merge(share: true),class: "-ml-px relative inline-flex items-center px-3 py-2 rounded-r-md border border-gray-300 text-sm font-medium focus:z-10 focus:outline-none focus:ring-1 focus:ring-indigo-500 focus:border-indigo-500 hover:no-underline hover:text-white hover:bg-blue-500 duration-300 ease-in-out" %>
<% end %>
</span>
</div>
</div>
</div>
<div class="py-2">
<div>
<%= f.label :floor_min,"Floor",class: "mx-1" %>
</div>
<div class="flex">
<%= f.select :floor_min,[["All Floors",["1st floor and up",["2nd floor and up",["3rd floor and up",["4th floor and up",["5th floor and up",{bubbles: true}))" } %>
</div>
</div>
<div class="py-2">
<div>
<%= f.label :surface_interior_min,"Interior Surface",class: "mx-1" %>
</div>
<div class="flex">
<%= f.number_field :surface_interior_min,value: params[:surface_interior_min],placeholder: "Min",{bubbles: true}))",class: "w-full rounded m-1" %>
<span class="flex items-center"> - </span>
<%= f.number_field :surface_interior_max,value: params[:surface_interior_max],placeholder: "Max",class: "w-full rounded m-1" %>
</div>
</div>
<div class="py-2">
<div>
<%= f.label :surface_plot_min,"Size Plot",class: "mx-1" %>
</div>
<div class="flex">
<%= f.select :surface_plot_min,["150m²+","150"],["200m²+","200"],["250m²+","250"],["300m²+","300"],["350m²+","350"],["400m²+","400"],["450m²+","450"],["500m²+","500"],["1000m²+","1000"],["1500m²+","1500"],["2000m²+","2000"],["2500m²+","2500"],["3000m²+","3000"],["3500m²+","3500"],["4000m²+","4000"],["4500m²+","4500"],["5000m²+","5000"],["6000m²+","6000"],["7000m²+","7000"],["8000m²+","8000"],["9000m²+","9000"],["1ha+","10000"],["2ha+","20000"]],{bubbles: true}))" } %>
</div>
</div>
<div class="py-2">
<div>
<%= f.label :q_bathrooms_min,"Bathrooms",class: "mx-1" %>
</div>
<div class="flex justify-center py-2">
<!-- radio_button(object_name,options = {}) public -->
<!-- This example requires Tailwind CSS v2.0+ -->
<span class="relative z-0 inline-flex shadow-sm rounded-md">
<button type="button" class="bathroom-btn relative inline-flex items-center px-4 py-2 rounded-l-md border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:bg-blue-500 focus:text-white hover:no-underline hover:text-white hover:bg-blue-500 duration-300 ease-in-out">
<%= f.radio_button :q_bathrooms_min,"",class: "absolute transform scale-0" %>
<%= f.label :q_bathrooms_min_1,"Any",class: "cursor-pointer" %>
</button>
<button type="button" class="bathroom-btn -ml-px relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:bg-blue-500 focus:text-white hover:no-underline hover:text-white hover:bg-blue-500 duration-300 ease-in-out">
<%= f.radio_button :q_bathrooms_min,"1","1+","2",class: "absolute transform scale-0" %>
<%= f.label :q_bathrooms_min_2,"2+",class: "cursor-pointer" %>
</button>
<button type="button" class="bathroom-btn -ml-px relative inline-flex items-center px-4 py-2 border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:bg-blue-500 focus:text-white hover:no-underline hover:text-white hover:bg-blue-500 duration-300 ease-in-out">
<%= f.radio_button :q_bathrooms_min,"3",class: "absolute transform scale-0" %>
<%= f.label :q_bathrooms_min_3,"3+","4",class: "absolute transform scale-0" %>
<%= f.label :q_bathrooms_min_4,"4+",class: "cursor-pointer" %>
</button>
<button type="button" class="bathroom-btn -ml-px relative inline-flex items-center px-4 py-2 rounded-r-md border border-gray-300 bg-white text-sm font-medium text-gray-700 hover:bg-gray-50 focus:z-10 focus:outline-none focus:ring-1 focus:ring-blue-500 focus:bg-blue-500 focus:text-white hover:no-underline hover:text-white hover:bg-blue-500 duration-300 ease-in-out">
<%= f.radio_button :q_bathrooms_min,"5",class: "absolute transform scale-0" %>
<%= f.label :q_bathrooms_min_5,"5+",class: "cursor-pointer" %>
</button>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
<% end %>
我已经为此苦苦挣扎了一段时间。我希望有人可以帮助我。我会很感激的。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。