如何将你的 Ruby 应用程序从 OpenSearch 迁移到 Elasticsearch

作者:来自 Elastic Fernando Briano

将 Ruby 代码库从 OpenSearch 客户端迁移到 Elasticsearch 客户端的指南。

OpenSearch Ruby 客户端是从 7.x 版 Elasticsearch Ruby 客户端分叉而来的,因此代码库相对相似。这意味着当将 Ruby 代码库从 OpenSearch 迁移到 Elasticsearch 时,来自相应客户端库的代码看起来会非常熟悉。在这篇博文中,我将展示一个使用 OpenSearch 的示例 Ruby 应用程序以及将此代码迁移到 Elasticsearch 的步骤。

这两个客户端都是根据流行的 Apache 许可证 2.0 发布的,因此它们是开源和免费软件。Elasticsearch 的许可证最近进行了更新,Elasticsearch 和 Kibana 的核心自 8.16 版起根据 OSI 批准的开源许可证 AGPL 发布。

版本

迁移时需要考虑的一个问题是要使用哪个版本的 Elasticsearch。我们建议使用最新的稳定版本,在撰写本文时为 8.17.0。Elasticsearch Ruby 客户端次要版本遵循 Elasticsearch 次要版本。因此,对于 Elasticsearch 8.17.x,你可以使用 Ruby gem 的 8.17.x 版本。

OpenSearch 是从 Elasticsearch 7.10.2 分叉而来的。因此,API 可能已更改,并且可以使用不同的功能。但这超出了本文的范围,我只会在示例应用程序中研究最常见的操作。

对于 Ruby on Rails,你可以使用官方 Elasticsearch 客户端或 Rails 集成库。我们建议分别迁移到 Elasticsearch 和客户端的最新稳定版本。elasticsearch-rails gem 版本 8.0.0 支持 Rails 6.1、7.0 和 7.1 以及 Elasticsearch 8.x。

代码

对于此示例,我按照以下步骤从 tarball 安装 OpenSearch。下载并解压 tarball 后,我需要设置一个初始管理员密码,稍后我将使用该密码来实例化客户端。

我创建了一个包含 Gemfile 的目录,如下所示:

source 'https://rubygems.org'gem 'opensearch-ruby'

运行 bundle install 后,我的项目就安装了 gem。这安装了 opensearch-ruby 版本 3.4.0,我运行的 OpenSearch 版本是 2.18.0。我在同一目录中的 example_code.rb 文件中编写了代码。此文件中的初始代码是 OpenSearch 客户端的实例化:

require 'opensearch'client = OpenSearch::Client.new(host: 'https://localhost:9200',user: 'admin',password: ENV['OPENSEARCH_INITIAL_ADMIN_PASSWORD'],transport_options: { ssl: { verify: false } }
)

传输选项 ssl: { verify: false} 参数按照用户指南传递,以便于测试。在生产中,应根据 OpenSearch 的部署进行设置。

自 OpenSearch 2.12.0 版起,运行安装脚本时必须将 OPENSEARCH_INITIAL_ADMIN_PASSWORD 环境变量设置为强密码。按照从 tarball 安装 OpenSearch 的步骤,我在控制台中导出了该变量,现在它可用于我的 Ruby 脚本。

确保客户端连接到 OpenSearch 的简单 API 是使用 cluster.health API:

puts 'HEALTH:'
pp client.cluster.health

确实有效:

$ be ruby example_code.rb
HEALTH:
{"cluster_name"=>"opensearch",
"status"=>"yellow","timed_out"=>false,"number_of_nodes"=>1,"number_of_data_nodes"=>1,

我测试了 Elasticsearch Ruby 客户端文档中的一些常见示例,它们按预期工作:

index = 'books'
puts 'Creating index'
response = client.indices.create(index: index)
puts response
# Creating index
# {"acknowledged"=>true, "shards_acknowledged"=>true, "index"=>"books"}puts 'Indexing a document'
document = { title: 'The Time Machine', author: 'H. G. Wells', year: 1895 }
response = client.index(index: index, body: document, refresh: true)
puts response
# Indexing document
# {"_index"=>"books", "_id"=>"esalT5MB4vnuJz5TtqOc", "_version"=>1, "result"=>"created", "forced_refresh"=>true, "_shards"=>{"total"=>2, "successful"=>1, "failed"=>0}, "_seq_no"=>0, "_primary_term"=>1}id = response['_id']
puts 'Getting document'
response = client.get(index: index, id: id)
puts response
# Getting document
# {"_index"=>"books", "_id"=>"esalT5MB4vnuJz5TtqOc", "_version"=>1, "_seq_no"=>0, "_primary_term"=>1, "found"=>true, "_source"=>{"title"= >"The Time Machine", "author"=>"H. G. Wells", "year"=>1895}}puts "Does an index exist?"
puts client.indices.exists(index: 'imaginary_index')
# Does an index exist?
# falseputs 'Processing Bulk request'
body = [{ index: { _index: 'books', data: { name: 'Leviathan Wakes', author: 'James S.A. Corey', release_date: '2011-06-02', page_count: 561 } } },{ index: { _index: 'books', data: { name: 'Hyperion', author: 'Dan Simmons', release_date: '1989-05-26', page_count: 482 } } },{ index: { _index: 'books', data: { name: 'Dune', author: 'Frank Herbert', release_date: '1965-06-01', page_count: 604 } } },{ index: { _index: 'books', data: { name: 'Dune Messiah', author: 'Frank Herbert', release_date: '1969-10-15', page_count: 331 } } },{ index: { _index: 'books', data: { name: 'Children of Dune', author: 'Frank Herbert', release_date: '1976-04-21', page_count: 408 } } },{ index: { _index: 'books', data: { name: 'God Emperor of Dune', author: 'Frank Herbert', release_date: '1981-05-28', page_count: 454 } } },{ index: { _index: 'books', data: { name: 'Consider Phlebas', author: 'Iain M. Banks', release_date: '1987-04-23', page_count: 471 } } },{ index: { _index: 'books', data: { name: 'Pandora\'s Star', author: 'Peter F. Hamilton', release_date: '2004-03-02', page_count: 768 } } },{ index: { _index: 'books', data: { name: 'Revelation Space', author: 'Alastair Reynolds', release_date: '2000-03-15', page_count: 585 } } },{ index: { _index: 'books', data: { name: 'A Fire Upon the Deep', author: 'Vernor Vinge', release_date: '1992-06-01', page_count: 613 } } },{ index: { _index: 'books', data: { name: 'Ender\'s Game', author: 'Orson Scott Card', release_date: '1985-06-01', page_count: 324 } } },{ index: { _index: 'books', data: { name: '1984', author: 'George Orwell', release_date: '1985-06-01', page_count: 328 } } },{ index: { _index: 'books', data: { name: 'Fahrenheit 451', author: 'Ray Bradbury', release_date: '1953-10-15', page_count: 227 } } },{ index: { _index: 'books', data: { name: 'Brave New World', author: 'Aldous Huxley', release_date: '1932-06-01', page_count: 268 } } },{ index: { _index: 'books', data: { name: 'Foundation', author: 'Isaac Asimov', release_date: '1951-06-01', page_count: 224 } } },{ index: { _index: 'books', data: { name: 'The Giver', author: 'Lois Lowry', release_date: '1993-04-26', page_count: 208 } } },{ index: { _index: 'books', data: { name: 'Slaughterhouse-Five', author: 'Kurt Vonnegut', release_date: '1969-06-01', page_count: 275 } } },{ index: { _index: 'books', data: { name: 'The Hitchhiker\'s Guide to the Galaxy', author: 'Douglas Adams', release_date: '1979-10-12', page_count: 180 } } },{ index: { _index: 'books', data: { name: 'Snow Crash', author: 'Neal Stephenson', release_date: '1992-06-01', page_count: 470 } } },{ index: { _index: 'books', data: { name: 'Neuromancer', author: 'William Gibson', release_date: '1984-07-01', page_count: 271 } } },{ index: { _index: 'books', data: { name: 'The Handmaid\'s Tale', author: 'Margaret Atwood', release_date: '1985-06-01', page_count: 311 } } },{ index: { _index: 'books', data: { name: 'Starship Troopers', author: 'Robert A. Heinlein', release_date: '1959-12-01', page_count: 335 } } },{ index: { _index: 'books', data: { name: 'The Left Hand of Darkness', author: 'Ursula K. Le Guin', release_date: '1969-06-01', page_count: 304 } } },{ index: { _index: 'books', data: { name: 'The Moon is a Harsh Mistress', author: 'Robert A. Heinlein', release_date: '1966-04-01', page_count: 288 } } }
]
puts client.bulk(body: body, refresh: true)
# Processing Bulk request
# {"took"=>38, "errors"=>false, "items"=>[{"index"=>{"_index"=>"books", "_id"=>" ...query = { query: { multi_match: { query: 'dune', fields: ['name'] } } }
puts 'Search results'
response = client.search(index: index, body: query)
puts response
# Search results
# {"_index"=>"books", "_id"=>"oEawT5MBOXHuGXdEu5Wu", "_score"=>2.2886353, "_source"=>{"name"=>"Dune", "author"=>"Frank Herbert", "release_date"=>"1965-06-01", "page_count"=>604}}
# {"_index"=>"books", "_id"=>"oUawT5MBOXHuGXdEu5Wu", "_score"=>1.8893257, "_source"=>{"name"=>"Dune Messiah", "author"=>"Frank Herbert", "release_date"=>"1969-10-15", "page_count"=>331}}
# {"_index"=>"books", "_id"=>"okawT5MBOXHuGXdEu5Wu", "_score"=>1.6086557, "_source"=>{"name"=>"Children of Dune", "author"=>"Frank Herbert", "release_date"=>"1976-04-21", "page_count"=>408}}
# {"_index"=>"books", "_id"=>"o0awT5MBOXHuGXdEu5Wu", "_score"=>1.40059, "_source"=>{"name"=>"God Emperor of Dune", "author"=>"Frank Herbert", "release_date"=>"1981-05-28", "page_count"=>454}}puts 'Updating document'
document = { title: 'Walkaway', author: 'Cory Doctorow', release_date: '2017' }
response = client.index(index: index, body: document, refresh: true)
id = response['_id']
response = client.update(index: index, id: id, body: { doc: { release_date: '2017-04-26' } })
puts response
# Updating document
# {"_index"=>"books", "_id"=>"degnZJMBIGr4X0Yim55L", "_version"=>2, "result"=>"updated", "_shards"=>{"total"=>2, "successful"=>1, "failed"=>0}, "_seq_no"=>26, "_primary_term"=>1}puts 'Retrieveing multiple documents'
response = client.search(index: index, body: { query: { match_all: {} }, size: 3, stored_fields: '_id' })
ids = response['hits']['hits']
ids.map { |a| a.delete('_score') }
response = client.mget(body: { docs: [{ _index: index, _id: ids }] })
puts response
# Retrieveing multiple documents
# {"docs"=>[{"_index"=>"books", "_id"=>"qeg2ZJMBIGr4X0YiiqD2", "_version"=>1, "_seq_no"=>0, "_primary_term"=>1, "found"=>true, "_source"=>{"title"=>"The Time Machine", "author"=>"H. G. Wells", "year"=>1895}}, {"_index"=>"books", "_id"=>"q-g2ZJMBIGr4X0Yii6Ah", "_version"=>1, "_seq_no"=>1, "_primary_term"=>1, "found"=>true, "_source"=>{"name"=>"Leviathan Wakes", "author"=>"James S.A. Corey", "release_date"=>"2011-06-02", "page_count"=>561}}, {"_index"=>"books", "_id"=>"rOg2ZJMBIGr4X0Yii6Ah", "_version"=>1, "_seq_no"=>2, "_primary_term"=>1, "found"=>true, "_source"=>{"name"=>"Hyperion", "author"=>"Dan Simmons", "release_date"=>"1989-05-26", "page_count"=>482}}]}puts "Count #{client.count(index: index)['count']}"
puts 'Deleting by query'
response = client.delete_by_query(index: index, body: { query: { match: { author: 'Robert A. Heinlein' } } }, refresh: true)
puts response
puts "Count #{client.count(index: index)['count']}"
# Count 26
# Deleting by query
# {"took"=>16, "timed_out"=>false, "total"=>2, "deleted"=>2, "batches"=>1, "version_conflicts"=>0, "noops"=>0, "retries"=>{"bulk"=>0, "search"=>0}, "throttled_millis"=>0, "requests_per_second"=>-1.0, "throttled_until_millis"=>0, "failures"=>[]}
# Count 24puts 'Deleting document'
response = client.delete(index: index, id: id)
puts response
# Deleting document
# {"_index"=>"books", "_id"=>"nEawT5MBOXHuGXdEu5WA", "_version"=>2, "result"=>"deleted", "_shards"=>{"total"=>2, "successful"=>1, "failed"=>0}, "_seq_no"=>25, "_primary_term"=>1}puts 'Deleting index'
response = client.indices.delete(index: index)
puts response
# Deleting index
# {"acknowledged"=>true}

迁移到 Elasticsearch

第一步是在 Gemfile 中添加 elasticsearch-ruby。运行 bundle install 后,将安装 Elasticsearch Ruby 客户端 gem。如果你想在完全迁移之前测试你的代码,你可以先将 opensearch-ruby gem 保留在那里。

下一个重要步骤是客户端实例化。这将取决于你如何运行 Elasticsearch。为了保持这些示例的类似方法,我按照下载 Elasticsearch 并在本地运行它中的步骤进行操作。

运行 bin/elasticsearch 时,Elasticsearch 将启动并自动配置安全功能。请确保复制 elastic 用户的密码(但你可以通过运行 bin/elasticsearch-reset-password -u elastic 来重置它)。如果你按照此示例操作,请确保在启动 Elasticsearch 之前停止 OpenSearch,因为它们在同一个端口上运行。

在 example_code.rb 的开头,我注释掉了 OpenSearch 客户端实例并添加了 Elasticsearch 客户端的实例:

# require 'opensearch'# client = OpenSearch::Client.new(
#   host: 'https://localhost:9200',
#   user: 'admin',
#   password: ENV['OPENSEARCH_INITIAL_ADMIN_PASSWORD']
#   transport_options: { ssl: { verify: false } }
# )require 'elasticsearch'client = Elasticsearch::Client.new(host: 'https://localhost:9200',user: ENV['ELASTICSEARCH_USER'],password: ENV['ELASTICSEARCH_PASSWORD'],transport_options: { ssl: { verify: false } }
)

如你所见,此测试场景中的代码几乎相同。它会根据 Elasticsearch 的部署以及你决定如何连接和验证它而有所不同。这里与 OpenSearch 中的安全性相同,不验证 SSL 的选项仅用于测试目的,不应在生产中使用。

设置客户端后,我使用以下命令再次运行代码:bundle exec ruby​​ example_code.rb。一切正常!

调试

根据你的应用程序使用的 API,如果 OpenSearch 的 API 不同,则在针对 Elasticsearch 运行代码时可能会收到错误。REST API 文档是有关如何使用 API 的详细信息的重要参考。请务必检查你正在使用的 Elasticsearch 版本的文档。你还可以参考 Elasticsearch::API 参考。

你可能遇到的一些 Elasticsearch 错误可能是:

  • ArgumentError: Required argument '<ARGUMENT>' missing - 这是一个客户端错误,当请求缺少必需参数时会引发此错误。
  • Elastic::Transport::Transport::Errors::BadRequest: [400] {"error":{"root_cause":[{"type":"illegal_argument_exception","reason":"request [/example/_doc] contains unrecognized parameter: [test]"}]... 此错误来自 Elasticsearch,这意味着客户端代码正在使用 Elasticsearch 无法识别的参数。

Elasticsearch 客户端将通过服务器发送的详细错误消息引发 Elasticsearch 错误。因此,即使对于不支持的参数或端点,错误也应该会告知你有什么不同。

结论

正如我们通过此示例代码所演示的那样,从 Ruby 的角度来看,将 Ruby 应用程序从 OpenSearch 迁移到 Elasticsearch 并不太复杂。你需要了解搜索引擎之间的版本控制和任何潜在的不同 API。但对于最常见的操作,迁移客户端时的主要变化是在实例化中。它们在这方面都很相似,但主机和凭据的定义方式因 Stack 的部署方式而异。设置客户端并验证它是否连接到 Elasticsearch 后,你可以用 Elasticsearch 客户端无缝替换 OpenSearch 客户端。

想要获得 Elastic 认证?了解下一次 Elasticsearch 工程师培训何时开始!

Elasticsearch 包含新功能,可帮助你为你的用例构建最佳搜索解决方案。深入了解我们的示例笔记本以了解更多信息,开始免费云试用,或立即在你的本地机器上试用 Elastic。

原文:https://www.elastic.co/search-labs/blog/ruby-opensearch-elasticsearch-migration

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/63273.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Day8 神经网络中的导数基础

Day8 神经网络中的导数基础 导数的定义 导数&#xff08;Derivative&#xff09;是微积分中的一个核心概念&#xff0c;用于描述函数在某一点的变化率。简单来说&#xff0c;导数就是函数值随自变量微小变化而产生的变化量&#xff0c;即斜率或变化率。假设有一个函数 f ( x…

【视频生成模型】——Hunyuan-video 论文及代码讲解和实操

&#x1f52e;混元文生视频官网 | &#x1f31f;Github代码仓库 | &#x1f3ac; Demo 体验 | &#x1f4dd;技术报告 | &#x1f60d;Hugging Face 文章目录 论文详解基础介绍数据预处理 &#xff08;Data Pre-processing&#xff09;数据过滤 (Data Filtering)数据标注 (Data…

52 基于单片机的超声波、温湿度、光照检测分阶段报警

目录 一、主要功能 二、硬件资源 三、程序编程 四、实现现象 一、主要功能 1.通过DHT11模块读取环境温度和湿度: 2.将湿度、障碍物距显示在lcd1602上面&#xff0c;第一行显示温度和湿度,格式为:xxCyy%&#xff0c;第二行显示超声波传感器测得的距离&#xff0c;格式为:Di…

大数据与AI:从分析到预测的跃迁

引言&#xff1a;数据时代的新纪元 从每天的社交分享到企业的运营决策&#xff0c;数据早已成为现代社会不可或缺的资源。我们正置身于一个数据爆炸的时代&#xff0c;数以亿计的信息流实时生成&#xff0c;为人类带来了前所未有的洞察能力。然而&#xff0c;数据的价值并不仅限…

3D视觉[一]3D计算机视觉

3D视觉[一]3D计算机视觉 3D计算机视觉概述 像机标定 文章目录 3D视觉[一]3D计算机视觉前言一、人类视觉二、计算机视觉2.1 计算机视觉的研究目的2.2 计算机视觉的研究任务2.3 计算机视觉的研究方法2.4 视觉计算理论2.5 马尔框架中计算机视觉表达的四个层次2.5.1 图像&#xff…

OpenCV目标检测 级联分类器 C++实现

一.目标检测技术 目前常用实用性目标检测与跟踪的方法有以下两种&#xff1a; 帧差法 识别原理&#xff1a;基于前后两帧图像之间的差异进行对比&#xff0c;获取图像画面中正在运动的物体从而达到目标检测 缺点&#xff1a;画面中所有运动中物体都能识别 举个例子&#xf…

QT从入门到精通(二) ——信号与槽机制

Qt 的信号与槽机制&#xff08;Signal and Slot&#xff09;是 Qt 框架 中用于对象间通信的核心机制之一。它允许对象之间进行松耦合的事件驱动式通信&#xff0c;尤其适合 GUI 应用程序 中的事件处理。 1. 基本概念 信号 (Signal) 当对象的状态发生变化时&#xff0c;它会发…

如何使用git新建本地仓库并关联远程仓库的步骤(详细易懂)

一、新建本地仓库并关联远程仓库的步骤 新建本地仓库 打开终端&#xff08;在 Windows 上是命令提示符或 PowerShell&#xff0c;在 Linux 和Mac上是终端应用&#xff09;&#xff0c;进入你想要创建仓库的目录。例如&#xff0c;如果你想在桌面上创建一个名为 “my - project”…

1Panel应用推荐:MaxKB开源知识库问答系统

1Panel&#xff08;github.com/1Panel-dev/1Panel&#xff09;是一款现代化、开源的Linux服务器运维管理面板&#xff0c;它致力于通过开源的方式&#xff0c;帮助用户简化建站与运维管理流程。为了方便广大用户快捷安装部署相关软件应用&#xff0c;1Panel特别开通应用商店&am…

element plus的table组件,点击table的数据是,会出现一个黑色边框

在使用 Element Plus 的 Table 组件时&#xff0c;如果你点击表格数据后出现了一个黑色边框&#xff0c;这通常是因为浏览器默认的焦点样式&#xff08;outline&#xff09;被触发了。如图&#xff1a; 你可以通过自定义 CSS 来隐藏这个黑色边框&#xff0c;代码如下&#xff1…

泷羽sec学习打卡-brupsuite7搭建IP炮台

声明 学习视频来自B站UP主 泷羽sec,如涉及侵权马上删除文章 笔记的只是方便各位师傅学习知识,以下网站只涉及学习内容,其他的都 与本人无关,切莫逾越法律红线,否则后果自负 关于brupsuite的那些事儿-Brup-IP炮台搭建 搭建炮台服务端安装zmap1、更新系统和安装基础依赖&#xff…

赫布定律 | 机器学习 / 反向传播 / 经验 / 习惯

注&#xff1a;本文为 “赫布定律” 相关文章合辑。 未整理。 赫布定律 Hebb‘s law 馥墨轩 2021 年 03 月 13 日 00:03 1 赫布集合的基本定义 唐纳德・赫布&#xff08;Donald Hebb&#xff09;在 1949 年出版了《行为的组织》&#xff08;The Organization of Behavior&a…

各个数据库优劣势对比

1.关系型数据库&#xff08;RDBMS&#xff09; 优势&#xff1a; • 数据一致性&#xff1a;通过严格的事务处理和ACID&#xff08;原子性、一致性、隔离性、持久性&#xff09;特性&#xff0c;确保数据的一致性和完整性。 • 易于理解和使用&#xff1a;关系型数据库的表结构…

Excel中如何消除“长短款”

函数微调可以可以实施&#xff0c;简单且易于操作的气球&#x1f388;涨缩更妙。 (笔记模板由python脚本于2024年12月17日 06:19:13创建&#xff0c;本篇笔记适合用Excel操作数据的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Fre…

RJ45 网线线序、E1线线序、2B+d线序

1、RJ45 网线线序 线序排列如下&#xff1a; T568A线序&#xff1a;绿白—1&#xff0c;绿—2&#xff0c;橙白—3&#xff0c;蓝—4&#xff0c;蓝白—5&#xff0c; 橙—6&#xff0c;棕白—7&#xff0c;棕—8 T568B线序&#xff1a;橙白—1&#xff0c;橙—2&#xff0c…

Fortify_SCA_v24.2.0

前言 Fortify SCA 支持丰富的开发环境、语言、平台和框架&#xff0c;可对开发与生产混合环境进行安全检查。25 种编程语言 超过 911,000 个组件级 API 可检测超过 961 个漏洞类别 支持所有主流平台、构建环境和 IDE。 Fortify SCA是一款商业软件&#xff0c;价格较为昂贵&am…

rust的axux框架开启负载均衡和重启自身的方法-会议签到的调优

开启负载均衡和重启自身 更换axum后台的意外解决的尝试在caddy反代,使用负载均衡,加多一个节点axum主程序 ip映射信息做全局共享axum重启自身刷新全局共享配置 前期刚实现了rust的后台关键业务.结果出现了两类大问题停止服务.在正用着的时候,出现很多意外,真是刺激… 更换axum…

深入理解数据库索引:原理、分类与优化

目录 1. 索引基础1.1 索引的工作原理 2. 最左匹配原则2.1 什么是最左匹配原则&#xff1f;2.2 示例说明2.3 最左匹配原则的图示 3. 索引分类3.1 按数据结构分类3.2 按索引列数分类3.3 按唯一性分类3.4 按存储方式分类 4. 聚集索引与非聚集索引的区别4.1 聚集索引4.2 非聚集索引…

Three.js相机Camera控件知识梳理

原文&#xff1a;https://juejin.cn/post/7231089453695238204?searchId20241217193043D32C9115C2057FE3AD64 1. 相机类型 Three.js 主要提供了两种类型的相机&#xff1a;正交相机&#xff08;OrthographicCamera&#xff09;和透视相机&#xff08;PerspectiveCamera&…

一条线上的点

给你一个数组 points &#xff0c;其中 points[i] [xi, yi] 表示 X-Y 平面上的一个点。求最多有多少个点在同一条直线上。 提示&#xff1a; 1 < points.length < 300points[i].length 2-104 < xi, yi < 104points 中的所有点 互不相同 解析&#xff1a;使用斜…