2017-02-03 54 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

我編輯了我原來的帖子。讓我知道,如果它對你有意義。我相信我在最近的評論中回答了大部分問題。如果你有建議如何使其成爲一個查詢,請讓我知道。 –