使用指南
版本计划:搜索更新

版本计划:搜索更新

💡

计划于 2023 年 12 月 31 日前更新。

原搜索语法

案例:查询 分类名称、分类下的 产品名称、产品下的 供应商公司名称 包含 指定字符串。

表模型:

原调用语法:

/* Old code */
from c in Categories.Search("a", c => new
{
    c.CategoryName,
    _0 = c.Products.Select(x => x.ProductName),
    _1 = c.Products.Select(x => x.SupplierLink.CompanyName),
})
select new
{
    c.CategoryName,
}

原设计方案有如下缺点:

  • New Expression 要求每个属性必须使用名称(或隐式名称)。例如,_0_1
  • 每条规则返回值必须是 最小变量 或者 数组,不支持 new 表达式,导致生成的 SQL 有一定程度的冗余。

下面是生成的 SQL 调用,由于每条规则是独立处理,因此会生成多个 EXISTS 部分,然后使用 OR 连接。

/* Generated SQL */
SELECT `@`.`CategoryName`
FROM `@n.Categories` AS `@`
WHERE (
    (`@`.`CategoryName` LIKE '%a%') OR EXISTS (
    SELECT 1
    FROM `@n.Products` AS `@0`
    WHERE (`@`.`CategoryID` = `@0`.`CategoryID`) AND (`@0`.`ProductName` LIKE '%a%'))
) OR EXISTS (
    SELECT 1
    FROM `@n.Products` AS `@1`
    LEFT JOIN `@n.Suppliers` AS `@2` ON `@1`.`SupplierID` = `@2`.`SupplierID`
    WHERE (`@`.`CategoryID` = `@1`.`CategoryID`) AND (`@2`.`CompanyName` IS NOT NULL AND (`@2`.`CompanyName` LIKE '%a%')))

重新设计

为了优化以上问题,我们决定:

  • 使用 Initializers 语法 代替 New Expression 语法 重新设计该功能。
  • 规则返回值允许使用 New Expression 来优化代码调用,同时也能够生成更合理的 SQL 调用。

使用 LINQ 调用,我们可以使用 let 语句来定义查询链,以更容易的方式来编写代码:

/* LINQ code */
from c in Categories.Search("a", c => new SearchSelector
{
    c.CategoryName,
    
    from p in c.Products
    let s = p.SupplierLink
    select new
    {
        p.ProductName,
        s.CompanyName,
    },
})
select new
{
    c.CategoryName,
}

同义的 Lambda 调用:

/* Lambda code */
from c in Categories.Search("a", c => new SearchSelector
{
    c.CategoryName,
    
    c.Products.Select(p => new
    {
        p.ProductName,
        p.SupplierLink.CompanyName,
    })
})
select new
{
    c.CategoryName,
}

生成的 SQL 调用:

/* Generated SQL */
SELECT `@`.`CategoryName`
FROM `@n.Categories` AS `@`
WHERE (`@`.`CategoryName` LIKE '%a%') OR EXISTS (
    SELECT 1
    FROM `@n.Products` AS `@0`
    LEFT JOIN `@n.Suppliers` AS `@1` ON `@0`.`SupplierID` = `@1`.`SupplierID`
    WHERE (`@`.`CategoryID` = `@0`.`CategoryID`) AND ((`@0`.`ProductName` LIKE '%a%') OR (`@1`.`CompanyName` LIKE '%a%')))

最佳实践

如果能够使用 单个规则,那么就不使用 多条规则

例如,在上述案例中,如果使用 多条规则 实现:

/* LINQ code */
from c in Categories.Search("a", c => new SearchSelector
{
    from p in c.Products select p.ProductName,				
    from p in c.Products select p.SupplierLink.CompanyName,
})
select new
{
    c.CategoryName,
}

那么,生成的 SQL 调用就会变为:

SELECT `@`.`CategoryName`
FROM `@n.Categories` AS `@`
WHERE EXISTS (
    SELECT 1
    FROM `@n.Products` AS `@0`
    WHERE (`@`.`CategoryID` = `@0`.`CategoryID`) AND (`@0`.`ProductName` LIKE '%a%')) OR EXISTS (
    SELECT 1
    FROM `@n.Products` AS `@1`
    LEFT JOIN `@n.Suppliers` AS `@2` ON `@1`.`SupplierID` = `@2`.`SupplierID`
    WHERE (`@`.`CategoryID` = `@1`.`CategoryID`) AND (`@2`.`CompanyName` LIKE '%a%'))

推荐使用:

/* LINQ code */
from c in Categories.Search("a", c => new SearchSelector
{
    from p in c.Products select new
    {
        p.ProductName,
        p.SupplierLink.CompanyName,
    },
})
select new
{
    c.CategoryName,
}
SELECT `@`.`CategoryName`
FROM `@n.Categories` AS `@`
WHERE EXISTS (
    SELECT 1
    FROM `@n.Products` AS `@0`
    LEFT JOIN `@n.Suppliers` AS `@1` ON `@0`.`SupplierID` = `@1`.`SupplierID`
    WHERE (`@`.`CategoryID` = `@0`.`CategoryID`) AND (
        (`@0`.`ProductName` LIKE '%a%') OR (`@1`.`CompanyName` LIKE '%a%'))
    )

开发计划

已实现的部分:

  • 基础解析
  • 字符串匹配:或者。

我们还需实现以下内容:

  • 为查询链提供 多字符串搜索
  • 更多的 单字符串匹配模式:包含 | 全等
  • 更多的 多字符串匹配模式:或者 | 并且

/* LINQ code */
from c in Categories.Search("a", c => new SearchSelector(SearchMode.Contains)
{
    from p in c.Products select new
    {
        p.ProductName,
        p.SupplierLink.CompanyName,
    },
})
select new
{
    c.CategoryName,
}

有更多想法?

👉 New Issue · zmjack/LinqSharp (github.com) (opens in a new tab)