• ElasticSearch的DSL高级查询操作


    1、ES的两种查询方式

    1、查询字符串搜索

    GET /user/_search?q=name:张三
    

    2、DSL查询

    Elasticsearch提供丰富且灵活的查询语言叫做DSL查询(Query DSL),它允许你构建更加复杂、强大的查询。 DSL(Domain Specific Language特定领域语言)以JSON请求体的形式出现。

    GET user/_search
    {
      "query": {
        "match": {
          "name": "张三"
        }
      }
    }
    

    平时更多采用这种方式,因为可操作性更强,处理复杂请求时更得心应手。

    2、精确查询(term)

    term查询不会分析查询条件,只有当词条和查询字符串完全匹配时才匹配,也就是精确查找,比如数字,日期,布尔值或 not_analyzed 的字符串(未经分析的文本数据类型):

    测试准备数据:

    PUT /test/_doc/1
    {
      "name":"张三",
      "age":18,
      "sex":"男",
      "address":"上海市浦东区"
    }
    PUT /test/_doc/2
    {
      "name":"李四",
      "age":29,
      "sex":"男",
      "address":"广东省广州市"
    }
    PUT /test/_doc/3
    {
      "name":"王五",
      "age":30,
      "sex":"男",
      "address":"广东省深圳市"
    }
    

    示例:查询年龄为29岁的用户信息

    POST /test/_search
    {
      "query": {
        "term": {
          "age": 29
        }
      }
    }
    

    image


    terms查询:terms 跟 term 有点类似,但 terms 允许指定多个匹配条件。 如果某个字段指定了多个值,那么文档需要一起去 做匹配:

    示例:查询年龄为29或者30岁的用户信息

    POST /test/_search
    {
      "query": {
        "terms": {
          "age": [
            29,
            30
          ]
        }
      }
    }
    

    image

    3、全文查询(match)

    全文查询会分析查询条件,先将查询条件进行分词,然后查询,求并集。

    term和match的区别是:match是经过analyer的,也就是说,文档首先被分析器给处理了。根据不同的分析器,分析的结果也稍显不同,然后再根据分词结果进行匹配。term则不经过分词,它是直接去倒排索引中查找了精确的值了。

    match 查询语法汇总:

    1. match_all:查询全部。
    2. match:返回所有匹配的分词。
    3. match_phrase:短语查询,在match的基础上进一步查询词组,可以指定slop分词间隔。
    4. match_phrase_prefix:前缀查询,根据短语中最后一个词组做前缀匹配,可以应用于搜索提示,但注意和max_expanions搭配。其实默认是50.......
    5. multi_match:多字段查询,使用相当的灵活,可以完成match_phrase和match_phrase_prefix的工作。

    测试准备数据:

    PUT /test1/_doc/1
    {
      "name":"Jack",
      "age":18,
      "sex":"男",
      "address":"美国纽约州",
      "remark":"美国是世界上军事实力最强大的国家"
    }
    PUT /test1/_doc/2
    {
      "name":"李四",
      "age":29,
      "sex":"男",
      "address":"中国广东省广州市",
      "remark":"中国是世界上人口最多的国家"
    }
    PUT /test1/_doc/3
    {
      "name":"阿三",
      "age":30,
      "sex":"男",
      "address":"印度新德里",
      "remark":"印度的食品干净又卫生"
    }
    PUT /test1/_doc/4
    {
      "name":"王五",
      "age":30,
      "sex":"男",
      "address":"中国北京市三里屯",
      "remark":"中国的首都是北京市"
    }
    

    1、match系列之match_all (查询全部)

    GET /test1/_search
    {
      "query": {
        "match_all": {}
      }
    }
    

    这个没啥好说的。


    2、match系列之match查询(单字段条件查询)

    POST /test1/_search
    {
      "query": {
        "match": {
          "address": "中国"
        }
      }
    }
    

    image

    从结果我们可以看到,我查询的条件是中国,虽然如期的返回了中国的文档,但是为什么含有美国的地址信息也被查询出来了,这并不是我们想要的,是怎么回事呢?因为这是elasticsearch在内部对文档做分词的时候,对于中文来说,就是一个字一个字分的,例如:'I Love You',此时会被分为三个词分别是:I、Love、You,而中文也是一样的,例如:‘我爱中国’,会被分词为:我、爱、中、国。所以我们的查询条件也会被分为:中和国,所以中和国都符合条件,当去查询文档的时候,此时美国中的 国 字也符合,所以匹配上了并且将结果返回了。

    而我们认为中国是个短语,是一个有具体含义的词。所以elasticsearch在处理中文分词方面比较弱势。后面会讲针对中文的分词插件。但目前我们还有办法解决,那就是使用短语查询 用match_phrase来处理。


    3、match系列之match_phrase(短语查询)

    GET /test1/_search
    {
      "query": {
        "match_phrase": {
          "address": {
            "query": "中国"
          }
        }
      }
    }
    

    image

    此时可以发现返回的结果中只包含了中国的文档数据。


    4、match系列之match_phrase_prefix(最左前缀查询)智能搜索--以什么开头

    示例:查询地址以 印度 开头的文档信息

    GET /test1/_search
    {
      "query": {
        "match_phrase_prefix": {
          "remark": "印度"
        }
      }
    }
    

    image


    5、match系列之multi_match(多字段查询)

    multi_match是要在多个字段中查询同一个关键字 除此之外,mulit_match甚至可以当做match_phrase和match_phrase_prefix使用,只需要指定type类型即可

    GET /test1/_search
    {
      "query": {
        "multi_match": {
          "query": "中国",
          "fields": ["address","remark"]
        }
      }
    }
    

    image

    加上phrase属性,当设置属性 type:phrase 时 等同于 短语查询。

    GET /test1/_search
    {
      "query": {
        "multi_match": {
          "query": "中国",
          "fields": ["address","remark"],
          "type": "phrase"
        }
      }
    }
    

    加上phrase_prefix属性,当设置属性 type:phrase_prefix时 等同于 最左前缀查询。

    GET /test1/_search
    {
      "query": {
        "multi_match": {
          "query": "中国",
          "fields": ["address","remark"],
          "type": "phrase_prefix"
        }
      }
    }
    

    4、通配符查询(wildcard)

    wildcard查询:会对查询条件进行分词。还可以使用通配符 ?(任意单个字符) 和 * (0个或多个字符)

    # wildcard 查询。查询条件分词,模糊查询
    GET /test1/_search
    {
      "query": {
        "wildcard": {
          "name": {
            "value": "李*"
          }
        }
      }
    }
    

    image

    prefix查询:前缀查询

    # 前缀查询
    GET /test1/_search
    {
      "query": {
        "wildcard": {
          "name": {
            "value": "三"
          }
        }
      }
    }
    

    image

    5、排序查询(sort)

    注意:需要分词的字段不可以直接排序,比如:text类型,如果想要对这类字段进行排序,需要特别设置:对字段索引两次,一次索引分词(用于搜索)一次索引不分词(用于排序),es默认生成的text类型字段就是通过这样的方法实现可排序的。

    GET /test1/_search
    {
      "query": {
        "match_all": {}
      },
      "sort": [
        {
          "age": {
            "order": "desc"
          }
        }
      ]
    }
    

    6、分页查询

    Elasticsearchde 的分页查询和 SQL 使用 LIMIT 关键字返回只有一页的结果一样,Elasticsearch 接受 from 和 size 参数:

    • size: 结果数,默认10
    • from: 跳过开始的结果数,即从哪一行开始获取数据,默认0

    示例:从第一页开始获取两条数据

    GET /test1/_search
    {
      "query": {
        "match_all": {}
      },
      "from": 0,
      "size": 2
    }
    

    image

    示例:分页查询用户名为Jack的用户信息

    GET /test1/_search
    {
      "query": {
        "match_phrase_prefix": {
          "name": "Jack"
        }
      },
      "from": 0,
      "size": 2
    }
    

    7、范围查询(range)

    range 过滤允许我们按照指定范围查找一批数据,范围操作符包含:

    • gt:大于,相当于关系型数据库中的 >。
    • gte:大于等于,相当于关系型数据库中的 >=。
    • lt:小于,相当于关系型数据库中的 <。
    • lte:小于等于,相当于关系型数据库中的 <=。

    示例:查询年龄在20-30岁之间的用户信息

    POST /test1/_search
    {
      "query": {
        "range": {
          "age": {
            "gte": 20,
            "lte": 30
          }
        }
      }
    }
    

    image

    8、布尔查询(bool)

    bool 查询可以用来合并多个条件查询结果的布尔逻辑,它包含一下操作符:

    • must:多个查询条件必须完全匹配,相当于关系型数据库中的 and。
    • should:至少有一个查询条件匹配,相当于关系型数据库中的 or。
    • must_not: 多个查询条件的相反匹配,相当于关系型数据库中的 not。
    • filter:过滤满足条件的数据。
      • range:条件筛选范围。
        • gt:大于,相当于关系型数据库中的 >。
        • gte:大于等于,相当于关系型数据库中的 >=。
        • lt:小于,相当于关系型数据库中的 <。
        • lte:小于等于,相当于关系型数据库中的 <=。

    1、must:多个查询条件必须完全匹配,相当于关系型数据库中的 and。如果有一个条件不满足则不返回数据。

    实例:查询用户名称满足叫 李四 的用户信息

    GET /test1/_search
    {
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "name": "李四"
              }
            }
          ]
        }
      }
    }
    

    image

    示例:查询用户名称满足叫 李四 并且年龄为29 的用户信息

    GET /test1/_search
    {
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "name": "李四"
              }
            },
            {
              "match": {
                "age": 29
              }
            }
          ]
        }
      }
    }
    

    image


    2、should:至少有一个查询条件匹配,相当于关系型数据库中的 or。只要符合其中一个条件就返回

    查询用户名称叫 李四 或者 年龄为27岁的用户信息

    GET /test1/_search
    {
      "query": {
        "bool": {
          "should": [
            {
              "match": {
                "name": "李四"
              }
            },
            {
              "match": {
                "age": 27
              }
            }
          ]
        }
      }
    }
    

    image


    3、must_not:多个查询条件的相反匹配,相当于关系型数据库中的 not。

    示例:查询名称不包含李四并且年龄不包含30岁的用户信息

    GET /test1/_search
    {
      "query": {
        "bool": {
          "must_not": [
            {
              "match": {
                "name": "李四"
              }
            },
            {
              "match": {
                "age": 30
              }
            }
          ]
        }
      }
    }
    

    image


    4、filter:条件过滤查询,过滤满足条件的数据。过滤条件的范围用range表示gt表示大于、lt表示小于、gte表示大于等于、lte表示小于等于

    示例:查询出用户年龄在20-30岁之间名称为 李四 的用户

    GET /test1/_search
    {
      "query": {
        "bool": {
          "must": [
            {
              "match": {
                "name": "李四"
              }
            }
          ],
          "filter": {
            "range": {
              "age": {
                "gte": 20,
                "lt": 30
              }
            }
          }
        }
      }
    }
    

    image

    9、查询结果过滤

    我们在查询数据的时候,返回的结果中,所有字段都给我们返回了,但是有时候我们并不需要那么多,所以可以对结果进行过滤处理。

    示例:只查看name和age两个属性的返回值,其他不需要。

    GET /test1/_search
    {
      "query": {
        "match_all": {}
      },
      "_source": [
        "name",
        "age"
      ]
    }
    

    image

    10、高亮查询

    我们平时在使用百度的时候,输入关键字查询内容后,关键字一般都是高亮显示的。

    image

    所以ES作为一个专业的搜索框架肯定也提供了这样的功能。

    ES的默认高亮显示:

    GET /test1/_search
    {
      "query": {
        "match": {
          "name": "李四"
        }
      },
      "highlight": {
        "fields": {
          "name": {}
        }
      }
    }
    

    image

    ES自定义高亮显示(在highlight中,pre_tags用来实现我们的自定义标签的前半部分,在这里,我们也可以为自定义的 标签添加属性和样式。post_tags实现标签的后半部分,组成一个完整的标签。至于标签中的内容,则还是交给fields来完成)

    GET /test1/_search
    {
      "query": {
        "match": {
          "remark": "中国"
        }
      },
      "highlight": {
        "pre_tags": "<b class='key' style='color:red'>",
        "post_tags": "</b>",
        "fields": {
          "remark": {}
        }
      }
    }
    
    # 或者
    GET /test1/_search
    {
      "query": {
        "match": {
          "remark": "中国"
        }
      },
      "highlight": {
        "fields": {
          "remark": {
            "pre_tags": "<b class='key' style='color:red'>",
            "post_tags": "</b>"
          }
        }
      }
    }
    

    image

    11、聚合查询

    我们平时在使用Elasticsearch时,更多会用到聚合操作,它类似SQL中的group by操作。ES的聚合查询一定是先查出结果,然后对结果使用聚合函数做处理,常用的操作有:avg:求平均、max:最大值、min:最小值、sum:求和等。

    在ES中聚合分为指标聚合和分桶聚合:

    • 指标聚合:指标聚合对一个数据集求最大、最小、和、平均值
    • 分桶聚合:除了有聚合函数外,还可以对查询出的数据进行分组group by,再在组上进行游标聚合。

    测试准备数据:

    PUT /test2/_doc/1
    {
      "name":"Jack",
      "age":18,
      "sex":"男",
      "salary":6000,
      "bithday":"1997-07-18",
      "address":"美国纽约州",
      "remark":"美国是世界上军事实力最强大的国家"
    }
    PUT /test2/_doc/2
    {
      "name":"李四",
      "age":29,
      "sex":"男",
      "salary":12000,
      "bithday":"1997-02-07",
      "address":"中国广东省广州市",
      "remark":"中国是世界上人口最多的国家"
    }
    PUT /test2/_doc/3
    {
      "name":"阿三",
      "age":30,
      "sex":"男",
      "salary":3000,
      "bithday":"1995-04-09",
      "address":"印度新德里",
      "remark":"印度的食品干净又卫生"
    }
    PUT /test2/_doc/4
    {
      "name":"王五",
      "age":30,
      "sex":"男",
      "salary":16000,
      "bithday":"1997-07-07",
      "address":"中国北京市三里屯",
      "remark":"中国的首都是北京市"
    }
    PUT /test2/_doc/5
    {
      "name":"老六",
      "age":29,
      "sex":"男",
      "salary":15000,
      "bithday":"1987-02-02",
      "address":"中国上海市浦东新区",
      "remark":"老六是中国某不知名小厂的程序员一枚"
    }
    PUT /test2/_doc/6
    {
      "name":"七七",
      "age":20,
      "sex":"女",
      "salary":15000,
      "bithday":"1999-11-17",
      "address":"中国上海市浦东新区",
      "remark":"七七是一个漂亮的Web前端程序猿"
    }
    

    11.1、Metric 指标聚合分析

    指标聚合:指标聚合对一个数据集求最大、最小、和、平均值

    • 单值分析,只输出一个分析结果——avg:求平均、max:最大值、min:最小值、sum:求和、cardinality:值去重计数。
    • 多值分析,输出多个分析结果
      • stats:统计了count max min avg sum5个值
      • extended_stats:extended_stats比stats多很多更加高级的统计结果:如平方和、方差、标准差、平均值加/减两个标准差的区间等
      • percentile:占比百分位对应的值统计,默认返回【1,5,25,50,75,95,99】分位上的值

    avg示例:查询所有用户的平均年龄

    GET /test2/_search
    {
      "query": {
        "match_all": {}
      },
      "aggs": {
        "avg_age": {
          "avg": {
            "field": "age"
          }
        }
      },
      "_source": [
        "name",
        "age"
      ]
    }
    

    上例中,首先匹配查询所有的数据。在此基础上做查询平均值的操作,这里就用到了聚合函数,其语法被封装在aggs中,而avg_age则是为查询结果起个别名,封装了计算出的平均值。

    image

    如果只想看输出的值,而不关心输出的文档的话可以通过size=0来控制

    GET /test2/_search
    {
      "query": {
        "match_all": {}
      },
      "aggs": {
        "avg_age": {
          "avg": {
            "field": "age"
          }
        }
      },
      "size": 0,
      "_source": [
        "name",
        "age"
      ]
    }
    

    image


    avg示例:查询地址在中国用户的平均工资

    GET /test2/_search
    {
      "query": {
        "match_phrase": {
          "address": "中国"
        }
      },
      "aggs": {
        "avg_salary": {
          "avg": {
            "field": "salary"
          }
        }
      },
      "size": 0
    }
    

    image


    max示例:查询年龄的最大值

    GET /test2/_search
    {
      "query": {
        "match_all": {}
      },
      "aggs": {
        "max_age": {
          "max": {
            "field": "age"
          }
        }
      },
      "size": 0
    }
    

    image


    min示例:查询年龄的最小值

    GET /test2/_search
    {
      "query": {
        "match_all": {}
      },
      "aggs": {
        "age_min": {
          "min": {
            "field": "age"
          }
        }
      },
      "size": 0
    }
    

    image


    sum示例:查询符合条件的年龄之和

    GET /test2/_search
    {
      "query": {
        "match_all": {}
      },
      "aggs": {
        "age_sum": {
          "sum": {
            "field": "age"
          }
        }
      },
      "size": 0
    }
    

    image


    示例:查询所有用户不同年龄的数量

    POST /test2/_search
    {
      "query": {
        "match_all": {}
      },
      "aggs": {
        "count_age": {
          "cardinality": {
            "field": "age"
          }
        }
      },
      "size": 0
    }
    

    示例:查出所有用户的年龄stats信息

    POST /test2/_search
    {
      "query": {
        "match_all": {}
      },
      "aggs": {
        "stats_age": {
          "stats": {
            "field": "age"
          }
        }
      },
      "size": 0
    }
    

    image


    示例:查出所有用户的年龄extended_stats信息

    POST /test2/_search
    {
      "query": {
        "match_all": {}
      },
      "aggs": {
        "extended_stats_age": {
          "extended_stats": {
            "field": "age"
          }
        }
      },
      "size": 0
    }
    

    image


    示例:查出所有用户的年龄占比

    POST /test2/_search
    {
      "query": {
        "match_all": {}
      },
      "aggs": {
        "pecent_age": {
          "percentiles": {
            "field": "age"
          }
        }
      },
      "size": 0
    }
    

    11.2、Bucket 分桶聚合分析

    分桶聚合:除了有聚合函数外,还可以对查询出的数据进行分组group by,再在组上进行游标聚合。

    分桶group by示例:根据年龄聚合查询

    GET /test2/_search
    {
      "size": 0,
      "query": {
        "match_all": {}
      },
      "aggs": {
        "age_group": {
          "terms": {
            "field": "age"
          }
        }
      }
    }
    

    image


    分桶group by示例:根据年龄段聚合查询

    GET /test2/_search
    {
      "size": 0,
      "query": {
        "match_all": {}
      },
      "aggs": {
        "age_group": {
          "range": {
            "field": "age",
            "ranges": [
              {
                "from": 15,
                "to": 20
              },
              {
                "from": 20,
                "to": 25
              },
              {
                "from": 25,
                "to": 30
              }
            ]
          }
        }
      }
    }
    

    image


    分桶group by示例:根据用户出生日期的年月分组分段聚合查询

    POST /test2/_search
    {
      "size": 0,
      "query": {
        "match_all": {}
      },
      "aggs": {
        "bithday_range": {
          "date_range": {
            "field": "bithday",
            "format": "yyy-MM",
            "ranges": [
              {
                "to": "1989-01"
              },
              {
                "from": "1989-01",
                "to": "1999-01"
              },
              {
                "from": "1999-01",
                "to": "2005-01"
              },
              {
                "from": "2005-01"
              }
            ]
          }
        }
      }
    }
    

    image


    分桶group by 进阶示例:按照年龄聚合,并且查询出这些年龄段的这些人的平均薪资

    #其实就是aggs里面又加了一个aggs,第二个aggs根据第一个aggs聚合后的结果在聚合
    GET /test2/_search
    {
      "query": {
        "match_all": {}
      },
      "aggs": {
        "ageAgg": {
          "terms": {
            "field": "age",
            "size": 100
          },
          "aggs": {
            "ageAvg": {
              "avg": {
                "field": "salary"
              }
            }
          }
        }
      },
      "size": 0
    }
    

    image


    分桶group by 进阶示例:查出所有年龄分布,并且这些年龄段中 性别为男 的平均薪资和 性别为女 的平均薪资以及这个年龄的用户信息

    GET /test2/_search
    {
      "query": {
        "match_all": {}
      },
      "size": 0,
      "aggs": {
        "age_agg": {
          "terms": {
            "field": "age",
            "size": 1000
          },
          "aggs": {
            "sex_agg": {
              "terms": {
                "field": "sex.keyword",
                "size": 10
              },
              "aggs": {
                "salary_avg": {
                  "avg": {
                    "field": "salary"
                  }
                }
              }
            },
            "salary_avg": {
              "avg": {
                "field": "salary"
              }
            }
          }
        }
      }
    }
    

    image

    12、queryString查询

    会对查询条件进行分词, 然后将分词后的查询条件和词条进行等值匹配,默认取并集(OR),可以指定单个字段也可多个查询字段

    POST /test2/_search
    {
      "query": {
        "query_string": {
          "default_field": "name",
          "query": "张三 OR 七七"
        }
      },
      "size": 100
    }
    
    GET /test2/_search
    {
      "query": {
        "query_string": {
          "fields": ["name","address"],
          "query": "张三 or 中国"
        }
      }
    }
    

    13、重建索引

    随着业务需求的变更,索引的结构可能发生改变。ElasticSearch的索引一旦创建,只允许添加字段,不允许改变字段。因为改变字段,需要重建倒排索引,影响内部缓存结构,性能太低。那么此时,就需要重建一个新的索引,并将原有索引的数据导入到新索引中。

    1. 原索引库 :test_index_v1
    2. 新索引库 :test_index_v2
    # 新建test_index_v1索引,索引名称必须全部小写
    PUT test_index_v1 
    {
      "mappings": {
        "properties": {
          "birthday":{
            "type": "date"
          }
        }
      }
    }
    # 查询索引
    GET test_index_v1
    # 添加数据
    PUT test_index_v1/_doc/1
    {
      "birthday":"2020-11-11"
    }
    # 查询数据
    GET test_index_v1/_search
    # 随着业务的变更,换种数据类型进行添加数据,程序会直接报错
    PUT test_index_v1/_doc/1
    {
      "birthday":"2020年11月11号"
    }
    # 业务变更,需要改变birthday数据类型为text
    # 1:创建新的索引 test_index_v2
    # 2:将test_index_v1 数据拷贝到 test_index_v2
    
    # 创建新的索引
    PUT test_index_v2 
    {
      "mappings": {
        "properties": {
          "birthday":{
            "type": "text"
          }
        }
      }
    }
    
    # 2:将test_index_v1 数据拷贝到 test_index_v2
    POST _reindex
    {
      "source": {
        "index": "test_index_v1"
      },
      "dest": {
        "index": "test_index_v2"
      }
    }
    
    # 查询新索引库数据
    GET test_index_v2/_search
    
    # 在新的索引库里面添加数据
    PUT test_index_v2/_doc/2
    {
      "birthday":"2020年11月13号"
    }
    
    DELETE test_index_v1
    

    14、别名的使用

    1、查询别名

    #获取指定索引的别名
    GET /test2/_alias
    #获取ES中所有索引的别名
    GET /_alias
    

    2、新增别名

    POST /_aliases
    {
      "actions": [
        {
          "add": {
            "index": "test2",
            "alias": "test2_alias_1.0"
          }
        }
      ]
    }
    

    3、删除别名

    #方式一
    POST /_aliases
    {
      "actions": [
        {
          "remove": {
            "index": "test2",
            "alias": "test2_alias_1.0"
          }
        }
      ]
    }
    
    #方式二
    DELETE /test2/_alias/test2_alias_1.0
    

    4、重命名别名

    POST /_aliases
    {
      "actions": [
        {
          "remove": {
            "index": "test2",
            "alias": "test2_alias_1.0"
          }
        },
        {
          "add": {
            "index": "test2",
            "alias": "test2_alias_1.0"
          }
        }
      ]
    }
    

    5、为多个索引指定一个别名

    POST /_aliases
    {
      "actions": [
        {
          "add": {
            "index": "test2",
            "alias": "test2_alias_2.0"
          }
        },{
          "add": {
            "index": "test1",
            "alias": "test1_alias_2.0"
          }
        }
      ]
    }
    

    6、为同个索引指定多个别名

    POST /_aliases
    {
      "actions": [
        {
          "add": {
            "index": "test2",
            "alias": "test2_alias_2.1"
          }
        },{
          "add": {
            "index": "test2",
            "alias": "test2_alias_2.2"
          }
        }
      ]
    }
    

    7、通过别名读索引

    GET /test2_alias_2.1
    

    8、通过别名写索引

    POST /test2_alias_2.1.0/_doc/
    {
      "name": "Tom",
      "age": "26"
    }
    


    参考链接:

  • 相关阅读:
    Erlang顺序型编程
    [转]理解gen_server behaviour
    [转]Parsing Text and Binary Files With Erlang
    [转]Running Hadoop On Ubuntu Linux (SingleNode Cluster)
    [转]erlang 监督树
    [转]Erlang之IO编程
    [转]分段表
    [转]如何“打败”CAP定理
    [转]A Millionuser Comet Application with Mochiweb
    [转]消息队列软件大比拼
  • 原文地址:https://www.cnblogs.com/tanghaorong/p/16297788.html
Copyright © 2020-2023  润新知