冰凍三尺非一日之寒-mysql(orm/sqlalchemy)

第十二章  mysqlhtml

  1. ORM介紹    2.sqlalchemy基本使用

ORM介紹:python

orm英文全稱object relational mapping,就是對象映射關係程序,簡單來講咱們相似python這種面向對象的程序來講一切皆對象,可是咱們使用的數據庫卻都是關係型的,爲了保證 一致的使用習慣,經過orm將編程語言的對象模型和數據庫的關係模型創建映射關係,這樣咱們在使用編程語言對數據庫進行操做的時候能夠直接使用編程語言的 對象模型進行操做就能夠了,而不用直接使用sql語言mysql

orm的優勢:sql

  1. 隱藏了數據訪問細節,「封閉」的通用數據庫交互,ORM的核心。他使得咱們的通用數據庫交互變得簡單易行,而且徹底不用考慮該死的SQL語句。快速開發,由此而來。
  2. ORM使咱們構造固化數據結構變得簡單易行。

缺點:數據庫

  1. 無可避免的,自動化意味着映射和關聯管理,代價是犧牲性能(早期,這是全部不喜歡ORM人的共同點)。如今的各類ORM框架都在嘗試使用各類方法來減輕這塊(LazyLoad,Cache),效果仍是很顯著的。

在Python中,最有名的ORM框架是SQLAlchemy。用戶包括openstack\Dropbox等知名公司或應用,主要用戶列表http://www.sqlalchemy.org/organizations.html#openstack編程

Dialect用於和數據API進行交流,根據配置文件的不一樣調用不一樣的數據庫API,從而實現對數據庫的操做,如:ubuntu

 1 MySQL-Python
 2     mysql+mysqldb://<user>:<password>@<host>[:<port>]/<dbname>
 3    
 4 pymysql
 5     mysql+pymysql://<username>:<password>@<host>/<dbname>[?<options>]
 6    
 7 MySQL-Connector
 8     mysql+mysqlconnector://<user>:<password>@<host>[:<port>]/<dbname>
 9    
10 cx_Oracle
11     oracle+cx_oracle://user:pass@host:port/dbname[?key=value&key=value...]
12    
13 更多詳見:http://docs.sqlalchemy.org/en/latest/dialects/index.html

安裝sqlalchemysession

pip install SQLAlchemy<br><br>pip install pymysql  #因爲mysqldb依然不支持py3,因此這裏咱們用pymysql與sqlalchemy交互

1.基本操做:

1)連接數據庫:create_engine()數據結構

 1 engine = create_engine("mysql+mysqldb://root:111111@121.42.195.15:3306/python", max_overflow=5)
 2 
 3 create_engine() 會返回一個數據庫引擎,
 4 mysql+mysqldb」指定了使用 MySQL-Python 來鏈接,
 5 使用用戶名‘root’和密碼‘111111’來連接數據庫
 6 121.42.195.15是數據庫連接地址能夠是localhost,127.0.0.1
 7 ‘python’是數據庫名
 8 max_overflow是最大鏈接數
 9 其餘方法:
10 「charset」指定了鏈接時使用的字符集(可省略)=utf8
11  echo 參數爲 True 時,會顯示每條執行的 SQL 語句,生產環境下可關閉。

2)字段和數據類型及操做方法oracle

在sqlalchemy.schema包裏有數據庫關係的描述,列舉幾個最經常使用的:

字段:Column

索引:Index

表:Table

數據類型在sqlalchemy.types包,列舉幾個最經常使用的:

二進制:BIGINT

布爾:BOOLEAN

字符:CHAR

可變字符:VARCHAR

日期:DATETIME

其餘方法 execute,update,insert,select,delete,join等 自行補腦

3)建立表結構

使用 Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 進行數據庫操做。Engine使用Schema Type建立一個特定的結構對象,以後經過SQL Expression Language將該對象轉換成SQL語句,而後經過 ConnectionPooling 鏈接數據庫,再而後經過 Dialect 執行SQL,並獲取結果。

from sqlalchemy import create_engine, Table, Column, Integer, /
String, MetaData, ForeignKey
import MySQLdb
#建立數據庫鏈接
engine = create_engine("mysql+mysqldb://liuyao:liuyao@121.42.195.15:3306/liuyao", max_overflow=5)
# 獲取元數據
metadata = MetaData()
# 定義表
user = Table('user', metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(20)),
    )

color = Table('color', metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(20)),
    )
# 建立數據表,若是數據表存在,則忽視
metadata.create_all(engine)
結果:
mysql> show tables;
+------------------+
| Tables_in_liuyao |
+------------------+
| color            |
| user             |
+------------------+
2 rows in set (0.00 sec)

3)插入一條數據

使用 Engine/ConnectionPooling/Dialect 進行數據庫操做,Engine使用ConnectionPooling鏈接數據庫,而後再經過Dialect執行SQL語句。

#!/usr/bin/env python3
#coding:utf8
from sqlalchemy import create_engine

engine = create_engine("mysql+mysqldb://root:111111@121.42.195.15:3306/
python", max_overflow=5)
engine.execute(
    "INSERT INTO python.color(id, name) VALUES ('1', 'chen');"
)
result = engine.execute('select * from color')
print(result.fetchall())
結果:
[(1L, 'chen'), (2L, 'v1')]

4) 增刪改查

先建立數據庫

#!/usr/bin/env python3
#coding:utf8
from sqlalchemy import create_engine, Table, Column, Integer, String,MetaData, ForeignKey
metadata = MetaData()   
#建立數據庫引擎
engine = create_engine("mysql+mysqldb://root:111111@121.42.195.15:3306/
python", max_overflow=5)
conn = engine.connect()
#建立一個表叫作user 在python庫裏
user = Table('user', metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(20)),
)
color = Table('color', metadata,
    Column('id', Integer, primary_key=True),
    Column('name', String(20)),
)       
metadata.create_all(engine)

增長

# 建立SQL語句,INSERT INTO "user" (id, name) VALUES (:id, :name)
conn.execute(user.insert(),{'id':7,'name':'seven'})
conn.close()
# 或者按照下面的方式建立
# sql = user.insert().values(id=123, name='wu')
# conn.execute(sql) 
# conn.close()
結果:
mysql> show tables;
+------------------+
| Tables_in_liuyao |
+------------------+
| color            |
| user             |
+------------------+
2 rows in set (0.00 sec)

mysql> select * from user;
+----+-------+
| id | name  |
+----+-------+
|  7 | seven |
+----+-------+
1 row in set (0.00 sec)

刪除

#刪除一條user表裏的 條件是id大於1的
sql = user.delete().where(user.c.id > 1)
#執行
conn.execute(sql)
#關閉連接
conn.close()
結果:
mysql> select * from user;
Empty set (0.00 sec)

mysql> 
#因表裏只有一條數據,刪除以後,沒有任何數據存在

修改/更新

先建立幾條數據步驟略
顯示以下:
mysql> select * from user;
+----+--------+
| id | name   |
+----+--------+
|  1 | chen |
|  2 | kobe |
|  3 | javk |
|  4 | yao    |
+----+--------+
4 rows in set (0.00 sec)
#更新
#把名字爲chen的修改成no1
sql = user.update().where(user.c.name == 'chen').values(name='no1')
conn.execute(sql)
conn.close()
結果:
mysql> select * from user;
+----+--------+
| id | name   |
+----+--------+
|  1 | no1    |
|  2 | kobe    |
|  3 | javk |
|  4 | yao    |
+----+--------+
4 rows in set (0.00 sec)

查詢

注:請導入查詢模塊

from sqlalchemy import select 其餘模塊同上
#查詢user表裏的內容
sql = select([user, ])
res =conn.execute(sql)
print res.fetchall()
conn.close()
結果:
[(1L, 'no1'), (2L, 'no1'), (3L, 'yaoyao'), (4L, 'yao')]
#查詢user表下的id
sql = select([user.c.id, ])
res =conn.execute(sql)
print res.fetchall()
conn.close()
結果:
[(1L,), (2L,), (3L,), (4L,)]
#查詢user表和color表的name,條件是user表的id1=color的id1
sql = select([user.c.name, color.c.name]).where(user.c.id==color.c.id)
結果:
[('no1', 'liuyao'), ('no1', 'v1')]
#查詢user表的name,並按照條件排序
#按照名字排序 
sql = select([user.c.name]).order_by(user.c.name)
res =conn.execute(sql)
print res.fetchall()
conn.close()
結果:
[('no1',), ('no1',), ('yao',), ('yaoyao',)]
#按照id排序
sql = select([user.c.name]).order_by(user.c.id)
res =conn.execute(sql)
print res.fetchall()
conn.close()
結果:
[('no1',), ('no1',), ('yaoyao',), ('yao',)]
#查詢user表的name,並按照條件分組
sql = select([user]).group_by(user.c.name)
res =conn.execute(sql)
print res.fetchall()
conn.close()
結果:
[(1L, 'no1'), (4L, 'yao'), (3L, 'yaoyao')]
#按照id
結果:
[(1L, 'no1'), (2L, 'no1'), (3L, 'yaoyao'), (4L, 'yao')]

 

5)繼承SqlORM類來操做數據庫

#!/usr/bin/env python3
#coding:utf8
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from  sqlalchemy.orm import sessionmaker
Base = declarative_base() #生成一個SqlORM 基類
engine = create_engine("mysql+mysqldb://liuyao:liuyao@121.42.195.15:3306/liuyao",echo=True)
#echo若是爲True,那麼當他執行整個代碼的數據庫的時候會顯示過程
#建立一個類繼承Base基類
class Host(Base):
    #表名爲hosts
    __tablename__ = 'hosts'
    #表結構
    #primary_key等於主鍵
    #unique惟一
    #nullable非空
    id = Column(Integer,primary_key=True,autoincrement=True)
    hostname = Column(String(64),unique=True,nullable=False)
    ip_addr = Column(String(128),unique=True,nullable=False)
    port = Column(Integer,default=22)

Base.metadata.create_all(engine) #建立全部表結構

if __name__ == '__main__':
    SessionCls = sessionmaker(bind=engine)
    #bind綁定
    #建立與數據庫的會話session class 
    #注意,這裏返回給session的是個class,不是實例 
    session = SessionCls()
    #插入字段
    h1 = Host(hostname='qd115',ip_addr='115.29.51.8')
    h2 = Host(hostname='ubuntu',ip_addr='139.129.5.191',port=80)
    h3 = Host(hostname='mysql',ip_addr='121.42.195.15',port=3306)
    #添加一個
    #session.add(h3)
    #能夠添加多個字段
    session.add_all( [h1,h2,h3])
    #修改字段名字,只要沒提交,此時修改也沒問題
    #h2.hostname = 'ubuntu_test'
    #支持數據回滾
    #session.rollback()
    #提交
    session.commit()

結果:
mysql> select * from hosts;
+----+----------+---------------+------+
| id | hostname | ip_addr       | port |
+----+----------+---------------+------+
|  1 | qd115    | 115.29.51.8   |   22 |
|  2 | ubuntu   | 139.129.5.191 |   80 |
|  4 | mysql    | 121.42.195.15 | 3306 |
+----+----------+---------------+------+
3 rows in set (0.00 sec)

注:SQLAlchemy沒法修改表結構,若是須要可使用SQLAlchemy開發者開源的另一個軟件Alembic來完成。

6.繼承類式增刪改查:

使用 ORM/Schema Type/SQL Expression Language/Engine/ConnectionPooling/Dialect 全部組件對數據進行操做。根據類建立對象,對象轉換成SQL,執行SQL。

Query對象能夠返回可迭代的值(iterator value),而後咱們能夠經過for in來查詢。不過Query對象的all()、one()以及first()方法將返回非迭代值(non-iterator value),好比說all()返回的是一個列表first()方法限制並僅做爲標量返回結果集的第一條記錄:

1)先建立相關數據庫

#!/usr/bin/env python
# -*- coding:utf-8 -*-
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String
from sqlalchemy.orm import sessionmaker
from sqlalchemy import create_engine
#建立數據庫
engine = create_engine("mysql+mysqldb://liuyao:liuyao@121.42.195.15:3306/liuyao", max_overflow=5)
#生成一個SqlORM 基類
Base = declarative_base()   
#定義表結構
class User(Base):
    #表名
    __tablename__ = 'users'
    #定義id,主鍵惟一,
    id = Column(Integer, primary_key=True)
    name = Column(String(50))
#尋找Base的全部子類,按照子類的結構在數據庫中生成對應的數據表信息
Base.metadata.create_all(engine)
#建立與數據庫的會話session class ,注意,這裏返回給session的是個class,不是實例
Session = sessionmaker(bind=engine)
session = Session()
#獲取session,而後把對象添加到session,
#最後提交併關閉。Session對象可視爲當前數據庫鏈接。

2.增長

########### 增 ##########
#定義一個字段
zengjia = User(id=2, name='sbliuyao')
#添加字段
session.add(zengjia)
#添加多個字段
session.add_all([
    User(id=3, name='sbyao'),
    User(id=4, name='liuyao')
])
#提交以上操做
session.commit()

結果:
mysql> select * from users;
+----+----------+
| id | name     |
+----+----------+
|  2 | sbliuyao |
|  3 | sbyao    |
|  4 | liuyao   |
+----+----------+
3 rows in set (0.00 sec)

3.刪除

# ########## 刪除 ##########
#刪除user表,id大於2的字段
session.query(User).filter(User.id > 2).delete()
session.commit()
結果:
mysql> select * from users;
+----+----------+
| id | name     |
+----+----------+
|  2 | sbliuyao |
+----+----------+
1 row in set (0.00 sec)

4.修改

因上次操做已經刪除好多數據
請從新執行插入字段操做
session.add_all([
    User(id=3, name='sbyao'),
    User(id=4, name='liuyao'),
    User(id=5, name='mayun')
])
session.commit()
結果:
mysql> select * from users;
+----+----------+
| id | name     |
+----+----------+
|  2 | sbliuyao |
|  3 | sbyao    |
|  4 | liuyao   |
|  5 | mayun    |
+----+----------+
4 rows in set (0.00 sec)

在執行如下操做
#user表裏的id等於2的字段修改成id=6
session.query(User).filter(User.id == 2).update({'id' : 6})
session.commit()
結果:
mysql> select * from users;
+----+----------+
| id | name     |
+----+----------+
|  3 | sbyao    |
|  4 | liuyao   |
|  5 | mayun    |
|  6 | sbliuyao |
+----+----------+
4 rows in set (0.00 sec)
其餘方法:
#把user表裏id大於2的name所有換成woshiyaoge
session.query(User).filter(User.id > 2).update({'name' :'woshiyaoge'})
session.commit()

mysql> select * from users;
+----+------------+
| id | name       |
+----+------------+
|  3 | woshiyaoge |
|  4 | woshiyaoge |
|  5 | woshiyaoge |
|  6 | woshiyaoge |
+----+------------+
4 rows in set (0.00 sec)

5.查詢

數據庫以下:
mysql> select * from users;
+----+------------+
| id | name       |
+----+------------+
|  3 | woshiyaoge |
|  4 | woshiyaoge |
|  5 | woshiyaoge |
|  6 | woshiyaoge |
|  7 | sbyao      |
|  8 | liuyao     |
|  9 | mayun      |
+----+------------+
7 rows in set (0.00 sec)

方式1:

#查詢user表下面name=liuyao的字段
ret = session.query(User).filter_by(name='liuyao').all()
#打印實例
print ret
for i in ret:
#打印結果
print(i.id,i.name,)
結果:
[<__main__.User object at 0x0000000002F55860>]
(8L, 'liuyao')
這種查詢方法能夠返回一個User對象以及它的name屬性字段的值。

方式2:
#查詢user表裏字段是name=liuyao的第一條數據
ret = session.query(User).filter_by(name='liuyao').first()
print ret.name
print ret.id
結果:
liuyao
8

方式3:
#查詢user表裏字段是name是liuyao或者mayun的信息打印出來
ret = session.query(User).filter(User.name.in_(['liuyao','mayun'])).all()
print ret
for i in ret:
    print(i.name,i.id)
結果:
[<__main__.User object at 0x00000000030F1E48>, <__main__.User object at 0x000000000311D8D0>]
('liuyao', 8L)
('mayun', 9L)

方式4:
#能夠給返回的結果起一個別名,或者叫標籤:無關緊要
ret = session.query(User.name.label('')).all()
print ret,type(ret)
這裏的關鍵是label方法,它的意思是把User的name字段改個名字叫name_label,
其至關於執行如下的SQL語句:
SELECT users.name AS name_label
FROM users
結果:
[('woshiyaoge',), ('woshiyaoge',), ('woshiyaoge',), ('woshiyaoge',), ('sbyao',), ('liuyao',), ('mayun',)] <type 'list'>

方式5:
#查詢User表根據id排序
ret = session.query(User).order_by(User.id).all()
print ret
for i in ret:
print(i.name)
結果:
[<__main__.User object at 0x00000000031978D0>, <__main__.User object at 0x0000000003197978>, <__main__.User object at 0x00000000031979E8>, <__main__.User object at 0x0000000003197A58>, <__main__.User object at 0x000000000316BE10>, <__main__.User object at 0x000000000316BE48>, <__main__.User object at 0x0000000003197940>]
woshiyaoge
woshiyaoge
woshiyaoge
woshiyaoge
sbyao
liuyao
mayun

方式6:
#查詢user表裏根據id排序輸入0到3的字段
ret = session.query(User).order_by(User.id)[0:3]
print ret
for i in ret:
print(i.name)
結果:
[<__main__.User object at 0x00000000030F59E8>, <__main__.User object at 0x00000000030C9E80>, <__main__.User object at 0x00000000030C9C88>]
woshiyaoge
woshiyaoge
woshiyaoge
方式7:
# 建立Query查詢,filter是where條件,最後調用one()返回惟一行,若是調用all()則返回全部行:
user = session.query(User).filter(User.id=='5').one()
#打印類型和對象的name屬性:
print 'type:', type(user)
print 'name:', user.name

7.外鍵關聯

因爲關係數據庫的多個表還能夠用外鍵實現一對多、多對多等關聯,相應地,ORM框架也能夠提供兩個對象之間的一對多、多對多等功能。

1)一對多(一個User能夠有多個Address)

外鍵引用relationship()

例:
#!/usr/bin/env python3
#coding:utf8
#導入所需模塊
from sqlalchemy import create_engine,func
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String,ForeignKey
from  sqlalchemy.orm import sessionmaker,relationship
#生成sqlorm基類
Base = declarative_base()
#建立數據庫鏈接 
engine = create_engine("mysql+mysqldb://liuyao:liuyao@121.42.195.15:3306/liuyao", max_overflow=5)
#目的是一我的能夠擁有多本書,那麼在數據庫裏的一對多關係
class User(Base):
    #表名
    __tablename__ = 'user'
    #id字段
    id = Column(String(20), primary_key=True)
    #名字字段
    name = Column(String(20))
    # 一對多:#內容不是表名而是定義的表結構名字
    books = relationship('Book')
class Book(Base):
    #表面
    __tablename__ = 'book'
    #id字段
    id = Column(String(20), primary_key=True)
    #名字字段
    name = Column(String(20))
    # 「多」的一方的book表是經過外鍵關聯到user表的:
    #ForeignKey是外鍵 關聯user表的id字段
    user_id = Column(String(20), ForeignKey('user.id'))
#建立所需表
Base.metadata.create_all(engine)

if __name__ == '__main__':
    #綁定,生成回話
    SessionCls = sessionmaker(bind=engine)
    session = SessionCls()
    #建立用戶
    liuyao = User(id='1',name='liuyao')
    ali=User(id='2',name='ali')
    #添加字段
    session.add_all([liuyao,ali])
    #提交
    session.commit()
    #建立白鹿原這本書,指定誰是擁有者
    Whitedeer = Book(id='1',name='White_deer',user_id = '1')
    #建立三體這本書,指定誰是擁有者
    Threebody = Book(id='2',name='Three_body',user_id = '2')
    #添加字段
    session.add_all([Whitedeer,Threebody])
    #提交
    session.commit()

結果:

表:
mysql> show tables;
+------------------+
| Tables_in_liuyao |
+------------------+
| book             |
| user             |
+------------------+
 rows in set (0.00 sec)

user表:
mysql> select * from user;
+----+--------+
| id | name   |
+----+--------+
| 1  | liuyao |
| 2  | ali    |
+----+--------+
2 rows in set (0.00 sec)

book表#已經顯示關聯哪一個user表id
mysql> select * from book;
+----+------------+---------+
| id | name       | user_id |
+----+------------+---------+
| 1  | White_deer | 1       |
| 2  | Three_body | 2       |
+----+------------+---------+
2 rows in set (0.00 sec)

2)多對多

創建一個雙向一對多關係,「反向」是一個許多人,指定一個額外的relationship()函數
並鏈接兩個使用relationship.back_populates參數
簡單來講, relationship函數是sqlalchemy對關係之間提供的一種便利的調用方式, backref參數則對關係提供反向引用的聲明。在最新版本的sqlalchemy中對relationship引進了back_populates參數。

先建立數據庫:

#!/usr/bin/env python3
#coding:utf8
from sqlalchemy import Column, Sequence, String, Integer, ForeignKey
from sqlalchemy import create_engine # 導入建立鏈接驅動
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm import relationship, backref
# 這個url能夠用urlparse解析, 其中echo=True表示執行時顯示sql語句
engine = create_engine("mysql+mysqldb://liuyao:liuyao@121.42.195.15:3306/liuyao", max_overflow=5)
#生成了declarative基類, 之後的model繼承此類
Base = declarative_base()
class Parent(Base):
    __tablename__ = 'parent'
    id = Column(Integer, primary_key=True)
    name = Column(String(64),unique=True,nullable=False)
    children = relationship("Child", back_populates="parent")
class Child(Base):
    __tablename__ = 'child'
    id = Column(Integer, primary_key=True)
    name = Column(String(64),unique=True,nullable=False)
    parent_id = Column(Integer, ForeignKey('parent.id'))
    parent = relationship("Parent", back_populates="children")
Base.metadata.create_all(engine) #建立全部表結構
if __name__ == '__main__':
SessionCls = sessionmaker(bind=engine)
#建立與數據庫的會話session class ,注意,這裏返回給session的是個class,不是實例
session = SessionCls()
mama = Parent(id='1',name='mamaxx')
baba = Parent(id='2',name='babaoo')
session.add_all([mama,baba])
# onesb = Child(id='1',name='onesb',parent_id='2')
# twosb = Child(id='2',name='twosb',parent_id='2')
#session.add_all([onesb,twosb])
session.commit()

3.)多對多之三表外鍵關聯

 

#!/usr/bin/env python3
#coding:utf8
from sqlalchemy import create_engine,func,Table
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import Column, Integer, String,ForeignKey
from  sqlalchemy.orm import sessionmaker,relationship
Base = declarative_base()
#關係表
Host2Group = Table('host_2_group',Base.metadata,
               Column('host_id',ForeignKey('hosts.id'),primary_key=True),
               Column('group_id',ForeignKey('group.id'),primary_key=True),
               )
engine = create_engine("mysql+mysqldb://liuyao:liuyao@121.42.195.15:3306/liuyao", max_overflow=5)
class Host(Base):
    __tablename__ = 'hosts'
    id = Column(Integer,primary_key=True,autoincrement=True)
    hostname = Column(String(64),unique=True,nullable=False)
    ip_addr = Column(String(128),unique=True,nullable=False)
    port = Column(Integer,default=22)
    groups = relationship('Group',
                      secondary= Host2Group,
                      backref = 'host_list')
class Group(Base):
    __tablename__ = 'group'
    id = Column(Integer,primary_key=True)
    name = Column(String(64),unique=True,nullable=False)

if __name__ == '__main__':
    SessionCls = sessionmaker(bind=engine)
    session = SessionCls()
    g1 = Group(name='g1')
    g2 = Group(name='g2')
    g3 = Group(name='g3')
    g4 = Group(name='g4')
    session.add_all([g1,g2,g3,g4])
    session.commit()

 

 

 

以上部分摘自:http://www.cnblogs.com/liu-yao/p/5342656.html

老師的博客:http://www.cnblogs.com/alex3714/articles/5978329.html

相關文章
相關標籤/搜索