#node.js #postgresql #express #sequelize.js
#node.js #postgresql #выразить #sequelize.js
Вопрос:
Я работаю над каким-то проектом по аренде домов и квартир, и я дошел до того, что мне нужно реализовать фильтрацию домов на основе имеющихся у них функций (Wi-Fi, охрана и другой персонал).). Вначале я решил впервые попробовать Sequelize ORM. Такие вещи, как добавление, создание, редактирование, работают нормально, но в части фильтрации у меня возникают некоторые проблемы.
Я работаю с nodejs, express и postgresql. Мне нужно найти все дома, в которых есть объекты, перечисленные в массиве идентификаторов объектов. Вот что я пробовал. В этом примере я пытаюсь получить дома, которые имеют функции с идентификаторами 1, 2 и 4.
db.House.findAll({
include: [{
model: db.HouseFeature,
as: 'HouseFeatures',
where: {
featureId: {
[Op.contains]: [1, 2, 4] //<- array of featuresIds
}
}
}]
})
Выборка домов по одному идентификатору объекта работает нормально, потому что я не использую Op.contains там.
Вот некоторые отношения, связанные с этим случаем:
House.hasMany(models.HouseFeature, { onDelete: 'CASCADE' });
HouseFeature.belongsTo(models.House);
HouseFeature содержит поле FeatureId.
Вот ошибка, которую я получаю:
error: оператор не существует: integer @> unknown
at Connection.parseE (C:***servernode_modulespglibconnection.js:601:11)
at Connection.parseMessage (C:***servernode_modulespglibconnection.js:398:19)
at Socket.<anonymous> (C:***servernode_modulespglibconnection.js:120:22)
at Socket.emit (events.js:182:13)
at addChunk (_stream_readable.js:283:12)
at readableAddChunk (_stream_readable.js:264:11)
at Socket.Readable.push (_stream_readable.js:219:10)
at TCP.onStreamRead [as onread] (internal/stream_base_commons.js:94:17)
name: 'error',
length: 397,
severity: 'ОШИБКА',
code: '42883',
detail: undefined,
hint:
'Оператор с данными именем и типами аргументов не найден. Возможно, вам следует добавить явные приведения типов.',
position: '851',
internalPosition: undefined,
internalQuery: undefined,
where: undefined,
schema: undefined,
table: undefined,
column: undefined,
dataType: undefined,
constraint: undefined,
file:
'd:\pginstaller.auto\postgres.windows-x64\src\backend\parser\parse_oper.c',
line: '731',
routine: 'op_error',
sql:
'SELECT "House"."id", "House"."title", "House"."description", "House"."price", "House"."address", "House"."lat", "House"."lon", "House"."kitchen", "House"."bathrooms", "House"."floor", "House"."totalFloors", "House"."people", "House"."area", "House"."bedrooms", "House"."trusted", "House"."createdAt", "House"."updatedAt", "House"."CityId", "House"."ComplexId", "House"."OwnerProfileId", "House"."HouseTypeId", "House"."RentTypeId", "HouseFeatures"."id" AS "HouseFeatures.id", "HouseFeatures"."featureId" AS "HouseFeatures.featureId", "HouseFeatures"."createdAt" AS "HouseFeatures.createdAt", "HouseFeatures"."updatedAt" AS "HouseFeatures.updatedAt", "HouseFeatures"."HouseId" AS "HouseFeatures.HouseId" FROM "Houses" AS "House" INNER JOIN "HouseFeatures" AS "HouseFeatures" ON "House"."id" = "HouseFeatures"."HouseId" AND "HouseFeatures"."featureId" @> '1,2';'
Извините за какой-то русский там.
Обновить:
Мне удалось сделать то, что мне было нужно, изменив каждый дом, относящийся только к одному дому, и изменив эту модель дома, чтобы хранить массив идентификаторов объектов. Op.contains работает нормально.
db.House.findAll({
include: [{
model: db.HouseFeature,
as: 'HouseFeature',
where: {
features: {
[Op.contains]: req.body.features
}
},
}]
})
// Associations
HouseFeature.belongsTo(models.House);
House.hasOne(models.HouseFeature, { onDelete: 'CASCADE' });
const HouseFeature = sequelize.define('HouseFeature', {
features: {
type: DataTypes.ARRAY(DataTypes.INTEGER)
}
}, {});
Теперь у меня есть одна маленькая проблема. Могу ли я как-то связать модель HouseFeature с моделью объекта, чтобы позже извлекать изображения значков объектов и их названия? При этом идентификаторы объектов хранятся внутри массива HouseFeature.
Комментарии:
1. Вы имеете в виду, что хотите получить дома с идентификатором (один из массива)?
2. Может быть, вы ищете
Op.in
operator вместоOp.contains
?3. Op.contains, похоже, предназначен для типов массивов Postgres… но ваша модель выглядит как 1: M …. так что, может быть, @Ihor прав? Вы ищете дома, в которых есть ВСЕ эти функции? Или дома, которые имеют хотя бы одну из этих функций?
4. Идея в том, что пользователь может создать house и установить в нем функции. И другие пользователи могут фильтровать дома в зависимости от функций. Например, пользователи могут находить дома с Wi-Fi, балконами и парковочными местами. Мне нужно, чтобы мой запрос возвращал дома с выбранными функциями.
Ответ №1:
Пожалуйста, проверьте разницу между Op.in
и Op.contains
:
[Op.in]: [1, 2], // IN [1, 2]
[Op.contains]: [1, 2] // @> [1, 2] (PG array contains operator)
Похоже HouseFeatures.featureId
, это PK с типом integer
, а не a postgres array
.
Пожалуйста, попробуйте:
db.House.findAll({
include: [{
model: db.HouseFeature,
as: 'HouseFeatures',
where: {
featureId: {
[Op.in]: [1, 2, 3]
}
}
}]
})
или даже
db.House.findAll({
include: [{
model: db.HouseFeature,
as: 'HouseFeatures',
where: {
featureId: [1, 2, 3]
}
}]
})
вместо
Комментарии:
1. Похоже, вы правы. Особенности дома. FeatureId — целое число. Но я уже пробовал Op.in , и у меня была проблема с этим — он возвращал один и тот же дом несколько раз, если у дома было более одной функции. Например, если я попросил дома с 3 функциями, Op.in возвращался в один и тот же дом 3 раза.
2. У меня отлично работает — возвращает массивы уникальных домов со списком объектов дома codeshare.io/adyqV4