I am trying to query data from DB using typeorm. How can I make the same query using queryBuilder?
const data = await this.dataRepository.findOne({
where: [
{ field1: input1, field2: input2 },
{ field1: input2, field2: input1 }
]
});
By using a complex where clause using Brackets
import { Brackets } from "typeorm"
const data = await this.dataRepository(Data) // Data refers to your Entity Class
.createQueryBuilder("data")
.where(new Brackets(qb => {
qb.where("data.field1 = :field1", { field1: 'input1' })
.andWhere("data.field2 = :field2", { field2: "input2" })
}))
.orWhere(new Brackets(qb => {
qb.where("data.field1 = :field1", { field1: 'input2' })
.andWhere("data.field2 = :field2", { field2: "input1" })
}))
.getOne();
Related
Schema:
new Schema({
productId: String,
types: [{
productType: String,
lastModified: Date
}]
});
Query:
{
productId: "1",
email: "test#test.com",
productType: "test",
}
I tried this but its returning only first matched element:
const productType = 'test';
const result = await this.model(email)
.find(
{ productId, 'types.productType': productType },
{ 'types.$': productType }
).lean();
with aggregate, it return empty array result:
const result = await this.model(email).aggregate([
{ $match: { productId, 'types.productType': 'productType' } },
{
$project: {
types: {
$filter: {
input: '$types',
as: 'r',
cond: { $eq: ['$$r.productType', productType] }
}
},
_id: 0
}
}
]);
I need to find all matching elements where projection $ returns the first matched
I have a schema.
const placeSchema = new Schema({
description: {
fr: String,
en: String,
},
comment: {
fr: String,
en: String,
},
...
...
});
const Place= mongoose.model('Place', placeSchema);
module.exports = Place;
If I want to get only 'en' value I am currently using
await Place.find({}, '-description.fr -comment.fr ...')
If the number of similar fields increases so does the length of the query. Is there a way to select all the similar fields like maybe $field.fr?
Technically yes there is a way. using $objectToArray and doing some structure manipulation.
It would look something like this:
db.collection.aggregate([
{
$match: {} //match your document.
},
{
$addFields: {
rootArr: {
$objectToArray: "$$ROOT"
}
}
},
{
$unwind: "$rootArr"
},
{
$match: {
"rootArr.v.en": {
$exists: true
}
}
},
{
$group: {
_id: "$_id",
data: {
$push: {
k: "$rootArr.k",
v: "$rootArr.v.en"
}
}
}
},
{
$replaceRoot: {
newRoot: {
$arrayToObject: "$data"
}
}
}
])
Mongo Playground
It's a little "hacky" thought, how strict are your schema needs?
Have you considered building it under the following structure?:
const placeSchema = new Schema({
data: [
{
lang: String,
description: String,
comment: String,
...
}
]
});
The following aggregation will check all the top level fields for a subfield en. If it's truthy (should work if you strictly have string values for the language properties), the subfield will be { field: { en: fieldValue.en } } otherwise it will be { field: fieldValue }
db.collection.aggregate([
{
$replaceRoot: {
newRoot: {
$arrayToObject: {
$map: {
input: { $objectToArray: "$$ROOT" },
in: {
k: "$$this.k",
v: {
$cond: [
"$$this.v.en", // works for string values, otherwise you will have to check more explicitly
{
en: "$$this.v.en"
},
"$$this.v"
]
}
}
}
}
}
}
}
])
Mongo Playground
Both the answers above are exactly what the question was looking for. This might be a more 'hacky' way of doing things.
First create a function that generates the query string '-description.fr -comment.fr ...'
let select = '';
const selectLanguage = (fields, lang) => {
switch (true) {
case lang === 'fr':
fields.forEach(field => {
select= `${select} -${field}.en `;
});
break;
case lang === 'en':
fields.forEach(field => {
select = `${select} -${field}.fr `;
});
break;
default:
break;
}
return select;
}
This generates a string like ' -fieldName1.fr -fieldName2.fr ..' for english and and ' -fieldName1.en ..' for french. Then we can use this statement in the query above.
const select = selectLanguage(['description', 'comment', ..], 'en')
await Place.find({}, select) //await Place.find({}, ' -description.fr -comment.fr ..')
I want to update multiple subdocuments from multiple documents using mongoose.
My current code is:
const ids: any[] = payload.imageIds.map(e => new ObjectId(e));
await this.userModel
.updateMany({ 'images._id': { $in: ids } }, { $inc: { 'images.$.views': 1 } })
.exec();
And part of the schema is:
export const UserSchema = new mongoose.Schema(
{
images: [ImageSchema],
}
const ImageSchema = new mongoose.Schema(
{
views: { type: Number, default: 0 },
},
But this code only updates the last element from the ids arr.
Solved!
For those who encounter the same problem:
const imageIds: ObjectId[] = payload.imageIds.map(e => new ObjectId(e));
const userIds: ObjectId[] = payload.userIds.map(e => new ObjectId(e));
await this.userModel
.updateMany(
{ _id: { $in: userIds } },
{ $inc: { 'images.$[element].views': 1 } },
{
arrayFilters: [
{
'element._id': { $in: imageIds },
},
],
},
)
.exec();
I am new in mongo. I need to update one nested field in MongoDB model. This is my code: -
const employee = await empModel.searchData(Query);
countryArray.push({lang: 'eng', result 100});
countryArray.push({lang: 'german', result 99});
employee[0].country = countryArray;
employee[0].markModified('country');
employee[0].save();
Schema of empModel:
onst mongoSchema = new Schema({
empId: [{
type: Schema.Types.ObjectId
}],
country:[{
lang:String,
result:Number
}],
projectId: {
type: String,
required: true
}
});
Use simple update query instead of find and the update
static async updateData(query, countryArray) {
const updateData= await this.update(query, { $set: { country: countryArray }});
console.log(updated successfully')
return 'Updated successfully'
}
const countryArray = []
countryArray.push({lang: 'eng', result 100})
countryArray.push({lang: 'german', result 99})
const update = await empModel.updateData(Query, countryArray)
This question already has answers here:
Node.js Mongoose.js string to ObjectId function
(9 answers)
Closed 4 years ago.
I have an array of ids which is launchIds.
I'm trying to push it on a model field trips with
$addToSet: { trips: { $each: launchIds }. This gives me an error: Cast to [ObjectId] failed for value \"[\"1\",\"2\",\"3\"]\...
if I try to map through launchIds and convert to Mongoose.Shema.Types.ObjectId I get in the database trips: [null,null,null]
lauchIds = ['1','2','3']
async bookTrips({ launchIds }) {
let userId = "5bf7f7b3817119363da48403";
const mongoIds = launchIds.map(l => Mongoose.Schema.Types.ObjectId(l));
return this.store.User.findByIdAndUpdate(
{ _id: userId },
{
$addToSet: { trips: { $each: mongoIds } }
},
{ new: true }
);
}
Here's my model Schema:
const UserSchema = new Mongoose.Schema(
{
email: {
type: String,
required: true
},
token: String,
trips: [
{
type: Mongoose.Schema.Types.ObjectId,
ref: "trip"
}
]
},
{ timestamps: true }
);
I'm passing ids via grapql playground. Here's my mutation:
bookTrips: async (_, { launchIds }, { dataSources }) => {
console.log(launchIds);
// logs ['1','2','3']
console.log(typeof launchIds);
//Object
const results = await dataSources.userAPI.bookTrips({ launchIds });
console.log(results);
return { message: "hello" };
}
To convert a string or a number into mongo object use Mongoose.Types.ObjectId,
const mongoIds = launchIds.map(l => Mongoose.Types.ObjectId(l));
I was getting back an array of strings where this should be numbers
The solution:
My model (same as above):
const UserSchema = new Mongoose.Schema(
{
email: {
type: String,
required: true
},
token: String,
trips: [
{
type: Mongoose.Schema.Types.ObjectId,
ref: "trip"
}
]
},
{ timestamps: true }
);
crud API:
async bookTrips({ launchIds }) {
let userId = "5bf7f7b3817119363da48403";
const idsToNums = launchIds.map(Number);
const mongoIds = idsToNums.map(l => Mongoose.Types.ObjectId(l));
return this.store.User.findByIdAndUpdate(
{ _id: userId },
{
$push: { trips: { $each: mongoIds } }
},
{ new: true }
);
}
Notice the Mongoose.Schema.Types.ObjectId on model and Mongoose.Types.ObjectId on api. If I remove Schema from model or add Schema to api I'm getting an error. Not sure why, but the above example works. I hope someone will find this helpful or suggests a better solution.