2017-02-03 26 views
1

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

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

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

这是我正在使用的完全匹配查询。由于Google Address 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 +南/北/东/西/等+约翰逊+大道/街道/法院/路/百汇/等

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

我想在一个查询如果可能的话。这也是我的问题的一部分,不仅使其执行速度更快,而且使其变得简单。但是,它必须是三个单独的查询,你会如何订购?如果我的逻辑不理想,那么你会如何建议?

+0

根据您的编辑 - 你很清楚。您需要与提供的地址的任何部分相匹配的地址,并且结果中匹配大多数部件的地址更高。你不需要3个单独的查询。查看我的答案,以便在一个查询中完成此操作。顺便说一下,虽然你不要求它,但如果你想要某些部分的匹配比其他部分更重要,你只需要对该部分的排名进行加权。 –

+0

谢谢!我读了你的答案,并在我的项目上工作,使其按照我的计划执行。我知道你理解我的问题,但我必须添加更多的细节,因为其他人问。 –

+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 South Main
等级:1 |地址:3000 South Grand
等级:0 |地址:1000 North Grand

+0

非常感谢。评分(或排名)地址是我为不同项目寻找的,但现在我可以将它用于两者。我省略了几个地址组件,例如预取,后置,后缀和公寓号码。你有建议如何减肥。我个人认为街道名称是最高的,然后是后缀,数字,前向,后向,然后是公寓号码。我有不同地址返回的结果,但我比其他人更喜欢其中一个。 –

+0

加权不是一个确切的过程,往往很大程度上取决于偏好。这完全是商业逻辑领域,将取决于结果的用途。但作为第一步,我会建议采取一种外在的方法 - 即更加重视更具体,更轻量级的更具体的,这与您提出的顺序非常相似。 –

+0

至于如何应用权重。您需要为每个零件(或零件组,如果您不需要单独的加权)定义权重,例如'streetWeight'这样使用'let streetRank = address.Street == streetToMatch? streetWeight:0'。重量值应该被广泛分配以“拉伸”所得到的值范围; '9','99','999'等可能是一个很好的开始。当然,体重更高的人数会更多。 –

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' 
         }); 
+0

这假定在匹配的字段上的首选顺序。具体而言,它会忽略那些与街道名称不符的地方,但与数字相符。 –

+0

您的解决方案不适用于他的场景,因为这更关注于找到最相关的匹配,其中涉及几个排列组合。 – Kalyan

+0

我仍然不相信为什么这不起作用。可以说用户正在搜索1 Test Str。所以我们得到Test Str中的所有地址。然后我们将它与具体数字的所有测试地址结合起来。然后我们将所有的测试地址与特定的数字和方向结合起来。没有它说我们应该得到一个特定号码的所有地址。 – JohanP

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 
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百老汇南

1234百老汇南1234百老汇北34百老汇东

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

+0

我的意思是查询我所拥有的地址表,而不是整个国家,州或城市的地址。这就是为什么我没有为城市,州,国家添加匹配条件。目前,我在地址表中有20行,大约需要40秒查询。对不起,我应该指定存储在数据库中的地址。我会尽快更新。 –

+0

40秒对20行来说太过分了。我认为您需要将您的查询调整为单个查询,而不是3个单独的查询并使用联合,使用&&和||条件做到这一点。 –

+0

我编辑了我原来的帖子。让我知道,如果它对你有意义。我相信我在最近的评论中回答了大部分问题。如果你有建议如何使其成为一个查询,请让我知道。 –