LINQ查询查找类似地址

时间:2017-02-03 00:11:56

标签: c# sql-server linq kendo-grid

我有一个存储地址的表。此表有多个地址组件字段,例如地址编号,街道名称,方向,后缀,前缀,城市,州和邮政编码。 编辑:此地址表包含以前由用户添加的地址。我希望他们来自同一个城镇,城市,州和国家。所以我确实保留了城市,州,国家和邮编从他们但不用于查询。)

我的应用程序是在数据库中找到用户输入的地址的完全匹配地址。如果没有完全匹配,则返回类似的地址。

用户输入或存储在数据库中的所有地址均由Google Map API标准化,以避免不匹配,例如1234 N Johnson St,1234 North Johnson St或1234 North John Street。

这是我正在使用的完全匹配的查询。由于存储和输入的地址均由Google地址API规范化,因此我可以获得完全匹配的结果。

var exactMatch = (from address in db.Addresses
                             where address.PrimaryAddressNumber == userInput.Number && address.Directional == userInput.Direction && address.Suffix == userInput.Suffix && address.StreetName  == userInput.StreetName
                             select new IncidentSite
                             {
                                 FullAddress = 'address components goes here'
                             });

但是,如果没有完全匹配,那么我想给用户一个选项。据我所知,构建多个查询然后组合在一起。它按照我的预期工作,但时间太长了。

我喜欢

    private IQueryable<IncidentSite> GetSimilarAddress(UserInput userInput)
            {
            var numberDirectionStreetname = (from address in db.Addresses
                                     where address.PrimaryAddressNumber == userInput.Number && address.Directional == userInput.Direction && address.StreetName  == userInput.StreetName
                                     select new IncidentSite
                                     {
                                         FullAddress = 'address components goes here'
                                     });

        var numberStreetname = (from address in db.Addresses
                                     where address.PrimaryAddressNumber == userInput.Number && address.StreetName  == userInput.StreetName
                                     select new IncidentSite
                                     {
                                         FullAddress = 'address components goes here'
                                     });

        var streetname = (from address in db.Addresses
                                     where address.StreetName  == userInput.StreetName
                                     select new IncidentSite
                                     {
                                         FullAddress = 'address components goes here'
                                     });

        var similarAddress = numberDirectionStreetname.Union(numberStreetname).Union(streetname);

return similarAddress;
    }

正如您在similarAdddress看到的那样,它将从dbo.Addresses表中运行三个查询,但使用不同的where语句,然后union所有三个结果构建一个结果

我相信我所做的并不是找到类似地址的更聪明的方法。有没有什么好的方法可以构建一个更加简单和高效的查询?

修改 我想我不清楚为什么我必须有三个不同的查询,而不是一个。原因是为用户提供所有可能的结果。要进行更详细的说明,请参阅下文。

如果用户搜索'1234 North Johnson St'并且返回的确切匹配不完整,则执行以下步骤。

首先,在numberDirectionStreetname中,选择所有地址匹配'1234 North Johnson'。所以结果可以是1234 North Johnson + Boulevard / Street / Court / Way / Parkway /等。我希望它显示在列表的顶部,因为存在比后续组件更多的匹配地址组件。

其次,numberStreetname,选择与'1234 Johnson'匹配的所有地址。所以结果可以是1234 +南/北/东/西/等+强生+大道/街/法院/路/百汇等等

第三,街道名称,选择与'Johnson'匹配的所有地址。所以结果可以是9999 +南/北/东/西/等+强生+大道/街/法院/路/百汇等等

如果可能,我想在一个查询中执行此操作。这也是我的问题的一部分,不仅使它表现得更快,而且使它变得简单。但是,它必须是三个单独的查询,您将如何订购它们?如果我的逻辑不理想,你会怎么建议?

4 个答案:

答案 0 :(得分:1)

不要担心直接比较。由于您需要一个紧密匹配列表,您只需根据匹配的组件数量对结果进行排名。

这是一个工作示例程序,可以根据地址的每个元素进行排名,根据排名计算整体排名和排名(排名越高,匹配越好)。

public class Program
{
    private static readonly IEnumerable<Address> Addresses = new List<Address>
    {
        new Address{ Number = "1000", Direction = "North", Street = "Grand" },
        new Address{ Number = "2000", Direction = "North", Street = "Broadway" },
        new Address{ Number = "1000", Direction = "South", Street = "Main" },
        new Address{ Number = "3000", Direction = "South", Street = "Grand" },
        new Address{ Number = "2000", Direction = "East", Street = "Broadway" },
    };

    static void Main()
    {
        const string streetToMatch = "Broadway";
        const string numberToMatch = "2000";
        const string directionToMatch = "South";

        var rankedAddresses = from address in Addresses
                              let streetRank = address.Street == streetToMatch ? 1 : 0
                              let numberRank = address.Number == numberToMatch ? 1 : 0
                              let directionRank = address.Direction == directionToMatch ? 1 : 0
                              let rank = streetRank + numberRank + directionRank
                              orderby rank descending
                              select new
                              {
                                  Address = address,
                                  Rank = rank
                              };

        foreach (var rankedAddress in rankedAddresses)
        {
            var rank = rankedAddress.Rank;
            var address = rankedAddress.Address;
            Console.WriteLine($"Rank: {rank} | Address: {address.Number} {address.Direction} {address.Street}");
        }
    }
}

public class Address
{
    public string Street { get; set; }
    public string Number { get; set; }
    public string Direction { get; set; }
}

<强>结果

  

等级:2 |地址:2000 North Broadway
  等级:2 |地址:2000 East Broadway
  等级:1 |地址:1000南主   等级:1 |地址:3000 Grand Grand
  等级:0 |地址:1000 North Grand

答案 1 :(得分:1)

类似地址是什么意思?我认为通过类似的地址你的意思是同一个州和国家的类似地址?在这种情况下,您需要使用国家/地区过滤数据集,状态可能按国家/地区,州名第二,城市第三等顺序排列。您需要按此顺序缩小范围以减少正在使用的行。完成此操作后,您可以使用逻辑按街道,数字等查找类似地址。即使在这里,我建议使用自上而下的方法。

您的查询花费时间可能是由于查询必须使用的数据量。因此,过滤掉行是可行的方法。

此外,您可以避免发送多个查询并进行联合。您是否可以在一个查询中使用适当的AND OR条件一次完成所有操作。

我的意思是这样的。使用Inersect和Union的组合来重写您的逻辑。

   using System;
using System.Linq;
using System.Collections.Generic;

namespace mns
{

public class Program
{
     private static readonly IEnumerable<Address> Addresses = new List<Address>
    {
           new Address{ Number = "1234", Direction = "South", Street = "Main" },
         new Address{ Number = "1234", Direction = "North", Street = "Broadway" },
         new Address{ Number = "1234", Direction = "North", Street = "Grand" },


        new Address{ Number = "1234", Direction = "South", Street = "Broadway" },
        new Address{ Number = "34", Direction = "East", Street = "Broadway" },
    };

    public static void Main()
    {
        const string streetToMatch = "Broadway";
        const string numberToMatch = "1234";
        const string directionToMatch = "South";
        var combinedAdrress = numberToMatch +" "+ streetToMatch + " "+ directionToMatch;

                    var rankedAddresses = from address in Addresses.Where(s=>numberToMatch== s.Number).Intersect(Addresses.Where(s=>directionToMatch==s.Direction)).Intersect(Addresses.Where(s=>streetToMatch == s.Street))
                        .Union(Addresses.Where(s=>numberToMatch== s.Number).Intersect(Addresses.Where(s=>streetToMatch == s.Street)))
                        .Union(Addresses.Where(s=>streetToMatch == s.Street))

                              select new
                              {
                                  Address = address.Number + " " + address.Street+ " "+ address.Direction

                              };
         Console.WriteLine("You are searching for: "+combinedAdrress);;

        foreach (var rankedAddress in rankedAddresses)
        {

            var address = rankedAddress.Address;
            Console.WriteLine(address);
        }
    }
}

public class Address
{
    public string Street { get; set; }
    public string Number { get; set; }
    public string Direction { get; set; }
}
}

您可以更改输入值以进行测试。我得到的是

  

您正在寻找:1234 Broadway South

     

1234 Broadway South 1234 Broadway North 34 Broadway East

小提琴:https://dotnetfiddle.net/Qpb5J1

答案 2 :(得分:0)

为什么不首先获取所有streetNames,然后使用它作为主要列表从那里过滤掉?

var streetname = (from address in db.Addresses
                         where address.StreetName  == userInput.StreetName
                         select new IncidentSite
                         {
                             FullAddress = 'address components goes here'
                         });

var numberStreetname = (from address in streetname
                         where address.PrimaryAddressNumber == userInput.Number && address.StreetName  == userInput.StreetName
                         select new IncidentSite
                         {
                             FullAddress = 'address components goes here'
                         });

var numberDirectionStreetname = (from address in numberStreetname
                         where address.PrimaryAddressNumber == userInput.Number && address.Directional == userInput.Direction && address.StreetName  == userInput.StreetName
                         select new IncidentSite
                         {
                             FullAddress = 'address components goes here'
                         });

答案 3 :(得分:0)

只是一个解决方案,而不是解决问题的确切代码。 通过应用或条件用户输入将所有地址放入列表中。然后从筛选列表中找出具有最大计数的地址。

例如:

List<Address> listOfAddress = new List<Address>{
            new Address(){Street="street 1", FlatNum="15", City="Auckland"},
            new Address(){Street="street 2", FlatNum="20", City="Napier"},
            new Address(){Street="street 1", FlatNum="15", City="Hamilton"}
        };



        string userInputStree = "street 1";
        string userInputFlatnum = "15";
        string userInputCity = "Whangrey";

        var addressList = (from address in listOfAddress

                             where address.Street  == userInputStree || address.City==userInputCity || address.FlatNum == userInputFlatnum
                             select address.FlatNum + ", " + address.Street + ", " + address.City

                           ).ToList();

        //from address List find the address which has maximum count