Algolia - Places autocomplete using php api client - algolia

I've been playing around with the Algolia autocomplete places.js library. When you use the library you get a list of suggestions e.g.
{
"query": "pari",
"suggestion": {
"name": "Paris",
"administrative": "Île-de-France",
"country": "France",
"countryCode": "fr",
"type": "city",
"latlng": {
"lat": 48.8546,
"lng": 2.34771
},
"postcode": "75000",
"highlight": {
"name": "<em>Pari</em>s",
"administrative": "Île-de-France",
"country": "France"
},
"value": "Paris, Île-de-France, France"
}
}
I have a need to use the php client and return a list of suggestions for my own applications api e.g.
$places = \AlgoliaSearch\Client::initPlaces();
$result = $places->search($term, [
'type' => ['city', 'country', 'address'],
'language' => 'en',
'aroundLatLngViaIP' => false,
]);
dd($result);
However when you use the php client (note I'm using laravel scout in this instance) you don't get a list of suggestions i.e. there is no value property (Full display name of the place found)in the response that you can return back to the end user - instead you end up with the following response?
{
"hits": [{
"objectID": "145746683_7444",
"locale_names": {
"default": ["Paris"],
},
"city": {
"default": ["Paris"],
},
"county": {
"default": ["Paris"],
},
"administrative": ["Île-de-France"],
"country": {
"default": "France",
},
"country_code": "fr",
"postcode": ["75000"],
"population": 2243833,
"_geoloc": {
"lat": 48.8564,
"lng": 2.3521
},
"_highlightResult": {
"locale_names": {
"default": [{
"value": "<em>Paris</em>",
"fullyHighlighted": true,
"matchedWords": ["paris"],
"matchLevel": "full"
}]
},
}
}],
"nbHits": 1,
"query": "Paris"
}

If you look at the Algolia Places JavaScript library, this is the data they're dealing with as well before it is given down to the Autocomplete:
const name = hit.locale_names[0];
const country = hit.country;
const administrative =
hit.administrative && hit.administrative[0] !== name
? hit.administrative[0]
: undefined;
const city = hit.city && hit.city[0] !== name ? hit.city[0] : undefined;
const suburb =
hit.suburb && hit.suburb[0] !== name ? hit.suburb[0] : undefined;
const county =
hit.county && hit.county[0] !== name ? hit.county[0] : undefined;
const { postcode, highlightedPostcode } =
hit.postcode && hit.postcode.length
? getBestPostcode(hit.postcode, hit._highlightResult.postcode)
: { postcode: undefined, highlightedPostcode: undefined };
const highlight = {
name: getBestHighlightedForm(hit._highlightResult.locale_names),
city: city
? getBestHighlightedForm(hit._highlightResult.city)
: undefined,
administrative: administrative
? getBestHighlightedForm(hit._highlightResult.administrative)
: undefined,
country: country ? hit._highlightResult.country.value : undefined,
suburb: suburb
? getBestHighlightedForm(hit._highlightResult.suburb)
: undefined,
county: county
? getBestHighlightedForm(hit._highlightResult.county)
: undefined,
postcode: highlightedPostcode,
};
const suggestion = {
name,
administrative,
county,
city,
suburb,
country,
countryCode: findCountryCode(hit._tags),
type: findType(hit._tags),
latlng: {
lat: hit._geoloc.lat,
lng: hit._geoloc.lng,
},
postcode,
postcodes: hit.postcode && hit.postcode.length ? hit.postcode : undefined,
};
// this is the value to put inside the <input value=
const value = formatInputValue(suggestion);
You should find all you need in this code, and more specifically for the full display name of the found place, it is built from this (where suggestion from the code above is the rendering context for the code below):
const out = `${name}${type !== 'country' && country !== undefined ? ',' : ''}
${city ? `${city},` : ''}
${administrative ? `${administrative},` : ''}
${country ? country : ''}`
.replace(/\s*\n\s*/g, ' ')
.trim();
return out;
(found in the formatInputValue.js file`)

Related

Sequelize nested include without null properties

using ExpressJs/Postgres/Sequelize and when doing findOne I am getting an object return that contains nested objects with following data in the response:
"CompanyVolume": null,
"CompanyMarkets": [],
Is there a way to avoid receiving these properties with null/[] from Postgres?
Appreciate any thoughts...:)
const kyc = await db.Kyc.findOne({
where: {id: req.params.id},
include: [
{model: db.Company,
include: [
{ model: db.CompanyStakeholder, through: db.Role },
{ model: db.CompanyDelivery,
// where: {
// deliveryAverageDurationAmount: {[Op.not]:null},
// }
//returns Cannot read properties of undefined (reading 'not')
},
{ model: db.CompanyVolume },
{ model: db.CompanyDelivery },
{ model: db.CompanyMarket },
],
nest: true,
})
the response object with properties including null or empty arrays
[
{
"id": 5,
"kycTitle": "KYC1006428",
"kycPasscode": null,
"kycDescription": "The best kyc ever, lorem epsum sanctus vita loca",
"kycStartDate": "2022-01-28",
"kycStatus": null,
"kycConsent": "approved",
"kycConsentDate": "2022-01-27T14:01:39.924Z",
"createdAt": "2022-01-27T13:19:41.649Z",
"updatedAt": "2022-01-27T14:01:39.919Z",
"id_User": 1,
"Company": {
"id": 6,
"companyName": "Niky",
"companyEntityType": null,
"companyVatNumber": "",
"companyRegistrationNumber": "",
"companyPubliclyListed": null,
"companyAddress1": "Pepovej 234",
"companyAddress2": "",
"companyCity": "Dada",
"companyPostCode": "3400",
"companyCountry": "Owner",
"companyPhone": "+21321321231",
"createdAt": "2022-01-27T13:19:41.669Z",
"updatedAt": "2022-01-27T13:19:41.676Z",
"KycId": 5,
"CompanyStakeholders": [], //I don't want this returned
"CompanyDelivery": null, //I don't want this returned
"CompanyVolume": null, //I don't want this returned
"CompanyMarkets": [], //I don't want this returned
},
}
]
The different Associations:
Company.associate = function (models) {
Company.hasOne(models.Contact)
Company.hasOne(models.CompanyDelivery)
Company.hasMany(models.CompanyMarket)
Company.hasMany(models.CompanyPosBilling)
Company.hasMany(models.CompanySettlement)
Company.hasOne(models.CompanySubscription)
Company.hasOne(models.CompanyVolume)
Company.hasMany(models.CompanyWebsite)
Company.belongsTo(models.Kyc)
Company.belongsToMany(models.CompanyStakeholder, {
through: models.Role,
unique: false
})
}
CompanyDelivery.associate = function (models) {
CompanyDelivery.belongsTo(models.Company)
}
CompanyMarket.associate = function (models) {
CompanyMarket.belongsTo(models.Company)
}
Kyc.associate = function (models) {
Kyc.hasOne(models.Company)
}
CompanyStakeholder.associate = function (models) {
CompanyStakeholder.belongsToMany(models.Company, {
through: models.Role,
unique: false
})
}

Converting list of map to a list of objects in dart

I am making a request to an API which returns me a response.
final response = await http.get(Uri.parse(requestUrl), headers: headers);
It returns the following response.
{
"meta": {
"upcomingMatchCount": 5,
"inProgressMatchCount": 10,
"completedMatchCount": 5
},
"matchList": {
"matches": [
{
"id": 49944,
"matchTypeId": 15,
"series": {
"id": 2739,
"name": "LV= Insurance County Championship 2021",
"shortName": "LV= Insurance County Championship 2021"
},
"name": "",
"status": "LIVE",
"venue": {
"name": "The Cooper Associates County Ground",
"shortName": "The Cooper Associates County Ground"
},
"homeTeam": {
"isBatting": true,
"id": 55,
"name": "Somerset",
"shortName": "SOM"
},
"awayTeam": {
"isBatting": false,
"id": 46,
"name": "Gloucestershire",
"shortName": "GLO"
},
"currentMatchState": "Live",
"isMultiDay": true,
"matchSummaryText": "Live: Gloucestershire won the toss and elected to bowl.",
"scores": {
"homeScore": "8-293",
"homeOvers": "88.0",
"awayScore": "0-0",
"awayOvers": "0"
},
"liveStreams": [],
"isLive": false,
"currentInningId": 1,
"isMatchDrawn": false,
"isMatchAbandoned": false,
"startDateTime": "2021-04-15T10:00:00Z",
"endDateTime": "2021-04-18T17:00:00Z",
"isWomensMatch": false,
"isGamedayEnabled": false,
"removeMatch": false
},
{
"id": 49942,
"matchTypeId": 15,
"series": {
"id": 2739,
"name": "LV= Insurance County Championship 2021",
"shortName": "LV= Insurance County Championship 2021"
},
"name": "",
"status": "LIVE",
"venue": {
"name": "The Spitfire Ground, St Lawrence",
"shortName": "The Spitfire Ground, St Lawrence"
},
"homeTeam": {
"isBatting": false,
"id": 45,
"name": "Kent",
"shortName": "KEN"
},
"awayTeam": {
"isBatting": true,
"id": 40,
"name": "Yorkshire",
"shortName": "YRK"
},
"currentMatchState": "Live",
"isMultiDay": true,
"matchSummaryText": "Live: Yorkshire won the toss and elected to bat.",
"scores": {
"homeScore": "0-0",
"homeOvers": "0",
"awayScore": "8-358",
"awayOvers": "100.0"
},
"liveStreams": [],
"isLive": false,
"currentInningId": 1,
"isMatchDrawn": false,
"isMatchAbandoned": false,
"startDateTime": "2021-04-15T10:00:00Z",
"endDateTime": "2021-04-18T17:00:00Z",
"isWomensMatch": false,
"isGamedayEnabled": false,
"removeMatch": false
},
]
I am retrieving the match list from this response as follows:
final list = map['matchList']['matches'] as List;
I have a Model class which represents each match from the matches key:
class MatchModel {
int id;
int matchTypeId;
Series series;
String name;
String status;
Venue venue;
HomeTeam homeTeam;
HomeTeam awayTeam;
String currentMatchState;
bool isMultiDay;
String matchSummaryText;
Scores scores;
List<Null> liveStreams;
bool isLive;
int currentInningId;
bool isMatchDrawn;
bool isMatchAbandoned;
String startDateTime;
String endDateTime;
bool isWomensMatch;
bool isGamedayEnabled;
bool removeMatch;
MatchModel(
{this.id,
this.matchTypeId,
this.series,
this.name,
this.status,
this.venue,
this.homeTeam,
this.awayTeam,
this.currentMatchState,
this.isMultiDay,
this.matchSummaryText,
this.scores,
this.liveStreams,
this.isLive,
this.currentInningId,
this.isMatchDrawn,
this.isMatchAbandoned,
this.startDateTime,
this.endDateTime,
this.isWomensMatch,
this.isGamedayEnabled,
this.removeMatch});
MatchModel.fromJson(Map<String, dynamic> json) {
id = json['id'];
matchTypeId = json['matchTypeId'];
series =
json['series'] != null ? new Series.fromJson(json['series']) : null;
name = json['name'];
status = json['status'];
venue = json['venue'] != null ? new Venue.fromJson(json['venue']) : null;
homeTeam = json['homeTeam'] != null
? new HomeTeam.fromJson(json['homeTeam'])
: null;
awayTeam = json['awayTeam'] != null
? new HomeTeam.fromJson(json['awayTeam'])
: null;
currentMatchState = json['currentMatchState'];
isMultiDay = json['isMultiDay'];
matchSummaryText = json['matchSummaryText'];
scores =
json['scores'] != null ? new Scores.fromJson(json['scores']) : null;
if (json['liveStreams'] != null) {
liveStreams = new List<Null>();
json['liveStreams'].forEach((v) {
});
}
isLive = json['isLive'];
currentInningId = json['currentInningId'];
isMatchDrawn = json['isMatchDrawn'];
isMatchAbandoned = json['isMatchAbandoned'];
startDateTime = json['startDateTime'];
endDateTime = json['endDateTime'];
isWomensMatch = json['isWomensMatch'];
isGamedayEnabled = json['isGamedayEnabled'];
removeMatch = json['removeMatch'];
}
How do i map data from the list of matches to the list of my MatchModel? Do let me know if you need anything else, any help will be appreciated.
the thing is the response object which is returned is actually a string, so you need to first convert that to json using like
var json = jsonDecode(response).
Once you have it in json format what you can do is access the list as json['matchList']['matches']. So now you can iterater over it like
List<MatchModel> matches = []
for(var match in json['matchList']['matches']){
matches.add(MatchModel.fromJson(match));
}
Hope it's useful.
I would recommend JSON serialization with code generation. In that way you just need a annotation #JsonSerializable for the class, a constructor and part 'match.g.dart'; at the begin of your file. After this, the json_serializable package will generate the Json-converter-methods/factories for you.
For more information you can use this article: https://flutter.dev/docs/development/data-and-backend/json.
Try this.
var json = jsonDecode(response.body)['matchList']['matches'];
List<MatchModel> matches = List.from(json).map((e) => MatchModel.fromJson(Map.from(e))).toList();

Parent.save() not working when sub document / deeply nested document is modified

I find my document from whole collection like this:
const account = await Account.findOne({ "buildings.gateways.devices.verificationCode": code })
const buildings = account.buildings
const gateways = buildings[0].gateways;
const devices = gateways[0].devices;
const device = _.filter(devices, d => d.verificationCode === code);
now I want to change one of the property "patientLastName" and then save the whole document. I am doing as below.
device.patientLastName = lastName;
const updated = await account.save();
This simply does not change anything. I have tried many solutions given but none of them working.
not sure if I can save parent document just like that?
I have few other calls where same code works but only change for this is that this is in my post call while working ones are in put call.
My Schema:
const accountSchema = new mongoose.Schema({
email: { type: String, unique: true, required: true },
password: { type: String, required: true },
userName: { type: String, unique: true, required: true },
companyName: { type: String, required: true },
apiKey: { type: String, unique: true, required: true },
apiCallCount: { type: Number, default: 0 },
solutionType: String,
parentCompany: String,
buildings:
[
new mongoose.Schema({
buildingName: String,
address: String,
suite: String,
floor: String,
timeZone: String,
gateways:
[
new mongoose.Schema({
gatewayName: String,
gatewayKey: { type: String, sparse: true },
suite: String,
devices: [
new mongoose.Schema({
serialNumber: { type: String, sparse: true },
area: String,
connectionStatus: Number,
gatewayKey: String,
applicationNumber: Number,
firmwareVersion: String,
needsAttention: Boolean,
verificationCode: String,
patientRiskStatus: String,
patientFirstName: String,
patientLastName: String
}, { timestamps: true })
]
}, { timestamps: true })
]
}, { timestamps: true })
]
}, { timestamps: true });
Update:
I am trying this:
it gives me error message -
"message": "Converting circular structure to JSON"
const updated = account.update(
{
"_id" : ObjectId(accountId),
"buildings.gateways.devices.verificationCode": code
},
{
"$set": {
"buildings.$.gateways.0.devices.0.patientFirstName": "name1",
"buildings.$.gateways.0.devices.0.patientLastName": "name2",
}
}
)
Your help is appreciated. Thanks
UPDATED -
complete call for your reference.
// Register User
loginRouter.post('/register', async (req, res, next) => {
try {
var { email, userName, password, firstName, lastName, role, deviceIds, code } = req.body;
console.log(req.body)
// checking if email or username already exist before registering.
const verifyEmail = await User.find({
$or: [
{ 'email': email },
{ 'userName': userName },
]
})
if (verifyEmail.length > 0) {
throw new BadRequestError('DuplicateEmailOrUserName', {
message: 'Email or Username already exists'
});
}
// finding accountId for verification code first
const account = await Account.findOne({ "buildings.gateways.devices.verificationCode": code })
//console.log(account)
if (account.length === 0) {
console.log("Invalid registration code")
throw new BadRequestError('InvalidVerificationCode', {
message: 'Invalid registration code'
});
}
var accountId = account ? account._id : null
const buildings = account.buildings
const gateways = buildings[0].gateways;
const devices = gateways[0].devices;
//console.log("devices", devices)
// finding deviceId to insert for user from that account
const device = _.filter(devices, d => d.verificationCode === code);
// console.log("device", device)
if (!deviceIds) {
deviceIds = device.map(item => item._id)
// console.log("deviceIds", deviceIds)
}
const hashedPassword = await hasher.hashPassword(password);
const newUser = new User({
accountId: accountId ? accountId : undefined,
userName: userName,
password: hashedPassword,
email: email,
firstName: firstName,
lastName: lastName,
role: role,
refreshToken: uuidv4(),
refreshTokenExpiryDate: moment().add(process.env.REFRESH_TOKEN_EXPIRY_IN_DAYS, 'days'),
deviceIds: deviceIds ? deviceIds : [],
isActive: true,
});
const newlySavedUser = await newUser.save();
const {
refreshToken,
refreshTokenExpiryDate,
password: pwd,
...userWithoutSensitiveInfo
} = newlySavedUser.toObject();
**// solutions by #SuleymanSah** <----
try {
let result = await Account.findByIdAndUpdate(
accountId,
{
$set: {
"buildings.$[building].gateways.$[gateway].devices.$[device].patientFirstName": "userName"
}
},
{
arrayFilters: [
{ "building._id": ObjectId("5d254bb179584ebcbb68b712") },
{ "gateway._id": ObjectId("5d254b64ba574040d9632ada") },
{ "device.verificationCode": "4144" }
],
new: true
}
);
if (!result) return res.status(404);
console.log(result)
//res.send(result);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
res.json(newlySavedUser);
next();
} catch (err) {
next(err);
}
});
Let me know if you need more information. Thanks
You can use the filtered positional operator $ for this.
Note that we also need to have the buildingId and gatewayId to make it work dynamically.
router.put("/account/:accountId/:buildingId/:gatewayId", async (req, res) => {
const { patientFirstName, verificationCode } = req.body;
try {
let result = await Account.findByIdAndUpdate(
req.params.accountId,
{
$set: {
"buildings.$[building].gateways.$[gateway].devices.$[device].patientFirstName": patientFirstName
}
},
{
arrayFilters: [
{ "building._id": req.params.buildingId },
{ "gateway._id": req.params.gatewayId },
{ "device.verificationCode": verificationCode }
],
new: true
}
);
if (!result) return res.status(404);
res.send(result);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
TEST
Let's have this document:
{
"_id" : ObjectId("5e0da052b4b3fe5188602e11"),
"apiCallCount" : 1,
"email" : "abc#def.net",
"password" : "123123",
"userName" : "username",
"companyName" : "companyName",
"apiKey" : "apiKey",
"solutionType" : "solutionType",
"parentCompany" : "parentCompany",
"buildings" : [
{
"_id" : ObjectId("5e0da052b4b3fe5188602e12"),
"buildingName" : "buildingName 1",
"address" : "address",
"suite" : "suite",
"floor" : "floor",
"timeZone" : "String",
"gateways" : [
{
"_id" : ObjectId("5e0da052b4b3fe5188602e13"),
"gatewayName" : "gatewayName 1",
"gatewayKey" : "gatewayKey",
"suite" : "suite",
"devices" : [
{
"_id" : ObjectId("5e0da052b4b3fe5188602e15"),
"serialNumber" : "serialNumber 1",
"area" : "area",
"connectionStatus" : 0,
"gatewayKey" : "gatewayKey",
"applicationNumber" : 11,
"firmwareVersion" : "firmwareVersion",
"needsAttention" : true,
"verificationCode" : "123456",
"patientRiskStatus" : "patientRiskStatus",
"patientFirstName" : "patientFirstName",
"patientLastName" : "patientLastName",
"createdAt" : ISODate("2020-01-02T10:48:34.287+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.287+03:00")
},
{
"_id" : ObjectId("5e0da052b4b3fe5188602e14"),
"serialNumber" : "serialNumber 2",
"area" : "area",
"connectionStatus" : 0,
"gatewayKey" : "gatewayKey",
"applicationNumber" : 22,
"firmwareVersion" : "firmwareVersion",
"needsAttention" : true,
"verificationCode" : "987654",
"patientRiskStatus" : "patientRiskStatus",
"patientFirstName" : "patientFirstName",
"patientLastName" : "patientLastName",
"createdAt" : ISODate("2020-01-02T10:48:34.288+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.288+03:00")
}
],
"createdAt" : ISODate("2020-01-02T10:48:34.288+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.288+03:00")
}
],
"createdAt" : ISODate("2020-01-02T10:48:34.288+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.288+03:00")
}
],
"createdAt" : ISODate("2020-01-02T10:48:34.289+03:00"),
"updatedAt" : ISODate("2020-01-02T10:48:34.289+03:00"),
"__v" : 0
}
To update the device patientFirstName with verificationCode 123456, we need to send a PUT request to the url http://..../account/5e0da052b4b3fe5188602e11/5e0da052b4b3fe5188602e12/5e0da052b4b3fe5188602e13
5e0da052b4b3fe5188602e11 is accountId.
5e0da052b4b3fe5188602e12 is buildingId.
5e0da052b4b3fe5188602e13 is gatewayId.
Request body:
{
"verificationCode": "123456",
"patientFirstName": "UPDATED!!!"
}
Result will be like this:
{
"apiCallCount": 1,
"_id": "5e0da052b4b3fe5188602e11",
"email": "abc#def.net",
"password": "123123",
"userName": "username",
"companyName": "companyName",
"apiKey": "apiKey",
"solutionType": "solutionType",
"parentCompany": "parentCompany",
"buildings": [
{
"gateways": [
{
"devices": [
{
"_id": "5e0da052b4b3fe5188602e15",
"serialNumber": "serialNumber 1",
"area": "area",
"connectionStatus": 0,
"gatewayKey": "gatewayKey",
"applicationNumber": 11,
"firmwareVersion": "firmwareVersion",
"needsAttention": true,
"verificationCode": "123456",
"patientRiskStatus": "patientRiskStatus",
"patientFirstName": "UPDATED!!!",
"patientLastName": "patientLastName",
"createdAt": "2020-01-02T07:48:34.287Z",
"updatedAt": "2020-01-02T07:48:34.287Z"
},
{
"_id": "5e0da052b4b3fe5188602e14",
"serialNumber": "serialNumber 2",
"area": "area",
"connectionStatus": 0,
"gatewayKey": "gatewayKey",
"applicationNumber": 22,
"firmwareVersion": "firmwareVersion",
"needsAttention": true,
"verificationCode": "987654",
"patientRiskStatus": "patientRiskStatus",
"patientFirstName": "patientFirstName",
"patientLastName": "patientLastName",
"createdAt": "2020-01-02T07:48:34.288Z",
"updatedAt": "2020-01-02T07:48:34.288Z"
}
],
"_id": "5e0da052b4b3fe5188602e13",
"gatewayName": "gatewayName 1",
"gatewayKey": "gatewayKey",
"suite": "suite",
"createdAt": "2020-01-02T07:48:34.288Z",
"updatedAt": "2020-01-02T07:48:34.288Z"
}
],
"_id": "5e0da052b4b3fe5188602e12",
"buildingName": "buildingName 1",
"address": "address",
"suite": "suite",
"floor": "floor",
"timeZone": "String",
"createdAt": "2020-01-02T07:48:34.288Z",
"updatedAt": "2020-01-02T07:48:34.288Z"
}
],
"createdAt": "2020-01-02T07:48:34.289Z",
"updatedAt": "2020-01-02T09:10:25.200Z",
"__v": 0
}
And if you always want to update in the first building's in the first gateway, you may use this:
router.put("/account/:accountId", async (req, res) => {
const { patientFirstName, verificationCode } = req.body;
try {
let result = await Account.findByIdAndUpdate(
req.params.accountId,
{
$set: {
"buildings.0.gateways.0.devices.$[device].patientFirstName": patientFirstName
}
},
{
arrayFilters: [{ "device.verificationCode": verificationCode }],
new: true
}
);
if (!result) return res.status(404);
res.send(result);
} catch (err) {
console.log(err);
res.status(500).send("Something went wrong");
}
});
Now you need to send only the accountId in the url like this: http://../account/5e0da052b4b3fe5188602e11

How to return only some columns of a relations with Typeorm

Ok, i'm having trouble with getting the relations with typeorm, when i run the service it returns me all the data from the relation, and i want only specific fields, like id and name.
Here's my code:
async findById(id: string): Promise<UsersUseOfferHistoric> {
return await this.repository.findOne({
where: { id },
relations: ['userId', 'offerId'],
});
}
Here's the json Output:
{
"id": "da0fd04e-17c6-4412-b342-a4361d191468",
"createdAt": "2020-01-07T19:48:30.840Z",
"userId": {
"id": "bdc00227-569f-44b5-9bdd-c8de03661ebd",
"name": "Alexandre Vieira",
"cpf": "10443771430",
"email": "av.souza2018#gmail.com",
"password": "asjdsifjdsfasf",
"imagePath": "/me.png",
"active": true,
"lastLogin": "2020-01-07T19:40:26.850Z",
"createdAt": "2020-01-07T19:40:26.850Z",
"updatedAt": "2020-01-07T19:40:26.850Z"
},
"offerId": {
"id": "e399560c-d2c2-4f4e-b2b1-94cae3af3779",
"offerDrescription": "Nova oferta top",
"discountCoupon": " Desconto top",
"discountValidity": "2020-01-07T14:18:19.803Z",
"discountPercentage": 20,
"discountQuantityLimit": 50,
"createdAt": "2020-01-07T19:45:33.589Z",
"updatedAt": "2020-01-07T19:45:33.589Z"
}
}
Here's the output i want:
{
"id": "da0fd04e-17c6-4412-b342-a4361d191468",
"createdAt": "2020-01-07T19:48:30.840Z",
"userId": {
"id": "bdc00227-569f-44b5-9bdd-c8de03661ebd",
"name": "Alexandre Vieira",
},
"offerId": {
"id": "e399560c-d2c2-4f4e-b2b1-94cae3af3779",
"offerDrescription": "Nova oferta top",
}
}
The findOne function accepts an select: ['id', 'createdAt'] property where you can filter the fields of the outgoing relation. To explicitly select the returned fields of a joined table (using the relations property does implicitly a left join) you have to use a query builder.
await getRepository(Foo).createQueryBuilder('foo')
.where({ id: 1})
.select(['foo.id', 'foo.createdAt', 'bar.id', 'bar.name'])
.leftJoin('foo.bars', 'bar') // bar is the joined table
.getMany();
Try something like that:
...findOne({
select: {
id: true,
createdAt: true,
userId: {
id: true,
name: true,
},
offerId: {
id: true,
offerDrescription: true,
},
},
...
where: {...},
})
You can do it like this if you rather use the repository API instead of the queryBuilder
return await this.repository.findOne({
where: { id },
select: {
userId: {
id: true,
name: true
},
offerId: {
id: true,
offerDrescription: true
}
},
relations: {
userId: true,
offerId: true,
}
});

How to multiply NumberDecimal values in mongodb

I have the following structure:
{
"_id": "5d0118f0f57a282f89bc5f71",
"product": {
"_id": "5cfed37375a13067dd01ddb7",
"name": "My product",
"description": "My description",
"purchased_amount": 15,
"unit_price_mex": "45",
"unit_price_to_sell": "5",
"travel": "5cf58713d6f7f1657e2d8302",
"__v": 0,
"id": "5cfed37375a13067dd01ddb7"
},
"client": {
"_id": "5cf1778efffb651fad89d8b6",
"name": "Client name",
"description": "",
"__v": 0
},
"purchased_amount": 3,
"fch": "13/6/2019",
"__v": 0
},
{
"_id": "5d0151afda1a446008f1817b",
"product": {
"_id": "5cfed1995eaf2665c45efd82",
"name": "Camisa",
"description": "Camisas buenas",
"purchased_amount": 10,
"unit_price_mex": "100",
"unit_price_to_sell": "15",
"travel": "5cf56b04462a865264fabb9d",
"__v": 0,
"id": "5cfed1995eaf2665c45efd82"
},
"client": {
"_id": "5cf1778efffb651fad89d8b6",
"name": "Randy",
"description": "El que trabaja aqui",
"__v": 0
},
"purchased_amount": 34,
"fch": "12/6/2019",
"__v": 0
},
Where client and product are of type ObjectId. This is the Schema:
Client Model
var mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
var clientSchema = new mongoose.Schema({
name: String,
description: String
}).plugin(mongoosePaginate);
var Client = mongoose.model('Client', clientSchema);
module.exports = Client;
Product Model
var mongoose = require('mongoose');
const mongoosePaginate = require('mongoose-paginate-v2');
var productSchema = new mongoose.Schema({
name: String,
description: String,
purchased_amount: Number,
unit_price_mex: mongoose.Schema.Types.Decimal128,
unit_price_to_sell: mongoose.Schema.Types.Decimal128,
travel: { type: mongoose.Schema.Types.ObjectId, ref: 'Travel' }
}).plugin(mongoosePaginate);
productSchema.set('toJSON', {
getters: true,
transform: (doc, ret) => {
if (ret.unit_price_mex) {
ret.unit_price_mex = ret.unit_price_mex.toString();
}
if ( ret.unit_price_to_sell ) {
ret.unit_price_to_sell = ret.unit_price_to_sell.toString();
}
}
})
var Product = mongoose.model('Product', productSchema);
module.exports = Product;
I need to get the multiplication sum of purchased_amount by product.unit_price_to_sell. My code is the following but always returns 0. Apparently, "$product.unit_price_to_sell" does not return the decimal value.
var aggregate = InvoiceModel.aggregate([
{ $match: { client: mongoose.Types.ObjectId( id ) } },
{ $group: { _id: null, total: { $sum: { $multiply: [ "$purchased_amount", "$product.unit_price_to_sell" ] } } } }
]);
InvoiceModel.aggregatePaginate(aggregate, {}, (error, aggs) => {
InvoiceModel.paginate({ client: id },{ page, limit, populate: 'client product' }, (err, value) => {
return res.status(200).send({
results: value.docs,
totalPages: value.totalPages,
totalDocs: value.totalDocs,
purchase_amount_total : aggs.docs[0].total
})
})
})
MongoDB cannot use string values in arithmetic expressions. You must either store the values using their numeric non-string representations, or you must use an aggregation operator like $toDecimal to convert the values to their numeric representations first.
Modifying your $group stage to something like the following should work:
{ $group: { _id: null, total: { $sum: { $multiply: [ "$purchased_amount", { $toDecimal: "$product.unit_price_to_sell" } ] } } }
Please note, however, that this will only work for MongoDB versions >= 4.0. If you're using an older version of MongoDB, you will either need to upgrade it to at least version 4.0 or begin converting your existing values from strings to numbers.