DUICUO

おそらくこれがオープンソースプロジェクトを行う意味なのでしょう!

この記事の主な焦点

  1. 最新バージョンの goframe v2 を使用するためのベストプラクティス
  2. スキャンによる動的な拡張を回避するためのリスト値の取得とスライス容量の初期化。
  3. スライスの遅延初期化
  4. 更新操作中に注意すべき問題

いつものように、開発の参考にしていただけるよう詳細な手順をまとめました。最適化に関するご提案も歓迎いたします。

値リストの最適化

以下のコード例は、以前のバージョンのプロジェクトでリストから値を取得する方法を示しています。これは公式コードのfocus-singleの例と同じで、ロジックは次のとおりです。

  1. 後続の呼び出しを容易にするために *gdb.Model オブジェクトを取得します。
  2. 構造体をインスタンス化して返す
  3. ページネーションクエリ
  4. クエリと割り当てを実行します (データが存在するかどうかを確認するためだけに実行され、応答構造には実際にはデータは割り当てられません)。
  5. 判断するためのデータがない
  6. 次に、カウントをクエリしてデータ項目の数を取得します。
  7. クエリ結果をレスポンス構造に割り当てます。

各コードは明確にコメントアウトされています。これで機能は実現できますが、パフォーマンスは十分ではなく、最適化の余地があります。

 // GetListはアイテムのリストを取得します
func ( s * sAdmin ) GetList ( ctx context .Context , in model .AdminGetListInput ) ( out * model .AdminGetListOutput , err error ) {
// 1.後続の呼び出しを簡単にするために、 * gdb.Modelオブジェクトを取得します。
var (
m = dao .AdminInfo .Ctx ( ctx )

// 2.構造体をインスタンス化して返す
out = & model.AdminGetListOutput {
ページ: .Page
サイズ: .サイズ
}
// 3.ページネーションクエリ
listModel : = m .Page ( .Page .Size)
// 4.クエリと割り当てを実行します (これはデータが存在するかどうかを確認するためだけのもので、応答構造には実際にはデータは割り当てられません)。
var list [ ] *エンティティ.AdminInfo
err : = listModel.Scan ( & list )の場合; err != nil {
戻り値エラー
}
// 5.判断に必要なデータはありません
len (リスト) == 0 の場合{
返す nil
}
// 6. count を再度クエリして、データ項目の数を取得します。
出力.合計エラー= m.Count ( )
err != nil の場合{
戻り値エラー
}
// 7.クエリ結果をレスポンス構造体に割り当てます。
err : = listModel.Scan ( & out.List ) ; err != nil {
戻り値エラー
}
戻る
}

上記のコードの問題点をまとめてみましょう。

  1. ステップ4は不要です。カウントを直接クエリできます。カウントが0の場合は直接戻り、それ以外の場合はクエリ割り当て操作を実行します。
  2. 上記のアプローチには問題があります。データが見つからない場合、リストの値は null になりますが、戻り値は空の​​配列 [] になることが予想されます。

  1. また、スキャン中に拡張されないように、スライスの容量を適切に初期化することをお勧めします。
  2. さらに、スライスの初期化は遅延されます。それ以前にエラーが発生した場合は、リストをインスタンス化する必要はありません。

コードを最適化してみましょう。最適化されたコードと詳細なコメントを以下に示します。

  1. 後続の呼び出しを容易にするために *gdb.Model オブジェクトを取得します。
  2. 応答構造をインスタンス化する
  3. ページネーションクエリ
  4. 次に、カウントを照会して、データがあるかどうかを判断します。
  5. データが見つかるまでリスト スライスの初期化を遅延し、その後スライスの容量を予想されるサイズに初期化します。
  6. クエリ結果をレスポンス構造に割り当てます。
 // GetListはアイテムのリストを取得します
func ( s * sAdmin ) GetList ( ctx context .Context , in model .AdminGetListInput ) ( out * model .AdminGetListOutput , err error ) {
// 1.後続の呼び出しを簡単にするために、 * gdb.Modelオブジェクトを取得します。
m : = dao .AdminInfo .Ctx ( ctx )
// 2.レスポンス構造体をインスタンス化する
out = & model.AdminGetListOutput {
ページ: .Page
サイズ: .サイズ
}
// 3.ページネーションクエリ
listModel : = m .Page ( .Page .Size)
// 4.再度 count をクエリして、データがあるかどうかを確認します。
出力.合計エラー= m.Count ( )
err != nil || out .Total == 0 の場合 {
//空のデータに対して nil ではなく[ ]を返す問題を解決します。
out .List = make ( [ ] model .AdminGetListOutputItem , 0 , 0 )
戻り値エラー
}
// 5.リスト スライスの初期化を遅延します。データの存在を確認した後にのみ、スライスの容量を予想されるサイズに初期化します。
out .List = make ( [ ] model .AdminGetListOutputItem , 0 , in .Size )
// 6.クエリ結果をレスポンス構造体に割り当てます。
err : = listModel.Scan ( & out.List ) ; err != nil {
戻り値エラー
}
戻る
}

コードの最適化後、データのないリストの返される形式は予想どおり [] になります。

以下は、データを含む返された結果の例です。

上記の最適化ノートはGitHubに同期されています。ぜひご覧いただき、同じ体験を再現してみてください。

https://github.com/wangzhongyang007/goframe-shop-v2/commit/ee020ea96616c30cb5bce5f7ab24417ad56e1a67

上記の例は非常に単純で、検索条件やモデルの関連付けのない通常のデータ クエリです。

関連クエリから値を取得する

理解を深めるために、より複雑な例を見てみましょう。最適化されたコードを直接お見せします。

  1. グローバルに適用可能なクエリステートメントを定義する
  2. 応答構造をインスタンス化する
  3. ページナビゲーション
  4. まず、カウントを照会します。エラーが発生した場合、またはデータが見つからない場合は、直接戻ります。
  5. リストの遅延初期化: データの存在を確認した後にのみ、予想されるサイズに応じてスライス容量をインスタンス化します。
  6. さらなる最適化: 入力パラメータに基づいて、クエリに対応する関連モデルを区別します。
 // GetListはアイテムのリストを取得します
func ( * sCollection ) GetList ( ctx context .Context , in model .CollectionListInput ) ( out * model .CollectionListOutput , err error ) {
// 1.グローバルに適用可能なクエリ文を定義する
ユーザーID : = gconv.Uint ( ctx.Value ( consts.CtxUserId ) )
m : = dao .CollectionInfo .Ctx ( ctx ) .Where ( dao .CollectionInfo .Columns ( ) .Type , in .Type ) .
場所( dao .CollectionInfo .Columns ( ) .UserId , userId )
// 2.レスポンス構造体をインスタンス化する
out = & model.CollectionListOutput {
ページ: .Page
サイズ: .サイズ
}
// 3.ページナビゲーション
listModel : = m .Page ( .Page .Size)
// 4.カウントのクエリを優先します。エラーが発生した場合やデータが見つからない場合は直接戻ります。
out.Total err = listModel.Count ( )
err != nil || out .Total == 0 の場合 {
out .List = make ( [ ] model .CollectionListOutputItem , 0 , 0 )
戻り値エラー
}
// 5.遅延リストの初期化: データの存在を確認した後でのみ、予想されるサイズに応じてスライス容量をインスタンス化します。
out .List = make ( [ ] model .CollectionListOutputItem , 0 , in .Size )
// 6.さらなる最適化: 入力パラメータに基づいて、クエリに対応する関連モデルを区別します。
.Type == consts .CollectionTypeGoods場合{
err の場合: = listModel .With ( model .GoodsItem { } ) .Scan ( & out .List ) ; err != nil {
戻り値エラー
}
} .Type == consts .CollectionTypeArticle場合
err の場合: = listModel .With ( model .ArticleItem { } ) .Scan ( & out .List ) ; err != nil {
戻り値エラー
}
}それ以外{
err : = listModel .WithAll ( ) .Scan ( & out .List ) ; err != nil {
戻り値エラー
}
}
戻る
}

上記の例では、関連付けに with モデルを使用しています。基本的な考え方は、入力パラメータに基づいて対応する関連モデルを識別し、無効なクエリの実行を回避することです。

更新操作

更新操作で null 値をフィルタリングするには、OmitEmpty を使用します。例:

 func ( * sAddress ) Update ( ctx context .Context , in model .UpdateAddressInput ) ( err error ) {
if _ err = dao .AddressInfo .Ctx ( ctx ) .Data ( in ) .OmitEmpty ( ) .Where ( dao .AddressInfo .Columns ( ) .Id in .Id ) .Update ( ) ; err != nil {
エラーを返す
}
nilを返す
}

開発中にOmitEmpty()を使用していなかったので、この問題を見落としていました。提案してくれた友人に感謝します。

オープンソースプロジェクトのアドレス:

オープンソース プロジェクトに関しては、すぐに成功するとは思っていません。常に継続的な改善と長期的な努力の投資を目指してきました。https: //github.com/wangzhongyang007/goframe-shop-v2

この記事は、WeChat公式アカウント「プログラマーのレベルアップとモンスターとの闘いの旅」(著者:王中洋)からの転載です。以下のQRコードをスキャンしてアカウントをフォローできます。

この記事を転載する場合は、WeChat公式アカウント「プログラマーのレベルアップとモンスターとの戦いの旅」までご連絡ください。