〈三〉ElasticSearch的認識:搜索、過濾、排序

来源:https://www.cnblogs.com/progor/archive/2019/09/20/11557869.html
-Advertisement-
Play Games

ElasticSearch第三篇,關於搜索、過濾、排序簡單講解。 ...


目錄

發表日期:2019年9月20日


上節回顧


1.講瞭如何對索引CRUD
2.重新解釋了type,只是元數據的效果
3.講瞭如何對文檔CRUD



本節前言


1.ElasticSearch的主要功能是搜索,這節也將會主要講搜索,將會涉及到如何使用關鍵字進行全文搜索
2.除了講搜索,也會講到搜索相關的“分頁”、“排序”、“聚合分析”等內容。
3.還會補充一些與搜索相關的知識。



文檔的搜索




測試數據:請先插入以下數據,以便練習搜索功能
【突然看了一下之前的博文,發現我後面去準備數據的時候寫錯格式了。所以導致id為1,2,3的文檔和後面的文檔的欄位不一樣。你可以僅僅基於以下的數據來測試】

PUT /douban/book/5
{
    "book_id":5,
    "book_name":"A Boy's Own Story",
    "book_author":"Edmund White",
    "book_pages":217,
    "book_express":"Vintage",
    "publish_date":"1994-02-01",
    "book_summary":"""

  An instant classic upon its original publication, A Boy's Own Story is the first of Edmund White's highly acclaimed trilogy of autobiographical novels that brilliantly evoke a young man's coming of age and document American gay life through the last forty years.
  
  The nameless narrator in this deeply affecting work reminisces about growing up in the 1950s with emotionally aloof, divorced parents, an unrelenting sister, and the schoolmates who taunt him. He finds consolation in literature and his fantastic imagination. Eager to cultivate intimate, enduring friendships, he becomes aware of his yearning to be loved by men, and struggles with the guilt and shame of accepting who he is. Written with lyrical delicacy and extraordinary power, A Boy's Own Story is a triumph."""
}


PUT /douban/book/6
{
    "book_id":6,
    "book_name":"The Lost Language of Cranes",
    "book_author":"David Leavitt",
    "book_pages":352,
    "book_express":"Bloomsbury Publishing PLC",
    "publish_date":"2005-05-02",
    "book_summary":"""David Leavitt's extraordinary first novel, now reissued in paperback, is a seminal work about family, sexual identity, home, and loss. Set in the 1980s against the backdrop of a swiftly gentrifying Manhattan, The Lost Language of Cranes tells the story of twenty-five-year-old Philip, who realizes he must come out to his parents after falling in love for the first time with a man. Philip's parents are facing their own crisis: pressure from developers and the loss of their longtime home. But the real threat to this family is Philip's father's own struggle with his latent homosexuality, realized only in his Sunday afternoon visits to gay porn theaters. Philip's admission to his parents and his father's hidden life provoke changes that forever alter the landscape of their worlds."""
}

PUT /douban/book/7
{
    "book_id":7,
    "book_name":"Immortality",
    "book_author":"Milan Kundera",
    "book_pages":400,
    "book_express":"Faber and Faber",
    "publish_date":"2000-08-21",
    "book_summary":"""Milan Kundera's sixth novel springs from a casual gesture of a woman to her swimming instructor, a gesture that creates a character in the mind of a writer named Kundera. Like Flaubert's Emma or Tolstoy's Anna, Kundera's Agnes becomes an object of fascination, of indefinable longing. From that character springs a novel, a gesture of the imagination that both embodies and articulates Milan Kundera's supreme mastery of the novel and its purpose: to explore thoroughly the great themes of existence."""
}


搜索的方式主要有兩種,URL搜索和請求體搜索,一個是將搜索的條件寫在URL中,一個是將請求寫在請求體中。


URL參數條件搜索

語法:GET /index/type/_search?參數

參數解析:

  • q:使用某個欄位來進行查詢,例如q:book_name=book,就是根據book_name中是否有book來進行搜索。
  • sort:使用某個欄位來進行排序,例如sort=cost:desc,就是根據cost欄位來進行降序desc排序。
  • 其他:fileds,timeout,analyzer【這些參數留在請求體搜索中講】
  • 不帶參數時,為“全搜索”
  • 多個參數使用&&拼接


示例:

GET /douban/book/_search?q=book_summary:character
GET /douban/book/_search?q=book_author:Milan
GET /douban/book/_search?q=book_summary:a
GET /douban/book/_search?q=book_summary:a&&sort=book_pages:desc
GET /douban/book/_search?q=book_summary:a&&q=book_author:Milan
【值得註意的是,請先不要對text類型的數據進行排序,這會影響搜索,對整數排序即可,後面會再細講】

查詢結果解析:
【考慮到數據太長的問題,所以我給了另一個搜索結果的返回截圖】

補充:把搜索條件寫在url中的搜索方式比較少用,因為查詢參數拼接到URL中會比較麻煩。




請求體條件搜索

語法與示例:

//全搜索
GET /index/type/_search
GET /douban/book/_search

//全搜索
GET /index/type/_search
{
  "query": {
    "match_all": {}
  }
}
GET /douban/book/_search
{
  "query": {
    "match_all": {}
  }
}

// 查詢指定欄位的數據(全文搜索,如果搜索值有多個詞,僅匹配一個詞的結果也可以查詢出來):
GET /index/type/_search
{
  "query": {
    "match": {
      "欄位名": "搜索值"
    }
  }
}
GET /douban/book/_search
{
  "query": {
    "match": {
      "book_name": "A The"
    }
  }
}


// 使用同一搜索值搜索多個欄位:
GET /index/type/_search
{
  "query": {
    "multi_match": {
      "query": "搜索值",
      "fields": [
        "搜索的欄位1","搜索的欄位2"]
    }
  }
}
GET /douban/book/_search
{
  "query": {
    "multi_match": {
      "query": "A",
      "fields": [
        "book_name","book_summary"]
    }
  }
}

// 短語查詢:【搜索值必須完全匹配,不會把搜索值拆分來搜索】
GET /index/type/_search
{
  "query": {
    "match_phrase": {
      "欄位": "搜索值"
    }
  }
}
GET /douban/book/_search
{
  "query": {
    "match_phrase": {
      "book_summary": "a character"
    }
  }
}

// 欄位過濾,查詢的結果只顯示指定欄位
GET /product/book/_search
{
  "query": {
    "查詢條件"
  },
  "_source": [
    "顯示的欄位1",
    "顯示的欄位2"
    ]
}
GET /douban/book/_search
{
  "query": {
    "match": {
      "book_name": "Story"
    }
  },
  "_source": [
    "book_name",
    "book_id"
    ]
}

// 高亮查詢:【根據查詢的關鍵字來進行高亮,高亮的結果會顯示在返回結果的會自動在返回結果中的highlight中,關鍵字會被加上<em>標簽】
// 如果想要多欄位高亮,也需要進行多欄位搜索
GET /index/book/_search
{
  "query": {
    "查詢條件"
  },
  "highlight": {
    "fields": {
      "高亮的欄位名1": {}
    }
  }
}
GET /douban/book/_search
{
  "query": {
    "match": {
      "book_summary": "Story"
    }
  },
  "highlight": {
    "fields": {
      "book_summary":{}
    }
  }
}
GET /douban/book/_search
{
  "query": {
    "multi_match": {
      "query": "Story",
      "fields": [
        "book_name","book_summary"]
    }

  },
  "highlight": {
    "fields": {
      "book_summary":{},
      "book_name":{}
    }
  }
}


上面展示了關於全搜索單欄位值全文搜索多欄位單一搜索值全文搜索短語搜索欄位過濾高亮搜索的代碼。


由於對多個欄位使用不同搜索值涉及條件拼接,所以單獨講。
前置知識講解:對於條件拼接,在SQL中有and,or,not,在ElasticSearch不太一樣,下麵逐一講解:

  • bool:用來表明裡面的語句是多條件的組合,用來包裹多個條件。
  • should:裡面可以有多個條件,查詢結果必須符合查詢條件中的一個或多個。
  • must:裡面的多個條件都必須成立
  • must_not:裡面的多個條件必須不成立


示例:

// 書名必須包含Story的
GET /douban/book/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match":{
            "book_name":"Story"
          }
        }
      ]
    }
  }
}

// 書名必須不包含Story的
GET /douban/book/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "match":{
            "book_name":"Story"
          }
        }
      ]
    }
  }
}

// 書名必須不包含Story,書名包含Adventures或Immortality的
GET /douban/book/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "match":{
            "book_name":"Story"
          }
        }
      ],
      "should": [
        {
          "match": {
            "book_name": "Adventures"
          }
        },
        {
          "match": {
            "book_name": "Immortality"
          }
        }
      ]
    }
  }
}


// 在should、must、must_not這些裡面都可以放多個條件
GET /douban/book/_search
{
  "query": {
    "bool": {
      "must_not": [
        {
          "match":{
            "book_name":"Story"
          }
        },
        {
          "match": {
            "book_name": "Adventures"
          }
        }
      ]
    }
  }
}

// 如果是單個條件的時候,還可以這樣寫,省去[]:
GET /douban/book/_search
{
  "query": {
    "bool": {
      "must_not": {
          "match":{
            "book_name":"Story"
          }
      }
    }
  }
}

// 還可以條件嵌套,也就是再嵌套一層bool,不過要註意邏輯,例如:
// 查詢出(書名有story)或者(書名有The而且作者名有David)的,第二個是可成立可不成立的。
GET /douban/book/_search
{
  "query": {
    "bool": {
      "should": [
        {
          "match": {
            "book_name": "Story"
          }
        },
        {
          "bool": {
            "must": [
              {
                "match": {
                  "book_name": "The"
                }
              },
              {
                "match": {
                  "book_author": "David"
                }
              }
            ]
          }
        }
      ]
    }
  }
}




補充:

  • 上面講了URL參數條件搜索和請求體條件搜索,講了全搜索單欄位值全文搜索多欄位單一搜索值全文搜索短語搜索欄位過濾高亮搜索的使用方法,還講了基於bool、should、must、must_not的多條件搜索,上面的知識已經能基礎地實現一些搜索功能了。但還是有一些知識由於比較晦澀,所以留到後面章節講,比如給搜索指定分詞器、給多條件指定匹配數量、滾動查詢等等。




小節總結:

上面講了URL參數條件搜索和請求體條件搜索。URL參數條件寫在URL裡面,用?來附帶參數,q用來指定搜索欄位。請求體參數把條件寫在請求體中,query是最外層的包裹,match_all用於查詢所有,match用來使用指定搜索值搜索某一欄位,match_phrase用來搜索連續的搜索,_source用來欄位過濾(與query同級,[]裡面是欄位名),highlight用來高亮搜索(與query同級,裡面是{field:{欄位名1:{},欄位名2:{}}}),bool、should、must、must_not用來多條件搜索。



文檔的過濾filter




過濾的效果其實有點像條件搜索,不過條件搜索會考慮相關度分數和考慮分詞,而過濾是不考慮這些的,過濾對相關度沒有影響。過濾一般用於結構化的數據上,也就是通常不用於使用了分詞的數據上,通常都會用在數值類型和日期類型的數據上。


在搜索的時候,如果你不希望要搜索的條件會影響到相關度,那麼就把它放在過濾中,如果希望影響相關度,那麼就放在條件搜索中。
使用過濾時,由於不考慮相關度,所以score固定為1。


文檔的過濾filter裡面主要有五種欄位,range,term,terms,exist,missing。range用於欄位數據比較大小;term主要用於比較字元類型的和數值類型的數據是否相等;terms是term的複數版,裡面可以有多個用於比較相等的值;exist和missing用於判斷文檔中是否包含指定欄位或沒有某個欄位(僅適用於2.0+版本,目前已經移除)




語法與舉例:

// range,gte是不小於,lte是不大於,eq是等於,gt是大於,lt是小於
GET / index/type/_search
{
  "query": {
    "range": {
      "欄位名": {
        "gte": 比較值
        [,"lte": 比較值]
      }
    }
  }
}
GET /douban/book/_search
{
  "query": {
    "range": {
      "book_pages": {
        "gte": 352,
        "lt":400
      }
    }
  }
}


// term用於匹配字元串和數值型類型的數據(解決了range中沒有eq的問題),但不能直接用於分詞的欄位。
//【這個並沒有那麼簡單,會後續再講,直接匹配一些會分詞的欄位時,會匹配失敗,
//因為這時候這個欄位拿來匹配的都是散亂的值,不是完整的原本的欄位數據,所以下麵用了不分詞的數值型的欄位來演示】
GET /douban/book/_search
{
  "query": {
    "term": {
      "欄位": "搜索值"
    }
  }
}
GET /douban/book/_search
{
  "query": {
    "term": {
      "book_pages": 352
    }
  }
}


//terms
GET /douban/book/_search
{
  "query": {
    "terms": {
      "欄位": ["搜索值1","搜索值2"]
    }
  }
}
GET /douban/book/_search
{
  "query": {
    "terms": {
      "book_pages": [
        "352",
        "400"
      ]
    }
  }
}




term的問題:

  • 首先,提一下的是,在搜索的時候,你並不直接面向原始文檔數據,而是面向倒排索引,這意思是什麼呢?比如你要進行全文搜索,那麼你的搜索值並不是與數據文件比對的,而是與倒排索引匹配的,也就是在我們與數據文件之間有一個專門用於搜索的層次。
  • 對於match和match_all,這些都是全文搜索,就不說了,直接就是通過索引詞在索引文件中找到對應的文檔;比較不同的是match_phrase這個會匹配一段詞的搜索,他是怎麼查詢的呢?他實際上也會去查索引文件中包括了搜索值中所有詞並且詞的在文檔中的位置順序也一致的記錄,所以這個短語匹配其實也是通過倒排索引來搜索的。
  • 而倒排索引中其實包含了所有欄位的標識,對於分詞的欄位,會存儲索引詞;對於不分詞的,會存儲整個數據。【對於分詞的欄位可以加一個keyword來保留完整的數據,這個後面再講。】
  • 而term的搜索主要面向不分詞的數據,所以無法直接用於分詞的欄位,除非加keyword。
    官方文檔中關於term




filter與bool

filter也可以用於多條件拼接。例如:

GET /douban/book/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match":{
            "book_name":"Story"
          }
        },
        {
          "range": {
            "book_pages": {
              "lte":300
              }
          }
        }
      ]
    }
  }
}

GET /douban/book/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match":{
            "book_name":"Story"
          }
        },
        {
          "range": {
            "book_pages": {
              "lte":300
              }
          }
        },
        {
          "term": {
            "publish_date": "1994-02-01"
          }
        }
      ]
    }
  }
}




在這樣條件搜索和過濾一起用的情況下,要註意filter過濾是不計算相關度的,在上面中,假設只有match,那麼某個文檔相關度為0.2,加上filter後,會變成1.2。因為filter預設提供的相關度為1。




constant_score

過濾還可以這樣寫:

GET /douban/book/_search
{
    "query": {
        "constant_score": {
            "filter": {
                "range": {
                    "book_pages": {
                        "gte": 352,
                        "lt": 400
                    }
                }
            }
        }
    }
}

// boost設置filter提供的相關度score值
GET /douban/book/_search
{
    "query": {
        "constant_score": {
            "filter": {
                "range": {
                    "book_pages": {
                        "gte": 352,
                        "lt": 400
                    }
                }
            },
            "boost": 1.2
        }
    }
}




cache

對於過濾,elasticsearch會臨時緩存它的結果,以便可能下次仍需使用它。因為過濾是不關心相關度的。
官方文檔--過濾緩存




小節總結:

這節介紹了不影響相關度的搜索--過濾,過濾通常用於過濾結構化數據,也就是那些不分詞的數據,其中range用於數值範圍過濾,term用於字元類型的數據或數值類型的數據的值是否相等,terms是term的複數版。過濾也支持bool拼接多個條件。過濾提供的相關度分數是一個常數,預設是1。





文檔的聚合分析




準備數據

先準備一批測試數據:

PUT /people/test/1
{
  "name":"lilei1",
  "age":18,
  "gender":1
}
PUT /people/test/2
{
  "name":"lilei2",
  "age":17,
  "gender":0
}

PUT /people/test/3
{
  "name":"lilei4",
  "age":21,
  "gender":1
}

PUT /people/test/4
{
  "name":"lilei4",
  "age":15,
  "gender":0
}
PUT /people/test/5
{
  "name":"lilei1 2",
  "age":15,
  "gender":0
}

像在SQL中會需要SUM(),MAX().AVG()函數。ElasticSearch也提供了關於聚合分析的函數。


ElasticSearch中常見的聚合分析函數有terms(分組函數)avg(平均數)range(區間分組)max(求最大值)min(求最小值)cardinality(獲取唯一值的數量)value_count(獲取值的數量,不去重,可以得出多少個值參與了聚合)




語法與舉例:

語法:

GET /index/type/_search
{
  "aggs": {
    "自定義聚合名稱": {
      "聚合函數": {
        聚合參數
      }
    }
  }
}

舉例:

// 按性別分組
GET /douban/book/_search
{
  "aggs": {
    "groud_by_express": {
      "terms": {
        "field": "book_id",
        "size": 10
      }
    }
  }
}
//求年齡的平均數
GET /people/test/_search
{
  "aggs": {
    "avg_of_age": {
      "avg": {
        "field": "age"
      }
    }
  }
}
// 求年齡的最大值:
GET /people/test/_search
{
  "aggs": {
    "max_of_age": {
      "max": {
        "field": "age"
      }
    }
  }
}
// 把年齡[15,17]的分成一組,把年齡[18,25]的分成一組
GET /people/test/_search
{
  "aggs": {
    "range_by_age": {
      "range": {
        "field": "age",
        "ranges": [
          {
            "from": 15,
            "to": 17
          },
          {
            "from": 18,
            "to": 25
          }
        ]
      }
    }
  }
}
// 獲取不同的年齡數:,比如有年齡[1,2,3,3,4,5],得到的結果是5,因為3只算一次
GET /people/test/_search
{
  "aggs": {
    "get_diff_age_count": {
      "cardinality": {
        "field": "age"
      }
    }
  }
}

返回結果解析:




其他語法:

先查詢後聚合:

GET /people/test/_search
{
  "query": {
    "match": {
      "name": "lilei1"
    }
  }, 
  "aggs": {
    "avg_of_age": {
      "avg": {
        "field": "age"
      }
    }
  }
}

先過濾後聚合:

// 先獲取年齡大於15的,再求平均值
GET /people/test/_search
{
  "query": {
    "range": {
      "age": {
        "gt":15
      }
    }
  }, 
  "aggs": {
    "avg_of_age": {
      "avg": {
        "field": "age"
      }
    }
  }
}

聚合函數嵌套:

// 先按性別分組,再獲取年齡平均值
GET /people/test/_search
{
    "aggs": {
        "groud_by_express": {
            "terms": {
                "field": "gender"
            },
            "aggs": {
                "avg_of_age": {
                    "avg": {
                        "field": "age"
                    }
                }
            }
        }
    }
}

聚合+排序:

// 先按性別分組,再按分組的年齡平均值降序排序,order中的avg_of_age就是下麵的聚合函數的自定義名稱
GET /people/test/_search
{
    "aggs": {
        "groud_by_express": {
            "terms": {
                "field": "gender",
                "order": {
                  "avg_of_age": "desc"
                }
            },
            "aggs": {
                "avg_of_age": {
                    "avg": {
                        "field": "age"
                    }
                }
            }
        }
    }
}




補充:

上面只講了一些基礎的聚合,聚合分析是一個比較重要的內容,會在後面的再講。




小節總結:

本節主要講了ElasticSearch中關於數據聚合的使用方法,aggs是與query同級的,使用聚合函數需要自己定義一個外層的聚合函數名稱,avg用於求平均值,max用於求最大值,range用於範圍分組,term用於數據分組。分組可以與條件搜索和過濾一起使用,aggs是與query同級的,聚合函數也可以嵌套使用。


文檔的分頁、排序




【使用一下上一節準備的數據】




分頁

// 從第一條開始,獲取兩條數據
GET /people/test/_search
{
  "from": 0,
  "size": 2
}

// 可以先查詢,再分頁
GET /people/test/_search
{
  "query": {
    "match": {
      "name": "lilei1"
    }
  }, 
  "from": 0,
  "size": 1
}




排序

【請註意,下麵的結果中你可以看到score為null,因為這時候你使用了age欄位來排序,而不是相關性,所以此時相關性意義不大,則不計算。】

排序處理:【sort與query同級別,是一個數組,裡面可以有多個排序參數,參數以{"FIELD":{"order":"desc/asc"}}為格式】
GET /people/test/_search
{
  "query": {
    "match_all": {}
  }, 
  "sort": [
    {
      "age": {
        "order": "desc"
      }
    }
  ]
}




deep paging

對於分頁和排序,需要共同面對一個問題:

  • 首先,你要想到一個索引中的數據是散落在多個分片上的,你如何確定另一個分片上的數據與其他分片上的順序問題?,比如可能A分片上的有數值為1和數值為3的數據,而B分片上有數值為2和數值為4的數據,所以B分片的部分數據與A分片數據的大小是不確定的。那麼排序的時候怎麼處理這些散落的數據呢?(就算是依據相對度來排序,這個時候散落的數據的相關度也是不太好確定的)

  • 分頁需要面對的問題也同樣是因為數據散落的而不好排序的問題,因為分頁也是要排序的,預設是按相關度排序。因為散落的數據的值的大小不確定,所以就需要把所有可能的數據取出來排完序再分頁,這就會導致需要取出遠遠超出“頁數”的數據來計算。

  • 有兩個primary shard(命名為A和B),現在要取第1000頁的數據,假設每一頁10條記錄,那麼理論上是只需要取第10000到第10010條記錄出來即可。
  • 但這時候我們並不知道A和B中的_score的大小如何,可能A中的最小的_score要比B中的最大的_score都要大,反過來也有可能,(所以我們並不能說僅僅從A和B中分別取10000到10010出來進行比較即可,我們需要對前面的數據都進行比較,以避免最小的_score都比另一個shard上的_score大的情況),為了確保數據的正確性,我們需要從A和B中都取出1到10010的數據來進行排序比較,然後再取出裡面的10000到10010條。
  • 所以,你看到了,我們只是為了拿十條數據,竟然要查10010數據出來。這就是deep paging了。




補充:

  • 除了上述的內容,一個沒講的而且比較重要的內容應該是滾動查詢,滾動查詢有點類似分頁查詢,但它會提前準備好數據,我暫時沒想好放在哪裡講合適,可能會在後面寫,也有可能某一天補充在這裡。

小節總結:

上面講了怎麼進行數據的分頁獲取和數據的排序,使用from和size分頁,使用sort排序;還講了一個如果查詢時頁數太深而可能導致的deep paging問題,問題的原因是多個分片上的數據大小不確定,不方便排序。






您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • CentOS7 以上機器一些命令和低版本CentOS是有些差異的,本文只針對CentOS7 以上版本。 CentOS7使用firewalld打開關閉防火牆與埠1、firewalld的基本使用啟動: systemctl start firewalld關閉: systemctl stop firewa ...
  • 安裝完成後在主機上登陸時,不管是輸入用戶,還是匿名都無法登陸 經過檢查,發現是因為/etc/hosts.deny禁止了所有ip訪問 將hosts.deny中的all:all刪除,或者在/etc/hosts.allow中添加vsftpd:all:allow都可以解決 ...
  • 前言 雖然現在已經9102年了硬碟空間越來越大越便宜,但win系統用久了系統盤還是會漸漸變小的,公司的電腦系統盤就已經不夠用了經常爆紅,這個時候兩個簡單快速高效的辦法 1.擴大系統盤空間,可以使用 win自帶的磁碟分區功能或者下個分區軟體,這點適用於其他盤空閑空間足的情況。 2.清理系統盤中的用不到 ...
  • 一、通過SecureCRT的rz、sz實現文件的上傳和下載 1.檢查是否安裝sz rz,命令如下 2.安裝lrzsz軟體,命令如下 3.文件上傳 4.文件下載 二、通過sftp實現文件的上傳和下載 為了數據和服務的安全, 很多生產環境中的Linux伺服器不能使用外網環境. 在只有SSH連接的情況下, ...
  • [toc] 1.Playbook劇本初識 1.什麼是playbook,playbook翻譯過來就是“劇本”,那playbook組成如下 play: 定義的是主機的角色task: 定義的是具體執行的任務playbook: 由一個或多個play組成,一個play可以包含多個task任務 簡單理解為: 使 ...
  • 所有關係的集合,構成一個關係資料庫。 以關係模型作為數據的邏輯模型,並採用關係作為數據組織方式的一類數 據庫,其資料庫操作建立在關係代數的基礎上。 ...
  • Mybatis的定義 MyBatis 是一款優秀的持久層框架,它支持定製化 SQL、存儲過程以及高級映射。MyBatis 避免了幾乎所有的 JDBC 代碼和手動設置參數以及獲取結果集。MyBatis 可 以使用簡單的 XML 或註解來配置和映射原生信息,將介面和 Java 的 POJOs(Plain ...
  • [TOC] 第十六章、初識資料庫 一、資料庫 二、資料庫的組成 三、資料庫的分類 四、卸載資料庫 五、安裝資料庫 六、連接資料庫 七、用戶信息查看 八、資料庫的基本操作 九、表的基本操作 十、記錄的基本操作 1)查看某個資料庫中的某個表的所有記錄,如果在對應資料庫中,可以直接查找表 mysql : ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...