copist
@copist
Empower people to give

Может ли Neo4J сделать такой запрос?

Интернет-магазин продаёт авто-товары. Нужно искать пары рекомендуемых товаров в тех же категориях с учётом дополнительных статистических ограничений. Сейчас в голове крутятся варианты на MySQL и Neo4J. Судя по графикам производительности, мне нужен будет именно Neo4J, но я не понимаю, как реализовать поиск с учётом агрегатных функций.

Модель данных
У каждой покупки есть дата
В одной покупке не менее одного товара.
У каждого товара в покупке есть стоимость.
Стоимость на один товар может меняться, потому что есть инфляция, курс доллара, рекламные акции.
У каждого товара есть не менее одной категории.

С точки SQL была бы такая модель
CREATE TABLE `product` (
  `productId` INT(11) NOT NULL,
  PRIMARY KEY (`productId`),
);

CREATE TABLE `product_category` (
  `productId` INT(11) NOT NULL,
  `categoryId` INT(11) NOT NULL,
  PRIMARY KEY (`productId`,`categoryId`)
);

-- purchasePair - это денормализованная пара из покупки
-- Означает, что товар productId с ценой price был в одном заказе с товаром
-- productPairId и таких пар будет несколько на одну покупку
CREATE TABLE `purchasePair` (
  `id` INT(11) NOT NULL AUTO_INCREMENT,
  `productId` INT(11) NOT NULL,
  `purchasedAt` datetime NOT NULL,
  `productPairId` INT(11) NOT NULL,
  `price` decimal(13,2) NOT NULL,
  PRIMARY KEY (`id`)
);


В частности, мне нужно выполнить поиск по такому запросу:
Для заданного товара найти 5 товаров в тех же категориях, но чтобы цена была не меньше чем у этого товара и первыми были те, которые покупают чаще за последние 30 дней.

Пример:
Были покупки:
1. "Фара модель 1" (категория "электрооборудование") за 1000р была куплена вместе с "Сигнал" (электрооборудование) за 500р
2. "Фара модель 2" (категория "электрооборудование") за 500р была куплена вместе с "Охлаждающая жидкость" (Технические жидкости) за 800р​​
3. "Фара модель 2" (категория "электрооборудование") за 500р была куплена вместе с "Набор ковриков" (Салон) за 1500р​​

Теперь если я сделаю запрос "пара для товара "Сигнал​ за 500 рублей", мне должен вернуть "Фара модель 1", потому что это тоже "электрооборудование", но цена выше чем у "Сигнал" и покупка была в последние 30 дней.

На SQL запрос был бы такой (приблизительно):
SELECT pc2.productId, -- найти парный товар
	(SELECT COUNT(id) -- определить количество продаж
	 FROM purchasePair pr
	 WHERE ((pr.productId = pc.productId AND pr.productPairId = pc2.productId)
			OR -- чтобы продажи были в паре
                       (pr.productId = pc2.productId AND pr.productId = pc.productId)
                      )
		     AND pr.price > '500 рублей' -- и цена больше чем у товара
		     AND purchasedAt > '30 дней назад' -- и не очень давно
	) as cnt
FROM productCategory pc
JOIN productCategory pc2 ON pc2.categoryId = pc.categoryId -- и чтобы товары были в одной категории
WHERE pc.productId = 'Сигнал'
ORDER BY cnt DESC -- по убыванию количества продаж
LIMIT 5 -- только 5 результатов из найденных


Как такое же сделать на Neo4J?
  • Вопрос задан
  • 347 просмотров
Решения вопроса 2
al_gon
@al_gon
Вам нужен скорее поисковый движок типа Lucene/Solr/Elasticsearch

А на основе него реализовать приблеженный поиск или подргому говоря нечеткий поиск, только не по строкам как это обычно презенуется, а комбинированную функцию на всём объкте.

То что вы собиратетесь сделать, это рекамедационный поиск.
Он так же решаем и на Neo4j, но скорость будет меньше. И он ну и не совсем для этого.

Тут я приводил ссылки
По какому принципу работают рекомендательные сервисы?

И да, если отвечать прямо на ваш вопрос, то да может.
Ответ написан
@romanc
уточните пожалуйста следующие моменты:
1. цена `price` указывается для товара `productId`,
а как нам узнать цену товара `productPairId` ?
2. если не секрет, расскажите почему вы выбрали модель в которой может быть не более двух товаров в покупке?
3. может ли цена меняться часто - и необходимо учитывать последнюю цену?

запрос может выглядеть так:
MACTH 
    (p1:Product {productId:'Сигнал'})<-[:HAS_PRODUCT]-(psc:Purchase)
WITH
    p1, psc
ORDER BY 
    psc.purchasedAt DESC
WITH  //получаем последнюю стоимость заданного товара
    p1, collect(psc)[0].price as lastPrice1 
MACTH
    (p1)-[:HAS_CATEGORY]->(c2:Category)<-[:HAS_CATEGORY]-(p2)<-[:HAS_PRODUCT]-(psc:Purchase)
WITH DISTINCT
    p1, lastPrice1, p2, psc
ORDER BY
    psc.purchasedAt DESC
WITH  //получаем последнюю стоимость товаров из тех же категорий
    p1, lastPrice1, p2, collect(psc)[0].price as lastPrice2
WHERE //оставляем более дешевые
    lastPrice1 > lastPrice2
MATCH 
    (p2)
OPTIONAL MATCH 
    (p2)<-[:HAS_PRODUCT|HAS_PAIR_PRODUCT]-(psc:Purchase)
WHERE  // считаем оплаты за последний месяц, при этом учитываем оба товара в оплате
    psc.purchasedAt > timestamp() - 60*60*24*30*1000
RETURN p2, count(psc) as cnt 
ORDER by cnt DESC
LIMIT 5
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы