Статьи

Приложение Movie Рекомендации с использованием Spring Data и Redis

В этом блоге объясняется, как создать приложение с рекомендациями для фильмов, используя Spring Data и Redis, базу данных NoSQL. Мы будем использовать подход NoXML и попытаемся определить нюансы базы данных NoSQL.

Приложение Рекомендации по фильмам хранит оценки для разных фильмов пользователями и пытается обеспечить оценки сходства между пользователями и рекомендовать фильмы. Он основан на примере из книги « Программирование коллективного интеллекта ». Мы будем использовать Redis ZSet для хранения данных. ZSet — это отсортированный набор, в котором члены набора отсортированы по предоставленному рангу.

Код находится на GitHub

Конфигурирование Redis

Установите Redis в соответствии с установками на сайте Redis. запустить ‘Redis-сервер’. Это оно.

Конфигурирование данных Spring

Документация Spring Data объясняет, как настроить и использовать Spring Data и redis . Наиболее важной частью является добавление репозиториев Spring milestone / snapshot в ваш pom.xml. Но я не буду повторять это здесь. Давайте воспользуемся подходом на основе JavaConfig для настройки Spring.

Нам нужна следующая конфигурация для настройки RedisConnectionFactory и StringRedisTemplate. Если вы не знакомы с Spring JavaConfig, вы могли бы указать на проблему с RedisConnectionFactory, которая больше не является Singleton. Но это то, для чего вам нужен cglib в вашем pom.xml (проблема с Singleton будет решена путем расширения вашего класса конфигурации).

@Configuration
public class Config {
@Bean
public RedisConnectionFactory getConnectionFactory() {
JedisConnectionFactory cf = new JedisConnectionFactory();
return cf;
}
@Bean
public StringRedisTemplate getRedisTemplate() {
return new StringRedisTemplate(getConnectionFactory());
}
}

Модель данных

Итак, теперь, как вы создаете нереляционную модель данных. В мире NoSQL вы пытаетесь оптимизировать свою модель данных для вариантов использования. Вы даете такие вещи, как дублирование данных и т. Д., Меньшее значение (и именно поэтому пуристы ненавидят решения NoSQL).

Наши варианты использования: —
    1. Рейтинг магазина для фильмов пользователем.
    2. Вычислить сходство между пользователями.
    3. Рекомендовать фильмы

Вот код в AllInOne * UberDao *. Игнорируйте строки, добавляя фильмы и пользователей. StringRedisTemplate автоматически подключается в DAO.

Мы создаем ZSetOperations, привязанный к пользовательскому ключу, а затем добавляем оценки для фильмов. Он также поддерживает ZSet для сопоставления фильма с пользовательскими рейтингами (это требуется для пользовательского случая № 3). Если мы не поддерживаем дубликаты данных, то логика потребуется для извлечения тех же данных позже (пространство против времени).

@Component
public class UberDao {

@Autowired
private StringRedisTemplate srt;

public void addRatings(String user, Map<String, Double> ratings) {
// Used for batch mode
srt.multi();

srt.boundSetOps("Users").add(user);
BoundZSetOperations<String, String> boundZSetOps = srt.boundZSetOps(user);

for (Map.Entry<String, Double> mr : ratings.entrySet()) {
srt.boundSetOps("Movies").add(mr.getKey());
// ZSet to keep track of movie => user rank map
srt.boundZSetOps(mr.getKey()).add(user, mr.getValue());
boundZSetOps.add(mr.getKey(), mr.getValue());
}
// runs all commands in batch
srt.exec();
}

   
Теперь наблюдение здесь, модель данных выглядит как карта. Да, это так: это хранилище значений ключей, и следует отметить, что база данных является расширением приложения. Между хранилищем данных и моделью приложения нет несоответствия. Это правильно или неправильно? Я буду держать этот вопрос открытым.

Операции в Redis позволяют вам выполнять транзакционные обновления счетчиков и выполнять операции на стороне сервера, такие как UNION и INTERSECT. Вы можете увидеть использование multi и exec для обновления транзакций.

Вычислительное сходство

Сходство между пользователями можно использовать, вычисляя евклидово расстояние между пользовательскими рейтингами для обычных фильмов или находя корреляцию. Класс рекомендует реализует оба (пожалуйста, обратитесь к исходному коду на github).

Чтобы получить обычные фильмы для двух пользователей, мы можем получить их фильмы и добавить циклы в клиентские коды. Но Redis имеет встроенный механизм пересечения для таких * социальных * задач. Мы используем zInterStore, чтобы вычислить разницу между оценками пользователей, а затем вычислить евклидово расстояние. См. Класс «Рекомендую» для получения подробной информации о расчете оценок сходства и «Коллективная интеллектуальность» для получения подробной информации.

    
public Map<String, Double> getScoreDiff(final String p1, final String p2) {
Map<String, Double> mScoreMap = new HashMap<String, Double>();
final String combinedKey = p1 + ":" + p2;

Set<Tuple> movieAndScores = srt.execute(new RedisCallback<Set<Tuple>>() {
@Override
public Set<Tuple> doInRedis(RedisConnection con)
throws DataAccessException {
// emits a new zset ...
con.zInterStore(combinedKey.getBytes(), Aggregate.SUM, new int[] {1,-1}, p1.getBytes(), p2.getBytes());
// remove this key after a while.
con.expire(combinedKey.getBytes(), 120);
return con.zRangeByScoreWithScore(combinedKey.getBytes(), 1, 20);
}

});

for (Tuple t : movieAndScores) {
mScoreMap.put(new String(t.getValue()), t.getScore());
}

return mScoreMap;
}

Человека можно сравнить с любым другим человеком, а затем можно найти список из 5/10 лучших людей с похожими вкусами. Фильмы, которые видят эти люди, могут быть интересны.

рекомендации

Чтобы вычислить рекомендации для пользователя, вы создаете взвешенный (по показателям сходства) рейтинг для фильмов, которые пользователи не видели. Для этого вам нужны оценки для фильма от всех пользователей. Класс Recom (метод getRecommendations) делает это.

Вы можете поиграть с рейтингом класса, чтобы изменить данные канала и найти рекомендации.

Как мне его запустить

Я использую тестовый сценарий (MovieTest) для захвата различных шагов (без утверждений там). Их нужно запускать в последовательности один за другим. Я не нашел JUnitRunner для JavaConfig весной, поэтому мы должны инициализировать приложение в тестовом примере.

public class MovieTest {

private AnnotationConfigApplicationContext ctx;
private UberDao dao;
private Recommend recomender;

@Before
public void init() {
// No junit runner to run app with javaconfig.
ctx = new AnnotationConfigApplicationContext(Config.class);
ctx.scan("xebia.moviez.dao");
ctx.scan("xebia.moviez.service");
dao = ctx.getBean(UberDao.class);
recomender = ctx.getBean(Recommend.class);
}

Вывод

Поначалу создание приложений с базой данных NoSQL может быть затруднено, поскольку мы пытаемся создать реляционную модель в базе данных NoSQL. Базы данных NoSQL содержат информацию о схеме, встроенную в код, и без этой информации данные более или менее бесполезны. NoSQL не подходит для всего, у него есть свои варианты использования. Это не только для масштабируемости. Представьте себе, если бы кто-то создал приложение, используя только HashMaps, до того, как появился термин NoSQL.