星空网 > 软件开发 > 数据库

【翻译】MongoDB指南/引言

【原文地址】https://docs.mongodb.com/manual/

引言

MongoDB.aspx' >MongoDB是一种开源文档型数据库,它具有高性能,高可用性,自动扩展性

1.文档数据库

MongoDB用一个文档来表示一条记录,文档的数据结构由键值对组成。MongoDB文档类似于JSON对象,字段值可能是文档,数组,或文档数组。

 【翻译】MongoDB指南/引言

使用文档的优点:

  • 文档中字段值的数据类型同大多数编程语言中的原生数据类型一致。
  • 嵌入式文档和数组减少了连接查询的需求。
  • 动态的文档结构支持多态性。

2.主要特性

高性能

MongoDB支持高性能数据存储。特别地:

  • 支持嵌入式数据模型以减少对数据库系统的I/O
  • 利用索引实现快速查询,并且嵌入式文档和集合也支持索引

丰富的查询语言

MongoDB提供了丰富的查询语言以支持读写操作和聚集操作、文本检索、地理信息查询

高可用性

MongoDB的复制能力被称作复制集(replica set),它提供了自动的故障迁移和数据冗余。一个复制集是一组包含了相同数据的多台MongoDB服务器,它提供了冗余性和加强了数据的可用性。

横向扩展

MongoDB的横向扩展能力是其核心功能的一部分:

  • 分片的数据分布在服务器集群上。
  • 带标签的分片能够引导数据到指定的分片上。

支持多存储引擎

包括:WiredTiger Storage Engine,MMAPv1 Storage Engine。此外,MongoDB 提供可插拔存储引擎API,允许第三方开发者为MongoDB开发存储引擎。

 

3.数据库和集合

MongoDB 存储BSON文档,例如数据记录在集合中,集合在数据库中。

 【翻译】MongoDB指南/引言

3.1数据库

在MongoDB 中数据库持有集合。

在Mongo shell中,选中一个数据库使用如下命令:use <db> ,例如:

use myDB

创建数据库

如果待操作的数据库不存在,那么在第一次向MongoDB 存储数据时,MongoDB会创建这个数据库。例如,使用如下命令操作一个不存在的数据库。

use myNewDB

db.myNewCollection1.insert( { x: 1 } )

insert()操作创建了数据库myNewDB,若集合myNewCollection1也不存在,同样地集合myNewCollection1也被创建。

3.2

MongoDB 在集合中存储文档,集合类似于关系数据库中的表。

创建一个集

如果一个集合不存在,使用下面命令时集合会被创建:

db.myNewCollection2.insert( { x: 1 } )

db.myNewCollection3.createIndex( { y: 1 } )

insert() 和 createIndex()在集合不存在的情况下会创建集合。

显式创建集合

MongoDB 提供了db.createCollection()方法来显示地创建一个集合。可以为创建的集合指定参数,例如设置集合的大小或者文档的验证规则,如果不需要指定这些参数,那么没必要显示地创建一个集合。

文档验证(3.2版新特性)

默认情况下,一个集合中的文档不必具有相同的结构 ,

一个集中的文档不需要具有一系列相同的字段,并且不同文档中字段的数据类型可以不同。

修改文档结构

可以更改集合中的文档结构,如添加新字段,删除现有字段,或将字段值更改为一种新的类型,更新文档结构

3.3固定集

3.3.1概述

固定集合,即具有固定大小的集合,它支持基于插入顺序的插入和查询这两种高通量操作。固定大小的集合的工作方式类似于循环缓存:一旦一个集合被填满,待插入的文档会覆盖掉最先插入的文档。

3.3.2行为

插入顺序

固定集合保证了插入顺序,因此对于查询操作而言,不需要索引的支持就可以返回多个按顺序排列的文档。没有索引的开销,固定集合支持更高的插入吞吐量。

自动删除最先插入的文档

为了给新文档让出存储空间,固定集合自动删除最先插入的文档而不需要显示的删除操作。

例如,集合oplog.rs中存储了副本集操作日志,这里副本集使用了固定集合。考虑下面对固定集合可能的操作:

  • 存储由大容量系统生成的日志信息。在无索引的情况下,文档插入固定集合的速度与将日志信息写入文件系统的速度相似。此外,先进先出的特性保证了事件的顺序,同时管理了存储的使用。
  • 在固定集合中缓存少量数据。由于缓存重读而非写,你应确保这个集合总在工作集中(例如,内存中)或接受一点点写操作,因为索引需要写操作。

_id 字段索引

固定集合含有_id字段,此字段索引是默认的。

3.3.3限制和建议

更新

如果你要更新固定集合中的文档,创建索引以防止全表扫描。

文档大小(3.2版本变更)

如果更新或替换操作改变了文档大小,则操作失败。

删除文档

不能删除固定集合中的文档,可使用drop() 命令删除整个固定集合并新建之。

分片

固定集合不允许分片。

查询效率

使用自然排序可高效地检索最新插入的元素。这是(有点)像追踪一个日志文件。

聚集操作符$out

不能使用聚集管道操作符$out将结果写入固定集合

3.3.4过程

创建固定集合

在mongo shel中,使用db.createCollection()方法创建固定集合,创建固定集合的时候要指定集合的字节大小,MongoDB将会提前为所创建的固定集合分配存储空间。固定集合的字节大小包含了内部使用的空间大小。

db.createCollection( "log", { capped: true, size: 100000 } )

如果字段的字节大小小于等于4096字节,那么固定集合的字节大小为4096字节。否则MongoDB 会将给定值提升为256字节的整数倍。

另外,你可以使用max 字段设置集合中文档的最大数量:

db.createCollection("log", { capped : true, size : 5242880, max : 5000 } )

注意:对size的设置是必须的。在集合中的文档数量还未达到最大值而集合的字节大小已经达到最大时, MongoDB 同样会移除最先插入的文档。

查询固定集合

如果使用 find()方法查询固定集合而没有指定排序规则,查询返回结果的排序和文档插入时的排序是一样的。

为了使查询结果的排序与插入时相反,可以使用sort() 方法并将$natural参数设置为-1:

db.cappedCollection.find().sort( { $natural: -1 } )

检查集合是否为固定集合

使用isCapped()方法检查集合是否为固定集合:

db.collection.isCapped()

将集合转换为固定集合

使用convertToCapped 命令将一个非固定集合转换为固定集合:

db.runCommand({"convertToCapped": "mycoll", size: 100000});

size 参数设定了固定集合的字节大小。

警告:这个命令将会获得全局写入锁,它会阻塞其他操作直到此操作完成为止。

在指定的一段时间后自动移除数据

对于数据过期的情形,为支持额外的灵活性,可使用MongoDB的TTL索引。这些索引允许你利用一种特殊的类型使数据过期并从普通集合中移除,这种特殊的类型是基于时间字段值和TTL值的。

TTL集合与固定集合不兼容。

Tailable游标

对于固定集合,可以使用Tailable游标。Tailable游标类似于Unix 的tail -f命令,Tailable游标追踪固定集合的末端。新文档插入固定集合的同时,可以使用Tailable游标检索文档。

 

4.文档

MongoDB将数据存储为BSON 文档,BSON是一个JSON文档的二进制表示形式,但它所包含的数据类型比JSON多。

 【翻译】MongoDB指南/引言

4.1 文档结构

MongoDB文档是由键值对构成的,形式如下:

{

   field1: value1,

   field2: value2,

   field3: value3,

   ...

   fieldN: valueN

}

字段值的类型可以是任何BSON类型,包括:文档,数组,文档数组,例如:

var mydoc = {

               _id: ObjectId("5099803df3f4948bd2f98391"),

               name: { first: "Alan", last: "Turing" },

               birth: new Date('Jun 23, 1912'),

               death: new Date('Jun 07, 1954'),

               contribs: [ "Turing machine", "Turing test", "Turingery" ],

               views : NumberLong(1250000)

            }

上面的例子包括以下类型:

  • _id为ObjectId类型
  • name为嵌入式文档类型(embedded document),包括first和last字段
  • birth和death为日期类型(Date
  • contribs为字符串数组类型(array of strings
  • views为长整型(NumberLong )

字段名称

字段的名称是字符串。

对于字段的命名有下面的约束:

  • _id为保留字段,用做主键,_id的值与其所在的集合中必须唯一,不可更改,可以是除数组以外的任何类型。
  • 字段名称不能以“$”符开始。
  • 字段名称不能包含“.”。
  • 字段名称不能包含空字符。

BSON 文档允许有相同的字段名称。大多数的MongoDB接口不支持字段名称重复。如果需要重复的字段名称,请查看你所使用的驱动文档。

MongoDB内部处理程序创建的文档可能会有重名的字段,但不会向用户文档中添加重名字段。

字段值约束

对于已经索引的集合来说,索引字段值有最大索引键值长度(Maximum Index Key Length)限制。

4.2 圆点记法

MongoDB使用圆点符号来访问数组中的元素和嵌入式文档字段。

数组

MongoDB中数组是基于0索引的。使用圆点连接集合名称和索引位置:

"<array>.<index>"

例如,给定下面的文档

{

   ...

   contribs: [ "Turing machine", "Turing test", "Turingery" ],

   ...

}

为了访问第三个元素,可以这样:contribs.2

嵌入式文档

使用圆点连接嵌入式文档名称和文档字段名称:

"<embedded document>.<field>"

例如,给定下面的文档

{

   ...

   name: { first: "Alan", last: "Turing" },

   contact: { phone: { type: "cell", number: "111-222-3333" } },

   ...

}

  • 为了指定last字段,使用"name.last"。
  • 为了指定number字段,使用"contact.phone.number"

4.3 文档约束

文档大小约束

BSON 文档最大为16MB。设置单个文档大小的最大值有助于确保单个文档不会耗尽系统内存,或者在传输的过程中不会占用太多的带宽。为了能够存储超过最大值的文档,MongoDB提供了GridFS API。

文档字段顺序

除以下情况外,MongoDB保持写入时的字段顺序:

  • _id字段总是位于文档的首位。
  • 重命名字段可能会引起字段重新排序。

从2.6版本开始MongoDB保持写入时的字段顺序,但之前的版本并非如此。

_id字段

在MongoDB中,文档需要_id字段作为主键,如果插入文档时没有指定_id字段,MongoDB会使用ObjectIds 作为默认的_id的默认值。例如,向集合中插入一个不包含位于文档开始处的_id字段的文档,MongoDB会将_id添加进来并且其类型为ObjectIds 。

另外,如果Mongod接收一个待插入的不包含_id字段的文档,Mongod将会添加一个ObjectIds 类型的字段。

_id字段有下列行为和约束:

  • 默认地,在创建集合的同时,MongoDB 为_id字段创建唯一索引。
  • _id字段总是文档中的第一个字段,如果插入文档的_id字段不是第一个字段,那么MongoDB会将其移动到首位。
  • _id字段可以是除数组以外的任何BSON 类型。

警告:为了保证复制功能,不要在_id字段存储BSON 正则表达式类型。

下面是关于_id字段值的常见选项:

  • 使用ObjectIds 类型。
  • 尽可能使用自然唯一字符,这样可以节省存储空间和避免额外的索引。
  • 生成自增长数值
  • 在你的应用程序中使用UUID。为了在集合和_id索引中更有效地存储UUID,将UUID存储为BSON BinData类型。如果满足下面的条件,索引键会更有效被存储。

    binary subtype 值取值范围为0-7 或 128-135

    字节数组的长度是:0,1,2,3,4,5,6,7,8,10,12,14,16,20,24或32.

  • 使用你正在用的MongoDB驱动生成UUID。注意你所用的驱动对于UUID的序列化与反序列化与其他驱动可能不兼容。

4.4 文档结构其他用途

除了定义数据记录,MongoDB使用文档结构贯穿始终,包括但不限于:查询过滤器,更新规范文档,索引规范文档。

查询过滤器文档

查询过滤器文档指定了检索,更新,删除文档的条件。

可以使用<field>:<value>表达式来指定相等条件和查询运算符表达式。

{

  <field1>: <value1>,

  <field2>: { <operator>: <value> },

  ...

}

更新规范文档

在db.collection.update()方法执行期间,更新规范文档使用更新运算符指明待修改字段。

{

  <operator1>: { <field1>: <value1>, ... },

  <operator2>: { <field2>: <value2>, ... },

  ...

}

索引规范文档

索引规范文档定义了要索引的字段和索引类型。

{ <field1>: <type1>, <field2>: <type2>, ...  }

 

5. BSON类型

BSON是一种用来存储文档和MongoDB执行远程调用的二进制序列化格式。BSON规范位于bsonspec.org。

BSON支持以下数据类型,每种数据类型都有一个相应的数字和字符串别名,可以使用别名和$type操作符基于类型匹配模式检索文档。

Type

Number

Alias

Notes

Double

1

“double”

 

String

2

“string”

 

Object

3

“object”

 

Array

4

“array”

 

Binary data

5

“binData”

 

Undefined

6

“undefined”

Deprecated.

ObjectId

7

“objectId”

 

Boolean

8

“bool”

 

Date

9

“date”

 

Null

10

“null”

 

Regular Expression

11

“regex”

 

DBPointer

12

“dbPointer”

 

JavaScript

13

“javascript”

 

Symbol

14

“symbol”

 

JavaScript (with scope)

15

“javascriptWithScope”

 

32-bit integer

16

“int”

 

Timestamp

17

“timestamp”

 

64-bit integer

18

“long”

 

Min key

-1

“minKey”

 

Max key

127

“maxKey”

 

5.1 比较/排序顺序

当比较不同BSON类型的值时,MongoDB使用下面的比较顺序,从最低到最高:

1.MinKey (内部类型)

2.Null

3.Numbers (ints, longs, doubles)

4.Symbol, String

5.Object

6.Array

7.BinData

8.ObjectId

9.Boolean

10.Date

11.Timestamp

12.Regular Expression

13.MaxKey (内部类型)

对于比较而言,MongoDB将一些类型看作是等价的。例如,数值类型在比较之前执行转换。

3.0.0版本的变化:Date排在Timestamp 之前。之前的版本,Date和Timestamp 排序相同。

对于比较而言,MongoDB将不存在的字段看作空BSON 对象,例如,对{ } 和{ a: null }在排序中被看作是等价的。

对于数组而言,小于比较或者升序排序比较的是数组中最小的元素,大于比较或者降序排序比较的是数组中最大的元素。例如,比较一个只有一个元素的数组类型字段(例如 [ 1 ]))和非数组字段(例如2),比较的是1和2。

空数组(例如[])的比较被看作是小于空(null)或被看作丢失的字段。

对于BinData 类型,按下面顺序排序:

1.首先,按数据的长度或大小排序。

2.然后,按BSON一个字节子类型排序。

3.最后,一个字节一个字节地比较。

下面的章节针对特定的BSON类型描述了特别的注意事项:

5.2 ObjectId

ObjectId占据存储空间小、唯一、可被快速生成和索引。ObjectId类型值为12字节,前四个字节是一个时间戳,表示其被创建的时间:

  • 前四个字节表示从UNIX新纪元来的秒数。
  • 接下来的三个字节表示机器编号。
  • 接下来的两个字节表示进程ID。
  • 最后三个字节表示以随机数开始的计数。

在MongoDB中,集合中的文档需要一个作为主键的唯一_id字段,如果没有指定_id字段,MongoDB默认将ObjectId类型值作为_id字段值。例如,待插入文档不包含顶级_id字段,MongoDB驱动就会添加一个ObjectId类型的_id字段。

另外,如果mongod接收的待插入文档不包含_id字段,mongod将会添加一个ObjectId类型的_id字段。

MongoDB 客户端应该添加一个值为ObjectId的_id字段,使用值为ObjectId的_id字段有如下好处:

  • 在mongo shell中,你可以使用ObjectId.getTimestamp() 方法获得ObjectId创建的时间。
  • 给值为ObjectId的_id字段排序大体等价于按时间排序。

重要的:

在一秒之内,ObjectId值的顺序与生成时间之间的关系并不是严格的。如果单系统中,多个系统或多个进程或多个线程在一秒内产生了多个ObjectId值,这些值并不会严格地按照插入顺序展示。多客户端之间的时钟偏移也会导致不严格排序,即使这些值由客户端驱动程序生成。

5.3 String

BSON 的String类型是UTF-8编码的。一般来说,每种语言对应的驱动程序在执行序列化和反序列化BSON时将语言自身的string类型转换为UTF-8编码,这使得BSON string可以接受大多数国际字符。另外,使用$regex 查询支持UTF-8编码的正则表达式字符。

5.4 Timestamp

BSON 中有一个特殊的时间戳类型供MongoDB内部使用,并且不能和Date 配合使用。时间戳类型是64位的值:

  • 第一个32位是time_t的值(从UNIX新纪元来的秒数)。
  • 第二个32位是给定时间里一些操作的递增序号。

在一个mongod实例中,时间戳的值是唯一的。

在复制功能中,oplog有一个ts字段,字段值使用DSON时间戳,它反映了操作时间。

注:

BSON时间戳类型(Timestape)是供MongoDB内部使用的。大多数情况下,开发应用程序时使用Date类型。

如果你所插入文档的顶级字段是一个空值的时间戳类型(Timestape),MongoDB 服务器将会用当前的时间戳(Timestape)替换它。例如执行下面的操作:

var a = new Timestamp();

db.test.insert( { ts: a } );

然后,使用db.test.find()方法查询,返回结果为:

{ "_id" : ObjectId("542c2b97bac0595474108b48"), "ts" : Timestamp(1412180887, 1) }

如果ts是嵌入式文档的一个字段,服务器会保持其为空值。

2.6版本中的变化:以前当插入文档时,服务器仅仅会替换头两个空值时间戳类型(Timestape)字段,包括_id字段。现在服务器会替换任何的顶级字段。

5.5 Date

BSON 日期类型是64位整型,表示从UNIX新纪元(Jan 1, 1970)来的毫秒数。这一结果表示了可表达的约2亿9000万年范围内的过去和未来。

官方的BSON规范指出DSON日期类型是通用协调时间(UTC datetime)。

BSON日期类型是有符号的,负值表示1970年之前的日期。

例如:

在mongo shell中,使用new Date()构建日期:var mydate1 = new Date()

在mongo shell中,使用ISODate()构建日期:var mydate2 = ISODate()

返回时间值的字符串:mydate1.toString()

返回日期中的月份,日期是基于0索引的,所以一月份就是:mydate1.getMonth()

 

6.MongoDB对JSON的扩展

JSON所表示的类型仅是BSON数据类型的子集。为了表示类型信息,MongoDB对JSON做如下扩展:

  • strict模式。BSON类型的strict模式形式符合JSON RFC。任何的JSON分析器都能够分析这些键值对形式的strict模式形式。然而,仅MongoDB内部的JSON分析器识别转化为这种格式的信息。
  • mongo Shell模式。MongoDB内部的JSON分析器和mongo shell都能解析这种模式。

这种形式被用于各种数据类型,这些类型依赖于JSON被解析的上下文环境。

6.1 解析器和支持的格式

以strict模式输入

以下能够解析strict模式形式,识别类型信息。

  • REST Interfaces
  • mongoimport
  • 各种MongoDB工具的查询选项

其他的JSON解析器,包括mongo shell 和db.eval()能够解析键值对形式的strict模式表示,但是不能够识别类型信息。

mongo Shell 模式输入

以下能够解析mongo Shell模式表达,识别类型信息。

  • REST Interfaces
  • mongoimport
  • 各种MongoDB工具的查询选项
  • mongo shell

以strict模式输出

mongoexport 和REST and HTTP Interfaces以Strict模式输出数据。

mongo Shell 模式输出

bsondump以mongo Shell 模式输出数据。

6.2 BSON数据类型和相关的描述

下面展示了strict模式和mongo Shell模式的一些BSON数据类型及相关描述。

Binary

Strict Mode

 

mongo Shell Mode

{ "$binary": "<bindata>", "$type": "<t>" }

 

BinData ( <t>, <bindata> )

  • <bindata>是base64编码形式的二进制字符串
  • <t> 表示用一个字节指明数据类型。在strict模式中它是十六进制字符串,在mongo Shell模式中它是整数。

Date

Strict Mode

 

mongo Shell Mode

{ "$date": "<date>" }

 

new Date ( <date> )

在strict模式中,<date>是ISO-8601数据格式的强制性时区字段,它的模板为:YYYY-MM-DDTHH:mm:ss.mmm<+/-Offset>。

当前的MongoDB JSON解析器不支持加载Unix新纪元之前的ISO-8601 字符串日期。当格式化系统的time_t 类型的纪元之前和之后的时间时,采用下面的格式:{ "$date" : { "$numberLong" : "<dateAsMilliseconds>" } }

在Shell 模式中,<date>是一个64字节有符号整数的JSON形式,这个整数的表示形式为协调世界时间(UTC)的毫秒数。

Timestamp

Strict Mode

 

mongo Shell Mode

{ "$timestamp": { "t": <t>, "i": <i> } }

 

Timestamp( <t>, <i> )

  • <t> 为32位无符号整型UTC毫秒形式的JSON表达
  • <i>为自增的32位无符号整型

Regular Expression正则表达式)

Strict Mode

 

mongo Shell Mode

{ "$regex": "<sRegex>", "$options": "<sOptions>" }

 

/<jRegex>/<jOptions>

  • <sRegex> 是由有效的JSON字符构成的字符串
  • <jRegex> 是由有效的JSON字符和转义双引号字符构成的字符串,但可能不包含转义的正斜杠(/),
  • <sOptions> 是一个包含以字母表示的正则表达式选项的字符串
  • <jOptions>是一个仅可能包含‘g’, ‘i’, ‘m’ 和‘s’ 的字符串,因为JavaScript和Mongo shell表示支持有限的选择范围,当转化成这种表示时,不合格选项将被丢弃。

OID

Strict Mode

 

mongo Shell Mode

{ "$oid": "<id>" }

 

ObjectId( "<id>" )

<id>是一个24字符的十六进制字符串。

DB Reference

Strict Mode

 

mongo Shell Mode

{ "$ref": "<name>", "$id": "<id>" }

 

DBRef("<name>", "<id>")

  • <name> 是由有效的JSON字符构成的字符串。
  • <id>是任何有效的JSON扩展类型。

Undefined Type

Strict Mode

 

mongo Shell Mode

{ "$undefined": true }

 

undefined

表示为JavaScript/BSON中未定义类型。

查询文档时不能使用未定义类型。将下面的文档插入people 集合:

db.people.insert( { name : "Sally", age : undefined } )

下面的查询会返回一个错误:

db.people.find( { age : undefined } )

db.people.find( { age : { $gte : undefined } } )

然而,可使用$type查询未定义类型:

db.people.find( { age : { $type : 6 } } )

这个查询返回所有age 字段为未定义类型的文档。

MinKey

Strict Mode

 

mongo Shell Mode

{ "$minKey": 1 }

 

MinKey

Minkey BSON数据类型的排序低于所有其他类型。

MaxKey

Strict Mode

 

mongo Shell Mode

{ "$maxKey": 1 }

 

MaxKey

MaxKey BSON数据类型的排序高于所有其他类型。

NumberLong2.6版本新增

Strict Mode

 

mongo Shell Mode

{ "$numberLong": "<number>" }

 

NumberLong( "<number>" )

NumberLong 是64位有符号整数,必须使用引号否则它将会被理解为浮点型,这会导致精度丢失。

例如,插入9223372036854775807,却没有将其用引号括起来:

db.json.insert( { longQuoted : NumberLong("9223372036854775807") } )

db.json.insert( { longUnQuoted : NumberLong(9223372036854775807) } )

当查询文档时,longUnquoted 的值改变了,而longQuoted 的值没变。

执行db.json.find(),返回结果为:

{ "_id" : ObjectId("54ee1f2d33335326d70987df"), "longQuoted" : NumberLong("9223372036854775807") }

{ "_id" : ObjectId("54ee1f7433335326d70987e0"), "longUnquoted" : NumberLong("-9223372036854775808") }

-----------------------------------------------------------------------------------------

转载与引用请注明出处。

时间仓促,水平有限,如有不当之处,欢迎指正。

 




原标题:【翻译】MongoDB指南/引言

关键词:MongoDB

*特别声明:以上内容来自于网络收集,著作权属原作者所有,如有侵权,请联系我们: admin#shaoqun.com (#换成@)。

“黑五”失声 跨境进口的明天会好吗?:https://www.ikjzd.com/articles/135263
关于亚马逊直播的一些技巧:https://www.ikjzd.com/articles/135264
欧洲KYC需要准备什么:https://www.ikjzd.com/articles/135265
黄五大促进行中:盘点NOON的绝杀营销:https://www.ikjzd.com/articles/135266
11月Facebook产品更新及政策汇总!:https://www.ikjzd.com/articles/135267
一个亚马逊屌丝卖家的自述!:https://www.ikjzd.com/articles/135268
大同旅游攻略一日游 山西大同一日游旅游景点有哪些:https://www.vstour.cn/a/408251.html
如何从帽儿山到哈尔滨火车站(详细交通路线及注意事项):https://www.vstour.cn/a/408252.html
相关文章
我的浏览记录
最新相关资讯
海外公司注册 | 跨境电商服务平台 | 深圳旅行社 | 东南亚物流