ES數據打平或者flat扁平化 導致嵌套對象查詢可能出錯


探索ES-對象和嵌套對象(三)

前文回顧

上篇文章寫了探索ES-入門Kibana(二),算是基本上講完了ElasticSearchKibana的安裝和基本的概念。今天來正式講一講一些ElasticSearch在使用中會遇到的問題和解決問題的方式方法。

引言

ElasticSearch作為一個Nosql的數據庫,其中一個特點是不支持多表關聯的。所以,在ElasticSearch中的數據都是以反范式的方式進行存儲的。簡單來說,就是會存儲為一個大的Json對象

對象數據在ElasticSearch中是作為object的類型存儲的。但是對象數據如果是數組的情況下,會存在查詢時候不能關聯查詢的問題。

不知道有沒有小伙伴看到這里一臉懵比?

如果你不知道這是什么問題,那么請看下面的例子說明。

如果你知道是什么意思,那么請看例子下面的解決方式。

對象

舉一個博客系統的例子。

假設我們要在ElasticSearch中建立一個blog索引。blog索引包括下面幾個內容。

  • title:博客的標題
  • content:博客的內容
  • comment:博客的評論
  • comment.username:博客評論的用戶
  • comment.content:博客評論的內容

不知道有沒有人發現這里博客和評論是一對多的關系,沒有發現也沒有關系,繼續往下面看。

根據上面分解的字段,我們在ElasticSearch中建立下面這個索引。

PUT blog
{
  "mappings": {
    "_doc": {
      "properties": {
        "blog": {
          "properties": {
            "title": {
              "type": "keyword"
            },
            "content": {
              "type": "text"
            },
            "comment": {
              "properties": {
                "content": {
                  "type": "text"
                },
                "username": {
                  "type": "keyword"
                }
              }
            }
          }
        }
      }
    }
  }
}
復制代碼

可以發現blogcomment都是作為object類型來存儲的。在ElasticSearch中會默認將字段作為object來存儲。

我們插入一條文檔。

PUT blog/_doc/1
{
  "blog": {
    "title": "this is my first blog",
    "content": "this is my first blog content",
    "comment": {
      "content": "so bad!",
      "username": "li si"
    }
  }
}
復制代碼

這條文檔的內容大致是有人寫了一篇博客,然后李四同學在下面批評說so bad

我們以評論為維度進行搜索。

GET blog/_search
{
  "query":{
    "match": {
      "blog.comment.content": "bad"
    }
  }
}
復制代碼

是可以搜索到數據的。

 

 

扁平化

這里要注意的是對象數據最后在ES中都是會被flat(扁平化)的

什么意思?

就是說上面的這條內容數據最后在ES中是以下面這種形式存儲的。

blog:this is my first blog content:this is my first blog content comment.content:so bad comment.username:li si
復制代碼

但是,一般來說博客肯定不止是一條數據,一般來說會有多條數據,就是上面提到的一對多的關系了。

我們更新之前的數據。

PUT blog/_doc/1
{
  "blog": {
    "title": "this is my first blog",
    "content": "this is my first blog content",
    "comment": [
      {
        "content": "oh so good!",
        "username": "zhang san"
      },
      {
        "content": "so bad!",
        "username": "li si"
      }
    ]
  }
}
復制代碼

上面這條數據的意思是除了李四說bad之后,還有個張三大哥說了good。

我們按照之前的查詢方式也是可以正常查詢到的。但是,我們現在如果要關聯查詢,我們既要評論者是李四的,又要李四說good的查詢。我們寫一個bool查詢。bool可以寫多個查詢,不論是term還是match都只能寫一個查詢。

GET blog/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "blog.comment.content": "good"
          }
        },
        {
          "match": {
            "blog.comment.username": "li si"
          }
        }
      ]
    }
  }
}
復制代碼

但是,我們居然還是能夠查詢到這條數據。明明這條數據,李四說的是bad而不是good,為什么還是能夠查詢到呢?

 

 

那是因為對象被扁平化了之后,在ES中是這么存儲的。

blog.comment.content:["oh so good!","so bad!"]
blog.comment.username:["zhang san","li si"]
復制代碼

兩個字段都是一個數組的形式來存儲數據的。數組與數組之間沒有對應關系。

ES先在blog.comment.coetent中看看有沒有good,發現有。再在blog.comment.username中看看有沒有li si發現還是有。

最后這條數據就被查詢出來了。

那么怎么才能實現關聯查詢呢?用nested,嵌套對象即可。

嵌套對象

默認字段的類型是object類型,要使用nested需要顯式指定。我們刪除之前的索引之后,重新建立索引。因為在ES中,我們沒有辦法修改之前的mapping,我們只能刪除重新建立。如果需要之前的數據,可以使用reindex來將數據導入到新索引中。

PUT blog
{
  "mappings": {
    "_doc": {
      "properties": {
        "blog": {
          "properties": {
            "title": {
              "type": "keyword"
            },
            "content": {
              "type": "text"
            },
            "comment": {
              "type":"nested",
              "properties": {
                "content": {
                  "type": "text"
                },
                "username": {
                  "type": "keyword"
                }
              }
            }
          }
        }
      }
    }
  }
}
復制代碼

我們再重新插入一條數據。

PUT blog/_doc/1
{
  "blog": {
    "title": "this is my first blog",
    "content": "this is my first blog content",
    "comment": [
      {
        "content": "oh so good!",
        "username": "zhang san"
      },
      {
        "content": "so bad!",
        "username": "li si"
      }
    ]
  }
}
復制代碼

我們進行關聯搜索。

GET blog/_search
{
  "query": {
    "bool": {
      "must": [
        {
          "match": {
            "blog.comment.content": "good"
          }
        },
        {
          "match": {
            "blog.comment.username": "li si"
          }
        }
      ]
    }
  }
}
復制代碼

這個時候,我們發現就搜索不到對應的數據了。因為這個時候,ESnested對象的關系額外進行了存儲。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM