胃热吃什么中成药| 妇科千金片和三金片有什么区别| 一花一草一世界的下一句是什么| 宝宝不喝奶是什么原因| 金福是什么生肖| 慢阻肺是什么病| 七四年属什么生肖| 煮玉米放什么好吃| 拔罐之后要注意什么| 梦见杀牛是什么预兆| 根源是什么意思| 荨麻疹抹什么药| 天秤座和什么座最配| 高血压可以吃什么水果| b型血rh阳性是什么意思| 火龙果对身体有什么好处| 亲和力是什么意思| 活动性肺结核是什么意思| 经常吃辣椒有什么好处和坏处| 流感吃什么药| 久经沙场是什么意思| 白带是什么意思| 英雄联盟msi是什么| 缅怀什么意思| 男友力是什么意思| 抖腿有什么好处| 尿酸高什么原因引起的| 毛囊炎是什么样子| 1999属什么| 缺钙会导致什么| dha什么牌子好| 欧了是什么意思| 肚子很硬是什么原因| 奇行种什么意思| 给猫咪取什么名字好听| 老过敏是缺什么维生素| 什么药治痒效果最好| 湿疹什么样子| 属牛的本命佛是什么佛| 跛子是什么意思| 什么是甲减病| 朴实无华是什么意思| 周杰伦得了什么病| 朋友圈提到了我是什么意思| 饭前饭后吃药有什么区别| 潴留是什么意思| 吃什么不长胖| 哺乳期可以吃什么感冒药| 硒是什么元素| 鼻窦在什么位置图片| 七月初七是什么生肖| 448是什么意思| 异想天开是什么意思| 10万个为什么的作者| 丘疹用什么药膏最有效| 过敏性咳嗽有什么症状| 墓志铭什么意思| 生化妊娠后需要注意什么| 灵芝和什么煲汤好| 述求是什么意思| 摸摸唱是什么意思| cdf1是什么意思| 女人绝经一般在什么年龄段| 中国的国花是什么花| 组cp是什么意思| 跳蚤为什么只咬一个人| 金刚钻是什么意思| 羽字五行属什么| 椎间盘变性是什么意思| 阻生齿是什么| 刮痧有什么好处和坏处| 吃什么可以降低血糖| 什么的蔷薇| 私密是什么意思| 小号避孕套是什么尺寸| 医院属于什么性质的单位| 三月三十号是什么星座| 味淋是什么东西| 皓是什么意思| 米粉是什么做的| 什么是碳水化合物食物| 狗狗湿疹用什么药膏最有效| 相向而行是什么意思| 痛风吃什么好得快| 马路杀手是什么意思| 领事是什么级别| 4月份什么星座| 脚脱皮用什么药膏| 西柚是什么季节的水果| 蜂王浆是什么味道| 鼠和什么生肖最配| 生物冰袋里面是什么| 胃疼需要做什么检查| 廉航是什么意思| 羡慕的意思是什么| 七一什么节| 天月二德是什么意思| 对乙酰氨基酚是什么药| 脚踝韧带拉伤吃什么| 开涮是什么意思| 什么叫感统训练| 吃羊肉不能吃什么水果| 什么减肥药最安全| 红醋是什么醋| 盐酸左氧氟沙星片治什么病| 小便有血尿是什么原因| 李子吃多了有什么坏处| 做nt需要做什么准备| 学渣什么意思| 晚上六点半是什么时辰| venes保温杯是什么品牌| 拔牙后能吃什么东西| 改善记忆力吃什么药好| 螺旋体感染是什么意思| .什么意思| 经常放屁是什么问题| 麦冬有什么作用| 西游记什么朝代写的| 正月十九是什么日子| 精神病吃什么药最好| 裙子搭配什么鞋子| 肚子长痘痘是什么原因| 工作性质是什么| 尿酸高可以吃什么水果| 得糖尿病的原因是什么| 昶字五行属什么| 牛宝是什么| 纹眉需要注意什么| 中药不能和什么一起吃| grace什么意思中文| mers是什么病毒| 马骝是什么意思| 子鱼是什么鱼| 寒战是什么症状| 胸口痛是什么原因| 肝斑一般在脸上的什么地方| 梦见狗咬自己是什么意思| 精索是什么| 圆脸适合什么镜框| 玫瑰茄和洛神花有什么区别吗| 吃什么有助于骨头愈合| 五月二十三日是什么星座| 什么是pv| 肛门痛什么原因| 什么病会引起牙疼| 兴风作浪什么意思| 一直不射精是什么原因| 四爱是什么| 心肌炎做什么检查| 一国两制什么时候提出的| 山药对人体有什么好处| 丙肝是什么| 心烦意乱焦躁不安吃什么药| 毫无保留什么意思| 今年9岁属什么| 74年属什么生肖| 231是什么意思| 离异什么意思| 2008年出生的属什么| 月经推迟吃什么| 文火是什么火| 肿瘤和囊肿有什么区别| 其他垃圾有什么| 鸡蛋和什么搭配最营养| 狗屎运是什么意思| 罐肠什么意思| 以什么见什么| 短效避孕药是什么| 海棠花什么时候开| 突然流鼻血是什么原因| 扁平苔藓有什么症状| 甲肝戊肝是什么病| 孕期感冒可以吃什么药| 正方形体积公式是什么| 姨妈期间可以吃什么水果| 骚扰是什么意思| 东坡肉属于什么菜系| 什么是间质性肺炎| 口腔扁平苔藓是什么原因造成的| 山楂有什么功效| 生物电是什么| 火烧火燎是什么意思| 黑暗料理是什么意思| 什么属相不能戴貔貅| 物心念什么| 六味地黄丸什么人不能吃| 花生吃多了有什么坏处| 为什么水不会燃烧| 鼠的三合生肖是什么| 半夜口渴是什么原因| 糖尿病吃什么水果| 放单是什么意思| 18k金和24k金有什么区别| 铁石心肠是什么意思| 脉搏是什么| 心电图逆钟向转位是什么意思| 指鹿为马是什么意思| 干可以加什么偏旁| 肺大泡用什么药| 骟是什么意思| 胆汁反流吃什么药| 为什么来我家| 什么是宫腔镜检查| 六味地黄丸起什么作用| 女生喜欢什么| 什么学步成语| 支原体吃什么药好得快| 什么颜色防晒| 浮肿是什么原因| 经常头疼是什么原因| 稀饭和粥有什么区别| 青蛙属于什么类动物| 为什么会做梦中梦| 血氧仪是干什么用的| 青蛙用什么呼吸| 耳朵真菌感染用什么药| nmr是什么意思| goldlion是什么档次| 坐以待毙是什么意思| 牛建读什么| 嘴唇发麻是什么原因| 胃有息肉的症状是什么| 红枣为什么要炒黑再泡水喝| 嗜睡是什么病的前兆| 添丁是什么意思| 吾日三省吾身是什么意思| 增生是什么| 鱼油功效和作用是什么| 蛇胆疮是什么原因引起的| 7月初是什么星座| gc是什么| 头疼是什么引起的| 木棉是什么| 见色起意是什么意思| 斑点狗是什么品种| 易是什么意思| 弊是什么意思| 6代表什么意思| 财神在什么方位| 仪轨是什么意思| 幻听一般会听到什么| 气短挂什么科| 什么玉好| 不寐病是什么意思| 鹞子是什么鸟| 梦见梅花鹿是什么预兆| 兔头是什么意思| 红颜知己代表什么关系| 鸡蛋炒什么好吃| 肺气不足吃什么中成药| 松绿色是什么颜色| 看十全十美是什么生肖| 马路上的菱形标志是什么意思| 蒙蒙的什么| 九孔藕和七孔藕有什么区别| 都市丽人什么意思| 荨麻疹吃什么药最管用| 什么牌子皮带结实耐用| 耄耋什么意思| 梦见掉头发是什么意思| 可以是什么意思| 围魏救赵是什么意思| 不安腿综合征吃什么药| 轧戏什么意思| 百度
BT

Facilitating the Spread of Knowledge and Innovation in Professional Software Development

Write for InfoQ

Topics

Choose your language

InfoQ Homepage Articles Optimizing Search Systems: Balancing Speed, Relevance, and Scalability

大迈芝麻石家庄成功上市 颠覆性营销模式获验证

Listen to this article -  0:00

Key Takeaways

  • Optimizing data indexing and layout can significantly reduce retrieval times and improve storage efficiency.
  • Categorizing and prioritizing relevant data based on specific factors, such as location or delivery times, enhances query accuracy and speed.
  • Sharding techniques, like geo-sharding, help balance system load and improve search efficiency in complex, large-scale systems.
  • Parallelizing queries and processing multiple match types simultaneously can boost search performance and relevance.
  • Ensuring consistency across different discovery surfaces leads to a smoother and more intuitive user experience.

As software engineers, we are constantly striving to build systems that are not only functional but also efficient and scalable. In a world where users demand faster, more accurate results, optimizing search performance has become a key focus in modern application development.

This article is based on our presentation at QCon San Francisco 2024, where we explored the evolving landscape of data indexing, retrieval, and ranking. With platforms like Uber Eats handling complex queries across massive datasets, optimizing search is now a critical challenge, which requires advanced strategies like indexing, sharding, and parallel query processing.

The complexity of search systems continues to grow, making the balance between speed, relevance, and scalability more crucial than ever. This article explores the techniques behind these optimizations and their impact on both user experience and system performance.

Expanding Selection in Uber Eats: The nX Approach

Selection on Uber Eats is a complex problem that varies based on perspective. For the merchant onboarding team and operations, success is measured by onboarding as many restaurants and stores as possible. For consumers, selection can mean different things. Some prioritize fast delivery times, while others seek their favorite restaurants or opportunities to discover new places within the app. The challenge is to accommodate these diverse needs while ensuring a seamless discovery experience.

Beyond the conceptual aspects of selection, there are significant technical hurdles to overcome. As the business has expanded, particularly during and after the pandemic, Uber Eats has incorporated new verticals beyond restaurants, such as grocery stores, retail stores, and even item/package delivery. This expansion has introduced ranking and recommendation complexity into the platform’s selection infrastructure.

A key difference in scaling selection is between onboarding restaurants and grocery stores. Restaurant menus usually feature twenty to thirty items, while grocery stores can have over one hundred thousand stock-keeping units (SKUs). This variation requires an advanced indexing system to efficiently present relevant options to users.

Another major development in Uber Eats’ selection strategy has been expanding the geographical reach of deliveries. Previously, users could only order from restaurants within a ten-to-fifteen-minute radius. Now, the platform can enable users to order from close to an hour radius. For example, a user in one city can place an order from a restaurant or retail store in another city and have it delivered through Uber Eats. This expanded reach introduces further technical challenges, particularly in logistics and fulfillment.

The primary focus of Uber Eats’ selection strategy is to maximize the quantity of available options across all discovery surfaces within the app. Personalization, while also an important aspect, is a separate challenge that requires distinct solutions. By continuously evolving its indexing, ranking, and recommendation technologies, Uber Eats aims to create a more comprehensive and dynamic selection for users, ensuring that they can quickly and efficiently find what they want, whether it is a favorite restaurant, a new culinary experience, or a much-needed grocery item.

Discovery surfaces such as Home Feed, Search, Suggestions, and Ads are crucial in connecting users with available options. Home Feed serves as the primary entry point for orders, featuring carousels based on user history, storefront listings, and shortcut sections for promotions and cuisines. Search functionality includes restaurant, dish, and cuisine searches, while suggestions help users discover similar or alternative options dynamically. Ads enhance visibility, helping merchants to reach relevant customers effectively. Ensuring consistency across these discovery surfaces is key to delivering a seamless and intuitive user experience.

How users discover content on Uber Eats: A look at Feed, Search, and Recommendations

Uber Eats Architecture: From Infrastructure to Application Layer

The architecture of Uber Eats spans multiple layers, from the infrastructure side to the application layer, ensuring seamless retrieval and placement of stores and restaurants. At the foundation is the infrastructure layer, which stores and indexes all available merchants and items. This serves as the core dataset from which relevant stores are retrieved.

Search Architecture: A Layered View of the Retrieval and Ranking Pipeline

The retrieval layer optimizes recall by fetching a wide range of potential stores, which are then refined for relevance through a "ranking" system. The first-pass ranking focuses on precision, using lexical matching to align user queries with retrieved documents. The hydration layer adds business logic, considering things such as promotions, membership benefits, and estimated delivery times. Finally, the second-pass ranking personalizes results based on the user’s order history and conversion rates to deliver the most relevant options.

As Uber Eats expanded its selection, it encountered significant scaling challenges. An initial attempt to increase the number of retrieved stores led to a fourfold increase in latency, prompting a deeper investigation into inefficiencies in data ingestion, query performance, and ranking.

Dot size reflects candidate volume; red dots indicate newly included stores based on greater Haversine distance.

One of the key challenges was indexing and querying. Uber Eats relies on H3 hexagonal geolocation indexing to determine delivery zones, but the ingestion process categorized stores inaccurately as either "close" or "far". This misclassification led to ranking inconsistencies, where stores that were actually nearby were deprioritized.

Another issue with searching was the exponential growth in search space. Expanding the delivery range slightly caused a square increase in the search area, dramatically increasing the number of candidates being processed. This meant that even a small adjustment in retrieval parameters could result in significant performance slowdowns.

Store distribution created another challenge. As search areas broadened, distant stores were often shown higher in the feed than nearby options. This led to a dilemma: while high-converting stores are important, customer experience suffers when faraway choices overshadow more convenient local options.

Haversine Rings of Retrieval: Inner Stores Are Few, Outer Growth Is Disproportionate

To address these issues, Uber Eats needed to refine its indexing, retrieval, and ranking strategies. The focus shifted to balancing scale with efficiency, ensuring that the platform could surface the most relevant stores without compromising performance. These optimizations were critical in maintaining a smooth user experience while supporting Uber Eats’ growing selection.

Uber Eats' Search Platform

The Search platform that powers Uber Eats, handling tens of millions of requests daily, is built on Apache Lucene and follows a Lambda architecture for data ingestion. This setup includes batch ingestion via Spark and real-time ingestion through a streaming path, ensuring up-to-date search results.

From data to discoverability: Ingestion, indexing, and Lucene-backed search orchestrated by a streaming architecture

One key feature is priority-aware ingestion, allowing high-priority requests to be processed first, maintaining data freshness. Uber also relies heavily on geo-sharding to optimize its geospatial search use cases. Additionally, custom index layouts and query operators enhance search efficiency by leveraging offline document ranking and early termination to speed up queries.

The search platform architecture consists of three main components:

  • Batch Indexing Pipeline – Spark jobs process data, convert them into search documents, partition them into shards, and generate Lucene indexes, which are stored in an object store.
  • Streaming/Real-Time Updates Path – Updates are ingested via a Streaming Service, which maps documents to specific Kafka partitions for real-time updates. There is one to one mapping between the Kafka partition and shard. Kafka acts as a write-ahead log, enabling graceful handling of ingestion spikes, priority aware ingestion, replication, and fault tolerance.
  • Serving Stack – The searcher node retrieves indexes from storage, catches up with streaming updates, and executes queries. A stateless aggregator service routes search requests to the appropriate searcher node, handles query fanouts, and aggregates results before returning them to the user.

Sharding Techniques

Efficiently managing geospatial search queries on Uber Eats is crucial, as users often seek outnearby restaurants or grocery stores. To achieve this, Uber Eats uses geo-sharding, a technique that ensures all relevant data for a specific location is stored within a single shard. This minimizes query overhead and eliminates inefficiencies caused by fetching and aggregating results from multiple shards. Additionally, geo sharding allows first-pass ranking to happen directly on data nodes, improving speed and accuracy. Uber Eats primarily employs two geo sharding techniques: latitude sharding and hex sharding.

Latitude sharding divides the world into horizontal bands, with each band representing a distinct shard. Shard ranges are computed offline using Spark jobs, which first divide the map into thousands of narrow latitude stripes and then group adjacent stripes to create shards of roughly equal size. Documents falling on shard boundaries are indexed in both neighboring shards to prevent missing results.

One key advantage of latitude sharding is its ability to distribute traffic efficiently across different time zones. Given that Uber Eats experiences peak activity following a "sun pattern" with high demand during the day and lower demand at night, this method helps prevent excessive load on specific shards. However, in densely populated urban areas, shards may become uneven, leading to indexing delays and increased query latencies.

Not Geo-Local, but Geo-Linear: A Visual Example Using U.S. and Europe Overlap

To address these challenges, Uber Eats also utilizes hex sharding, which is based on the H3 geospatial indexing system. Instead of dividing the world into bands, hex sharding organizes data into hexagonal tiles of varying resolutions. Choosing the right hexagon size is crucial. Uber typically opts for H3 size 2 or 3 to balance efficiency and precision. Like latitude sharding, hex sharding also incorporates buffer zones, ensuring that documents near shard boundaries are indexed in multiple hexagons to prevent search gaps. The key advantage of hex sharding is its ability to create a more balanced shard distribution, particularly in dense urban areas, where latitude sharding struggles.

Hexes Over Heat: A Sharding Strategy Built for Urban Search Load Balancing

By combining these two techniques, Uber Eats optimizes its search infrastructure to deliver fast, accurate, and scalable geospatial queries, providing a seamless experience for users no matter where they’re ordering from.

To optimize Uber Eats’ search performance, several improvements were made by leveraging query patterns and refining data layouts to enhance recall while reducing latency. One key enhancement was building specialized data layouts tailored for different use cases, such as food delivery and grocery searches. By aligning the data structure with query patterns, retrieval efficiency improved, cutting down unnecessary computations. Another major optimization was indexing Estimated Time of Delivery (ETD), which allowed the search space to be divided into non-overlapping ranges and processed in parallel, accelerating query execution while maintaining accurate ranking.

ETA as a Spatial Constraint: Structuring Candidate Retrieval for Efficiency and Isolation

Furthermore, tasks such as differentiating between nearby and distant stores were prioritized earlier in the indexing pipeline, which led to a reduction in query-time processing and an improvement in overall performance. These enhancements greatly increased search efficiency, providing quicker and more relevant results while maintaining scalability.

Data Layout Improvements for Faster and More Scalable Queries

Uber Eats improved its search efficiency by optimizing its data layout to better align with query patterns, significantly reducing latency and improving scalability. The Eats index was restructured to first group restaurants by city, followed by individual restaurants, and then their menu items. This approach allowed queries to filter out irrelevant cities, quickly reducing unnecessary processing. Additionally, because Lucene uses delta encoding, clustering similar attributes together improved compression efficiency, leading to a twenty percent reduction in index size.

Index Layout in Lucene: Geographic and Merchant-Aware Document Ordering

For grocery searches, a slightly different strategy was required due to the higher scale of grocery inventory compared to restaurants. Stores were ranked based on offline conversion rates, and their items were grouped within each store. This layout enabled the system to set a retrieval limit per store, preventing excessive results from a single store and ensuring more diverse results across multiple grocery providers. This was particularly useful for searches with generic keywords like "chicken", where thousands of matches could appear from a single store.

Optimizing Grocery Search: Store-Level Grouping and Conversion-Based Ranking

These data layout optimizations led to a 60% improvement in retrieval latency, reducing query times from 145 milliseconds to 60 milliseconds. Additionally, sorting the documents resulted in more efficient lookups, decreasing the per-document retrieval time from ten to 60 microseconds to under 5 microseconds. The overall impact was a 50% improvement in P95 latencies, ensuring a much faster and smoother search.

Performance Wins: Average and Real-Time Latency Cut Nearly in Half

Optimizing Uber's ETA Indexing

Uber improved its ETA (Estimated Time of Arrival) Indexing to optimize search efficiency, reduce latency, and enhance recall by incorporating metadata about restaurant delivery zones and time estimates into the search platform. Initially, the system lacked information on relative distances between delivery zones (hexagons), making it difficult for rankers to assess faraway restaurants efficiently. To address this, Uber categorized delivery times into fixed time ranges and indexed restaurants based on their proximity to each hexagon.

ETA-Aware Hex-Based Indexing: Mapping Reachable Areas for Restaurants R1 and R2

This approach introduced a tradeoff between storage and processing efficiency. Restaurants had to be stored multiple times if they fell within different time ranges for various hexagons. Although this increased storage overhead, it significantly enhanced query speed, resulting in much faster retrieval of relevant results. Uber tested alternative indexing methods, including BKD trees and gRPC-based retrieval, but ultimately determined that precomputing these relationships and storing them in the index provided the best performance.

The new system enabled query parallelization, allowing a single request to trigger multiple searches for different ETA buckets at constant latency. This resulted in a fifty percent reduction in query latency and improved recall, as rankers gained access to more relevant restaurant candidates without compromising performance.

Optimized Retrieval: Parallel Range Queries Partitioned by ETA Buckets

Another key enhancement was handling non-deliverable but discoverable stores. Restaurants sometimes appear in searches but cannot accept orders due to various factors like courier availability, location, and time of day. Instead of handling this dynamically at query time, Uber precomputed deliverability at the ingestion layer. This allowed the system to differentiate between deliverable and discoverable-only restaurants quickly, improving the user experience.

Key Aspects of Uber's Search Optimization

Uber’s journey in optimizing search and retrieval performance showcases the critical role of well-structured indexing, efficient sharding strategies, and parallelization techniques. Initially, performance issues stemmed from inefficient data storage and retrieval. This caused delays as queries processed large volumes of documents across multiple regions. By carefully analyzing query patterns, Uber restructured its index layout to prioritize city-based and store-based clustering, ensuring that relevant results could be retrieved faster.

The reorganization reduced retrieval times by over fifty percent and improved compression ratios, resulting in a more storage-efficient system. The introduction of ETA indexing further refined the ranking process by "penalizing" faraway stores while prioritizing those within an optimal delivery range. Through strategic data ingestion and indexing, Uber optimized the balance between recall and latency, ensuring that users received more relevant store results faster.

Search Index Performance Boost: 50% Drop in Latency via Range Query Execution

Beyond restructuring the index, Uber also focused on shifting complex fallback operations from the query layer to the ingestion layer. This transition reduced unnecessary processing during query execution, streamlining the search experience. The use of parallelized range queries allowed Uber to expand the selection space without affecting latency, enhancing the diversity of search results while maintaining fast response times. The system was further optimized by removing inefficiencies, such as processing test stores alongside production data, which had previously inflated query times. By utilizing non-overlapping subqueries, Uber maximized parallelization and enabled the simultaneous execution of various match types, including strong, fuzzy, and partial keyword matches, for greater efficiency.

Conclusion

These optimizations required extensive cross-team collaboration, with teams across search, feed, ads, and suggestions aligning on changes that impacted the entire search ecosystem. Coordinating these efforts was complex, as each team needed to ensure their respective services were integrated seamlessly. The process spanned several months of benchmarking, testing, and fine-tuning, but the outcomes were transformative. Latencies were significantly reduced, recall was enhanced, and system scalability saw considerable improvements, laying the groundwork for a more responsive and intuitive search experience across Uber Eats.

As Uber continues to expand and refine its platform globally, these advancements underscore the importance of data-driven decision-making, efficient system design, and continuous iteration. By consistently evaluating and optimizing its systems, Uber ensures that it remains agile and competitive in the fast-evolving landscape of large-scale distributed systems, providing an increasingly seamless experience for users and supporting its expanding service offerings.

About the Authors

Rate this Article

Adoption
Style

BT
肽有什么作用 鼻干眼干口干属于什么症状 夏枯草是什么样子 什么孩子该看心理医生 恋爱脑是什么意思
6.7是什么星座 少一个肾有什么影响 kg什么意思 风云际会的意思是什么 菁字五行属什么
三点水开念什么意思 什么拜之交 乌鸡白凤丸适合什么人吃 gel是什么意思 早茶是什么意思
指甲是白色的是什么原因 runosd是什么牌子的手表 什么叫辟谷 样本是什么意思 猪八戒姓什么
狸猫是什么猫hcv9jop7ns0r.cn 妤什么意思hcv8jop4ns7r.cn 谢霆锋什么学历hcv9jop7ns0r.cn 味淋是什么调料hcv8jop3ns8r.cn 俄罗斯信奉什么教hcv7jop5ns5r.cn
慢性扁桃体炎吃什么药hcv8jop3ns0r.cn 两个a型血的人生的孩子什么血型hcv8jop1ns6r.cn 菠萝蜜什么季节成熟hcv9jop0ns5r.cn 斑秃去医院挂什么科jingluanji.com 胚胎是什么意思hcv8jop5ns6r.cn
猪肝有什么功效hcv9jop0ns2r.cn 狗狗呕吐吃什么药hcv7jop6ns1r.cn upi是什么意思cl108k.com 刚满月的小狗吃什么hcv7jop9ns5r.cn 膝盖痛什么原因hcv8jop3ns5r.cn
sdh是什么意思hcv9jop1ns7r.cn 结婚23年是什么婚hebeidezhi.com 黑洞里面是什么hcv7jop6ns6r.cn 轻贱是什么意思baiqunet.com 白色裤子配什么上衣hcv9jop0ns9r.cn
百度