6

我正在寻找一些关于相当复杂的搜索算法的建议/帮助。任何有关技术等的文章将不胜感激。

背景

我正在构建一个应用程序,简而言之,它允许用户为任何一天设置他们的“可用性”。用户首先设置一个通用可用性模板,允许他们说:

Monday - AM   
Tuesday - PM  
Wednesday - All Day  
Thursday - None  
Friday - All Day

所以这个用户通常在周一上午、周二下午等可用。

架构:

id  
user_id  
day_of_week  (1-7)(Monday to Sunday)
availability

然后,他们可以手动覆盖特定日期,例如:

2013-03-03 - am  
2013-03-04 - pm  
2013-03-05 - all_day

架构:

id
user_id
date
availability

这一切都很好 - 我正在生成一个日历,它结合了模板和覆盖,并允许用户修改他们的可用性等。

问题

我现在需要允许管理员用户搜索具有特定可用性的用户。因此管理员用户将使用日历来选择所需的日期和可用性并点击搜索。

例如,找到我可用的用户:

2013-03-03 - pm
2013-03-04 - pm
2013-03-05 - pm

搜索过程必须使用模板化可用性和覆盖来搜索可用用户,然后返回最佳结果。理想情况下,它会返回始终可用的用户,但在没有单个用户可以匹配日期的情况下,我需要提供可以匹配的用户组合。

我知道这是一个相当复杂的问题,我不是在寻找完整的答案,也许只是一些指导或潜在相关技术的链接等。

我试过的

目前,我有一个半途而废的解决方案。我正在抓取所有可用的用户,遍历每个用户,并在该循环内,遍历所有必需的日期,并在用户不符合要求的日期时立即中断。这显然是非常不可扩展的,而且它也只返回“完美匹配”。

可能的解决方案

使用聚合表进行全文搜索

我考虑过创建一个具有以下架构的单独表:

user_id
body

body字段将填充用户模板天数和覆盖,因此示例记录可能如下所示:

user_id: 2
body: monday_am tuesday_pm wednesday_pm thursday_am friday_allday 2013-03-03_all_day 2013-03-03_pm

然后,我会将用户搜索查询转换为类似的格式。因此,如果用户正在寻找在 2013 年 3 月 19 日 - 全天和 2013 年 3 月 20 日 - 下午有空的人,我会将其转换为字符串。

首先,由于 3 月 19 日是星期二,我会将其转换为 tuesday_allday,与 20 日相同。因此,我最终会得到:

tuesday_allday wednesday_pm 2013-03-19_allday 2013-03-20_pm

然后我会对我们的聚合表进行全文搜索并返回一个“加权”结果集,然后我可以循环并进一步询问。

我不确定这在实践中是如何工作的,所以我问是否有人有任何我可以使用的技术或相关文章的链接。

4

2 回答 2

2

我相信这个问题可以通过定义更明确的数据库模式来解决。通过使用更详细的数据库模式,您将能够在任何给定的时间范围内(不仅仅是上午和下午)找到任何可用的用户,如果您应该选择的话。它还允许您保留模板数据,同时不会用模板信息污染您的可用性数据(相反,您可以从模板表中选择以编程方式填写给定日期的可用性,然后可以由用户修改)。

我花了一些时间绘制这个问题的图表,并提出了一个模式结构,我相信它可以解决您指定的问题,并允许您以最少的模式更改来扩展您的应用程序。(为了使这更容易阅读,我在此建议答案的末尾添加了 SQL)

我还包含了一个示例 select 语句,它允许您使用任意数量的参数提取可用性数据。为了清楚起见,SELECT 在我的解释性文本的结尾处位于架构的 SQL 之上。请不要被选择吓倒,乍一看它可能看起来很复杂,但实际上是整个模式的映射(保存模板表)。(顺便说一句,我并不是说因为我怀疑你能理解它,我相信你能理解,但我知道许多程序员忽略了更复杂的数据库结构而对自己不利,因为它看起来过于复杂但分析时实际上比他们在程序中为获得相似结果而必须做的杂技要简单......关系数据库基于数学的一个分支这很好@准确,一致,&(相对)简洁,关联数据)。

一般使用: (有关更多详细信息,请阅读 SQL CREATE TABLE 语句中的注释) - 填充 DaysOfWeek 表。- 使用您要跟踪的一些时间范围填充 TimeFrames 表(上午时间范围的开始时间可能为 00:00:00 和结束时间为 11:59:59,而下午可能有开始时间为 12:00:00 和结束时间23:59:59)-添加用户-添加要跟踪的日期(请参阅 SQL 中的注释以了解避免膨胀的想法以及此表的优点)-为每个用户填充模板表-生成默认可用性列表(与每个用户相关联的可用时间数据) - 向用户公开默认可用性,以便他们可以覆盖默认值注意:您还可以为参与添加一个可选表以与可用性相反(或者可能有一个更好的抽象可以包括这两个概念...) 免责声明:我没有花额外的时间来完全填充我的本地数据库并验证所有内容,因此可能存在一些我在图表中没有看到的弱点/错误......(对不起,我在这方面花费的时间比预期的要长得多,必须开始工作在一个过期的项目上完成)。虽然我在数据库结构和其他人创建了 12 年以上的数据库方面进行了相当广泛的工作,但我确信我并非没有错,但我希望 StackOverflow 上的其他人能够完善我可能包含的错误。

对于没有包含更多示例数据,我深表歉意。如果我在不久的将来有时间,我会提供一些,(考虑将 George、Fred 和 Harry 添加到 users 表中,在 Dates 表中添加一些日期,然后详细说明 George 和 Fred 在上学期间与 Harry 相比有多忙可用性、可用时间和时间框架表)。

SELECT 语句(注意:我强烈建议将其放入视图中……这样您就可以选择所需的任何列并在 WHERE 子句中添加所需的任何参数/条件,而不必每次都写出联接。 ..所以视图将不包括 WHERE 子句...只是为了说明这一点):

SELECT *
FROM Users Us
JOIN Availabilities Av
ON Us.User_ID=Av.User_ID
JOIN Dates Da
ON Av.Date_ID=Da.Date_ID
JOIN AvailableTimes Avt
ON Av.Av_ID=Avt.Av_ID
WHERE Da.Date='2014-01-03' -- whatever date 
-- alternately: WHERE Da.DayOWeek_ID=3 -- which would be Wednesday
-- WHERE Da.Date BETWEEN() -- whatever date range...
-- etc...

DaysOfWeek 中的推荐数据(实际上是一个查找表):

INSERT INTO DaysOfWeek(DayOWeek_ID,Name,Description)
VALUES (1,'Sunday', 'First Day of the Week'),(1,'Monday', 'Second Day of the Week')...(7,'Saturday', 'Last Day of the Week'),(8,'AllWeek','The entire week'),(9,'Weekdays', 'Monday through Friday'),(10,'Weekends','Saturday & Sunday')

示例模板数据

INSERT INTO Templates(Time_ID,User_ID,DayOWeek_ID)
VALUES (1,1,9)-- this would show the first user is available for the first time frame every weekday as their default... 
,(1,2,2) -- this would show the first user available on Tuesdays for the second time frame

以下是推荐的架构结构

CREATE  TABLE `test`.`Users` (

User_IDINT NOT NULL AUTO_INCREMENT , UserNameVARCHAR(45) NULL , PRIMARY KEY ( User_ID) );

CREATE  TABLE `test`.`Templates` (
  `Template_ID` INT NOT NULL AUTO_INCREMENT ,
  `Time_ID` INT NULL ,
  `User_ID` INT NULL ,
  `DayOWeek_ID` INT NULL ,
  PRIMARY KEY (`Template_ID`) )
`COMMENT = 'This table holds the template data for general expected availability of a user/agent/person (so the person would use this to set their general availability)'`;

CREATE  TABLE `test`.`Availabilities` (
  `Av_ID` INT NOT NULL AUTO_INCREMENT ,
  `User_ID` INT NULL ,
  `Date_ID` INT NULL ,
  PRIMARY KEY (`Av_ID`) )
COMMENT = 'This table holds a users actual availability for a particular date.\nIf the use is not available for a date then this table has no entry for that user for that date.\n(btw, this suggests the possiblity of an alternate table that could utilize all other structures except the templates called Engagements which would record when a user is actually busy... in order to use this table & the other table together would need to always join to AvailableTimes as a date would actually be in both tables but associated with different time frames).';

CREATE  TABLE `test`.`Dates` (
  `Date_ID` INT NOT NULL AUTO_INCREMENT ,
  `DayOWeek_ID` INT NULL ,
  `Date` DATE NULL ,
  PRIMARY KEY (`Date_ID`) )
COMMENT = 'This table is utilized to hold actual dates whith which users/agents can be associated.\nThe important thing to note here is: this may end up holding every day of every year... this suggests a need to archive this data (and everything associated with it for performance reasons as this database is utilized).\nOne more important detail... this is more efficient than associating actual dates directly with each user/agent with an availability on that date... this way the date is only recorded once, the other approach records this date with the user for each availability.';

 CREATE  TABLE `test`.`AvailableTimes` (
  `AvTime_ID` INT NOT NULL AUTO_INCREMENT ,
  `Av_ID` INT NULL ,
  `Time_ID` INT NULL ,
  PRIMARY KEY (`AvTime_ID`) )
COMMENT = 'This table records the time frames that a user is available on a particular date.\nThis allows the time frames to be flexible without affecting the structure of the DB.\n(e.g. if you only keep track of AM & PM at the beginning of the use of the DB but later decide to keep track on an hourly basis you simply add the hourly time frames & start populating them, no changes to the DB schema need to be made)';

CREATE  TABLE `test`.`TimeFrames` (
  `Time_ID` INT NOT NULL AUTO_INCREMENT ,
  `StartTime` TIME NOT NULL ,
  `EndTime` TIME NOT NULL ,
  `Name` VARCHAR(45) NOT NULL ,
  `Desc` VARCHAR(128) NULL ,
  PRIMARY KEY (`Time_ID`) ,
  UNIQUE INDEX `Name_UNIQUE` (`Name` ASC) )
COMMENT = 'Utilize this table to record the times that are being tracked.\nThis allows the flexibility of having multiple time frames on the same day.\nIt also provides the flexibility to change the time frames being tracked without changing the DB structure.';

CREATE  TABLE `test`.`DaysOfWeek` (
  `DaysOWeek_ID` INT NOT NULL AUTO_INCREMENT ,
  `Name` VARCHAR(45) NOT NULL ,
  `Description` VARCHAR(128) NULL ,
  PRIMARY KEY (`DaysOWeek_ID`) ,
  UNIQUE INDEX `Name_UNIQUE` (`Name` ASC) )
COMMENT = 'This table is a lookup table to hold the days of the week.\nI personally would recommend adding a row for:\nWeekends, All Week, & WeekDays \nThis will often be used in conjunction with the templates and will allow less entries in that table to be made with those 3 entries in this table.';
于 2014-03-01T10:56:16.530 回答
0

好的,这是我会做的:

  • 在用户表中为星期日、星期一...星期六创建字段。
  • 使用pm,amboth表示这些字段中的值。
  • 您还应该索引数据库中的每个字段以加快查询速度。
  • 然后为用户/日期/子午线字段(子午线表示上午或下午)创建一个单独的表。再次,子午线字段值将是pmamboth
  • 您将需要对php 的 date 函数进行一些研究,以提取星期几,并可能使用 switch 语句来反对它。
  • 使用请求的日期并提取星期几并查询用户表以了解其星期几的可用性。
  • 然后使用请求的日期/子午线本身并查询新的用户/日期/子午线表以获取用户的个人可用日期/子午线。

我认为这里不会有太多算法,除非在日期请求中提取星期几。如果你正在做一个日期范围,那么你可以从算法中受益,但如果它只是一堆樱桃挑选的日期,那么你只需要一个一个地做。让我知道,也许我会给你一个算法。

于 2013-03-18T18:22:15.030 回答