一、 结构化搜索

结构化搜索(Structured search) 是指有关探询那些具有内在结构数据的过程。比如日期、时间和数字都是结构化的:它们有精确的格式,我们可以对这些格式进行逻辑操作。比较常见的操作包括比较数字或时间的范围,或判定两个值的大小。

在结构化查询中,我们得到的结果 总是 非是即否,要么存于集合之中,要么存在集合之外。结构化查询不关心文件的相关度或评分;它简单的对文档包括或排除处理。

1、精确值查找

当进行精确值查找时, 我们会使用过滤器(filters)。过滤器很重要,因为它们执行速度非常快,不会计算相关度(直接跳过了整个评分阶段)而且很容易被缓存。请尽可能多的使用过滤式查询。

term 查询数字

我们首先来看最为常用的 term 查询, 可以用它处理数字(numbers)、布尔值(Booleans)、日期(dates)以及文本(text)

首先,让我们以下面的例子开始介绍,创建并索引一些表示产品的文档,文档里有字段 `price` 和 `productID` ( `价格` 和 `产品ID` ):

POST /my_store/products/_bulk
{ "index": { "_id": }}
{ "price" : , "productID" : "XHDK-A-1293-#fJ3" }
{ "index": { "_id": }}
{ "price" : , "productID" : "KDKE-B-9947-#kL5" }
{ "index": { "_id": }}
{ "price" : , "productID" : "JODL-X-1937-#pV7" }
{ "index": { "_id": }}
{ "price" : , "productID" : "QQPX-R-3956-#aD8" }

在 Elasticsearch 的查询表达式(query DSL)中,我们可以使用 term 查询达到相同的目的。 term 查询会查找我们指定的精确值。作为其本身, term 查询是简单的。它接受一个字段名以及我们希望查找的数值:

{
"term" : {
"price" :
}
}

通常当查找一个精确值的时候,我们不希望对查询进行评分计算。只希望对文档进行包括或排除的计算,所以我们会使用 constant_score 查询以非评分模式来执行 term 查询并以一作为统一评分。

最终组合的结果是一个 constant_score 查询,它包含一个 term 查询:

GET /my_store/products/_search
{
"query" : {
"constant_score" : {
"filter" : {
"term" : {
"price" :
}
}
}
}
}

注:term查询文本时,要想查询其精确值,要将其设置成 not_analyzed 无需分析的属性。

2、布尔过滤器

一个 bool 过滤器由三部分组成:

{
"bool" : {
"must" : [],
"should" : [],
"must_not" : [],
}
}

must

  所有的语句都 必须(must) 匹配,与 AND 等价。

must_not

  所有的语句都 不能(must not) 匹配,与 NOT 等价。

should

  至少有一个语句要匹配,与 OR 等价

下面例子是查询price为20或productID为XHDK-A-1293-#fJ3,且price不等于30的查询,

用SQL语句表示如下:

SELECT product
FROM products
WHERE (price = OR productID = "XHDK-A-1293-#fJ3")
AND (price != )

用 Elasticsearch 语句如下:

GET /my_store/products/_search
{
"query" : {
"filtered" : {
"filter" : {
"bool" : {
"should" : [
{ "term" : {"price" : }},
{ "term" : {"productID" : "XHDK-A-1293-#fJ3"}}
],
"must_not" : {
"term" : {"price" : }
}
}
}
}
}
}

3、嵌套布尔过滤器

对于以下这个 SQL 语句:

SELECT document
FROM products
WHERE productID = "KDKE-B-9947-#kL5"
OR ( productID = "JODL-X-1937-#pV7"
AND price = )

我们将其转换成一组嵌套的 bool 过滤器:

GET /my_store/products/_search
{
"query" : {
"filtered" : {
"filter" : {
"bool" : {
"should" : [
{ "term" : {"productID" : "KDKE-B-9947-#kL5"}},
{ "bool" : {
"must" : [
{ "term" : {"productID" : "JODL-X-1937-#pV7"}},
{ "term" : {"price" : }}
]
}}
]
}
}
}
}
}

、全文索引

全文搜索两个最重要的方面是:

相关性(Relevance)
它是评价查询与其结果间的相关程度,并根据这种相关程度对结果排名的一种能力,这种计算方式可以是 TF/IDF 方法(参见 相关性的介绍)、地理位置邻近、模糊相似,或其他的某些算法。
分析(Analysis)
  它是将文本块转换为有区别的、规范化的 token 的一个过程,(参见 分析的介绍) 目的是为了(a)创建倒排索引以及(b)查询倒排索引
1、基于词项与基于全文

所有查询会或多或少的执行相关度计算,但不是所有查询都有分析阶段。 和一些特殊的完全不会对文本进行操作的查询(如 bool 或 function_score )不同,文本查询可以划分成两大家族:

基于词项的查询

  如 term 或 fuzzy 这样的底层查询不需要分析阶段,它们对单个词项进行操作。用 term 查询词项Foo 只要在倒排索引中查找 准确词项 ,并且用 TF/IDF 算法为每个包含该词项的文档计算相关度评分_score 。

  term 查询只对倒排索引的词项精确匹配,这点很重要,它不会对词的多样性进行处理(如, foo或 FOO )。这里,无须考虑词项是如何存入索引的。如果是将 ["Foo","Bar"] 索引存入一个不分析的( not_analyzed )包含精确值的字段。

基于全文的查询

像 match 或 query_string 这样的查询是高层查询,它们了解字段映射的信息:

  • 如果查询 日期(date) 或 整数(integer) 字段,它们会将查询字符串分别作为日期或整数对待。
  • 如果查询一个( not_analyzed )未分析的精确值字符串字段, 它们会将整个查询字符串作为单个词项对待。
  • 但如果要查询一个( analyzed )已分析的全文字段, 它们会先将查询字符串传递到一个合适的分析器,然后生成一个供查询的词项列表。

一旦组成了词项列表,这个查询会对每个词项逐一执行底层的查询,再将结果合并,然后为每个文档生成一个最终的相关度评分。

 2、查询语句提升权重

假设想要查询关于 “full-text search(全文搜索)” 的文档, 但我们希望为提及 “Elasticsearch” 或 “Lucene” 的文档给予更高的 权重 ,这里 更高权重 是指如果文档中出现 “Elasticsearch” 或 “Lucene” ,它们会比没有的出现这些词的文档获得更高的相关度评分 _score ,也就是说,它们会出现在结果集的更上面。

一个简单的 bool 查询 允许我们写出如下这种非常复杂的逻辑:content 字段必须包含 full 、 text 和 search 所有三个词。如果 content 字段也包含 Elasticsearch 或 Lucene ,文档会获得更高的评分 _score

GET /_search
{
"query": {
"bool": {
"must": {
"match": {
"content": {
"query": "full text search",
"operator": "and"
}
}
},
"should": [
{ "match": { "content": "Elasticsearch" }},
{ "match": { "content": "Lucene" }}
]
}
}
}

但是如果我们想让包含 Lucene和 Elasticsearch的有更高的权重,并且包含 Elasticsearch 的语句比 Lucene 的权重更高,该如何处理?

我们可以通过指定 boost 来控制任何查询语句的相对的权重, boost 的默认值为 1 ,大于 1 会提升一个语句的相对权重。所以下面重写之前的查询:

GET /_search
{
"query": {
"bool": {
"must": {
"match": {
"content": {
"query": "full text search",
"operator": "and"
}
}
},
"should": [
{ "match": {
"content": {
"query": "Elasticsearch",
"boost":
}
}},
{ "match": {
"content": {
"query": "Lucene",
"boost":
}
}}
]
}
}
}

boost 参数被用来提升一个语句的相对权重( boost 值大于 1 )或降低相对权重( boost值处于 0 到 1 之间),但是这种提升或降低并不是线性的,换句话说,如果一个 boost 值为 2 ,并不能获得两倍的评分 _score 。

3、多字符串查询评分计算方式

如果我们知道 War and Peace 是标题,Leo Tolstoy 是作者,translator是译者,语句如下:

GET /_search
{
"query": {
"bool": {
"should": [
{ "match": { "title": "War and Peace" }},
{ "match": { "author": "Leo Tolstoy" }},
{ "bool": {
"should": [
{ "match": { "translator": "Constance Garnett" }},
{ "match": { "translator": "Louise Maude" }}
]
}}
]
}
}
}

为什么将译者条件语句放入另一个独立的 bool 查询中呢?所有的四个 match 查询都是 should 语句,所以为什么不将 translator 语句与其他如 title 、 author 这样的语句放在同一层呢?

答案在于评分的计算方式。 bool 查询运行每个 match 查询,再把评分加在一起,然后将结果与所有匹配的语句数量相乘,最后除以所有的语句数量。处于同一层的每条语句具有相同的权重。在上面这个例子中,包含 translator 语句的 bool 查询,只占总评分的三分之一。如果将 translator 语句与 title 和 author 两条语句放入同一层,那么 title 和 author 语句只贡献四分之一评分。

、多字段搜索

1、单字符查询

bool 查询是多语句查询的主干。 它的适用场景很多,特别是当需要将不同查询字符串映射到不同字段的时候。

问题在于,目前有些用户期望将所有的搜索项堆积到单个字段中,并期望应用程序能为他们提供正确的结果。有意思的是多字段搜索的表单通常被称为 高级查询 (Advanced Search) —— 只是因为它对用户而言是高级的,而多字段搜索的实现却非常简单。

对于多词(multiword)、多字段(multifield)查询来说,不存在简单的 万能 方案。为了获得最好结果,需要 了解我们的数据 ,并了解如何使用合适的工具。

当用户输入了单个字符串查询的时候,通常会遇到以下三种情形:

最佳字段
当搜索词语具体概念的时候,比如 “brown fox” ,词组比各自独立的单词更有意义。像 title 和 body 这样的字段,尽管它们之间是相关的,但同时又彼此相互竞争。文档在 相同字段 中包含的词越多越好,评分也来自于 最匹配字段 。
多数字段

为了对相关度进行微调,常用的一个技术就是将相同的数据索引到不同的字段,它们各自具有独立的分析链。

主字段可能包括它们的词源、同义词以及 变音词 或口音词,被用来匹配尽可能多的文档。

相同的文本被索引到其他字段,以提供更精确的匹配。一个字段可以包括未经词干提取过的原词,另一个字段包括其他词源、口音,还有一个字段可以提供 词语相似性 信息的瓦片词(shingles)。

其他字段是作为匹配每个文档时提高相关度评分的 信号 , 匹配字段越多 则越好。

混合字段

对于某些实体,我们需要在多个字段中确定其信息,单个字段都只能作为整体的一部分:

  • Person: first_name 和 last_name (人:名和姓)
  • Book: title 、 author 和 description (书:标题、作者、描述)
  • Address: street 、 city 、 country 和 postcode (地址:街道、市、国家和邮政编码)

在这种情况下,我们希望在 任何 这些列出的字段中找到尽可能多的词,这有如在一个大字段中进行搜索,这个大字段包括了所有列出的字段。

上述所有都是多词、多字段查询,但每个具体查询都要求使用不同策略。本章后面的部分,我们会依次介绍每个策略。

1、最佳字段

假设有个网站允许用户搜索博客的内容, 以下面两篇博客内容文档为例:

PUT /my_index/my_type/
{
"title": "Quick brown rabbits",
"body": "Brown rabbits are commonly seen."
} PUT /my_index/my_type/
{
"title": "Keeping pets healthy",
"body": "My quick brown fox eats rabbits on a regular basis."
}

用户输入词组 “Brown fox” 然后点击搜索按钮。事先,我们并不知道用户的搜索项是会在 title 还是在body 字段中被找到,但是,用户很有可能是想搜索相关的词组。用肉眼判断,文档 2 的匹配度更高,因为它同时包括要查找的两个词:

现在运行以下 bool 查询:

{
"query": {
"bool": {
"should": [
{ "match": { "title": "Brown fox" }},
{ "match": { "body": "Brown fox" }}
]
}
}
}

但是我们发现查询的结果是文档 1 的评分更高:

{
"hits": [
{
"_id": "",
"_score": 0.14809652,
"_source": {
"title": "Quick brown rabbits",
"body": "Brown rabbits are commonly seen."
}
},
{
"_id": "",
"_score": 0.09256032,
"_source": {
"title": "Keeping pets healthy",
"body": "My quick brown fox eats rabbits on a regular basis."
}
}
]
}

为了理解导致这样的原因, 需要回想一下 bool 是如何计算评分的:

  1. 它会执行 should 语句中的两个查询。
  2. 加和两个查询的评分。
  3. 乘以匹配语句的总数。
  4. 除以所有语句总数(这里为:2)。

文档 1 的两个字段都包含 brown 这个词,所以两个 match 语句都能成功匹配并且有一个评分。文档 2 的body 字段同时包含 brown 和 fox 这两个词,但 title 字段没有包含任何词。这样, body 查询结果中的高分,加上 title 查询中的 0 分,然后乘以二分之一,就得到比文档 1 更低的整体评分。

在本例中, title 和 body 字段是相互竞争的关系,所以就需要找到单个 最佳匹配 的字段。

如果不是简单将每个字段的评分结果加在一起,而是将 最佳匹配 字段的评分作为查询的整体评分,结果会怎样?这样返回的结果可能是: 同时 包含 brown 和 fox 的单个字段比反复出现相同词语的多个不同字段有更高的相关度。

dis_max 查询

不使用 bool 查询,可以使用 dis_max 即分离 最大化查询(Disjunction Max Query) 。分离(Disjunction)的意思是 或(or) ,这与可以把结合(conjunction)理解成 与(and) 相对应。分离最大化查询(Disjunction Max Query)指的是: 将任何与任一查询匹配的文档作为结果返回,但只将最佳匹配的评分作为查询的评分结果返回 :

{
"query": {
"dis_max": {
"queries": [
{ "match": { "title": "Brown fox" }},
{ "match": { "body": "Brown fox" }}
]
}
}
}

得到我们想要的结果为:

{
"hits": [
{
"_id": "",
"_score": 0.21509302,
"_source": {
"title": "Keeping pets healthy",
"body": "My quick brown fox eats rabbits on a regular basis."
}
},
{
"_id": "",
"_score": 0.12713557,
"_source": {
"title": "Quick brown rabbits",
"body": "Brown rabbits are commonly seen."
}
}
]
}

2、多数字段

全文搜索被称作是 召回率(Recall) 与 精确率(Precision) 的战场: 召回率 ——返回所有的相关文档;精确率 ——不返回无关文档。目的是在结果的第一页中为用户呈现最为相关的文档。

为了提高召回率的效果,我们扩大搜索范围 ——不仅返回与用户搜索词精确匹配的文档,还会返回我们认为与查询相关的所有文档。如果一个用户搜索 “quick brown box” ,一个包含词语 fast foxes 的文档被认为是非常合理的返回结果。

提高全文相关性精度的常用方式是为同一文本建立多种方式的索引, 每种方式都提供了一个不同的相关度信号 signal 。主字段会以尽可能多的形式的去匹配尽可能多的文档。举个例子,我们可以进行以下操作:

  • 使用词干提取来索引 jumps 、 jumping 和 jumped 样的词,将 jump 作为它们的词根形式。这样即使用户搜索 jumped ,也还是能找到包含 jumping 的匹配的文档。
  • 将同义词包括其中,如 jump 、 leap 和 hop 。
  • 移除变音或口音词:如 ésta 、 está 和 esta 都会以无变音形式 esta 来索引。

尽管如此,如果我们有两个文档,其中一个包含词 jumped ,另一个包含词 jumping ,用户很可能期望前者能排的更高,因为它正好与输入的搜索条件一致。

为了达到目的,我们可以将相同的文本索引到其他字段从而提供更为精确的匹配。一个字段可能是为词干未提取过的版本,另一个字段可能是变音过的原始词,第三个可能使用 shingles 提供 词语相似性 信息。这些附加的字段可以看成提高每个文档的相关度评分的信号 signals ,能匹配字段的越多越好。

一个文档如果与广度匹配的主字段相匹配,那么它会出现在结果列表中。如果文档同时又与 signal 信号字段匹配,那么它会获得额外加分,系统会提升它在结果列表中的位置。

跨字段实体搜索

现在讨论一种普遍的搜索模式:跨字段实体搜索(cross-fields entity search)。 在如 person 、 product或 address (人、产品或地址)这样的实体中,需要使用多个字段来唯一标识它的信息。 person 实体可能是这样索引的:

{
"firstname": "Peter",
"lastname": "Smith"
}

或地址:

{
"street": "5 Poland Street",
"city": "London",
"country": "United Kingdom",
"postcode": "W1V 3DG"
}

我们的用户可能想搜索 “Peter Smith” 这个人,或 “Poland Street W1V” 这个地址,这些词出现在不同的字段中,所以如果使用 dis_max 或 best_fields 查询去查找 单个 最佳匹配字段显然是个错误的方式。

简单的方式

依次查询每个字段并将每个字段的匹配评分结果相加,听起来真像是 bool 查询:

{
"query": {
"bool": {
"should": [
{ "match": { "street": "Poland Street W1V" }},
{ "match": { "city": "Poland Street W1V" }},
{ "match": { "country": "Poland Street W1V" }},
{ "match": { "postcode": "Poland Street W1V" }}
]
}
}
}

为每个字段重复查询字符串会使查询瞬间变得冗长,可以采用 multi_match 查询, 将 type 设置成most_fields 然后告诉 Elasticsearch 合并所有匹配字段的评分:

{
"query": {
"multi_match": {
"query": "Poland Street W1V",
"type": "most_fields",
"fields": [ "street", "city", "country", "postcode" ]
}
}
}

most_fields 方式的问题

用 most_fields 这种方式搜索也存在某些问题,这些问题并不会马上显现:

  • 它是为多数字段匹配 任意 词设计的,而不是在 所有字段 中找到最匹配的。
  • 它不能使用 operator 或 minimum_should_match 参数来降低次相关结果造成的长尾效应。
  • 词频对于每个字段是不一样的,而且它们之间的相互影响会导致不好的排序结果。

四、近似匹配

1、短语匹配

什么是短语

一个被认定为和短语 quick brown fox 匹配的文档,必须满足以下这些要求:

  • quick 、 brown 和 fox 需要全部出现在域中。
  • brown 的位置应该比 quick 的位置大 1 。
  • fox 的位置应该比 quick 的位置大 2 。

如果以上任何一个选项不成立,则该文档不能认定为匹配。

像 match 查询对于标准全文检索是一种最常用的查询一样,当你想找到彼此邻近搜索词的查询方法时,就会想到 match_phrase 查询:

GET /my_index/my_type/_search
{
"query": {
"match_phrase": {
"title": "quick brown fox"
}
}
}

2、越近越好

鉴于一个短语查询仅仅排除了不包含确切查询短语的文档, 而 邻近查询 — 一个 slop 大于 0— 的短语查询将查询词条的邻近度考虑到最终相关度 _score 中。 通过设置一个像 50 或者 100 这样的高 slop 值, 你能够排除单词距离太远的文档, 但是也给予了那些单词临近的的文档更高的分数。

下列对 quick dog 的邻近查询匹配了同时包含 quick 和 dog 的文档, 但是也给了与 quick 和 dog 更加临近的文档更高的分数 :

POST /my_index/my_type/_search
{
"query": {
"match_phrase": {
"title": {
"query": "quick dog",
"slop":
}
}
}
}

注意高 slop 值:分数较高因为 quick 和 dog 很接近,分数较低因为 quick 和 dog 分开较远

{
"hits": [
{
"_id": "",
"_score": 0.75,
"_source": {
"title": "The quick brown fox jumps over the quick dog"
}
},
{
"_id": "",
"_score": 0.28347334,
"_source": {
"title": "The quick brown fox jumps over the lazy dog"
}
}
]
}

3、使用邻近度提高相关度

我们可以将一个简单的 match 查询作为一个 must 子句。 这个查询将决定哪些文档需要被包含到结果集中。 我们可以用 minimum_should_match 参数去除长尾。 然后我们可以以 should 子句的形式添加更多特定查询。 每一个匹配成功的都会增加匹配文档的相关度。

GET /my_index/my_type/_search
{
"query": {
"bool": {
"must": {
"match": {
"title": {
"query": "quick brown fox",
"minimum_should_match": "30%"
}
}
},
"should": {
"match_phrase": {
"title": {
"query": "quick brown fox",
"slop":
}
}
}
}
}
}

must 子句从结果集中包含或者排除文档。 should 子句增加了匹配到文档的相关度评分。

 

ElasticSearch深入搜索的更多相关文章

  1. ElasticSearch位置搜索

    ElasticSearch位置搜索 学习了:https://blog.csdn.net/bingduanlbd/article/details/52253542 学习了:https://blog.cs ...

  2. ElasticSearch入门-搜索(java api)

    ElasticSearch入门-搜索(java api) package com.qlyd.searchhelper; import java.util.Map; import net.sf.json ...

  3. PHP使用ElasticSearch做搜索

    PHP 使用 ElasticSearch 做搜索 https://blog.csdn.net/zhanghao143lina/article/details/80280321 https://www. ...

  4. 十九种Elasticsearch字符串搜索方式终极介绍

    前言 刚开始接触Elasticsearch的时候被Elasticsearch的搜索功能搞得晕头转向,每次想在Kibana里面查询某个字段的时候,查出来的结果经常不是自己想要的,然而又不知道问题出在了哪 ...

  5. Elasticsearch实现搜索推荐词

    本篇介绍的是基于Elasticsearch实现搜索推荐词,其中需要用到Elasticsearch的pinyin插件以及ik分词插件,代码的实现这里提供了java跟C#的版本方便大家参考. 1.实现的结 ...

  6. Elasticsearch 为了搜索

    前言 Elasticsearch 是一个开源的搜索引擎,建立在一个全文搜索引擎库 Apache Lucene 基础之上. Lucene 可以说是当下最先进.高性能.全功能的搜索引擎库--无论是开源还是 ...

  7. Elasticsearch分布式搜索和数据分析引擎-ElasticStack(上)v7.14.0

    Elasticsearch概述 **本人博客网站 **IT小神 www.itxiaoshen.com Elasticsearch官网地址 https://www.elastic.co/cn/elast ...

  8. Elasticsearch 教程--搜索

    搜索 – 基本工具 到目前为止,我们已经学习了Elasticsearch的分布式NOSQL文档存储,我们可以直接把JSON文档扔到Elasticsearch中,然后直接通过ID来进行调取.但是Elas ...

  9. Elasticsearch 数据搜索篇·【入门级干货】

    ES即简单又复杂,你可以快速的实现全文检索,又需要了解复杂的REST API.本篇就通过一些简单的搜索命令,帮助你理解ES的相关应用.虽然不能让你理解ES的原理设计,但是可以帮助你理解ES,探寻更多的 ...

  10. Elasticsearch分布式搜索集群配置

    配置文件位于%ES_HOME%/config/elasticsearch.yml文件中,用Editplus打开它,你便可以进行配置.   所有的配置都可以使用环境变量,例如:node.rack: ${ ...

随机推荐

  1. Zeal - 开源离线开发文档浏览器

    https://zealdocs.org/ win10上暂时安装版会crash,请用portalable的解压版

  2. OI数据结构&&分治 简单学习笔记

    持续更新!!! [例题]简单题(K-D tree) 题目链接 线段树 [例题](环上最大连续和) 给定一个长度为n的环形序列A,其中A1与A_n是相临的,现在有q次修改操作,每次操作会更改其中一个数, ...

  3. 2018OCP最新题库052新加考题及答案整理-27

    27.Examine these facts about a database: 1. USERS is the database default tablespace. 2. USER1, USER ...

  4. [ActionScript 3.0] PrintJob打印功能

    package { import flash.display.Bitmap; import flash.display.Sprite; import flash.events.MouseEvent; ...

  5. [ActionScript 3.0] 实现放大镜效果的简单方法

    //mc和bgmc是同一对象的不同实例 //mc放大的对象 //bgmc源对象 //mag放大镜 var scale:Number = 1.3;//放大倍数 mc.mask = mag; mag.st ...

  6. [Swift]在Swift中实现自增(++)、自减(--)运算符:利用extension扩展Int类

    自增(++).自减(--)运算符主要用在For循环中,Swift有自己更简易的循环遍历方法,而且类似x- ++x这种代码不易维护. Swift为了营造自己的编码风格,树立自己的代码精神体系,已经不支持 ...

  7. python cookbook

    一 .数据结构 python collections包中 deque :固定长度队列,(例如固定长度的cache什么的) defaultdict:如果每个键值不存在,默认返回值 orderdict:有 ...

  8. L01-RHEL6.5中部署NTP(ntp server + client)

    RHEL6.5集群中部署NTP NTP全称为Network Time Protocol,即网络时间协议.一般在Linux系统中用来同步集群中不同机器的时间. 本文描述的ntp服务部署框架如下图示 如上 ...

  9. iOS 音频/视频 学习目录

    参考 iOS原生API  音/视频录制 编辑 https://www.cnblogs.com/kenshincui/p/4186022.html#summary iOS视频编解码常用库比较 http: ...

  10. JavaWeb学习笔记(二十)—— Ajax

    一.Ajax概述 1.1 什么是Ajax AJAX(Asynchronous Javascript And XML)翻译成中文就是“异步Javascript和XML”.即使用Javascript语言与 ...