2017-02-03 6 views
1

私は、アドレスを格納するテーブルを持っています。このテーブルには、住所番号、住所の名前、方向、接尾辞、接頭辞、市区町村、州、および郵便番号などの住所コンポーネント用の複数のフィールドがあります。 :この住所表には、以前にユーザーが追加した住所がありますが、同じ町、都道府県、国から来ると思われますので、都市、州、国、クエリ)。類似のアドレスを見つけるためのLINQクエリ

私のアプリケーションは、データベースにユーザが入力したアドレスと正確に一致するものを見つけることです。完全一致がない場合は、同様のアドレスを返します。

1234 N Johnson St、1234 North Johnson St、1234 North John Streetなどの不一致を避けるため、Google Map APIによって正規化された、ユーザーが入力した、またはデータベースに格納されたすべてのアドレス。

これは私が使用している完全一致のクエリです。ストアドアドレスと入力アドレスの両方が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' 
          }); 

ただし、完全一致がない場合は、ユーザーにオプションを提示します。私が考えている限り、複数のクエリを作成して組み合わせることです。期待どおりに動作しますが、時間がかかりすぎます。あなたはsimilarAdddressで見たよう

私は

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; 
    } 

のようにやって、それは1件の結果を構築するために3つのdbo.Addressesテーブルからのクエリが異なるwhere声明で、その後、unionすべての3つの結果を実行します。

私がやっていることは、同様のアドレスを見つけるよりスマートな方法ではないと思います。はるかにシンプルで効率的なクエリを構築する良い方法はありますか?

EDIT: なぜ私は3つの異なるクエリを持つ必要があるのか​​分かりませんでした。その理由は、すべての可能な結果を​​ユーザーに提供するためです。より詳細な説明をするには、下記をご覧ください。

ユーザーが「1234 North Johnson St」を検索し、完全一致が返されない場合は、以下の手順を実行します。

まず、numberDirectionStreetnameは、すべてのアドレスが「1234 North Johnson」と一致するものを選択します。その結果は1234 North Johnson + Boulevard/Street/Court/Way/Parkway /などになります。後続のものよりも一致するアドレスコンポーネントが存在するので、リストの先頭に表示します。

次に、numberStreetnameは、 '1234 Johnson'と一致するすべてのアドレスを選択します。結果は1234 +南/北/東/西/ etc + Johnson + Boulevard/Street/Court/Way/Parkway/etcになります。

3番目のstreetnameは、 'Johnson'と一致するすべてのアドレスを選択します。結果は9999 + South/North/East/West/etc + Johnson + Boulevard/Street/Court/Way/Parkway/etcになります。

可能であれば、1つの質問でそれを行いたいと思います。これは私の質問の一部でもあり、速く実行するだけでなく、簡単にすることもできます。しかし、それは3つの別々のクエリでなければなりません。どのようにそれらを注文しますか?そして私の論理が理想的でないなら、あなたはどのように示唆しますか?

+0

あなたの編集ごとに - あなたは十分にはっきりしていた。提供された住所のいずれかの部分に一致する住所を検索したい場合は、結果の上位に注文されたほとんどの部分に一致する住所が必要です。このために3つの個別のクエリは必要ありません。 1つのクエリでこれを達成する方法については、私の答えを参照してください。ちなみに、あなたがそれを求めていないにもかかわらず、あなたがいくつかの部分のマッチングを他のものよりも重要にしたいなら、あなたはその部分のランク付けを重み付けする必要があります。 –

+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

ありがとうございます。スコアリング(またはランク付け)アドレスは、私が別のプロジェクトのために探していたものですが、今は両方で使用できます。私は、前向き、後向き、接尾辞、アパート番号などのいくつかのアドレス構成要素を省いた。あなたはそれらを重み付けする方法を提案していますか?私は個人的には、streetnameが最高で、次に接尾辞、数字、方向性、後向き、そし​​てアパート番号が続くと思います。同じスコアが別の住所に返された結果がありますが、私は他のものよりもその1つを好む。 –

+0

重み付けは正確なプロセスではなく、多くの場合、優先度に大きく依存します。これはまさにビジネスロジックの領域にあり、結果がどのような目的で使用されているかに依存します。しかし、最初のパスとして、私は外部からのアプローチを提案します。つまり、より一般的で、より具体的なものへの重み付けを減らし、提示した順序に非常に似ています。 –

+0

重み付けを適用する方法。 'let streetRank = address.Street == streetToMatch? 'のように使用される' streetWeight'のように、個々の重み付けが不要な場合は、各パーツ(または複数のパーツのグループ)の重みを定義する必要があります。 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

どういう意味ですか?私は同じような住所で同じ州と国の同様の住所を意味すると思いますか?この場合、国を使用してデータセットをフィルタリングする必要があります。おそらく、国の順、州の2番目の都市、都市の3番目の順などとなります。作業している行を減らすには、この順序で絞り込む必要があります。これが行われた後、あなたはストリートやナンバーなどで同様のアドレスを見つけるロジックを使うことができます。ここでも私はトップダウンアプローチを使うことを提案します。

クエリには、おそらくクエリが処理するデータ量が原因です。行をフィルタリングすることは、方法です。

また、マルチクエリの送信やユニオンの実行を避けることができます。 1つのクエリで適切なOR条件を使用して一度にすべてを行うことはできません。

私はこのような意味を持っていました。 InserectとUnionの組み合わせを使用してロジックを書き換えます。 1234年ブロードウェイ南

1234ブロードウェイ南1234年ブロードウェイ北34ブロードウェイ東

:あなたは私が得た.WHatテストするために、入力値を変更することができます

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; } 
} 
} 

は、あなたが探している

ました

フィドル:https://dotnetfiddle.net/Qpb5J1

+0

私は、私が持っているものからアドレステーブルを照会することを意味しました、国家、州または都市全体にアドレスが存在するわけではありません。それで、私は都市、州、国の条件を一致させなかったのです。現在、アドレステーブルには20行、クエリには約40秒かかっています。申し訳ありませんが、データベースに格納されているアドレスを指定する必要があります。私はすぐに更新を行います。 –

+0

20行では40秒が多すぎます。私はあなたがあなたのクエリを単一のクエリにチューンアップし、3つの別々のクエリを持たず、ユニオンを使用する必要があると思う、&&と||これを行うための条件。 –

+0

オリジナルの投稿を編集しました。それがあなたに合っているかどうか教えてください。私はあなたの最近のコメントで私がほとんどの質問に答えたと信じています。それを1つのクエリにする方法があれば教えてください。 –

関連する問題