How to build a JSON object in select query in Objection.js - objection.js

I have a "location" jsonb type column in table "Ads". It stores values like these:
{"lat": 33.742001,
"lng": -117.823639,
"zip": "92780",
"city": "Tustin",
"state": "CA"}
How would I write a select query in Objection.js that returns the same location object with only the "city" property in it. I need something like:
const ads = AdModel.query().select([
...
? // <- need the result to be {location: {city: "Tustin"}}
])
Basically, I need to build {location: city: ...} object and fill in the city name.

Add the $parseDatabaseJson to parse the location field
class Ads extends Model {
static get tableName() {
return 'Ads';
}
$parseDatabaseJson(json) {
json = super.$parseDatabaseJson(json);
let location = json.location;
if(location){
location = JSON.parse(location)
}
return Object.assign({}, json,{ location });
}
}
add the map for selecting specific field
Ads.query()
.select('location')
.map((data)=>data.location.city)
.then((city)=>console.log(city));

I was actually able to build the JSON object I need as such inside .select([...]) method:
raw("json_build_object('city', ??.location->'city') as location", ['Ads'])

Related

Complete child field with parent field on mongodb

enter image description hereI have this problem that I need to complete a child field with a value that already exists on parent field, like this:
{
"title":"learning mongo",
"description":"how to add child field",
"createdAt":"2020-04-25 09:19:28.285Z"
"user":{
"name":"John",
"email":"john#gmail.com"
}
}
And I'm writing a script that must use the createdAt value inside user, as below:
{
"title":"learning mongo",
"description":"how to add child field",
"createdAt":"2020-04-25 09:19:28.285Z"
"user":{
"name":"John",
"email":"john#gmail.com",
"joinedAt":"2020-04-25 09:19:28.285Z"
}
}
I'm using kotlin and mongock-spring-v5 and I wrote this code to do this migration:
#ChangeSet(id = "addUserJoinedAtField", author = "Paulo", systemVersion = "2.7.0", order = "047")
fun addUserJoinedAtField(mongoTemplate: MongockTemplate) {
val update = Update().set("users.$[user].joinedAt", "createdAt").filterArray(Criteria.where("user.joinedAt").exists(false));
mongoTemplate.updateMulti(Query(), update, PROJECT_COLLECTION)
}
But when I do this, the result is the field "joinedAt":"createdAt" added to the user. How do I pick up the createdAt value instead?
my attempts and the following results

Strapi : populate result from a relation collection

I'm using strapi 3.4.4 with mongodb.
I've got a location collection with a reservation linked collection (a location can have many reservations).
Location Model
text field : title of the location
Reservations Model
linked collection : has one author
My goal is to populate the author name when I made a get request to /location
But right now, my request is :
{
"reservations": [
{
"_id": "60be41626a15ab675c91f417",
"author": "60be3d40ea289028ccbd4d5a",
"id": "60be41626a15ab675c91f417"
}
],
"_id": "60be40a26a15ab675c91f414",
"title": "New York Plaza",
},
As you can see, the author is an id, so I try to edit location/controllers/location.js to
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
/**
* Retrieve records.
*
* #return {Array}
*/
async find(ctx) {
let entities;
if (ctx.query._q) {
entities = await strapi.services.location.search(ctx.query);
} else {
entities = await strapi.services.location.find(ctx.query, ['reservations', 'reservations.author']);
}
return entities.map(entity => sanitizeEntity(entity, { model: strapi.models.location }));
},
};
I followed this guide : https://www.youtube.com/watch?v=VBNjCgUokLk&list=PL7Q0DQYATmvhlHxHqfKHsr-zFls2mIVTi&index=27
After further research, it seems that the syntax ['reservations', 'reservations.author'] doesn't work to populate with mongodb.
await strapi.services.post.find(ctx.query, {path:'reservations', populate:{path:'author'}});
works
You need to provide the API key you're using "Full access" for what ever reason, "Read only" won't return relations.
On top of that you will still need to use the query parameter populate.
http://localhost:1337/api/locations?populate=*

how can I set the value of objectId to another property different that _id when creating a document?

I'm trying to create an object that looks like this:
const userSettingsSchema = extendSchema(HistorySchema, {
user: //ObjectId here,
key:{type: String},
value:{type: String}
});
this is the post method declared in the router
app.post(
"/api/user/settings/:key",
userSettingsController.create
);
and this is the method "create":
async create(request, response) {
try {
const param = request.params.key;
const body = request.body;
console.log('body', body)
switch (param) {
case 'theme':
var userSettings = await UserSettings.create(body) // user:objecId -> missing...
response.status(201).send(userSettings);
break;
}
} catch (e) {
return response.status(400).send({ msg: e.message });
}
}
I don't know how to assign the value of ObjectId to the user property, because ObjectId is generate when the doc is created, thus, I can not do this: userSettings.user = userSettings._id, because the objectr is already. I only manage to get something like this created:
{
"_id": "60c77565f1ac494e445cccfe",
"key": "theme",
"value": "dark",
}
But it should be:
{
"user": "60c77565f1ac494e445cccfe",
"key": "theme",
"value": "dark",
}
_id is the only mandatory property of a document. It is unique identifier of the document and you cannot remove it.
If you provide document without _id the driver will generate one.
You can generate ObjectId as
let id = mongoose.Types.ObjectId();
and assign it to as many properties as you want.
You can even generate multiple different ObjectIds for different properties of the same document.
Now, you don't really need to assign ObjectId to "user" property. _id will do just fine. In fact it is most likely you don't need user's settings in separate collection and especially as multiple documents with single key-value pair.
You should be good by embedding "settings" property to your "user" collection as a key-value json.

Comparing two objects in Joi validation (eg. to avoid duplicates)

I'm using Joi to validate a complex form entry. The form asks for two addresses, mainContactAddress and seniorContactAddress. I want to validate them to ensure they aren't the same address.
Each address is an object like this:
{
"line1": "123 Some Street",
"line2": "Some Town",
"county": "Some County",
"postcode": "123 ABC",
"townCity": "City"
}
I initially tried this:
Joi.ukAddress().invalid(Joi.ref('seniorContactAddress'))
(ukAddress() is a custom extension I've created which specifies each of the above fields as a required string.)
This doesn't work, because the equality === comparison between the two objects returns false even when they have the same string values.
I can't see a Joi method to do this. I was hoping to be able to serialise the object (eg. something like Object.values(mainContactAddress).join(',') and then compare the resulting strings) but Joi.ref() only gives, well, a reference to the object, so I can't call functions against it directly.
Any thoughts on how I could achieve this validation/comparison?
I ended up writing a custom rule for my extension:
{
// Enforce a unique address compared to the senior contact
name: 'mainContact',
validate(params, value, state, options) {
// Format addresses into a comparable string,
// making sure we sort them as the stored version
// is in a different order to the form-submitted one.
const serialize = address =>
Object.values(address)
.sort()
.join(',');
const seniorContactAddress = get(
state.parent,
'seniorContactAddress',
[]
);
if (serialize(seniorContactAddress) === serialize(value)) {
return this.createError(
'address.matchesSenior',
{ v: value },
state,
options
);
} else {
return value;
}
}
}
This does feel like an anti-pattern (eg. abusing state to look at other values in the Joi object) but it does what I needed.

How to update a object in mongodb via mongoose?

I have mongoose schema as:
var Organization = new Schema({
name: String,
address: {
street : String,
city: String
}
}, { collection: 'organization' });
How do I update only street part of address for an organization via mongoose?
I can't find any docs that cover this simple case so I can see why you're having trouble. But it's as simple as using a $set with a key that uses dot notation to reference the embedded field:
OrganizationModel.update(
{name: 'Koka'},
{$set: {'address.street': 'new street name'}},
callback);
Now you can update directly .
OrganizationModel.update(
{name: 'Koka'},
{'address.street': 'new street name'},
callback);
Using Document set also, specified properties can be updated. With this approach, we can use "save" which validates the data also.
doc.set({
path : value
, path2 : {
path : value
}
}
Example: Update Product schema using Document set and save.
// Update the product
let productToUpdate = await Product.findById(req.params.id);
if (!productToUpdate) {
throw new NotFoundError();
}
productToUpdate.set({title:"New Title"});
await productToUpdate.save();
Note - This can be used to update the multiple and nested properties also.