Elasticsearch:painless script 語法基礎(chǔ)和實(shí)戰(zhàn)

摘要:Elasticsearch嗅虏,Java

script的作用

script是Elasticsearch的拓展功能卧秘,通過定制的表達(dá)式實(shí)現(xiàn)已經(jīng)預(yù)設(shè)好的API無法完成的個(gè)性化需求杆勇,比如完成以下操作

  • 字段再加工/統(tǒng)計(jì)輸出
  • 字段之間邏輯運(yùn)算
  • 定義查詢得分的計(jì)算公式
  • 定義特殊過濾條件完成搜索
  • 類似于pandas的個(gè)性化增刪改操作

內(nèi)容概述

  • (1)script格式說明,inline和stored腳本的調(diào)用方法
  • (2)在無新增文檔的情況下,對(duì)現(xiàn)有文檔的字段個(gè)性化字段更新(update物咳, _update_by_query诫钓,ctx._source旬昭,Math,數(shù)組add/remove)
  • (3)在不修改文檔的情況下菌湃,在搜索返回中添加個(gè)性化統(tǒng)計(jì)字段(_search问拘,docscript_fieldsreturn
  • (4)在無新增文檔的情況下骤坐,對(duì)現(xiàn)有文檔的字段進(jìn)行新增和刪除(ctx._source绪杏,ctx._source.remove,條件判斷)
  • (5)在無新增文檔的情況下或油,基于現(xiàn)有的多個(gè)字段生成新字段(加權(quán)求和寞忿,大小比較)
  • (6)搜索文檔時(shí)使用script腳本
  • (7)其他painless語法(循環(huán),null判斷)

script格式

語法都遵循相同的模式

"script": {
    "lang":   "...",  
    "source" | "id": "...", 
    "params": { ... } 
  }

其中三要素功能如下

  • lang:指定編程語言顶岸,默認(rèn)是painless腔彰,還有其他編程語言選項(xiàng)如expression
  • source | id: source,id二者選其一辖佣,source后面接inline腳本(就是將腳本邏輯直接放在DSL里面)霹抛,id對(duì)應(yīng)一個(gè)stored腳本(就是預(yù)先設(shè)置類似UDF,使用的時(shí)候根據(jù)UDF的id進(jìn)行調(diào)用和傳參
  • params:在腳本中任何有名字的參數(shù)卷谈,用params傳參

inline和stored腳本快速開始

使用script腳本修改某文檔的某個(gè)字段杯拐,先插入一條文檔

POST /hotel/_doc/100
{
    "name": "蘇州木棉花酒店",
    "city": "蘇州",
    "price": 399,
    "start_date": "2023-01-01"
}
(1)使用inline的方式將腳本寫在DSL里面
POST /hotel/_doc/100/_update
{
    "script": {
        "source": "ctx._source.price=333"
    }
}

注意在kibiban客戶端帶上_update,否則相當(dāng)于覆蓋整個(gè)文檔世蔗,新建了一個(gè)含有script字段的文檔端逼。本例中將price字段修改為333,如果是帶有單引號(hào)的'333'則修改為字符串?dāng)?shù)據(jù)污淋,字符串還可以使用\轉(zhuǎn)義

POST /hotel/_doc/100/_update
{
    "script": {
        "source": "ctx._source.price=\"333\""
    }
}

獲取字段的方式除了使用ctx._source.字段之外顶滩,還可以ctx._source['字段']

POST /hotel/_doc/100/_update
{
    "script": {
        "source": "ctx._source['price']=333"
    }
}

只要inline腳本中的內(nèi)容出現(xiàn)些許不一樣就需要重新編譯,因此推薦的方法是把inline中固定的部分編譯一次寸爆,變量命名放在params中傳參使用礁鲁,這樣只需要編譯一次,下次使用調(diào)用緩存

POST /hotel/_doc/100/_update
{
    "script": {
        "source": "ctx._source.price=params.price",
        "params": {
            "price": 334
        }
    }
}
(2)使用stored預(yù)先設(shè)置腳本的方式

這種類似于先注冊(cè)UDF函數(shù)赁豆,使用PUT對(duì)_scripts傳入腳本

PUT /_scripts/my_script_1
{
     "script": {
        "lang": "painless", 
        "source": "ctx._source.price=params.price"
    }
}

在插入之后使用GET可以查看到對(duì)應(yīng)的腳本內(nèi)容

GET /_scripts/my_script_1
{
  "_id" : "my_script_1",
  "found" : true,
  "script" : {
    "lang" : "painless",
    "source" : "ctx._source.price=params.price"
  }
}

腳本中并沒有指定params仅醇,params在調(diào)用的是有進(jìn)行設(shè)置,調(diào)用的時(shí)候使用id指定my_script_1這個(gè)id即可魔种,不再使用source

POST /hotel/_doc/100/_update
{
    "script": {
        "id": "my_script_1",
        "params": {
            "price": 335
        }
    }
}

script腳本更新字段

所有update/update_by_query 腳本使用 ctx._source

(1)普通字段更新

除了上面快速開始的直接使用=賦值修改的情況析二,還可以對(duì)字段做數(shù)值運(yùn)算,比如加減乘除開方等等

POST /hotel/_doc/100/_update
{
    "script": {
        "source": "ctx._source.price += 100"
    }
}

使用Math.pow對(duì)數(shù)值進(jìn)行開方

POST /hotel/_doc/100/_update
{
    "script": {
        "source": "ctx._source.price=Math.pow(ctx._source.price, 2)"
    }
}

Math下的方法還有sqrt节预,log

(2)集合字段更新

主要說明下數(shù)組類型字段的更新甲抖,使用ctx._source.字段.add/remove,先新建一個(gè)帶有數(shù)組字段的文檔

POST /hotel/_doc/101
{
    "name": "蘇州大酒店",
    "city": "蘇州",
    "tag": ["貴"]
}

使用script將tag數(shù)組字段增加元素心铃,使用add

POST /hotel/_doc/101/_update
{
    "script": {
        "source": "ctx._source.tag.add('偏')"
    }
}

插入新元素后看下數(shù)據(jù)准谚,已經(jīng)成功

      {
        "_index" : "hotel",
        "_type" : "_doc",
        "_id" : "101",
        "_score" : 1.0,
        "_source" : {
          "name" : "蘇州大酒店",
          "city" : "蘇州",
          "tag" : [
            "貴",
            "偏"
          ]
        }

刪除數(shù)組元素使用remove指定對(duì)應(yīng)的索引位置即可

POST /hotel/_doc/101/_update
{
    "script": {
        "source": "ctx._source.tag.remove(0)"
    }
}

如果位數(shù)不足會(huì)報(bào)錯(cuò)類似數(shù)組越界


script腳本對(duì)字段再加工返回

此功能使用search腳本,配合script中的doc實(shí)現(xiàn)去扣,整體效果類似于map操作柱衔,對(duì)所選定的文檔操作返回

(1)提取日期類型的元素并返回一個(gè)自定義字段

先設(shè)置一個(gè)字段schema

POST /hotel/_doc/_mapping
{
    "properties": {
        "dt": {
            "type": "date", 
            "format":  "yyyy-MM-dd HH:mm:ss"
        }
    }
}

插入一條日期數(shù)據(jù)

POST  /hotel/_doc/301
{
    "dt": "2021-01-01 13:13:13"
}

插入效果如下

      {
        "_index" : "hotel",
        "_type" : "_doc",
        "_id" : "301",
        "_score" : 1.0,
        "_source" : {
          "dt" : "2021-01-01 13:13:13"
        }

下面檢索所有文檔樊破,提取日期的年份,使用GET+_search請(qǐng)求唆铐,DSL中指定script_fields的自定義字段year哲戚,給year設(shè)置script腳本

GET /hotel/_doc/_search
{
    "script_fields": {
        "year": {
            "script": {"source": "if (doc.dt.length != 0) {doc.dt.value.year}"}
        }
    }
}

doc的取值方式
假設(shè)有一個(gè)字段:"a": 1,那么:

  • doc['a']返回的是[1]艾岂,是一個(gè)數(shù)組顺少,如果文檔沒有該字段,返回空數(shù)組及doc['a'].length=0
  • doc['a'].value返回的是1王浴,也就是取第一個(gè)元素脆炎。
  • doc['a'].values與doc['a']表現(xiàn)一致,返回整個(gè)數(shù)組[1]

script_fields腳本字段
每個(gè)_search 請(qǐng)求的匹配(hit)可以使用 script_fields定制一些屬性氓辣,一個(gè) _search 請(qǐng)求能定義多于一個(gè)以上的 script field這些定制的屬性通常是:

  • 針對(duì)原有值的修改(比如秒裕,價(jià)錢的轉(zhuǎn)換,不同的排序方法等)
  • 一個(gè)嶄新的及算出來的屬性(比如钞啸,總和几蜻,加權(quán),指數(shù)運(yùn)算体斩,距離測(cè)量等)

script_fields在結(jié)果中的返回是{fileds: 字段名:[]}的json格式和_source同一級(jí)

doc.dt.value獲取第一個(gè)數(shù)組元素梭稚,存儲(chǔ)數(shù)據(jù)類型為amic getter [org.elasticsearch.script.JodaComp,該類型通過year屬性獲得年份絮吵。查看以下返回結(jié)果弧烤,由于沒有篩選條件所有文檔都被返回,存在dt字段的提取年份源武,不存在dt字段的也會(huì)有返回值為null,由此可見_search + doc操作實(shí)際上是完成了原始文檔的一個(gè)映射轉(zhuǎn)換操作想幻,并產(chǎn)生了一個(gè)自定義的臨時(shí)字段粱栖,不會(huì)對(duì)原始索引做任何更改操作

    {
        "_index" : "hotel",
        "_type" : "_doc",
        "_id" : "301",
        "_score" : 1.0,
        "fields" : {
          "year" : [
            2021
          ]
        }
      },
      {
        "_index" : "hotel",
        "_type" : "_doc",
        "_id" : "002",
        "_score" : 1.0,
        "fields" : {
          "year" : [
            null
          ]
        }
      },
...

如果只返回存在dt字段的,需要在DSL中增加query邏輯

GET /hotel/_doc/_search
{
    "query": {
        "exists": {
            "field": "dt"
        }
    },
    "script_fields": {
        "year": {
            "script": {"source": "doc.dt.value.year"}
        }
    }
}
(2)統(tǒng)計(jì)一個(gè)數(shù)組字段數(shù)組的和并且返回

插入一個(gè)數(shù)值數(shù)組字段脏毯,搜索統(tǒng)計(jì)返回?cái)?shù)組的和

POST /hotel/_doc/_mapping
{
    "properties": {
        "goals" : {"type": "keyword"}
    }
}

插入數(shù)據(jù)

POST /_bulk
{"index": {"_index": "hotel", "_type": "_doc", "_id": "123"}}
{"name": "a酒店","city": "揚(yáng)州", "goals": [1, 5, 3] }
{"index": {"_index": "hotel", "_type": "_doc", "_id": "124"}}
{"name": "b酒店","city": "杭州", "goals": [9, 5, 1] }
{"index": {"_index": "hotel", "_type": "_doc", "_id": "125"}}
{"name": "c酒店","city": "云州", "goals": [2, 7, 9] }

下面計(jì)算有g(shù)oals字段的求goals的和到一個(gè)臨時(shí)字段

GET /hotel/_doc/_search
{
    "query": {
        "exists": {
            "field": "goals"
        }
    },
    "script_fields": {
        "goals_sum": {
            "script": {"source": """
                               int total =0;
                              for (int i=0; i < doc.goals.length; i++) {
                                     total += Integer.parseInt(doc.goals[i])
                               }
                               return total
                               """
            }
        }
    }
}

在script中每一行結(jié)束要加分號(hào);闹究,使用Java語法的循環(huán)求得數(shù)組的和,每個(gè)數(shù)組元素需要使用Java語法中的Integer.parseInt解析食店,否則報(bào)錯(cuò)String類型無法轉(zhuǎn)Num渣淤,查看返回

    "hits" : [
      {
        "_index" : "hotel",
        "_type" : "_doc",
        "_id" : "123",
        "_score" : 1.0,
        "fields" : {
          "goals_sum" : [
            9
          ]
        }
      },
      {
        "_index" : "hotel",
        "_type" : "_doc",
        "_id" : "124",
        "_score" : 1.0,
        "fields" : {
          "goals_sum" : [
            15
          ]
        }
      },
      {
        "_index" : "hotel",
        "_type" : "_doc",
        "_id" : "125",
        "_score" : 1.0,
        "fields" : {
          "goals_sum" : [
            18
          ]
        }
      }

script腳本新建/刪除字段

新建字段和刪除字段都是update操作,使用ctx._source

(1)新建字段

對(duì)于存在dt字段的文檔吉嫩,新增一個(gè)字段dt_year价认,值為dt的年份

POST /hotel/_doc/_update_by_query
{
    "query": {
        "exists": {
            "field": "dt"
        }
    }, 
    "script": {
        "source": "ctx._source.dt_year = ctx._source.dt.year"
    }
}

以上直接在source中使用ctx._source.dt_year引入一個(gè)新列,可惜直接報(bào)錯(cuò)

   "reason": "dynamic getter [java.lang.String, year] not found

此處并沒有向doc一樣數(shù)據(jù)為日期類型而是字符串自娩,因此需要引入Java解析

POST /hotel/_doc/_update_by_query
{
    "query": {
        "exists": {
            "field": "dt"
        }
    }, 
    "script": {
        "source": """
                            LocalDateTime time2Parse = LocalDateTime.parse(ctx._source.dt, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                            ctx._source.dt_year = time2Parse.getYear()
                            """
    }
}

查看結(jié)果

      {
        "_index" : "hotel",
        "_type" : "_doc",
        "_id" : "301",
        "_score" : 1.0,
        "_source" : {
          "dt" : "2021-01-01 13:13:13",
          "dt_year" : 2021
     }
}

也可以做其他操作比如獲得LocalDateTime類型之后再做格式化輸出

POST /hotel/_doc/_update_by_query
{
    "query": {
        "exists": {
            "field": "dt"
        }
    }, 
    "script": {
        "source": """
                            LocalDateTime time2Parse = LocalDateTime.parse(ctx._source.dt, DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                            ctx._source.dt_year = time2Parse.format(DateTimeFormatter.ofPattern("yyyy-MM-dd"))
                            """
    }
}
(2)刪除字段

刪除字段直接使用ctx._source.remove(\"字段名\")用踩,可以刪除單個(gè)文檔,也可以u(píng)pdate_by_query批量刪除

POST /hotel/_doc/123
{
    "script": {
        "source": "ctx._source.remove(\"goals\")"
    }
}
POST /hotel/_doc/_update_by_query
{
    "query": {
        "exists": {
            "field": "goals"
        }
    },
    "script": {
        "source": "ctx._source.remove(\"goals\")"
    }
}

script腳本條件判斷

支持if,else if脐彩,else碎乃,比如根據(jù)某值進(jìn)行二值判斷生成新字段

POST /hotel/_doc/_update_by_query
{
    "query": {
        "exists": {
            "field": "price"
        }
    },
    "script": {
        "source": """
                          double price = ctx._source.price;
                          if (price >= 10) {
                                ctx._source.expensive = 1
                           } else {
                               ctx._source.expensive = 0
                           }
                          """
    }
}
POST /hotel/_doc/_update_by_query
{
    "query": {
        "exists": {
            "field": "price"
        }
    },
    "script": {
        "source": """
                          double price = ctx._source.price;
                          if (price >= 10) {
                                ctx._source.expensive = 1
                           } else if (price == 0) {
                               ctx._source.expensive = -1
                           } else {
                               ctx._source.expensive = 0
                           }
                          """
    }
}

注意:經(jīng)過多輪測(cè)試如果source中有多輪if判斷語法會(huì)報(bào)錯(cuò),貌似只能支持一個(gè)if惠奸,解決方案是使用Java的三元表達(dá)式?;梅誓,三元表達(dá)式寫多少個(gè)判斷都行


script使用return

return用在_search操作中,配合script_fields使用佛南,例如在搜索結(jié)果中新增一個(gè)字段area為china梗掰,此字段不更新到索引只是在搜索時(shí)返回

GET /hotel/_doc/_search
{
    "_source": true,
    "script_fields": {
        "area": {
            "script": {
                "source": "return \"china\""
            }
        }
    }
}

以上指定"_source": true防止被script_fields覆蓋,一條輸出結(jié)果如下

    {
        "_index" : "hotel",
        "_type" : "_doc",
        "_id" : "123",
        "_score" : 1.0,
        "_source" : {
          "city" : "揚(yáng)州",
          "name" : "a酒店"
        },
        "fields" : {
          "area" : [
            "china"
          ]
        }

script多個(gè)字段組合/邏輯判斷
(1)多個(gè)字段加權(quán)求和

先插入3個(gè)子模型分共虑,在生成一個(gè)總分愧怜,權(quán)重是0.6,0.2,0.2

POST /_bulk
{"index": {"_index": "hotel", "_type": "_doc", "_id": "333"}}
{"name": "K酒店","city": "揚(yáng)州", "model_1": 0.79, "model_2": 0.39, "model_3": 0.72}
{"index": {"_index": "hotel", "_type": "_doc", "_id": "334"}}
{"name": "L酒店","city": "江州", "model_1": 0.62, "model_2": 0.55, "model_3": 0.89}
{"index": {"_index": "hotel", "_type": "_doc", "_id": "335"}}
{"name": "S酒店","city": "兗州", "model_1": 0.83, "model_2": 0.45, "model_3": 0.58}

現(xiàn)在計(jì)算總分給到score字段

POST /hotel/_doc/_update_by_query
{
      "query": {
              "bool": {
                   "must":  [
                           {"exists": {
                                 "field": "model_1"
                                  }},
                             {"exists": {
                                   "field": "model_2"
                                 }},
                            {"exists": {
                                   "field": "model_3"
                                 }}
                    ]
            }
      },
    "script": {
           "source": "ctx._source.score = 0.6 * ctx._source.model_1 + 0.2 * ctx._source.model_2 + 0.2 * ctx._source.model_3"  
    }
}

看一下運(yùn)行結(jié)果

GET /hotel/_doc/_search
{
    "query": {
        "exists": {
            "field": "score"
        }
    }
}
   "hits" : [
      {
        "_index" : "hotel",
        "_type" : "_doc",
        "_id" : "335",
        "_score" : 1.0,
        "_source" : {
          "score" : 0.704,
          "city" : "兗州",
          "name" : "S酒店",
          "model_1" : 0.83,
          "model_3" : 0.58,
          "model_2" : 0.45
        }
      },
      {
        "_index" : "hotel",
        "_type" : "_doc",
        "_id" : "333",
        "_score" : 1.0,
        "_source" : {
          "score" : 0.6960000000000001,
          "city" : "揚(yáng)州",
          "name" : "K酒店",
          "model_1" : 0.79,
          "model_3" : 0.72,
          "model_2" : 0.39
        }
      },
    ...
(2)兩個(gè)字段大小比較

直接取ctx._source對(duì)應(yīng)字段進(jìn)行比較,使用Java三元表達(dá)式?:賦值給新字段

POST /hotel/_doc/_update_by_query
{
      "query": {
              "bool": {
                   "must":  [
                           {"exists": {
                                 "field": "model_1"
                                  }},
                             {"exists": {
                                   "field": "model_2"
                                 }}
                    ]
            }
      },
    "script": {
           "source": "ctx._source.max_score = ctx._source.model_1 > ctx._source.model_2 ? ctx._source.model_1 : ctx._source.model_2"  
    }
}

script腳本null判斷

有兩種情況字段為null和params為null

(1)字段為null

如果某字段為空妈拌,文檔不存在該字段拥坛,則填充為0

POST /hotel/_doc/_update_by_query
{
    "script": {
        "source": "if (ctx._source.score == null) ctx._source.score = 0.0"
    }
}
(2)params傳參為null

如果傳入params不存在某個(gè)key,則刪除該字段

POST /hotel/_doc/_update_by_query
{
    "script": {
        "source": """
                            String[] cols = new String[3];
                            cols[0] = "name";
                            cols[1] = "city";
                            cols[2] = "price";
                            for (String c : cols) {
                                    if (params[c] == null) {
                                           ctx._source.remove(c)
                                   } else {
                                       ctx._source[c] = params[c]
                                   }
                            }
                            """,
        "params": {
               "name": "test",
               "city": "test_loc"
          }
    }
}

注意:在循環(huán)中拿到局部變量c傳遞給params尘分,params[c]不能用點(diǎn).或者帶有雙引號(hào)params["c"]猜惋,否則是判斷params中是否有c這個(gè)名字的字段

在本例中使用String[] cols = new String[3];創(chuàng)建了一個(gè)靜態(tài)變量,對(duì)于這種集合類的變量painless的語法和Java略有不同培愁,寫幾個(gè)例子如下

ArrayList l = new ArrayList();  // Declare an ArrayList variable l and set it to a newly allocated ArrayList
Map m = new HashMap();          // Declare a Map variable m and set it   to a newly allocated HashMap

List l = new ArrayList(); // Declare List variable l and set it a newly allocated ArrayList
List m;                   // Declare List variable m and set it the default value null
int[] ia1;                      //Declare int[] ia1; store default null to ia1    
int[] ia2 = new int[2];               //Allocate 1-d int array instance with length [2] → 1-d int array reference; store 1-d int array reference to ia1        
ia2[0] = 1;                     //Load from ia1 → 1-d int array reference; store int 1 to index [0] of 1-d int array reference 
int[][] ic2 = new int[2][5];    //Declare int[][] ic2; allocate 2-d int array instance with length [2, 5] → 2-d int array reference; store 2-d int array reference to ic2
ic2[1][3] = 2;                  //Load from ic2 → 2-d int array reference; store int 2 to index [1, 3] of 2-d int array reference
ic2[0] = ia1;                   //Load from ia1 → 1-d int array reference; load from ic2 → 2-d int array reference; store 1-d int array reference to index [0] of 2-d int array reference; (note ia1, ib1, and index [0] of ia2 refer to the same instance)

List著摔,Map這些集合都沒有泛型,并且集合的值貌似不能直接初始化定续,需要add谍咆,put進(jìn)來


script作為查詢過濾條件

查看某列的值大于某列,在query下可以使用script私股,注意格式script下還套著一個(gè)script摹察,search請(qǐng)求使用doc獲取值

GET /hotel/_doc/_search
{
    "query": {
                "script" : {
                    "script" : {
                        "source": "doc.score.value < doc.model_3.value"
                     }
                }
            }
    }

以上語句會(huì)報(bào)warn,doc選取字段如果字段為空會(huì)填充默認(rèn)值倡鲸,因此再限制一下字段不為空

GET /hotel/_doc/_search
{
    "query": {
        "bool" : {
            "must" : [{
                "script" : {
                    "script" : {
                        "source": "doc.score.value < doc.model_3.value"
                     }
                }
            },
            {"exists": {"field": "score"}}, 
            {"exists": {"field": "model_3"}}
          ]
        }
    }
}
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末供嚎,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子峭状,更是在濱河造成了極大的恐慌克滴,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件优床,死亡現(xiàn)場(chǎng)離奇詭異劝赔,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)胆敞,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門望忆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來罩阵,“玉大人,你說我怎么就攤上這事启摄「灞冢” “怎么了?”我有些...
    開封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵歉备,是天一觀的道長(zhǎng)傅是。 經(jīng)常有香客問我,道長(zhǎng)蕾羊,這世上最難降的妖魔是什么喧笔? 我笑而不...
    開封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮龟再,結(jié)果婚禮上书闸,老公的妹妹穿的比我還像新娘。我一直安慰自己利凑,他們只是感情好浆劲,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著哀澈,像睡著了一般牌借。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上割按,一...
    開封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天膨报,我揣著相機(jī)與錄音,去河邊找鬼适荣。 笑死现柠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的弛矛。 我是一名探鬼主播够吩,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼汪诉!你這毒婦竟也來了废恋?” 一聲冷哼從身側(cè)響起谈秫,我...
    開封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤扒寄,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后拟烫,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體该编,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年硕淑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了课竣。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片嘉赎。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖于樟,靈堂內(nèi)的尸體忽然破棺而出公条,到底是詐尸還是另有隱情,我是刑警寧澤迂曲,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布靶橱,位于F島的核電站,受9級(jí)特大地震影響路捧,放射性物質(zhì)發(fā)生泄漏关霸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一杰扫、第九天 我趴在偏房一處隱蔽的房頂上張望队寇。 院中可真熱鬧,春花似錦章姓、人聲如沸佳遣。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽苍日。三九已至,卻和暖如春窗声,著一層夾襖步出監(jiān)牢的瞬間相恃,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工笨觅, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留拦耐,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓见剩,卻偏偏與公主長(zhǎng)得像杀糯,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子苍苞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容