跳到主要内容

get

根据 ID 直接获取实体,这是最简单、最高效的查询方式。

方法签名

get(id: EntityIdType): Observable<InstanceType<T>>

使用场景

1. 基础用法

import { firstValueFrom, switchMap } from 'rxjs';

// 根据 ID 获取 todo
const todo = await rxdb.pipe(
switchMap(() => Todo.get('591e97aa-f7e3-499c-9fa4-4e2ae4459ef6')),
firstValueFrom
);

console.log(todo.title);

2. 在组件中使用

import { Component, Input, OnInit } from '@angular/core';
import { Observable } from 'rxjs';

@Component({
selector: 'app-todo-detail',
template: `
<div *ngIf="todo$ | async as todo">
<h2>{{ todo.title }}</h2>
<p>{{ todo.description }}</p>
<p>状态: {{ todo.completed ? '已完成' : '未完成' }}</p>
</div>
`
})
export class TodoDetailComponent implements OnInit {
@Input() todoId!: string;
todo$!: Observable<Todo>;

ngOnInit() {
this.todo$ = rxdb.pipe(
switchMap(() => Todo.get(this.todoId))
);
}
}

3. React 中使用

import { useEffect, useState } from 'react';
import { firstValueFrom, switchMap } from 'rxjs';

function TodoDetail({ todoId }: { todoId: string }) {
const [todo, setTodo] = useState<Todo | null>(null);

useEffect(() => {
const subscription = rxdb.pipe(
switchMap(() => Todo.get(todoId))
).subscribe(setTodo);

return () => subscription.unsubscribe();
}, [todoId]);

if (!todo) return <div>加载中...</div>;

return (
<div>
<h2>{todo.title}</h2>
<p>{todo.description}</p>
</div>
);
}

4. Vue 中使用

<template>
<div v-if="todo">
<h2>{{ todo.title }}</h2>
<p>{{ todo.description }}</p>
</div>
<div v-else>加载中...</div>
</template>

<script setup lang="ts">
import { ref, watchEffect } from 'vue';
import { firstValueFrom, switchMap } from 'rxjs';

const props = defineProps<{ todoId: string }>();
const todo = ref<Todo | null>(null);

watchEffect(async () => {
const subscription = rxdb.pipe(
switchMap(() => Todo.get(props.todoId))
).subscribe(value => {
todo.value = value;
});

return () => subscription.unsubscribe();
});
</script>

5. 批量获取

// 批量获取多个实体
const todoIds = ['id-1', 'id-2', 'id-3'];

const todos = await Promise.all(
todoIds.map(id =>
rxdb.pipe(
switchMap(() => Todo.get(id)),
firstValueFrom
)
)
);

6. 结合错误处理

import { catchError, of } from 'rxjs';

const todo$ = rxdb.pipe(
switchMap(() => Todo.get(todoId)),
catchError(error => {
console.error('获取 todo 失败:', error);
return of(null);
})
);

get vs findOne

方法查询方式性能参数返回值
get主键索引最快仅需要 IDObservable<T>
findOne条件查询较慢复杂查询条件Observable<T | undefined>
// get - 适合已知 ID 的情况
const todo1 = await rxdb.pipe(
switchMap(() => Todo.get('todo-id')),
firstValueFrom
);

// findOne - 适合需要条件查询的情况
const todo2 = await rxdb.pipe(
switchMap(() => Todo.findOne({
where: {
combinator: 'and',
rules: [
{ field: 'userId', operator: '=', value: 'user-123' },
{ field: 'completed', operator: '=', value: false }
]
}
})),
firstValueFrom
);

性能优化

1. 使用 get 而非 findOne

当你已经知道实体的 ID 时,始终优先使用 get 而不是 findOne

// ✅ 好:直接通过 ID 获取
Todo.get(todoId);

// ❌ 不必要的复杂:使用 findOne 查询 ID
Todo.findOne({
where: {
combinator: 'and',
rules: [
{ field: 'id', operator: '=', value: todoId }
]
}
});

2. 缓存策略

实体获取后会被缓存,重复调用 get 时会直接返回缓存的实体引用:

// 第一次调用会从数据库读取
const todo1 = await rxdb.pipe(
switchMap(() => Todo.get(todoId)),
firstValueFrom
);

// 第二次调用会返回相同的实体引用
const todo2 = await rxdb.pipe(
switchMap(() => Todo.get(todoId)),
firstValueFrom
);

console.log(todo1 === todo2); // true - 相同的对象引用

3. 响应式更新

使用 Observable 订阅,当实体更新时自动收到通知:

const subscription = rxdb.pipe(
switchMap(() => Todo.get(todoId))
).subscribe(todo => {
console.log('Todo 更新了:', todo);
});

// 在其他地方更新这个 todo
await Todo.update(todo, { completed: true });
// 上面的订阅会自动收到更新后的 todo

错误处理

ID 不存在时的处理

import { catchError, EMPTY } from 'rxjs';

// 方式 1: 使用 catchError 捕获错误
const todo$ = rxdb.pipe(
switchMap(() => Todo.get(todoId)),
catchError(error => {
if (error.code === 'ENTITY_NOT_FOUND') {
console.log('Todo 不存在');
return EMPTY;
}
throw error;
})
);

// 方式 2: 使用 try-catch
try {
const todo = await rxdb.pipe(
switchMap(() => Todo.get(todoId)),
firstValueFrom
);
} catch (error) {
console.error('获取失败:', error);
}

常见用例

1. 路由参数获取实体

import { ActivatedRoute } from '@angular/router';
import { switchMap } from 'rxjs';

@Component({
selector: 'app-todo-detail'
})
export class TodoDetailComponent {
todo$ = this.route.params.pipe(
switchMap(params => Todo.get(params['id']))
);

constructor(private route: ActivatedRoute) {}
}

2. 实时数据同步

// 订阅实体变化,实现实时更新
const todoSubscription = rxdb.pipe(
switchMap(() => Todo.get(todoId))
).subscribe(todo => {
// 每当这个 todo 在数据库中被更新时
// 这里会自动收到最新的数据
updateUI(todo);
});

3. 预加载相关数据

// 获取 todo 并预加载相关的用户信息
const todoWithUser$ = rxdb.pipe(
switchMap(() => Todo.get(todoId)),
switchMap(todo =>
User.get(todo.userId).pipe(
map(user => ({ todo, user }))
)
)
);

最佳实践

  1. 优先使用 get:当有 ID 时,始终使用 get 而不是 findOne
  2. 利用缓存:理解实体缓存机制,避免不必要的重复查询
  3. 响应式编程:使用 Observable 订阅而非 Promise,享受自动更新的好处
  4. 错误处理:始终处理实体不存在的情况
  5. 类型安全:利用 TypeScript 类型推断,避免类型错误

参考

  • findOne - 根据条件查找单个实体
  • findOneOrFail - 查找单个实体,找不到抛出错误
  • find - 查询多个实体