通过过滤然后排序定义一个 QueryBuilder

Define a QueryBuilder by filtering then sorting

过滤

控制器动作中的以下代码:

    students <- case searchString' of
        Nothing -> query @Student |> fetch
        (Just str) -> query @Student 
            |> queryOr
                (filterWhereILike (#lastName, "%" <> str <> "%"))
                (filterWhereILike (#firstMidName, "%" <> str <> "%"))
            |> fetch        

会:

效果很好!

正在排序

过滤后,我想按照另一个Maybe Text排序。我想做这样的事情:

设置 queryBuilder 并完成过滤步骤:

        let queryBuilder = case searchString' of
                            Nothing -> query @Student
                            (Just str) -> query @Student 
                                |> queryOr
                                    (filterWhereILike (#lastName, "%" <> str <> "%"))
                                    (filterWhereILike (#firstMidName, "%" <> str <> "%"))   

然后进行排序:

        students <- case sortOrder of
                        (Just "NameAsc") -> queryBuilder |> orderByAsc  #lastName |> fetch
                        (Just "NameDsc") -> queryBuilder |> orderByDesc #lastName |> fetch
                        (Just "DateAsc") -> queryBuilder |> orderByAsc  #enrollmentDate |> fetch
                        (Just "DateDsc") -> queryBuilder |> orderByDesc #enrollmentDate |> fetch
                        Nothing -> queryBuilder |> orderByAsc #lastName |> fetch
                        _ -> queryBuilder |> orderByAsc #lastName |> fetch

然而,这(可以理解)导致以下结果:

• Couldn't match expected type ‘QueryBuilder "students"’
              with actual type ‘NoJoinQueryBuilderWrapper "students"’
• In the expression:
    query @Student
      |>
        queryOr
          (filterWhereILike (#lastName, "%" <> str <> "%"))
          (filterWhereILike (#firstMidName, "%" <> str <> "%"))
  In a case alternative:
      (Just str)
        -> query @Student
             |>
               queryOr
                 (filterWhereILike (#lastName, "%" <> str <> "%"))
                 (filterWhereILike (#firstMidName, "%" <> str <> "%"))
  In the expression:
    case searchString' of
      Nothing -> query @Student
      (Just str)
        -> query @Student
             |>
               queryOr
                 (filterWhereILike (#lastName, "%" <> str <> "%"))
                 (filterWhereILike (#firstMidName, "%" <> str <> "%"))typecheck

问题

有什么关于如何设置的建议吗?

原代码

如果有任何帮助,这或多或少是对以下 C#

的转换
            var students = _context.Students.Select(student => student);
                        
            if (!String.IsNullOrEmpty(searchString)) 
                students = students.Where(student => 
                    student.LastName.Contains(searchString) || 
                    student.FirstMidName.Contains(searchString));

            if      (sortOrder == SortOrder.NameAsc) students = students.OrderBy(          student => student.LastName);
            else if (sortOrder == SortOrder.NameDsc) students = students.OrderByDescending(student => student.LastName);
            else if (sortOrder == SortOrder.DateAsc) students = students.OrderBy(          student => student.EnrollmentDate);
            else if (sortOrder == SortOrder.DateDsc) students = students.OrderByDescending(student => student.EnrollmentDate);
            else                                     students = students.OrderBy(          student => student.LastName);

参考资料

IHP 手册的 QueryBuilder 部分:

https://ihp.digitallyinduced.com/Guide/querybuilder.html

除非我遗漏了什么,否则似乎没有与上述类似的示例。

Raw SQL Queries 还有一个部分。

更新 1

这是一种有效的方法:

students <- case searchString' of

    Nothing -> query @Student 
        |> (case sortOrder of
                (Just "NameAsc") -> orderByAsc #lastName
                (Just "NameDsc") -> orderByDesc #lastName
                (Just "DateAsc") -> orderByAsc  #enrollmentDate
                (Just "DateDsc") -> orderByDesc #enrollmentDate
                Nothing -> orderByAsc #lastName
                _ -> orderByAsc #lastName)
        |> fetch

    (Just str) -> query @Student 
        |> queryOr
            (filterWhereILike (#lastName, "%" <> str <> "%"))
            (filterWhereILike (#firstMidName, "%" <> str <> "%"))
        |> (case sortOrder of
                (Just "NameAsc") -> orderByAsc #lastName
                (Just "NameDsc") -> orderByDesc #lastName
                (Just "DateAsc") -> orderByAsc  #enrollmentDate
                (Just "DateDsc") -> orderByDesc #enrollmentDate
                Nothing -> orderByAsc #lastName
                _ -> orderByAsc #lastName)
        |> fetch    

但是,如您所见,排序子句是重复的。

如果我尝试按如下方式分解排序子句:

let sortClause = (case sortOrder of
                    (Just "NameAsc") -> orderByAsc #lastName
                    (Just "NameDsc") -> orderByDesc #lastName
                    (Just "DateAsc") -> orderByAsc  #enrollmentDate
                    (Just "DateDsc") -> orderByDesc #enrollmentDate
                    Nothing -> orderByAsc #lastName
                    _ -> orderByAsc #lastName)

我(可以理解地)得到以下信息:

因为我们不在查询上下文中。

更新 2

好的,我能够通过为未引用的查询提供参数来分解排序子句:

let sortClause q = (case sortOrder of
                    (Just "NameAsc") -> orderByAsc #lastName
                    (Just "NameDsc") -> orderByDesc #lastName
                    (Just "DateAsc") -> orderByAsc  #enrollmentDate
                    (Just "DateDsc") -> orderByDesc #enrollmentDate
                    Nothing -> orderByAsc #lastName
                    _ -> orderByAsc #lastName)                

我可以使用 sortClause,但是,我必须添加对 queryOr 的“无操作”调用,以便使类型对齐:

students <- case searchString' of

    Nothing -> query @Student 
        |> queryOr
            (filterWhereILike (#lastName, "%"))
            (filterWhereILike (#firstMidName, "%"))
        |> sortClause (query @Student)
        |> fetch

    (Just str) -> query @Student 
        |> queryOr
            (filterWhereILike (#lastName, "%" <> str <> "%"))
            (filterWhereILike (#firstMidName, "%" <> str <> "%"))
        |> sortClause (query @Student)
        |> fetch  

所以,我想对于这种方法的问题是,有没有办法避免在第一个分支中使用“no-op”queryOr

我可以重现这个问题。这是 IHP 中的一个错误,其中 queryOr 的类型过于具体。通过 https://github.com/digitallyinduced/ihp/pull/1081

修复

你能试试看吗?请参阅 https://ihp.digitallyinduced.com/Guide/updating.html#updating-to-a-specific-git-commit 以更新到特定提交。

与错误无关,这是该操作的另一个版本:

    action StudentsAction = do
        let searchString' = paramOrNothing @Text "query"
        let sortOrder = paramOrNothing @Text "sort"

        let filterWhereName = case searchString' of
                (Just searchQuery) -> filterWhere (#lastName, "%" <> searchQuery <> "%")
                Nothing -> \query -> query
        
        let orderBySortParam = case sortOrder of
                (Just "NameAsc") -> orderByAsc  #lastName
                (Just "NameDsc") -> orderByDesc #lastName
                (Just "DateAsc") -> orderByAsc  #enrollmentDate
                (Just "DateDsc") -> orderByDesc #enrollmentDate
                _                -> orderByAsc #lastName

        students <- query @Student
                |> filterWhereName
                |> orderBySortParam
                |> fetch

        render IndexView { .. }