使用 Linq 搜索包含多个术语的一组数据

发布于 2024-08-26 06:11:10 字数 2947 浏览 2 评论 0原文

我正在从 ADO.NET 迁移到 Linq。该应用程序是一个用于查找人员的目录搜索程序。允许用户在单个文本框中输入搜索条件。他们可以用空格分隔每个术语,或者将短语用引号引起来,例如“park place”以表明它是一个术语。

在幕后,数据来自一个 XML 文件,该文件包含大约 90,000 条记录,大小约为 65 兆。我将数据加载到 DataTable 中,然后使用 .Select 方法和 SQL 查询来执行搜索。我传递的查询是根据用户传递的搜索词构建的。我使用正则表达式将文本框中的字符串拆分为数组,该正则表达式会将所有内容拆分为其中包含空格的单独元素。但是,如果短语周围有引号,则该短语将成为数组中自己的元素。然后,我最终得到一个包含 x 个元素的单维数组,我对其进行迭代以构建一个长查询。

然后,我构建下面的搜索表达式:

      query = query & _
    "((userid LIKE '" & tempstr & "%') OR " & _
    "(nickname LIKE '" & tempstr & "%') OR " & _
    "(lastname LIKE '" & tempstr & "%') OR " & _
    "(firstname LIKE '" & tempstr & "%') OR " & _
    "(department LIKE '" & tempstr & "%') OR " & _
    "(telephoneNumber LIKE '" & tempstr & "%') OR " & _
    "(email LIKE '" & tempstr & "%') OR " & _
    "(Office LIKE '" & tempstr & "%'))"

每个术语都有一组上述查询。如果有多个术语,我会在两者之间放置一个 AND,并使用下一个术语构建另一个查询,如上所示。我不知道如何在 Linq 中执行此操作。到目前为止,我已经正确加载了 XML 文件。我可以使用特定条件进行搜索,但我不确定如何最好地实现多个术语的搜索。

'this works but far too simple to get the job done
    Dim results = From c In m_DataSet...<Users> _
    Where c.<userid>.Value = "XXXX" _
    Select c    

上面的代码也没有使用 LIKE 运算符。所以部分匹配不起作用。看起来我想使用的是 .Startswith 但这似乎只在 Linq2SQL 中。任何指导将不胜感激。我是 Linq 新手,所以我可能缺少一种简单的方法来执行此操作。

XML 文件如下所示:

<?xml version="1.0" standalone="yes"?>
<theusers>

<Users>
<userid>person1</userid>
<nickname></nickname>
<lastname></lastname>
<firstname></firstname>
<department></department>
<telephoneNumber></telephoneNumber>
<email></email>
</Users>

<Users>
<userid>person2</userid>
<nickname></nickname>
<lastname></lastname>
<firstname></firstname>
<department></department>
<telephoneNumber></telephoneNumber>
<email></email>
</Users>

######## 更新 ######## 以下是 VB 中完整的工作解决方案,感谢我们的好心回答者。


Here is the query you would run:

Dim query = From d In m_DataSet.Descendants("Users") _
                Where d.ChildrenBeginWith(rezsplit) _
                Select d     


Here is the extension method:

Public Module SearchEngine
<System.Runtime.CompilerServices.Extension()> _
Public Function ChildrenBeginWith(ByVal parent As XElement, _ 
  ByVal ParamArray     searchTerms As String()) As Boolean
  Dim ret As Boolean = False
      Dim children = parent.Elements().ToList()
      For Each searchTerm In searchTerms
          ret = children.Any(Function(x) x.Value.StartsWith(searchTerm))
          If Not ret Then
              Exit For
          End If
      Next
      Return ret
  End Function
End Module

I'm in the process of moving from ADO.NET to Linq. The application is a directory search program to look people up. The users are allowed to type the search criteria into a single textbox. They can separate each term with a space, or wrap a phrase in quotes such as "park place" to indicate that it is one term.

Behind the scenes the data comes from a XML file that has about 90,000 records in it and is about 65 megs. I load the data into a DataTable and then use the .Select method with a SQL query to perform the searches. The query I pass is built from the search terms the user passed. I split the string from the textbox into an array using a regular expression that will split everything into a separate element that has a space in it. However if there are quotes around a phrase, that becomes it's own element in the array. I then end up with a single dimension array with x number of elements, which I iterate over to build a long query.

I then build the search expression below:

      query = query & _
    "((userid LIKE '" & tempstr & "%') OR " & _
    "(nickname LIKE '" & tempstr & "%') OR " & _
    "(lastname LIKE '" & tempstr & "%') OR " & _
    "(firstname LIKE '" & tempstr & "%') OR " & _
    "(department LIKE '" & tempstr & "%') OR " & _
    "(telephoneNumber LIKE '" & tempstr & "%') OR " & _
    "(email LIKE '" & tempstr & "%') OR " & _
    "(Office LIKE '" & tempstr & "%'))"

Each term will have a set of the above query. If there is more than one term, I put an AND in between, and build another query like above with the next term. I'm not sure how to do this in Linq. So far, I've got the XML file loading correctly. I'm able to search it with specific criteria, but I'm not sure how to best implement the search over multiple terms.

'this works but far too simple to get the job done
    Dim results = From c In m_DataSet...<Users> _
    Where c.<userid>.Value = "XXXX" _
    Select c    

The above code also doesn't use the LIKE operator either. So partial matches don't work. It looks like what I'd want to use is the .Startswith but that appears to be only in Linq2SQL. Any guidance would be appreciated. I'm new to Linq, so I might be missing a simple way to do this.

The XML file looks like so:

<?xml version="1.0" standalone="yes"?>
<theusers>

<Users>
<userid>person1</userid>
<nickname></nickname>
<lastname></lastname>
<firstname></firstname>
<department></department>
<telephoneNumber></telephoneNumber>
<email></email>
</Users>

<Users>
<userid>person2</userid>
<nickname></nickname>
<lastname></lastname>
<firstname></firstname>
<department></department>
<telephoneNumber></telephoneNumber>
<email></email>
</Users>

######## UPDATE ########
Below is the full working solution in VB thanks to our kind answerer.


Here is the query you would run:

Dim query = From d In m_DataSet.Descendants("Users") _
                Where d.ChildrenBeginWith(rezsplit) _
                Select d     


Here is the extension method:

Public Module SearchEngine
<System.Runtime.CompilerServices.Extension()> _
Public Function ChildrenBeginWith(ByVal parent As XElement, _ 
  ByVal ParamArray     searchTerms As String()) As Boolean
  Dim ret As Boolean = False
      Dim children = parent.Elements().ToList()
      For Each searchTerm In searchTerms
          ret = children.Any(Function(x) x.Value.StartsWith(searchTerm))
          If Not ret Then
              Exit For
          End If
      Next
      Return ret
  End Function
End Module

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

清风不识月 2024-09-02 06:11:10

如果您只想使用 linq-to-xml 的功能,您可以将 xml 加载到 XDocument 中并执行以下查询。它将包括以指定文本开头的所有子节点值。

Dim doc = XDocument.Parse("this is where your xml string goes")
Dim query = From d In doc.Descendants("Users") _
            Where d.Elements().Any(Function(x As XElement) x.Value.StartsWith(tempStr)) _
            Select d
For Each A In query
  //Do Something
Next

编辑:抱歉,我不是 VB 人员,因此以下示例是 c#(我最初用 c# 编写了第一个答案,但对我来说转换为 vb 非常容易)。我不知道有什么原生方法可以干净地执行您想要的操作,因此最简单的方法可能是辅助方法或扩展方法,如下所示:

新查询:

var query = from d in doc.Descendants("Users")
            where d.ChildrenBeginWith(tempStr, tempStr2)
            select d;

扩展方法:

public static class Extension
{
    public static bool ChildrenBeginWith(this XElement parent, params string[] searchTerms)
    {
        bool ret = false;
        var children = parent.Elements().ToList();
        foreach (var searchTerm in searchTerms)
        {
            ret = children.Any(x => x.Value.StartsWith(searchTerm));
            if (!ret)
                break;
        }
        return ret;
    }
}

If you just want something that works with linq-to-xml you can load your xml into an XDocument and execute the following query. It will include any child node values that begin with the specified text.

Dim doc = XDocument.Parse("this is where your xml string goes")
Dim query = From d In doc.Descendants("Users") _
            Where d.Elements().Any(Function(x As XElement) x.Value.StartsWith(tempStr)) _
            Select d
For Each A In query
  //Do Something
Next

Edit: Sorry I am not a VB guy, so the following sample is c# (i originally wrote my first answer in c# but it was pretty easy for me to convert to vb). I don't know of a native way to cleanly do what you want, so the easiest way might be a helper method or an extension method like the following:

New Query:

var query = from d in doc.Descendants("Users")
            where d.ChildrenBeginWith(tempStr, tempStr2)
            select d;

Extension Method:

public static class Extension
{
    public static bool ChildrenBeginWith(this XElement parent, params string[] searchTerms)
    {
        bool ret = false;
        var children = parent.Elements().ToList();
        foreach (var searchTerm in searchTerms)
        {
            ret = children.Any(x => x.Value.StartsWith(searchTerm));
            if (!ret)
                break;
        }
        return ret;
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文