我们举行了一个company-wide hackathon,我们互相挑战,使用流数据源Kafka,Memgraph和Web应用程序后端构建引人注目的有用应用程序。本周,我们正在寻找在Memgraph之上构建Spotify歌曲推荐引擎。
我们公司中有很多音乐爱好者,当我们的一位队友遇到了Spotify播放列表的开放数据集时,我们知道这将使我们挠痒痒地尝试在Memgraph上构建推荐引擎。这很有趣 - 我们很高兴能展示它。
数据源
我们的应用程序与包含歌曲,播放列表和用户的 Spotify dataset 一起使用。如果您想自己尝试该应用程序,您所要做的就是访问GitHub page并遵循安装说明,如果您想了解更多信息,请加入我们的Discord Community Chat!
数据模型
Spotify播放列表数据集包含来自不同用户的500万首歌播放列表。每个播放列表都包含音乐曲目列表。数据模型样本如下:
{
"name": "musical",
"collaborative": "false",
"pid": 5,
"modified_at": 1493424000,
"num_albums": 7,
"num_tracks": 12,
"num_followers": 1,
"num_edits": 2,
"duration_ms": 2657366,
"num_artists": 6,
"tracks": [
{
"pos": 0,
"artist_name": "Degiheugi",
"track_uri": "spotify:track:7vqa3sDmtEaVJ2gcvxtRID",
"artist_uri": "spotify:artist:3V2paBXEoZIAhfZRJmo2jL",
"track_name": "Finalement",
"album_uri": "spotify:album:2KrRMJ9z7Xjoz1Az4O6UML",
"duration_ms": 166264,
"album_name": "Dancing Chords and Fireflies"
},
// 10 tracks omitted
{
"pos": 11,
"artist_name": "Mo' Horizons",
"track_uri": "spotify:track:7iwx00eBzeSSSy6xfESyWN",
"artist_uri": "spotify:artist:3tuX54dqgS8LsGUvNzgrpP",
"track_name": "Fever 99\u00b0",
"album_uri": "spotify:album:2Fg1t2tyOSGWkVYHlFfXVf",
"duration_ms": 364320,
"album_name": "Come Touch The Sun"
}
],
}
应用程序体系结构
应用程序接口为用户提供了创建网站的方法。中间列是用户可以在播放列表中添加歌曲的地方。建议的歌曲在右专栏中列出。左列显示了建议的播放列表。用户可以从建议的播放列表中检查歌曲,并将其直接添加到其播放列表中。当用户播放列表包含不同的歌曲时,建议会自动改变。
要了解如何推荐歌曲,让我们潜水。
初始数据以JSON文件的形式。每个JSON文件包含Spotify播放列表的列表。使用脚本koude0,他们根据名为spotify
的主题将其发送到Kafka。 Memgraph读取来自Kafka的播放列表,并将它们存储在具有两种类型的图形数据模型中:Track
和Playlist
。播放列表通过HAS
边缘连接到轨道。
在用户方面,我们有一个 vue.js 应用程序来创建播放列表并建议与用户品味相似的音乐曲目。该网站调用烧瓶后端,该后端使用自定义法师算法查询Memgraph。收到新的播放列表后,Memgraph可以检测到上升的命中并将其直接发送到Kafka。然后,后端保留了一首时尚的歌曲表,并向所有用户建议在单独的“时尚”部分中向所有用户建议。
建议算法
在将任何歌曲添加到用户的播放列表中之前,建议最多的播放列表中包含的曲目。一旦用户将歌曲添加到他的播放列表中,Memgraph 就可以从用户播放列表中包含的所有歌曲开始浏览该图,并对所有其他可以联系的其他歌曲进行了广泛的搜索,并将其等级为基础。在距离上,曲目包含在,影响和顺序的播放列表数量。
同样,类似的播放列表是根据其中包含的歌曲和用户的播放列表的数量来计算的,结合了播放列表中其他歌曲与用户播放列表的相似性。这有一个很大的缺陷,播放列表的歌曲越多,推荐的可能性就越大。
。使用法师开发建议算法
我们将开源MAGE project用于
开发执行建议的自定义查询模块。这样,我们只能调用Cypher查询的过程。已实施以下程序:
-
similar_tracks.get(playlist_id: int) -> mgp.Record(track_ids=list[int]) """Returns a list of track_ids that are similar to the tracks in the given playlist. Calculates similar tracks by calculating the proximity of each track to the given playlist. :param int playlist_id: User playlist. :return: List of track ids. :rtype: mgp.Record(track_ids=list[int]) """
-
similar_playlists.get(playlist_id: int) -> mgp.Record(playlist_ids=list[int]) """Returns a list of playlist_ids that are similar to the given playlist. Calculates similar playlists by calculating the proximity of each to the given one. :param int playlist_id: User playlist. :return: List of playlist ids that are currently trendy. :rtype: mgp.Record(playlist_ids=list[int]) """
-
trendy_tracks.get() → mgp.Record(tracks=list[dict[str][Any]]) """Returns a list of track_ids of trendy songs. Calculates recently popular tracks by comparing the popularity of songs using the `followers`, `created_at`, and proximity to other popular songs (pagerank). :return: List of track ids that are currently trendy. :rtype: mgp.Record(track_ids=list[dict[str][Any]]) """
后端服务器
后端是一个提供以下休息
的Python烧瓶应用程序
端点:
-
获取
/
为主页 -
post
/create-playlist
- 有效载荷
{ "playlist_name": str }
- 返回
{ "status": int, "message": str, "playlist_id": int, }
-
put
/rename-playlist
- 有效载荷
{ "playlist_id": int, "playlist_name": str }
- 返回
{ "status": int, "message": str, "playlist_name": str }
-
post
/add-track
- 有效载荷
{ "playlist_id": int, "track_uri": int }
- 返回
{ "status": int, "message": str, "track": { "artist_name": str, "track_uri": str, "artist_uri": str, "track_name": str, "album_uri": str, "duration_ms": int, "album_name": str, } }
-
发布
/track-recommendation
- 有效载荷
{ "playlist_id": int, "tracks": [ { "track_id": int, "artist_name": str, "track_uri": str, "artist_uri": str, "track_name": str, "album_uri": str, "duration_ms": int, "album_name": str, }... ] }
- 返回
{ "status": int, "message": str, "tracks": [ { "track_id": int, "artist_name": str, "track_uri": str, "artist_uri": str, "track_name": str, "album_uri": str, "duration_ms": int, "album_name": str, }... ] }
-
发布
/playlist-recommendation
- 有效载荷
{ "playlist_id": int, "track_ids": [ int, int, ... ] }
- 返回
{ "status": int, "message": str, "playlists": [ { "playlist_id": int, "playlist_name": str, "tracks": [ { "track_id": int, "artist_name": str, "track_uri": str, "artist_uri": str, "track_name": str, "album_uri": str, "duration_ms": int, "album_name": str, }... ] }, { "playlist_id": int, "playlist_name": str, ... } ] }
-
获取
/trending-tracks
- 返回
{ "status": int, "message": str, "tracks": [ { "track_id": int, "artist_name": str, "track_uri": str, "artist_uri": str, "track_name": str, "album_uri": str, "duration_ms": int, "album_name": str, }... ] }
-
获取
/top-tracks/<number_of_tracks:10>
- 返回
{ "status": int, "message": str, "tracks": [ { "track_id": int, "artist_name": str, "track_uri": str, "artist_uri": str, "track_name": str, "album_uri": str, "duration_ms": int, "album_name": str, }... ] }
-
获取
/top-playlists/<number_of_tracks:10>
- 返回
{ "status": int, "message": str, "playlists": [ { "playlist_id": int, "playlist_name": str, "tracks": [ { "track_id": int, "artist_name": str, "track_uri": str, "artist_uri": str, "track_name": str, "album_uri": str, "duration_ms": int, "album_name": str, }... ] }... ] }
结论
我们学到了很多东西,并且在Memgraph上实现歌曲推荐算法很有趣。我们认为,在开发人员人体工程学和设置方面,我们有一些改进的空间,这是我们最近一直在研究的事情。如果您试一试,我们很想知道您的想法!
如果这个项目对您来说很酷,check out the GitHub repo,让我们知道您在我们的Discord Community Chat!中的想法,您也可以download Memgraph并开始探索自己的网络或分析流数据。
](https://memgraph.com/blog?topics=Real-Time+Analytics&utm_source=devto&utm_medium=referral&utm_campaign=blog_repost&utm_content=banner#list)