bookshelf.js recursive query plus ordering - recursion

I've found one example of recursive tree fetch but ordering does not work. What is the best way to fetch all items recursively with ordering at each level?
Model
knex.schema.createTable('items', function (table) {
table.increments();
table.string('title');
table.integer('parent');
table.integer('order');
});
var Item = bookshelf.Model.extend({
tableName: 'items',
items: function() {
return this.hasMany(Item, 'parent');
}
});
Recursive but only to certain amount of level, all would be preferred instead of specifying depth. This works but doesn't order related items correctly. Reference
Item.query({where: {parent: null}, orderBy: 'id'})
.fetchAll(
{
withRelated: ['items.items.items.items.items'],
orderBy: 'id'
});
Edit
I'm updating this with a recursive query that works for SQLite (note I'm using id for ordering because my bad choice of column name). Solution maybe to use bookshelf for crud operations and a raw query to populate objects that I need.
with recursive tc( i )
as ( select id FROM items WHERE parent IS NULL
UNION SELECT id FROM items, tc WHERE items.parent = tc.i ORDER BY id
)
select * FROM items WHERE id IN tc;

Related

Meteor: how to find record's position in the collection

I'm working on a cursor-based pagination and I need to find record's position in the collection by its _id. Is it possible to do so, using mongo query only (or several queries), and not fetching all the records and counting because that is inefficient in case a collection contains millions of records as well as using createdBy field, because few records can be created at the same time.
The _ids are strings so I cannot just do something like
n = myCollection.find({ _id: { $lte : 12345}}).count() ;
By default, the Meteor .find() query sorts records in their natural order. This is not based on _id, but rather the position of the records on disk.
Instead, you need to explicitly define the sort parameter if you want records to be sorted by _id. Thus your query should look something like this:
// pageNo = the page number
// pageSize = number of records per page
records = myCollection.find({}, {
sort: { '_id' : 1 }, // Sort by _id in ascending order
limit: pageSize // Only return pageSize elements
skip: pageSize*pageNo // Skip the elements fetched on previous pages.
});

About normalize in redux real-world example

In the example's src(UserPage.js):
const mapStateToProps = (state, ownProps) => {
// We need to lower case the login due to the way GitHub's API behaves.
// Have a look at ../middleware/api.js for more details.
const login = ownProps.params.login.toLowerCase()
const {
pagination: { starredByUser },
entities: { users, repos }
} = state
const starredPagination = starredByUser[login] || { ids: [] }
const starredRepos = starredPagination.ids.map(id => repos[id])
const starredRepoOwners = starredRepos.map(repo => users[repo.owner])
return {
login,
starredRepos,
starredRepoOwners,
starredPagination,
user: users[login]
}
}
I notice that there is many templates like xxx.ids.map(id => someEntities[id]),I am not sure why use this pattern to work.IMO,I would use something like import { map } from 'lodash'; someList && map(someList, item => {...}) in the container component and just pass the entities in the mapStateToProps.
So,could someone explains it's purpose?Thanks.
The standard suggestion for normalizing data in Redux is to store data items in an object, with IDs as the keys and the items as the values. However, an object doesn't have an inherent order to it. (Technically, the order of iteration for object keys should be consistent, but it's a bad practice to rely on that as the sole means of ordering.)
Because of that, it's also standard to store an array of just the IDs as well. A typical example might look like:
{
byId : {
qwerty : { },
abcd : { },
aj42913 : { }
},
items : ["qwerty", "aj42913", "abcd"],
sorted : ["abcd", "aj42913", "qwerty"],
selected : ["qwerty", "abcd"]
}
In this example, items contains all item IDs, probably in the order they were insert. sorted contains the IDs in some sort of sorted order, while selected contains a subset of the IDs.
This allows the items themselves to only be stored once, while multiple representations of those items can be saved using various arrays of IDs.
From there, you can pull together a list of the actual items by mapping over whatever array of IDs you care about, and retrieving the items by their IDs.
So, ultimately the answer is that relying just on the keys of the byId object doesn't give you any kind of ordering, and doesn't allow defining subsets of the data.

Joining a table through another join table using withRelated

I have 3 tables:
Order: id
Item: id
OrderItems: order_id, item_id
I want to be able to fetch a particular Order and get all the Items related to that Order, without the clutter introduced by doing Order.where(...).fetch({withRelated: ['orderItem.item']).
Ideally I'd like to do Order.where(...).fetch({withRelated: ['items']) and the items relationship knows to go through the OrderItems table to get the information.
I have tried the relationship
items() {
return this.hasMany('Item').through('OrderItem')
}
but that doesn't seem to work as I'd expected.
Is this possible using Bookshelf's API without writing a manual join?
The relationship you tried:
items() {
return this.hasMany('Item').through('OrderItem')
}
Means to Bookshelf:
Item(id, OrderItem_id) -> OrderItem(id, Order_id) -> Order(id)
Or a 1:n:m relationship.
But your database says:
Item(id) <- OrderItem(Item_id, Order_id) -> Order(id)
This kind of relationship on Bookshelf maps better as belongsToMany():
bookshelf.plugin('registry') // to ease the cyclic mapping
const Item = bookshelf.Model.extend({
tableName: 'item',
orders: function() {
this.belongsToMany('Order', 'OrderItem')
},
})
bookshelf.model('Item', Item)
const Order = bookshelf.Model.extend({
tableName: 'order',
items: function() {
//return this.hasMany('Item').through('OrderItem')
return this.belongsToMany('Item', 'OrderItem')
},
})
bookshelf.model('Order', Order)
Note that until you start placing useful data on the intermediate table you don't need to create a model for it.
With that a Order.where(...).fetch({withRelated: 'items'}).then(...) query should work as expected.

Meteor: how to extract id of first item from a collection

I have a Meteor collection and want to get the id of the first item. The query has a limit 1 clause and I just want the id of the first item if there is one. I can't figure out how to code it.
Here is my query.
var myGames = Games.find(
{
game_minutes: {$gt: MinutesSinceMidnightNow},
court_id: court,
game_date: {$gt: lastMidnight}
},
{
sort: { "game_minutes": 1},
limit: 1
});
Then, if I iterate over the results, my app goes off to the races.
myGames.forEach(function (game) {
gameId = game._id;
console.log('gameId: ' + gameId );
});
I know the query is working and currently returning a record but I can't figure out how to access the record.
find() returns a cursor. Instead of using forEach use fetch which will fetch all data from the cursor to an array. So in your case:
var id = myGames.fetch()[0]._id;
(when you are certain that the data is there. otherwise check if array is not empty)

How do I perform database queries in parallel in Meteor?

Collection:
docs:
_id
name
Code:
names = Docs.findOne(id).name for id in doc_ids
But I would like the findOne queries to be sent off in parallel.
A possibly easier alternative is to use $in and do the parallel query on the database itself. Here's an example in CoffeeScript:
ids = ['abc123', 'def456', 'hij789']
names = (doc.name for doc in Docs.find({_id: $in: ids}, {fields: name: 1}).fetch())
You can restrict your query to a certain subsets of fields, e.g.
names = _.pluck(Docs.find({_id:{$in:doc_ids}}, {
fields: {name:1}
}).fetch(), 'name');

Resources