Php 向下搜索/过滤搜索的设计模式

Php 向下搜索/过滤搜索的设计模式,php,search,Php,Search,我希望为网站建立一个强大的搜索功能,类似于NewEgg的深入搜索,例如 我正在处理各种各样的对象,这些对象类似于具有不同标准的产品。有谁能推荐一个好的设计来构建像NewEgg这样的搜索引擎吗?以“垂直”的方式存储数据,即以(EAV)格式存储数据,以及EAV隐含的[元]数据驱动的模式管理,提供了一个框架,其中每个产品的属性彼此“独立”。这反过来有助于实现向下钻取(即引导查询细化,在每个步骤中,向最终用户提供仍然适用的可能属性列表,为每个此类属性提供可能值列表) 一个小小的警告是,这更适用于较小的

我希望为网站建立一个强大的搜索功能,类似于NewEgg的深入搜索,例如

我正在处理各种各样的对象,这些对象类似于具有不同标准的产品。有谁能推荐一个好的设计来构建像NewEgg这样的搜索引擎吗?

以“垂直”的方式存储数据,即以(EAV)格式存储数据,以及EAV隐含的[元]数据驱动的模式管理,提供了一个框架,其中每个产品的属性彼此“独立”。这反过来有助于实现向下钻取(即引导查询细化,在每个步骤中,向最终用户提供仍然适用的可能属性列表,为每个此类属性提供可能值列表)

一个小小的警告是,这更适用于较小的目录(比如少于100万个产品),因为EAV模型可能会在较大的数据库中引入一些性能和/或扩展问题。关注性能的实际大小因目录的具体情况而异(每个产品的平均属性数、不同类型产品之间属性的共性、“本体”的一般复杂性等),但对于较小的目录来说,EAV是一条相当不错的路。除了支持“深入”过滤外,它还引入了灵活的数据模式(能够添加/删除属性和/或产品类型等,而无需更改物理(数据库)模式;只更改逻辑模式)

编辑:有关EAV的更多详细信息/资源
诚然,维基百科关于它的文章有些抽象…
简而言之,该模型确定了以下概念:

  • 实体(又名产品或项目)=传统关系术语中的“记录”
  • 属性=RDBMS行话中的“列”(又名“字段”)
  • Value=给定记录的给定列的数值(或字符串或其他值)
  • Type(aka category)=[松散地]RDBMS中的“表”,即通常共享同一组属性的一组记录
为了举例说明这一点,比如电子产品目录,一个实体可以是一个特定的“平板显示器”,它的类型可以是“显示设备”,它的属性“大小”、“分辨率”、“价格”等等

使用EAV,大部分信息存储在两个表中,即Product表和ProductAttributes表:

Product table  
   "ProductID" (primary key, the "EntityId")
    "TypeId" 
    optionally, some common attributes found in all/most other Products, say...
      price
      ManufacturerId
      Photo

ProductAttributes table
    "ProductID" (Foreign Key to Product table)
    "AttributeID"  (FK to Attribute table)
    "Value"   (actual value; note: sometimes we can have several SQL fields for
               this say IntValue, StringValue, DateValue, allowing to store 
               values in their natural format)
上面的表构成了大部分数据,并由存储目录的[逻辑]模式(也称为“元数据”)的表进行补充。这些表格包括:

  • 定义属性的属性表:名称、数据类型、isRequired等
  • 定义类型(类别)的类型表:名称,在层次本体中可能是父类型
  • 类型\列出给定类型的可能属性的属性(例如,“电视机”具有“频道数”、“屏幕大小”等属性,“录像机”具有“磁头数”、“支持的格式”、“主体颜色”等属性)
与传统方法相比,所有这一切似乎有点复杂,传统方法是在SQL模式中对逻辑模式进行“硬编码”,即我们有一个“TVSets”表,其列集为每个属性一个,然后是一个“VCR”表具有自己的、不同的列/属性集。但是,使用这种方法,应用程序逻辑最终以某种方式(如果仅通过排序映射中的间接寻址)硬编码表和列名。
相比之下,EAV模型允许程序发现可能类型的列表,以及每个类型可能属性的列表(必需或可选)。此外,由于属性值都存储在同一个表中,因此可以对属性进行过滤,而不考虑类型(或子类型)例如,为了让所有商品都比50美元便宜(在另一种方法中,我们可能不得不在十几张表格中查找)

返回“向下搜索”功能…
一旦执行了初始搜索(例如搜索名称[全文索引]中包含“屏幕”一词的所有产品),ProductAttributes表就可以为满足第一个搜索条件的产品生成所有不同AttributeID(因此通过在属性表中查找属性名)的不同列表。
用户选择给定属性(如“制造商”)后,ProductAttributes表可以生成制造商的不同列表(以及每个制造商的产品数量)。(或者,当用户请求时,可以先搜索此类信息,而不是延迟搜索)。
然后,用户选择一个(或多个)给定的制造商,并运行一个新的查询以减少初始结果列表。由于最初选择的某些产品(实体)现在已被排除,因此可能属性的列表(以及每个属性中可能值的列表)将减少。
该过程将继续,为最终用户提供对目录的引导搜索。当然,用户可以回溯等

可能有助于解释这个冗长的解释(或者可能进一步混淆读者…)下面的代码片段更精确地说明了使用此结构实现搜索的方式。此代码适用于上述解释中使用的表名,可能会包含一些拼写错误,但通常会提供一些东西的味道。此外,这是使用通用表表达式(CTE)编写的但是可以很好地编写为子查询。也不是说我们不与逻辑模式(元数据)表连接,而是可以这样做,直接在结果集中获取属性名、类型名等。
正如前面所暗示的,支持这种体系结构的查询和逻辑更加复杂,但也更加通用,并且能够容忍存储项类型的变化
WITH SearchQry AS (
  SELECT ROW_NUMBER() OVER (ORDER BY P.EntityId ASC) AS RowNum,  
         P.EntityId AS EId
         FROM  Products P
         INNER JOIN ProductAttributes PA1 ON P.EntitityId = PA1.EntityId and PA1.AttributeID = <some attribute id, say for Manufacturer> 
         INNER JOIN ProductAttributes PA2 ON P.EntitityId = PA2.EntityId and PA2.AttributeID = <some other attribute id, say for Color>
         -- here for additional PAn JOINs as more criteria is added
         WHERE  P.ProductType IN (ProdId_x, ProdId_y, ProdId_z)  -- for example where these x,y,z Ids correspond to say "TV Sets", "LapTop Computers" and "PDAs" respectively
            AND PA1.Value = 'SAMSUNG' -- for example
            AND PA2.Value = 'YELLOW' -- for example
         GROUP BY P.EntityId
   )  

SELECT  P.EntityId, PA.AttributeId, PA.Value -- PA.IntValue (if so structured)
FROM (SELECT * FROM SearchQry WHERE RowNum BETWEEN  1 AND  15)  AS S
JOIN ProductAttributes PA ON PA.EntityId = S.EId
INNER JOIN Products P on P.EntityID = PA.EntityId
ORDER BY P.EntityId, P.AttributeId  -- or some other sort order