文档

MeiliSearch文档是由一个或多个字段组成的对象。每个字段由一个属性及其关联值组成。

文档作为组织数据的容器,是 Meilisearch 数据库的基本构件。要搜索文档,必须首先将其添加到索引中。

结构

MeiliSearch文档结构

重要术语

  • 文档: 包含一个或多个字段形式的数据的对象
  • 字段: 链接在一起的两个数据项的集合: 属性和值
  • 属性(Key): 字段的第一部分。用作其关联值的名称或说明。
  • 值(Value): 字段的第二部分,由任何有效 JSON 类型的数据组成。
  • 主字段(Primary Field):在所有文档中都必须填写的特殊字段。它包含主键和文档标识符。
  • 主键(Primary Key):主字段的属性。同一索引中的所有文档必须具有相同的主键。它的关联值是文档标识符。
  • 文档标识符: 主字段的值。给定索引中的每个文档都必须有一个唯一标识符。

数据集格式

您可以提供以下格式的数据集:

  • JSON
  • NDJSON
  • CSV

JSON

用 JSON 对象表示的文档是由花括号括起来的键值对。因此,应用于格式化 JSON 对象的任何规则,也适用于格式化 Meilisearch 文档。例如,属性必须是字符串,而值必须是有效的 JSON 数据类型。

假设您正在创建一个包含有关电影信息的索引。示例如下:

{
  "id": "1564saqw12ss",
  "title": "Kung Fu Panda",
  "genres": "Children's Animation",
  "release-year": 2008,
  "cast": [
    { "Jack Black": "Po" },
    { "Jackie Chan": "Monkey" }
  ]
}

在上面的例子中,“ id”、“ title”、“ genre”、“ release-year”和“ cast”都是属性。每个属性必须与一个值相关联,例如“ Kung Fu Panda”是“ title”的值。 文档至少必须包含一个字段,其主键属性和唯一的文档 id 作为其值。上面的代码是: “ id”: “1564saqw12ss”。

NDJSON

NDJSON 对象由单独的行组成,其中每一行都是有效的 JSON 文本,每一行都用换行符分隔。适用于格式化 NDJSON 的任何规则适用于 Meilisearch 文件。

与 JSON 相比,NDJSON 具有更好的写入性能和更低的 CPU 和内存密集度。它更容易验证,而且不像 CSV,可以处理嵌套结构。

上面的 JSON 文档在 NDJSON 中看起来像这样:

{ 
  "id": "1564saqw12ss", 
  "title": "Kung Fu Panda", 
  "genres": "Children's Animation", 
  "release-year": 2008, 
  "cast": [
    { "Jack Black": "Po" },
    { "Jackie Chan": "Monkey" }
  ]
}

CSV

CSV 文件将数据表示为由分隔符分隔的值序列。目前,Meilisearch 只支持逗号(,)分隔符。应用于格式化 CSV 的任何规则适用于 Meilisearch 文件。

上面的 JSON 文档在 CSV 中看起来像这样:

  "id:string","title:string","genres:string","release-year:number"
  "1564saqw12ss","Kung Fu Panda","Children's Animation","2008"

由于 CSV 不支持数组或嵌套对象,因此不能将强制转换转换为 CSV。

提示: 如果没有为属性指定数据类型,它将默认为: string。

限制和要求

  • 文档最多有1000个软字段; 超过这个数字,排名规则可能不再有效,导致未定义行为。
  • 此外,每个文档必须至少有一个包含主键和唯一 id 的字段。
  • 果试图索引格式不正确的文档、缺少主键或者为给定的索引使用了错误的主键,将导致错误并且不会添加任何文档。

字段(Fields)

字段是两个连接在一起的数据项的集合: 属性和值。文件由字段组成。

在大多数编程语言中,属性函数有点像变量,即它是一个允许您存储、访问和描述某些数据的名称。该数据是属性的值。

每个字段都有一个由其值决定的数据类型。每个值都必须是有效的 JSON 数据类型。

注意,对于字符串,一个值最多可以包含65535个位置。超过65535位置限制的单词将被忽略。

您还可以将排名规则应用于某些字段。例如,你可能认为最近的电影应该比旧的电影更有关联。

如果您想调整 Meilisearch 处理字段的方式,可以在设置中进行调整。

字段属性

一个字段也可能具有字段属性。字段属性决定添加到该字段的数据的特征和行为。此时,有两个字段属性: searchable 和 displayed。一个字段可以有一个、两个或多个属性。默认情况下,文档中的所有字段都是显示并可搜索的。

一个字段可以是:

  • 可搜索但不显示
  • 显示但无法搜索
  • 显示和可搜索(默认值)
  • 既不显示也不可搜索

在后一种情况下,执行搜索时将完全忽略该字段。但是,它仍然会存储在文档中。

主字段

主字段是所有文档中都必须出现的特殊字段。它的属性是主键,其值为文档的id。主字段的重要作用是对存储在索引中的每个文档进行唯一标识,确保不可能在同一索引中存在两个完全相同的文档。因此,同一索引中的每个文档都必须具有与唯一文档 id 相关联的主键值。

示例

假设有一个名为 movie 的索引,其中包含20万个文档。如下所示,每个文档都由包含主键 movie_id 和唯一值(文档 id)的主字段标识。

除了主键之外,同一索引中的文档不需要共享属性,例如,您可以在该索引中使用文档而不使用“ title”属性。

[
  {
    "movie_id": "1564saqw12ss",
    "title": "Kung fu Panda",
    "runtime": 95
  },
  {
    "movie_id": "15k1j2kkw223s",
    "title": "Batman Begins",
    "gritty reboot": true
  }
]

主键

主键是一个强制属性,链接到文档 id 的唯一值。它是主要领域的一部分。

每个索引只能识别一个主键属性。一旦为索引设置了主键,就不能再更改它了。如果在文档中没有找到主键,则不会存储任何文档。

有几种方法可以设置索引的主键:

  • 您可以在创建索引时手动设置它
  • 您可以在文档添加时手动设置它
  • 如果没有设置主键,则在添加文档时,meilisesearch 会自动猜测主键

在创建索引时设置主键

下面的代码创建了一个名为 movies 的索引,它的主键是 reference _ number:

cURL
curl \
  -X POST 'http://localhost:7700/indexes' \
  -H 'Content-Type: application/json' \
  --data-binary '{
    "uid": "movies",
    "primaryKey": "reference_number"
  }'

设置文档添加的主键

下面的代码添加一个文档并将 reference _ number 设置为索引的主键:

cURL
curl \
  -X POST 'http://localhost:7700/indexes/movies/documents?primaryKey=reference_number' \
  -H 'Content-Type: application/json' \
  --data-binary '[{
      "reference_number": 287947,
      "title": "Shazam",
      "poster": "https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg",
      "overview": "A boy is given the ability to become an adult superhero in times of need with a single magic word.",
      "release_date": "2019-03-23"
   }]' 

数据搜索猜测你的主键

如果主键既没有在创建索引时设置,也没有作为 add documents route 的参数,则 Meilisearch 将搜索第一个文档中包含不区分大小写的字符串 ID 的属性(例如,uid、 MovieId、 ID、123id123) ,并将其设置为索引的主键。

如果没有找到相应的属性,则索引将没有已知的主键,因此不会添加文档。

缺少主键错误:

如果没有设置主键,将会得到一个 “the primary_key_inference_failed error” 的错误

可以使用主键名作为添加文档路由或更新索引路由的参数来手动添加主键。

文档ID

文档 id 是与主键关联的值。它是主字段的一部分,并且作为给定索引的每个文档的唯一标识符。

这个唯一值确保同一索引中的两个文档不能完全相同。如果同一个索引中的两个文档具有相同的 id,那么它们将被视为同一个文档,最近的文档将替换旧的文档。

文档 id 必须只包含 A-Z A-Z 0-9和-_ 字符。

正确的示例:

"id": "_Aabc012_"

错误的示例:

"id": "@BI+* ^5h2%"

注意,Meilisearch 的文档添加请求是原子的。这意味着,即使单个文档 id 格式不正确,也会出现错误,不会添加任何文档。

上传

默认情况下,Meilisearch 将所有有效载荷的大小限制在100mb 以内,因此文档上传也不会超过100mb。

要一次上传更多的文档,可以使用 http-payload-size-limit 选项在运行时更改有效负载大小限制。

./meilisearch --http-payload-size-limit=1048576000

上面的代码将有效负载限制设置为1 GB,而不是100 MB 的默认值。

在索引文档时,Meilisearch 使用了大量的 RAM。当您增加批处理的大小时,请注意您的 RAM 可用性,因为这可能会导致 Meilisearch 崩溃。

当使用路由添加新文档时,即使只有一个文档,所有文档也必须以数组的形式发送。

cURL

curl -X POST `http://localhost:7700/indexes/movies/documents` \
-H 'Content-Type: application/json' \
  --data-binary '[
    {
      "movie_id": "123sq178",
      "title": "Amelie Poulain"
    }
  ]'

// JavaScript
client.index('movies').addDocuments([{
  'movie_id': '123sq178',
  'title': 'Amelie Poulain'
}])
// python
client.index('movies').add_documents([{
  'movie_id': '123sq178',
  'title': 'Amélie Poulain'
}])

// php
$client->index('movies')->addDocuments([['movie_id' => '123sq178', 'title' => 'Amelie Poulain']]);

// java
client.index("movies").addDocuments("[{
  + "\"movie_id\": 123sq178,"
  + "\"title\": \"Amelie Poulain\""
  + "}]"
);

// ruby
client.index('movies').add_documents([{
  "movie_id": "123sq178",
  "title": "Amelie Poulain"
}])

// go
documents := []map[string]interface{}{
  {
    "movie_id": "123sq178",
    "title":    "Amelie Poulain",
  },
}
client.Index("movies").AddDocuments(documents)

// rust
// Define the type of our documents
#[derive(Serialize, Deserialize, Debug)]
struct IncompleteMovie {
  id: String,
  title: String
}
impl Document for IncompleteMovie {
  type UIDType = String;
  fn get_uid(&self) -> &Self::UIDType { &self.id }
}

// Add a document to our index
let progress: Progress = client.index("movies").add_documents(&[
  IncompleteMovie {
    id: "123sq178".to_string(),
    title: "Amélie Poulain".to_string(),
  }
], None).await.unwrap();
// swift
let documentJsonString = """
[{
    "movie_id": 123sq178,
    "title": "Amélie Poulain"
}]
"""
let documents: Data = documentJsonString.data(using: .utf8)!
client.index("movies").addDocuments(documents: documents) { (result: Result<Update, Swift.Error>) in
    switch result {
    case .success(let update):
        print(update)
    case .failure(let error):
        print(error)
    }
}
//dart
let documentJsonString = """
[{
    "movie_id": 123sq178,
    "title": "Amélie Poulain"
}]
"""
let documents: Data = documentJsonString.data(using: .utf8)!
client.index("movies").addDocuments(documents: documents) { (result: Result<Update, Swift.Error>) in
    switch result {
    case .success(let update):
        print(update)
    case .failure(let error):
        print(error)
    }
}