---
title: 关系模式
slug: docs-ch/get-started/relational-schema
description: 探索如何在Back4App中通过Parse SDK高效查询和管理关系模式数据，包括Pointers和Relations的使用示例。
image: https://www.back4app.com/_public/img/back4app-og.png
createdAt: 2024-01-24T13:29:02.752Z
---

本指南解释了如何在 Back4app 上使用关系模式，包括创建相关类和使用 Parse Server 执行高效查询。您将学习如何有效地使用 `Pointers` 和 `Relations`，以及实际示例。

## 什么是关系模式？

关系模式将数据组织成不同的类，并相互连接。在 Parse Server 中，这些关系通过以下方式管理：

- **Pointers**: 直接引用单个对象。
- **Relations**: 管理多个连接（多对多关系）。

这些工具使您能够高效且一致地执行复杂查询。

## 目标

在本指南结束时，您将能够：

- 使用 *Pointers* 和 *Relations* 创建类之间的关系。
- 执行关系查询以检索连接的数据。
- 优化您的模式以获得更好的性能。

## 先决条件

:::hint{type="info"}
- 一个 Back4app 应用程序。 [创建应用程序指南](https://www.back4app.com/docs/get-started/new-parse-app).
- 已安装 Parse SDK。 [安装指南](https://www.back4app.com/docs/get-started/parse-sdk).
:::

## 1 - 创建相关类

### 实际示例: 州和城市

想象一下，您想要建模一个城市与州相关联的系统:

- 类 State 具有字段 state\_name.
- 类 City 具有字段 city\_name 和一个 *指针* 指向 State.

### 创建类和关系

:::CodeblockTabs
JavaScript

```javascript
async function createStateAndCity() {
  try {
    // Create a State
    const State = Parse.Object.extend('State');
    const california = new State();
    california.set('state_name', 'California');
    const savedState = await california.save();

    console.log(`State created with objectId: ${savedState.id}`);

    // Create a City with a pointer to State
    const City = Parse.Object.extend('City');
    const losAngeles = new City();
    losAngeles.set('city_name', 'Los Angeles');
    losAngeles.set('state', savedState);

    const savedCity = await losAngeles.save();
    console.log(`City created with objectId: ${savedCity.id}`);
  } catch (error) {
    console.error('Error creating state and city:', error.message);
  }
}

createStateAndCity();
```

Flutter

```dart
Future<void> createClasses() async {
  await Parse().initialize('YOUR_APP_ID', 'https://parseapi.back4app.com/',
      clientKey: 'YOUR_CLIENT_KEY', autoSendSessionId: true);

  // Create a State
  final state = ParseObject('State')
    ..set('state_name', 'California');
  final stateResult = await state.save();

  if (stateResult.success) {
    // Create a City with a pointer to State
    final city = ParseObject('City')
      ..set('city_name', 'Los Angeles')
      ..set('state', state);
    final cityResult = await city.save();

    if (cityResult.success) {
      print('State and City created successfully.');
    }
  }
}
```

Android

```java
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // Initialize Parse
        Parse.initialize(new Parse.Configuration.Builder(this)
                .applicationId("YOUR_APP_ID")
                .clientKey("YOUR_CLIENT_KEY")
                .server("https://parseapi.back4app.com/")
                .build()
        );

        // Create State and City
        createClasses();
    }

    private void createClasses() {
        // Create a State
        ParseObject state = new ParseObject("State");
        state.put("state_name", "California");

        state.saveInBackground(e -> {
            if (e == null) {
                Log.d("Parse", "State created successfully with objectId: " + state.getObjectId());

                // Save City with a pointer to State
                ParseObject city = new ParseObject("City");
                city.put("city_name", "Los Angeles");
                city.put("state", state);

                city.saveInBackground(ex -> {
                    if (ex == null) {
                        Log.d("Parse", "City created successfully with objectId: " + city.getObjectId());
                    } else {
                        Log.e("Parse", "Failed to create City: " + ex.getMessage());
                    }
                });
            } else {
                Log.e("Parse", "Failed to create State: " + e.getMessage());
            }
        });
    }
}
```

iOS

```swift
struct State: ParseObject {
    var objectId: String?
    var state_name: String?
    var createdAt: Date?
    var updatedAt: Date?
}

struct City: ParseObject {
    var objectId: String?
    var city_name: String?
    var state: State?
    var createdAt: Date?
    var updatedAt: Date?
}

func createClasses() async throws {
    try await ParseSwift.initialize(applicationId: "YOUR_APP_ID",
                                    clientKey: "YOUR_CLIENT_KEY",
                                    serverURL: URL(string: "https://parseapi.back4app.com")!)

    let state = State(state_name: "California")
    let savedState = try await state.save()

    var city = City(city_name: "Los Angeles", state: savedState)
    city = try await city.save()

    print("State and City created successfully.")
}
```

PHP

```php
require 'vendor/autoload.php';

use Parse\ParseClient;
use Parse\ParseObject;

ParseClient::initialize('YOUR_APP_ID', 'YOUR_CLIENT_KEY', 'https://parseapi.back4app.com/');

try {
    // Create a State
    $state = new ParseObject("State");
    $state->set("state_name", "California");
    $state->save();

    // Create a City with a pointer to State
    $city = new ParseObject("City");
    $city->set("city_name", "Los Angeles");
    $city->set("state", $state);
    $city->save();

    echo "State and City created successfully.";
} catch (Exception $ex) {
    echo "Error: " . $ex->getMessage();
}
```

.NET

```csharp
namespace ParseExampleApp
{
    class Program
    {
        static async Task Main(string[] args)
        {
            // Initialize Parse
            ParseClient.Initialize(new ParseClient.Configuration
            {
                ApplicationId = "YOUR_APP_ID",
                Server = "https://parseapi.back4app.com/",
                Key = "YOUR_CLIENT_KEY"
            });

            // Call the method to create State and City
            await CreateClassesAsync();
        }

        static async Task CreateClassesAsync()
        {
            try
            {
                // Create a State
                var state = new ParseObject("State");
                state["state_name"] = "California";
                await state.SaveAsync();
                Console.WriteLine($"State created with objectId: {state.ObjectId}");

                // Create a City with a Pointer to the State
                var city = new ParseObject("City");
                city["city_name"] = "Los Angeles";
                city["state"] = state; // Set the pointer to the State
                await city.SaveAsync();
                Console.WriteLine($"City created with objectId: {city.ObjectId}");
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error: {ex.Message}");
            }
        }
    }
}
```

REST API

```curl
# Create State
curl -X POST \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
-H "Content-Type: application/json" \
-d '{"state_name": "California"}' \
https://parseapi.back4app.com/classes/State

# Use the objectId of the created state to create a city
curl -X POST \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
-H "Content-Type: application/json" \
-d '{"city_name": "Los Angeles", "state": {"__type": "Pointer", "className": "State", "objectId": "STATE_OBJECT_ID"}}' \
https://parseapi.back4app.com/classes/City
```
:::

## 2 - 查询相关数据

现在数据已经相关，您可以执行查询以检索它。

### 示例 1: 获取特定州的城市

:::CodeblockTabs
JavaScript

```javascript
const stateQuery = new Parse.Query("State");
stateQuery.equalTo("state_name", "California");

stateQuery.first().then(state => {
  const cityQuery = new Parse.Query("City");
  cityQuery.equalTo("state", state);
  return cityQuery.find();
}).then(cities => {
  cities.forEach(city => {
    console.log(`City: ${city.get("city_name")}`);
  });
}).catch(error => {
  console.error("Error fetching cities:", error.message);
});
```

Flutter

```dart
final stateQuery = QueryBuilder(ParseObject('State'))..whereEqualTo('state_name', 'California');

final stateResult = await stateQuery.query();
if (stateResult.success && stateResult.results != null) {
  final state = stateResult.results!.first;
  final cityQuery = QueryBuilder(ParseObject('City'))..whereEqualTo('state', state);
  final cityResult = await cityQuery.query();

  if (cityResult.success && cityResult.results != null) {
    for (var city in cityResult.results!) {
      print('City: ${city.get<String>('city_name')}');
    }
  }
}
```

Android

```java
ParseQuery<ParseObject> stateQuery = ParseQuery.getQuery("State");
stateQuery.whereEqualTo("state_name", "California");

stateQuery.getFirstInBackground((state, e) -> {
    if (e == null) {
        ParseQuery<ParseObject> cityQuery = ParseQuery.getQuery("City");
        cityQuery.whereEqualTo("state", state);
        cityQuery.findInBackground((cities, ex) -> {
            if (ex == null) {
                for (ParseObject city : cities) {
                    Log.d("Parse", "City: " + city.getString("city_name"));
                }
            } else {
                Log.e("Parse", "Error fetching cities: " + ex.getMessage());
            }
        });
    } else {
        Log.e("Parse", "Error fetching state: " + e.getMessage());
    }
});
```

iOS

```swift
let stateQuery = State.query("state_name" == "California")

stateQuery.first { result in
    switch result {
    case .success(let state):
        let cityQuery = City.query("state" == state)
        cityQuery.find { cityResult in
            switch cityResult {
            case .success(let cities):
                cities.forEach { city in
                    print("City: \(city.city_name ?? "Unknown")")
                }
            case .failure(let error):
                print("Error fetching cities: \(error.localizedDescription)")
            }
        }
    case .failure(let error):
        print("Error fetching state: \(error.localizedDescription)")
    }
}
```

PHP

```php
use Parse\ParseQuery;

// Query State
$stateQuery = new ParseQuery("State");
$stateQuery->equalTo("state_name", "California");
$state = $stateQuery->first();

if ($state) {
    // Query Cities
    $cityQuery = new ParseQuery("City");
    $cityQuery->equalTo("state", $state);
    $cities = $cityQuery->find();

    foreach ($cities as $city) {
        echo "City: " . $city->get("city_name") . "\n";
    }
}
```

.NET

```csharp
var stateQuery = new ParseQuery<ParseObject>("State").WhereEqualTo("state_name", "California");
var state = await stateQuery.FirstAsync();

if (state != null)
{
    var cityQuery = new ParseQuery<ParseObject>("City").WhereEqualTo("state", state);
    var cities = await cityQuery.FindAsync();

    foreach (var city in cities)
    {
        Console.WriteLine($"City: {city["city_name"]}");
    }
}
```

REST API

```curl
# Query State:
curl -X GET \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
"https://parseapi.back4app.com/classes/State?where={\"state_name\":\"California\"}"

# Query Cities:
# Replace STATE_OBJECT_ID with the objectId from the state query.
curl -X GET \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
"https://parseapi.back4app.com/classes/City?where={\"state\":{\"__type\":\"Pointer\",\"className\":\"State\",\"objectId\":\"STATE_OBJECT_ID\"}}"
```
:::

### 示例 2: 查询与城市相关的州

创建一个查询，返回与任何城市相连的州：

:::CodeblockTabs
JavaScript

```javascript
const stateQuery = new Parse.Query("State");
const cityQuery = new Parse.Query("City");
cityQuery.matchesQuery("state", stateQuery);
cityQuery.include("state");

cityQuery.find().then(cities => {
  cities.forEach(city => {
    const state = city.get("state");
    console.log(`City: ${city.get("city_name")} belongs to state: ${state.get("state_name")}`);
  });
}).catch(error => {
  console.error("Error fetching data:", error.message);
});
```

Flutter

```dart
final stateQuery = QueryBuilder(ParseObject('State'));
final cityQuery = QueryBuilder(ParseObject('City'))
  ..whereMatchesQuery('state', stateQuery)
  ..includeObject(['state']);

final cityResult = await cityQuery.query();

if (cityResult.success && cityResult.results != null) {
  for (var city in cityResult.results!) {
    final state = city.get<ParseObject>('state');
    print('City: ${city.get<String>('city_name')} belongs to state: ${state?.get<String>('state_name')}');
  }
}
```

Android

```java
ParseQuery<ParseObject> stateQuery = ParseQuery.getQuery("State");
ParseQuery<ParseObject> cityQuery = ParseQuery.getQuery("City");
cityQuery.whereMatchesQuery("state", stateQuery);
cityQuery.include("state");

cityQuery.findInBackground((cities, e) -> {
    if (e == null) {
        for (ParseObject city : cities) {
            ParseObject state = city.getParseObject("state");
            Log.d("Parse", "City: " + city.getString("city_name") + " belongs to state: " + state.getString("state_name"));
        }
    } else {
        Log.e("Parse", "Error: " + e.getMessage());
    }
});
```

iOS

```swift
let stateQuery = State.query()
let cityQuery = City.query(matchesQuery(key: "state", query: stateQuery))
cityQuery.include("state")

cityQuery.find { result in
    switch result {
    case .success(let cities):
        cities.forEach { city in
            if let state = city.state {
                print("City: \(city.city_name ?? "Unknown") belongs to state: \(state.state_name ?? "Unknown")")
            }
        }
    case .failure(let error):
        print("Error: \(error.localizedDescription)")
    }
}
```

PHP

```php
use Parse\ParseQuery;

$stateQuery = new ParseQuery("State");
$cityQuery = new ParseQuery("City");
$cityQuery->matchesQuery("state", $stateQuery);
$cityQuery->includeKey("state");

$cities = $cityQuery->find();
foreach ($cities as $city) {
    $state = $city->get("state");
    echo "City: " . $city->get("city_name") . " belongs to state: " . $state->get("state_name") . "\n";
}
```

.NET

```csharp
var stateQuery = new ParseQuery<ParseObject>("State");
var cityQuery = new ParseQuery<ParseObject>("City").WhereMatchesQuery("state", stateQuery).Include("state");

var cities = await cityQuery.FindAsync();
foreach (var city in cities)
{
    var state = city.Get<ParseObject>("state");
    Console.WriteLine($"City: {city["city_name"]} belongs to state: {state["state_name"]}");
}
```

REST API

```curl
curl -X GET \
-H "X-Parse-Application-Id: YOUR_APP_ID" \
-H "X-Parse-REST-API-Key: YOUR_REST_API_KEY" \
"https://parseapi.back4app.com/classes/City?where={\"state\":{\"$inQuery\":{\"className\":\"State\"}}}&include=state"
```
:::

## 最佳实践

要有效地使用 Parse Server 在 Back4App 中处理关系模式，请遵循以下最佳实践，以确保性能、可维护性和可扩展性:

### 选择正确的关系类型

- **使用指针** 来表示 **一对一关系**, 例如将用户与其个人资料链接。
- **使用关系** 来表示 **多对多关系**, 例如将一个项目链接到多个任务。

### 高效查询

- **使用&#x20;**`.include()` 在同一查询中加载相关数据，减少多次请求的需要。
- **限制结果** 使用 `limit()` 和 `skip()` 以避免一次获取大量数据集。
- **索引经常查询的字段** 以加快搜索速度。

### 避免过度嵌套

- **保持查询扁平化** 以减少复杂性并提高性能。仅在必要时使用嵌套查询。

### 将复杂查询卸载到云代码

- 对于 **涉及多个关系的复杂查询** 或大型数据集，将这些卸载到 **云代码** 以保持客户端轻量和响应迅速。

## 结论

在本指南中，您学习了如何在 Back4app 上创建类之间的关系并查询相关对象。继续探索 [特定 SDK 文档](https://docs.parseplatform.org) 以深入了解更多内容！
