Elasticsearch -- 映射和分析

映射(mapping)機制用於進行字段類型確認,將每一個字段匹配爲一種肯定的數據類型(stringnumber,booleansdate等)。算法

分析(analysis)機制用於進行全文文本(Full Text)的分詞,以創建供搜索用的反向索引。sql

 

映射及分析

當在索引中處理數據時,咱們注意到一些奇怪的事。有些東西彷佛被破壞了:數據庫

在索引中有12個tweets,只有一個包含日期2014-09-15,可是咱們看看下面查詢中的total hits。數組

GET /_search?q=2014              # 12 個結果
GET /_search?q=2014-09-15        # 仍是 12 個結果 !
GET /_search?q=date:2014-09-15   # 1  一個結果
GET /_search?q=date:2014         # 0  個結果 !

爲何全日期的查詢返回全部的tweets,而針對date字段進行年度查詢卻什麼都不返回? 爲何咱們的結果因查詢_all字段(譯者注:默認全部字段中進行查詢)或date字段而變得不一樣?app

想必是由於咱們的數據在_all字段的索引方式和在date字段的索引方式不一樣而致使。dom

讓咱們看看Elasticsearch在對gb索引中的tweet類型進行mapping(也稱之爲模式定義[注:此詞有待從新定義(schema definition)])後是如何解讀咱們的文檔結構:nosql

GET /gb/_mapping/tweet

返回:elasticsearch

{
   "gb": {
      "mappings": {
         "tweet": {
            "properties": {
               "date": {
                  "type": "date",
                  "format": "dateOptionalTime"
               },
               "name": {
                  "type": "string"
               },
               "tweet": {
                  "type": "string"
               },
               "user_id": {
                  "type": "long"
               }
            }
         }
      }
   }
}

Elasticsearch爲對字段類型進行猜想,動態生成了字段和類型的映射關係。返回的信息顯示了date字段被識別爲date類型。_all由於是默認字段因此沒有在此顯示,不過咱們知道它是string類型。工具

date類型的字段和string類型的字段的索引方式是不一樣的,所以致使查詢結果的不一樣,這並不會讓咱們以爲驚訝。測試

你會指望每一種核心數據類型(strings, numbers, booleans及dates)以不一樣的方式進行索引,而這點也是現實:在Elasticsearch中他們是被區別對待的。

可是更大的區別在於確切值(exact values)(好比string類型)及全文文本(full text)之間。

這二者的區別才真的很重要 - 這是區分搜索引擎和其餘數據庫的根本差別。

 

確切值(Exact values) vs. 全文文本(Full text)

Elasticsearch中的數據能夠大體分爲兩種類型:

確切值 及 全文文本

確切值是肯定的,正如它的名字同樣。好比一個date或用戶ID,也能夠包含更多的字符串好比username或email地址。

確切值"Foo""foo"就並不相同。確切值20142014-09-15也不相同。

全文文本,從另外一個角度來講是文本化的數據(經常以人類的語言書寫),好比一片推文(Twitter的文章)或郵件正文。

1


全文文本經常被稱爲非結構化數據,實際上是一種用詞不當的稱謂,實際上天然語言是高度結構化的。

問題是天然語言的語法規則是如此的複雜,計算機難以正確解析。例如這個句子:

May is fun but June bores me.

究竟是說的月份仍是人呢?


確切值是很容易查詢的,由於結果是二進制的 -- 要麼匹配,要麼不匹配。下面的查詢很容易以SQL表達:

WHERE name    = "John Smith"
  AND user_id = 2
  AND date    > "2014-09-15"

而對於全文數據的查詢來講,卻有些微妙。咱們不會去詢問這篇文檔是否匹配查詢要求?。 可是,咱們會詢問這篇文檔和查詢的匹配程度如何?。換句話說,對於查詢條件,這篇文檔的相關性有多高?

咱們不多確切的匹配整個全文文本。咱們想在全文中查詢包含查詢文本的部分。不只如此,咱們還指望搜索引擎能理解咱們的意圖

  • 一個針對"UK"的查詢將返回涉及"United Kingdom"的文檔

  • 一個針對"jump"的查詢同時可以匹配"jumped", "jumps", "jumping"甚至"leap"

  • "johnny walker"也能匹配"Johnnie Walker", "johnnie depp""Johnny Depp"

  • "fox news hunting"能返回有關hunting on Fox News的故事,而"fox hunting news"也能返回關於fox hunting的新聞故事。

爲了方便在全文文本字段中進行這些類型的查詢,Elasticsearch首先對文本分析(analyzes),而後使用結果創建一個倒排索引。咱們將在如下兩個章節討論倒排索引及分析過程。

 

倒排索引

Elasticsearch使用一種叫作倒排索引(inverted index)的結構來作快速的全文搜索。倒排索引由在文檔中出現的惟一的單詞列表,以及對於每一個單詞在文檔中的位置組成。

例如,咱們有兩個文檔,每一個文檔content字段包含:

  1. The quick brown fox jumped over the lazy dog
  2. Quick brown foxes leap over lazy dogs in summer

爲了建立倒排索引,咱們首先切分每一個文檔的content字段爲單獨的單詞(咱們把它們叫作詞(terms)或者表徵(tokens))(譯者注:關於termstokens的翻譯比較生硬,只需知道語句分詞後的個體叫作這兩個。),把全部的惟一詞放入列表並排序,結果是這個樣子的:

Term Doc_1 Doc_2
Quick   X
The X  
brown X X
dog X  
dogs   X
fox X  
foxes   X
in   X
jumped X  
lazy X X
leap   X
over X X
quick X  
summer   X
the X  

如今,若是咱們想搜索"quick brown",咱們只須要找到每一個詞在哪一個文檔中出現便可:

Term Doc_1 Doc_2
brown X X
quick X  
----- ------- -----
Total 2 1

兩個文檔都匹配,可是第一個比第二個有更多的匹配項。 若是咱們加入簡單的類似度算法(similarity algorithm),計算匹配單詞的數目,這樣咱們就能夠說第一個文檔比第二個匹配度更高——對於咱們的查詢具備更多相關性。

可是在咱們的倒排索引中還有些問題:

  1. "Quick""quick"被認爲是不一樣的單詞,可是用戶可能認爲它們是相同的。
  2. "fox""foxes"很類似,就像"dog""dogs"——它們都是同根詞。
  3. "jumped""leap"不是同根詞,但意思類似——它們是同義詞。

上面的索引中,搜索"+Quick +fox"不會匹配任何文檔(記住,前綴+表示單詞必須匹配到)。只有"Quick""fox"都在同一文檔中才能夠匹配查詢,可是第一個文檔包含"quick fox"且第二個文檔包含"Quick foxes"。(譯者注:這段真囉嗦,說白了就是單複數和同義詞無法匹配)

用戶能夠合理地但願兩個文檔都能匹配查詢,咱們也能夠作得更好。

若是咱們將詞爲統一爲標準格式,這樣就能夠找到不是確切匹配查詢,可是足以類似從而能夠關聯的文檔。例如:

  1. "Quick"能夠轉爲小寫成爲"quick"
  2. "foxes"能夠被轉爲根形式"fox"。同理"dogs"能夠被轉爲"dog"
  3. "jumped""leap"同義就能夠只索引爲單個詞"jump"

如今的索引:

Term Doc_1 Doc_2
brown X X
dog X X
fox X X
in   X
jump X X
lazy X X
over X X
quick X X
summer   X
the X X

但咱們還未成功。咱們的搜索"+Quick +fox"依舊失敗,由於"Quick"的確切值已經不在索引裏,不過,若是咱們使用相同的標準化規則處理查詢字符串的content字段,查詢將變成"+quick +fox",這樣就能夠匹配到兩個文檔。

IMPORTANT

這很重要。你只能夠找到確實存在於索引中的詞,因此索引文本和查詢字符串都要標準化爲相同的形式

這個標記化和標準化的過程叫作分詞(analysis),這個在下節中咱們討論。

 

分析和分析器

分析(analysis)是這樣一個過程:

  • 首先,標記化一個文本塊爲適用於倒排索引單獨的詞(term)
  • 而後標準化這些詞爲標準形式,提升它們的「可搜索性」或「查全率」

這個工做是分析器(analyzer)完成的。一個分析器(analyzer)只是一個包裝用於將三個功能放到一個包裏:

字符過濾器

首先字符串通過字符過濾器(character filter),它們的工做是在標記化前處理字符串。字符過濾器可以去除HTML標記,或者轉換"&""and"

分詞器

下一步,分詞器(tokenizer)被標記化成獨立的詞。一個簡單的分詞器(tokenizer)能夠根據空格或逗號將單詞分開(譯者注:這個在中文中不適用)。

標記過濾

最後,每一個詞都經過全部標記過濾(token filters),它能夠修改詞(例如將"Quick"轉爲小寫),去掉詞(例如停用詞像"a""and""the"等等),或者增長詞(例如同義詞像"jump""leap"

Elasticsearch提供不少開箱即用的字符過濾器,分詞器和標記過濾器。這些能夠組合來建立自定義的分析器以應對不一樣的需求。咱們將在《自定義分析器》章節詳細討論。

內建的分析器

不過,Elasticsearch還附帶了一些預裝的分析器,你能夠直接使用它們。下面咱們列出了最重要的幾個分析器,來演示這個字符串分詞後的表現差別:

"Set the shape to semi-transparent by calling set_trans(5)"

標準分析器

標準分析器是Elasticsearch默認使用的分析器。對於文本分析,它對於任何語言都是最佳選擇(譯者注:就是沒啥特殊需求,對於任何一個國家的語言,這個分析器就夠用了)。它根據Unicode Consortium的定義的單詞邊界(word boundaries)來切分文本,而後去掉大部分標點符號。最後,把全部詞轉爲小寫。產生的結果爲:

set, the, shape, to, semi, transparent, by, calling, set_trans, 5

簡單分析器

簡單分析器將非單個字母的文本切分,而後把每一個詞轉爲小寫。產生的結果爲:

set, the, shape, to, semi, transparent, by, calling, set, trans

空格分析器

空格分析器依據空格切分文本。它不轉換小寫。產生結果爲:

Set, the, shape, to, semi-transparent, by, calling, set_trans(5)

語言分析器

特定語言分析器適用於不少語言。它們可以考慮到特定語言的特性。例如,english分析器自帶一套英語停用詞庫——像andthe這些與語義無關的通用詞。這些詞被移除後,由於語法規則的存在,英語單詞的主體含義依舊能被理解(譯者注:stem English words這句不知道該如何翻譯,查了字典,我理解的大概意思應該是將英語語句比做一株植物,去掉無用的枝葉,主幹依舊存在,停用詞比如枝葉,存在與否並不影響對這句話的理解。)。

2

english分析器將會產生如下結果:

set, shape, semi, transpar, call, set_tran, 5

注意"transparent""calling""set_trans"是如何轉爲詞幹的。

當分析器被使用

當咱們索引(index)一個文檔,全文字段會被分析爲單獨的詞來建立倒排索引。不過,當咱們在全文字段搜索(search)時,咱們要讓查詢字符串通過一樣的分析流程處理,以確保這些詞在索引中存在。

全文查詢咱們將在稍後討論,理解每一個字段是如何定義的,這樣纔可讓它們作正確的事:

  • 當你查詢全文(full text)字段,查詢將使用相同的分析器來分析查詢字符串,以產生正確的詞列表。
  • 當你查詢一個確切值(exact value)字段,查詢將不分析查詢字符串,可是你能夠本身指定。

如今你能夠明白爲何《映射和分析》的開頭會產生那種結果:

  • date字段包含一個確切值:單獨的一個詞"2014-09-15"
  • _all字段是一個全文字段,因此分析過程將日期轉爲三個詞:"2014""09""15"

當咱們在_all字段查詢2014,它一個匹配到12條推文,由於這些推文都包含詞2014

GET /_search?q=2014              # 12 results

當咱們在_all字段中查詢2014-09-15,首先分析查詢字符串,產生匹配任一20140915的查詢語句,它依舊匹配12個推文,由於它們都包含詞2014

GET /_search?q=2014-09-15        # 12 results !

當咱們在date字段中查詢2014-09-15,它查詢一個確切的日期,而後只找到一條推文:

GET /_search?q=date:2014-09-15   # 1  result

當咱們在date字段中查詢2014,沒有找到文檔,由於沒有文檔包含那個確切的日期:

GET /_search?q=date:2014         # 0  results !

測試分析器

尤爲當你是Elasticsearch新手時,對於如何分詞以及存儲到索引中理解起來比較困難。爲了更好的理解如何進行,你可使用analyze API來查看文本是如何被分析的。在查詢字符串參數中指定要使用的分析器,被分析的文本作爲請求體:

GET /_analyze?analyzer=standard&text=Text to analyze

結果中每一個節點在表明一個詞:

{
   "tokens": [
      {
         "token":        "text",
         "start_offset": 0,
         "end_offset":   4,
         "type":         "<ALPHANUM>",
         "position":     1
      },
      {
         "token":        "to",
         "start_offset": 5,
         "end_offset":   7,
         "type":         "<ALPHANUM>",
         "position":     2
      },
      {
         "token":        "analyze",
         "start_offset": 8,
         "end_offset":   15,
         "type":         "<ALPHANUM>",
         "position":     3
      }
   ]
}

token是一個實際被存儲在索引中的詞。position指明詞在原文本中是第幾個出現的。start_offsetend_offset表示詞在原文本中佔據的位置。

analyze API 對於理解Elasticsearch索引的內在細節是個很是有用的工具,隨着內容的推動,咱們將繼續討論它。

指定分析器

當Elasticsearch在你的文檔中探測到一個新的字符串字段,它將自動設置它爲全文string字段並用standard分析器分析。

你不可能老是想要這樣作。也許你想使用一個更適合這個數據的語言分析器。或者,你只想把字符串字段看成一個普通的字段——不作任何分析,只存儲確切值,就像字符串類型的用戶ID或者內部狀態字段或者標籤。

爲了達到這種效果,咱們必須經過映射(mapping)人工設置這些字段。

 

映射

爲了可以把日期字段處理成日期,把數字字段處理成數字,把字符串字段處理成全文本(Full-text)或精確的字符串值,Elasticsearch須要知道每一個字段裏面都包含了什麼類型。這些類型和字段的信息存儲(包含)在映射(mapping)中。

正如《數據吞吐》一節所說,索引中每一個文檔都有一個類型(type)。 每一個類型擁有本身的映射(mapping)或者模式定義(schema definition)。一個映射定義了字段類型,每一個字段的數據類型,以及字段被Elasticsearch處理的方式。映射還用於設置關聯到類型上的元數據。

在《映射》章節咱們將探討映射的細節。這節咱們只是帶你入門。

核心簡單字段類型

Elasticsearch支持如下簡單字段類型:

類型 表示的數據類型
String string
Whole number byteshortintegerlong
Floating point floatdouble
Boolean boolean
Date date

當你索引一個包含新字段的文檔——一個以前沒有的字段——Elasticsearch將使用動態映射猜想字段類型,這類型來自於JSON的基本數據類型,使用如下規則:

JSON type Field type
Boolean: true or false "boolean"
Whole number: 123 "long"
Floating point: 123.45 "double"
String, valid date: "2014-09-15" "date"
String: "foo bar" "string"

注意

這意味着,若是你索引一個帶引號的數字——"123",它將被映射爲"string"類型,而不是"long"類型。然而,若是字段已經被映射爲"long"類型,Elasticsearch將嘗試轉換字符串爲long,並在轉換失敗時會拋出異常。

查看映射

咱們可使用_mapping後綴來查看Elasticsearch中的映射。在本章開始咱們已經找到索引gb類型tweet中的映射:

GET /gb/_mapping/tweet

這展現給了咱們字段的映射(叫作屬性(properties)),這些映射是Elasticsearch在建立索引時動態生成的:

{
   "gb": {
      "mappings": {
         "tweet": {
            "properties": {
               "date": {
                  "type": "date",
                  "format": "strict_date_optional_time||epoch_millis"
               },
               "name": {
                  "type": "string"
               },
               "tweet": {
                  "type": "string"
               },
               "user_id": {
                  "type": "long"
               }
            }
         }
      }
   }
}

小提示

錯誤的映射,例如把age字段映射爲string類型而不是integer類型,會形成查詢結果混亂。

要檢查映射類型,而不是假設它是正確的!

自定義字段映射

雖然大多數狀況下基本數據類型已經可以知足,但你也會常常須要自定義一些特殊類型(fields),特別是字符串字段類型。 自定義類型可使你完成一下幾點:

  • 區分全文(full text)字符串字段和準確字符串字段(譯者注:就是分詞與不分詞,全文的通常要分詞,準確的就不須要分詞,好比『中國』這個詞。全文會分紅『中』和『國』,但做爲一個國家標識的時候咱們是不須要分詞的,因此它就應該是一個準確的字符串字段)。
  • 使用特定語言的分析器(譯者注:例如中文、英文、阿拉伯語,不一樣文字的斷字、斷詞方式的差別)
  • 優化部分匹配字段
  • 指定自定義日期格式(譯者注:這個比較好理解,例如英文的 Feb,12,2016 和 中文的 2016年2月12日
  • 以及更多

映射中最重要的字段參數是type。除了string類型的字段,你可能不多須要映射其餘的type

{
    "number_of_clicks": {
        "type": "integer"
    }
}

string類型的字段,默認的,考慮到包含全文本,它們的值在索引前要通過分析器分析,而且在全文搜索此字段前要把查詢語句作分析處理。

對於string字段,兩個最重要的映射參數是indexanalyer

index

index參數控制字符串以何種方式被索引。它包含如下三個值當中的一個:

解釋
analyzed 首先分析這個字符串,而後索引。換言之,以全文形式索引此字段。
not_analyzed 索引這個字段,使之能夠被搜索,可是索引內容和指定值同樣。不分析此字段。
no 不索引這個字段。這個字段不能爲搜索到。

string類型字段默認值是analyzed。若是咱們想映射字段爲確切值,咱們須要設置它爲not_analyzed

{
    "tag": {
        "type":     "string",
        "index":    "not_analyzed"
    }
}

其餘簡單類型(longdoubledate等等)也接受index參數,但相應的值只能是nonot_analyzed,它們的值不能被分析。

分析

對於analyzed類型的字符串字段,使用analyzer參數來指定哪種分析器將在搜索和索引的時候使用。默認的,Elasticsearch使用standard分析器,可是你能夠經過指定一個內建的分析器來更改它,例如whitespacesimpleenglish

{
    "tweet": {
        "type":     "string",
        "analyzer": "english"
    }
}

在《自定義分析器》章節咱們將告訴你如何定義和使用自定義的分析器。

更新映射

你能夠在第一次建立索引的時候指定映射的類型。此外,你也能夠晚些時候爲新類型添加映射(或者爲已有的類型更新映射)。

重要

你能夠向已有映射中增長字段,但你不能修改它。若是一個字段在映射中已經存在,這可能意味着那個字段的數據已經被索引。若是你改變了字段映射,那已經被索引的數據將錯誤而且不能被正確的搜索到。

咱們能夠更新一個映射來增長一個新字段,可是不能把已有字段的類型那個從analyzed改到not_analyzed

爲了演示兩個指定的映射方法,讓咱們首先刪除索引gb

DELETE /gb

而後建立一個新索引,指定tweet字段的分析器爲english

PUT /gb <1>
{
  "mappings": {
    "tweet" : {
      "properties" : {
        "tweet" : {
          "type" :    "string",
          "analyzer": "english"
        },
        "date" : {
          "type" :   "date"
        },
        "name" : {
          "type" :   "string"
        },
        "user_id" : {
          "type" :   "long"
        }
      }
    }
  }
}

<1> 這將建立包含mappings的索引,映射在請求體中指定。

再後來,咱們決定在tweet的映射中增長一個新的not_analyzed類型的文本字段,叫作tag,使用_mapping後綴:

PUT /gb/_mapping/tweet
{
  "properties" : {
    "tag" : {
      "type" :    "string",
      "index":    "not_analyzed"
    }
  }
}

注意到咱們再也不須要列出全部的已經存在的字段,由於咱們無法修改他們。咱們的新字段已經被合併至存在的那個映射中。

測試映射

你能夠經過名字使用analyze API測試字符串字段的映射。對比這兩個請求的輸出:

GET /gb/_analyze?field=tweet&text=Black-cats <1>

GET /gb/_analyze?field=tag&text=Black-cats <2>

<1> <2> 咱們想要分析的文本被放在請求體中。

tweet字段產生兩個詞,"black""cat",tag字段產生單獨的一個詞"Black-cats"。換言之,咱們的映射工做正常。

 

 

複合核心字段類型

除了以前提到的簡單的標量類型,JSON還有null值,數組和對象,全部這些Elasticsearch都支持:

多值字段

咱們想讓tag字段包含多個字段,這很是有可能發生。咱們能夠索引一個標籤數組來代替單一字符串:

{ "tag": [ "search", "nosql" ]}

對於數組不須要特殊的映射。任何一個字段能夠包含零個、一個或多個值,一樣對於全文字段將被分析併產生多個詞。

言外之意,這意味着數組中全部值必須爲同一類型。你不能把日期和字符竄混合。若是你建立一個新字段,這個字段索引了一個數組,Elasticsearch將使用第一個值的類型來肯定這個新字段的類型。

當你從Elasticsearch中取回一個文檔,任何一個數組的順序和你索引它們的順序一致。你取回的_source字段的順序一樣與索引它們的順序相同。

然而,數組是作爲多值字段被索引的,它們沒有順序。在搜索階段你不能指定「第一個值」或者「最後一個值」。倒不如把數組看成一個值集合(bag of values)

空字段

固然數組能夠是空的。這等價於有零個值。事實上,Lucene無法存放null值,因此一個null值的字段被認爲是空字段。

這四個字段將被識別爲空字段而不被索引:

"empty_string":             "",
"null_value":               null,
"empty_array":              [],
"array_with_null_value":    [ null ]

多層對象

咱們須要討論的最後一個天然JSON數據類型是對象(object)——在其它語言中叫作hash、hashmap、dictionary 或者 associative array.

內部對象(inner objects)常常用於在另外一個對象中嵌入一個實體或對象。例如,作爲在tweet文檔中user_nameuser_id的替代,咱們能夠這樣寫:

{
    "tweet":            "Elasticsearch is very flexible",
    "user": {
        "id":           "@johnsmith",
        "gender":       "male",
        "age":          26,
        "name": {
            "full":     "John Smith",
            "first":    "John",
            "last":     "Smith"
        }
    }
}

內部對象的映射

Elasticsearch 會動態的檢測新對象的字段,而且映射它們爲 object 類型,將每一個字段加到 properties字段下

{
  "gb": {
    "tweet": { <1>
      "properties": {
        "tweet":            { "type": "string" },
        "user": { <2>
          "type":             "object",
          "properties": {
            "id":           { "type": "string" },
            "gender":       { "type": "string" },
            "age":          { "type": "long"   },
            "name":   { <3>
              "type":         "object",
              "properties": {
                "full":     { "type": "string" },
                "first":    { "type": "string" },
                "last":     { "type": "string" }
              }
            }
          }
        }
      }
    }
  }
}

<1> 根對象.

<2><3> 內部對象.

username字段的映射與tweet類型本身很類似。事實上,type映射只是object映射的一種特殊類型,咱們將 object 稱爲根對象。它與其餘對象如出一轍,除非它有一些特殊的頂層字段,好比 _source,_all 等等。

內部對象是怎樣被索引的

Lucene 並不瞭解內部對象。 一個 Lucene 文件包含一個鍵-值對應的扁平表單。 爲了讓 Elasticsearch 能夠有效的索引內部對象,將文件轉換爲如下格式:

{
    "tweet":            [elasticsearch, flexible, very],
    "user.id":          [@johnsmith],
    "user.gender":      [male],
    "user.age":         [26],
    "user.name.full":   [john, smith],
    "user.name.first":  [john],
    "user.name.last":   [smith]
}

內部欄位可被歸類至name,例如"first"。 爲了區別兩個擁有相同名字的欄位,咱們可使用完整路徑,例如"user.name.first" 或甚至類型名稱加上路徑:"tweet.user.name.first"

注意: 在以上扁平化文件中,並無欄位叫做user也沒有欄位叫做user.name。 Lucene 只索引階層或簡單的值,而不會索引複雜的資料結構。

對象-數組

內部對象數組

最後,一個包含內部對象的數組如何索引。 咱們有個數組以下所示:

{
    "followers": [
        { "age": 35, "name": "Mary White"},
        { "age": 26, "name": "Alex Jones"},
        { "age": 19, "name": "Lisa Smith"}
    ]
}

此文件會如咱們以上所說的被扁平化,但其結果會像如此:

{
    "followers.age":    [19, 26, 35],
    "followers.name":   [alex, jones, lisa, smith, mary, white]
}

{age: 35}{name: Mary White}之間的關聯會消失,因每一個多值的欄位會變成一個值集合,而非有序的陣列。 這讓咱們能夠知道:

  • 是否有26歲的追隨者?

但咱們沒法取得準確的資料如:

  • 是否有26歲的追隨者且名字叫Alex Jones?

關聯內部對象可解決此類問題,咱們稱之爲嵌套對象,咱們之後會在嵌套對象中提到它。

相關文章
相關標籤/搜索