Bookshelf- how to order by relation - bookshelf.js

` new RevisionModel()
.query('where', 'bbid', '=', req.params.bbid)
.fetchPage({
limit: size,
offset: from,
withRelated: ['revision']
});`
I want to sort the results according to revision.created_at. How do I achieve it?
This is how my RevisionModel is created.
`bookshelf.Model.extend({
///
revision() {
return this.belongsTo('Revision', 'id');
},
///
});`

Related

What is the way to update or insert a record on Firebase with Flutter?

I have a collection users like this:
[{
'uid' : '1',
'favourites' : [
{ // fav1 },
{ // fav2 },
{ // fav3 },
etc
]
},
{
'uid' : '2',
'favourites' : [
{ // fav1 },
{ // fav2 },
{ // fav3 },
etc
]
},
etc
]
In some situations I have to update the favourites collection with a new "fav" and I can do that in this way:
final doc = FirebaseFirestore.instance.collection('users').doc(userId);
doc.update({ 'favourites': FieldValue.arrayUnion([fav.toJson()]) });
however the item might be not there so I have to use doc.set to create a new item. As I am new with Firebase, what is a "best practice" for a problem like this (if the element is not there create it first, otherwise update it)?
You can specify a merge option to set, which does precisely what you want:
doc.set({ 'favourites': FieldValue.arrayUnion([fav.toJson()]) }, SetOptions(merge : true))
You can use a function that can check if there is a doc or not with that specific info. And you can create a if-else statement depends on if there is a doc named like that or not.
An example function for checking the doc:
Future<bool> checkIfDocExists(String stuffID) async {
try {
/// Check If Document Exists
// Get reference to Firestore collection
var collectionRef = FirebaseFirestore.instance
.collection('favorites');
var doc = await collectionRef.doc(userId).get();
return doc.exists;
} catch (e) {
throw e;
}
}

why map with condition return always value

I'm using rxjs map to retrive data in firestore like this:
getArtists(): Observable<DocumentData> {
const users$ = this.firestore.collection('/Users').get()
users$.subscribe((users) => {
users.docs.map(user => user.data().artistName !== "" && user.data().role === 'ARTIST')
});
return users$;
}
but when i'm getting value like this :
this.userService.getArtists().subscribe(
(userDocs) => {
userDocs.docs.map((user) => {
this.artists.push(user.data());
console.log(this.artists)
this.record = this.artists.length;
})
});
it's return always the user when the artistName is equals to "" and role is not equals to 'ARTIST'.
why ?
thank's everybody!
you need to map data in a map operator instead of a subscription and return a value in as a pipe.
Unfortunately, in your code isn't clear what and when you want to filter, why a user is in users.docs when it tend to be a doc.
Please check an example below and consider updating your question with more info.
import {filter, map} from 'rxjs/opreators';
getArtists(): Observable<DocumentData> {
return this.firestore.collection('/Users').get().pipe( // <- use pipe
map(users => {
// here some changes in users if we need.
return users;
}),
),
filter(users => {
returns true; // or false if we don't want to emit this value.
}),
}

How do I combine multiple http requests and merge results

I need to handle a situation where I have 3 endpoints to call and would like to get the data in the most convenient/efficient way. The first call can be handled independently and returns a single result. The second endpoint returns a collection but will need to initiate 0-* subsequent calls, where a given key is present.
Ideally would like to receive the collection (from the 2nd endpoint call) as a mutated/new collection that includes the result from the 3rd endpoint call.
I am currently using forkJoin(observableA$, observableB$) to handle the first 2 calls in parallel but I cannot work out how to include the sequential calls and have the data included in observableB$
//Customer observable
const customer$ = this._customerManagementService.getCustomer(
accountNumber
);
return forkJoin({
customer: customer$,
saleCycles: saleCyclesWithVehicle$
}).pipe(finalize(() => this._loaderFactoryService.hide()));
getSalesWithVehicle(accountNumber: string, dealerKey: string) {
return this._salesCycleService
.getCyclesForCustomer({
customerNumber: accountNumber,
dealerKey: dealerKey
})
.pipe(
concatMap((results: ISaleCycle[]) => {
return results.map(cycle => {
return this._purchaseVehicleService.getPurchaseVehicle(
cycle.vehicleKey
);
});
})
);
}
I expect the collection to include further data as a new property on the original collection
UPDATE
After a bit more thought maybe I should be using reduce somewhere in the solution. This way I can be in control of what's getting push into the array and it could be dynamic?
getSalesWithVehicle(accountNumber: string, dealerKey: string) {
return this._salesCycleService
.getCyclesForCustomer({
customerNumber: accountNumber,
dealerKey: dealerKey
})
.pipe(
switchMap((results: ISaleCycle[]) => {
return results.map(cycle => {
if (cycle.vehicleKey) {
return this._purchaseVehicleService
.getPurchaseVehicle(cycle.vehicleKey)
.pipe(
reduce((acc, vehicle) => {
return { cycle: cycle, vehicle: vehicle };
}, []),
toArray()
);
}
else {
///No extra data to be had
}
});
}),
concatAll()
);
}
I would use concatMap() to merge the responses of HTTP requests 2 and 3.
import { of } from 'rxjs';
import { map, concatMap } from 'rxjs/operators';
const pretendGetCustomer = of({accountNumber: 123, name:"John Doe"});
const pretendGetVehiculeHttpRequest = (customerNumber) => {
return of([{custNum: 123, vehicleId:"2"}, {custNum: 123, vehicleId:"1"}]);
}
const pretendGetCyclesHttpRequest = (cycleIds) => {
return of([{id:"1", name:"yellow bike", retailPrice:"$10"}, {id:"2", name:"red bike", retailPrice:"$20"}]);
}
const yourFunction = () => {
pretendGetCustomer.subscribe(customer => {
// Assuming you do other things here with cust, reason why we are subscribing to this separately
// isHappy(customer)
// Your second & third calls
pretendGetVehiculeHttpRequest(customer.accountNumber).pipe(
// Need to use concatMap() to subscribe to new stream
// Note: use mergeMap() if you don't need the 1st stream to be completed
// before calling the rest
concatMap(purchases => {
const cyclesIds = purchases.map(p => p.vehicleId);
// concatMap() requires an Observable in return
return pretendGetCyclesHttpRequest(cyclesIds).pipe(
// Use map() here because we just need to use the data,
// don't need to subscribe to another stream
map(cycles=>{
// Retrun whatever object you need in your subscription
return {
customerNumber: customer.accountNumber,
customerName: customer.name,
purchases: purchases.map(p => cycles.find(c => p.vehicleId === c.id))
}
})
);
})
).subscribe(resultof2and3 => {
// Do something with the new/mutated Object which is a result of
// your HTTP calls #2 and #3
console.log(resultof2and3);
});
});
}
yourFunction();
I made a stackblitz if you want to see the above run (see console): https://stackblitz.com/edit/rxjs-nqi7f1
This is the solution I eventually came up with. I've taken the advice from BoDeX and used concatMap(). In my mind it was clear that I wanted to use forkJoin and be able to reference the results by object key, I.e customer or saleCycles.
In the scenario where a vehicleKey was present I needed to return the results in a defined data structure, using map(). Likewise, if no vehicle was found then I just needed the outer observable.
const customer$ = this._customerManagementService.getCustomer(accountNumber);
const saleCyclesWithVehicle$ = this.getSalesWithVehicle(accountNumber,dealerKey);
getSalesWithVehicle(accountNumber: string, dealerKey: string) {
return this._salesCycleService
.getCyclesForCustomer({
customerNumber: accountNumber,
dealerKey: dealerKey
})
.pipe(
concatMap(cycles => {
return from(cycles).pipe(
concatMap((cycle: ISaleCycle) => {
if (cycle.vehicleKey) {
return this._purchaseVehicleService
.getPurchaseVehicle(cycle.vehicleKey)
.pipe(
map(vehicle => {
return { cycle: cycle, vehicle: vehicle };
})
);
} else {
return of({ cycle: cycle });
}
}),
toArray()
);
})
);
}
return forkJoin({
customer: customer$,
saleCycles: saleCyclesWithVehicle$
}).pipe(finalize(() => this._loaderFactoryService.hide()));

Meteor collections: upsert w/ array

Forms of this question have been asked a few times, but I've been unable to find a solution:
I have a schema like this (simplified):
StatusObject = new SimpleSchema({
statusArray: [statusSchema]
});
where statusSchema is
{
topicId:{
type: String,
optional: true
},
someInfo:{
type: Number,
optional: true,
decimal: true
},
otherInfo:{
type: Number,
optional: true
}
}
I am trying to upsert - with the following meteor method code:
var upsertResult = BasicInfo.update({
userId: this.userId,
statusArray: {
$elemMatch: { topicId : newStatus.topicId }
}
}, {
$set: {
"statusArray.$.topicId": newStatus.topicId,
"statusArray.$.someInfo": newStatus.someInfo,
"statusArray.$.otherInfo": newStatus.otherInfo
}
}, {
multi: true,
upsert: true
});
But I keep getting an error: statusArray must be an array
I thought by adding the $, I was making sure it is recognized as an array? What am I missing?
It seems (after your clarification comments), that you want to find a document with particular userId and modify its statusArray array using one of these scenarios:
Update existing object with particular topicId value;
Add a new object if the array doens't have one with particular topicId value.
Unfortunately, you can't make it work using just one DB query, so it should be like this:
// try to update record
const updateResult = BasicInfo.update({
userId: this.userId,
'statusArray.topicId': newStatus.topicId
}, {
$set: {
"statusArray.$": newStatus
}
});
if (!updateResult) {
// insert new record to array or create new document
BasicInfo.update({
userId: this.userId
}, {
$push: {
statusArray: newStatus
},
$setOnInsert: {
// other needed fields
}
}, {
upsert: true
});
}
Your code is treating StatusArray as an object,
Before you do the upsert, build the status array first, assuming that your current value is currentRecord
newStatusArray = currentRecord.statusArray
newStatusArray.push({
topicId: newStatus.topicId,
someInfo : newStatus.someInfo,
otherInfo: newStatus.otherInfo
})
and in the upsert, simply refer to it like this
$set: { statusArray: newStatusArray}

dgrid JsonRest store not working

I have the following:
require([
"dojo/dom",
"dojo/on",
"dojo/store/Observable",
"dojo/store/JsonRest",
"dojo/store/Memory",
"dgrid/OnDemandGrid"
], function (dom, on, Observable, JsonRest, Memory, OnDemandGrid) {
var store = new JsonRest({
target: 'client/list',
idProperty: 'id'
});
var grid = new OnDemandGrid({
columns: {
"id": "ID",
"number": "Name",
"description": "Description"
},
sort: "lastName",
store: store
}, "grid");
});
client/list is a rest url returning a json object {data:[...]}, but the content of the list never shows up :/
I think the problem is caused by the async data loading, because with a json hard coded object the content show up
EDIT :
I've succeeded in achieving this by using a dojo/request, but the JsonRest shouldn't normally act the same way ? Can someone point me to the right direction ?
require([
'dojo/dom',
'dojo/on',
'dojo/store/Memory',
'dojo/request',
'dgrid/OnDemandGrid'
], function (dom, on, Memory, request, OnDemandGrid) {
request('client/list', {
handleAs: 'json'
}).then(function (response) {
// Once the response is received, build an in-memory store with the data
var store = new Memory({ data: response });
// Create an instance of OnDemandGrid referencing the store
var grid = new OnDemandGrid({
store: store,
sort: 'id', // Initialize sort on id, ascending
columns: {
'id': 'ID',
'number': 'Name',
'description': 'Description'
}
}, 'grid');
console.log(store);
on(dom.byId('queryForm'), 'input', function (event) {
event.preventDefault();
grid.set('query', {
// Pass a RegExp to Memory's SimpleQueryEngine
// Note: this code does not go out of its way to escape
// characters that have special meaning in RegExps
description: new RegExp(this.elements.last.value, 'i')
});
});
on(dom.byId('queryForm'), 'reset', function () {
// Reset the query when the form is reset
grid.set('query', {});
});
});
});
Ok problem found :/
My "client/list" url was returning a json object like this:
{data: [{id:"1", label: "test"}, {id:"2", label: "test"}]}
Turns out that the JsonRest object is already encapsulating data in a data node, so by returning a json like this:
{[{id:"1", label: "test"}, {id:"2", label: "test"}]}
everything worked fine :)

Resources