Get count result with knex.js / bookshelf.js - bookshelf.js

I'm trying to perform a simple count with knex (since it seems to not be supported by bookshelf yet). The following code is working:
bookshelf.knex('hosts').count('id').then(function(total) {
res.send({
meta: {
total: total[0]['count(`id`)']
}
});
});
It just seems odd to me that I have to do total[0]['count('id')'] to get the actual result. Am I doing things right here?
Thanks!

All the results from knex.js are arrays. A query could be successful and simply return 0 results.
Also, you can alias the column directly in the column name (or count() call). Like this:
bookshelf.knex('hosts').count('id as CNT').then(function(total) {
res.send({
meta: {
total: total[0].CNT
}
});
});
Still need to get the first element, but you can reference the column as a normal JSON property.

While knex does return results as arrays, it also has a method for returning the first result, which will be an object--not an array. It's pretty simple to get straight to the count without having to rely on [0] or anything to access your count within an array. For your example, a cleaner solution could be:
bookshelf
.knex("hosts")
.count("id")
.first()
.then(function(total) {
res.send({
meta: {
total: total.count
}
});
});

code for node js
let result = await knex.count("id").from('events').first();
if (result) {
console.log(result.count);
}

this seems to work correctly and is a bit simpler
knex('Quotes').count('quoteBody')
.then((res)=>{
//console.log("rows "+JSON.stringify(res))
console.log("rowspl2 "+res[0]['count(`quoteBody`)'])
})
.catch((err)=>{
console.log("err "+err)
})
or try it like this
knex('Quotes').count('quoteBody', {as: 'rows'})
.then((res)=>{
// console.log("rows "+JSON.stringify(res))
console.log("rowsp "+res[0]['rows'])
})
.catch((err)=>{
console.log("err "+err)
})

Related

How do I return an entire paged set from the Jira API using Ramda?

I'm using the Nodejs library for talking to Jira called jira-connector. I can get all of the boards on my jira instance by calling
jira.board.getAllBoards({ type: "scrum"})
.then(boards => { ...not important stuff... }
the return set looks something like the following:
{
maxResults: 50,
startAt: 0,
isLast: false,
values:
[ { id: ... } ]
}
then while isLast === false I keep calling like so:
jira.board.getAllBoards({ type: "scrum", startAt: XXX })
until isLast is true. then I can organize all of my returns from promises and be done with it.
I'm trying to reason out how I can get all of the data on pages with Ramda, I have a feeling it's possible I just can't seem to sort out the how of it.
Any help? Is this possible using Ramda?
Here's my Rx attempt to make this better:
const pagedCalls = new Subject();
pagedCalls.subscribe(value => {
jira.board.getAllBoards({ type:"scrum", startAt: value })
.then(boards => {
console.log('calling: ' + value);
allBoards.push(boards.values);
if (boards.isLast) {
pagedCalls.complete()
} else {
pagedCalls.next(boards.startAt + 50);
}
});
})
pagedCalls.next(0);
Seems pretty terrible. Here's the simplest solution I have so far with a do/while loop:
let returnResult = [];
let result;
let startAt = -50;
do {
result = await jira.board.getAllBoards( { type: "scrum", startAt: startAt += 50 })
returnResult.push(result.values); // there's an array of results under the values prop.
} while (!result.isLast)
Many of the interactions with Jira use this model and I am trying to avoid writing this kind of loop every time I make a call.
I had to do something similar today, calling the Gitlab API repeatedly until I had retrieved the entire folder/file structure of the project. I did it with a recursive call inside a .then, and it seems to work all right. I have not tried to convert the code to handle your case.
Here's what I wrote, if it will help:
const getAll = (project, perPage = 10, page = 1, res = []) =>
fetch(`https://gitlab.com/api/v4/projects/${encodeURIComponent(project)}/repository/tree?recursive=true&per_page=${perPage}&page=${page}`)
.then(resp => resp.json())
.then(xs => xs.length < perPage
? res.concat(xs)
: getAll(project, perPage, page + 1, res.concat(xs))
)
getAll('gitlab-examples/nodejs')
.then(console.log)
.catch(console.warn)
The technique is pretty simple: Our function accepts whatever parameters are necessary to be able to fetch a particular page and an additional one to hold the results, defaulting it to an empty array. We make the asynchronous call to fetch the page, and in the then, we use the result to see if we need to make another call. If we do, we call the function again, passing in the other parameters needed, the incremented page number, and the merge of the current results and the ones just received. If we don't need to make another call, then we just return that merged list.
Here, the repository contains 21 files and folders. Calling for ten at a time, we make three fetches and when the third one is complete, we resolve our returned Promise with that list of 21 items.
This recursive method definitely feels more functional than your versions above. There is no assignment except for the parameter defaulting, and nothing is mutated along the way.
I think it should be relatively easy to adapt this to your needs.
Here is a way to get all the boards using rubico:
import { pipe, fork, switchCase, get } from 'rubico'
const getAllBoards = boards => pipe([
fork({
type: () => 'scrum',
startAt: get('startAt'),
}),
jira.board.getAllBoards,
switchCase([
get('isLast'),
response => boards.concat(response.values),
response => getAllBoards(boards.concat(response.values))({
startAt: response.startAt + response.values.length,
})
]),
])
getAllBoards([])({ startAt: 0 }) // => [...boards]
getAllBoards will recursively get more boards and append to boards until isLast is true, then it will return the aggregated boards.

Help regarding Meteor Pub/Sub

I have more than one million records in mongo collection.
It is obviously is bad strategy to send all these record to client browser.
So, I created a publish function which just return count for "Searched" parameter only amongst those million records.
Meteor.publish('count', function(query) {
return Collection.find({ fieldName : "query" }).count();
});
So, I got response from above query in following format.
We are counting for pain and count is [object Object]
I want to show the count from above publication,But I receive objects only in response not number. Though When I executed this same query in dev console then , it did showed "Number" as output.
Where am I going wrong?
Can anyone show me a simple example that helps this to resolve?
There is a package for it: https://atmospherejs.com/tmeasday/publish-counts
Simply call Counts.publish within a publication, passing in a name and a cursor:
//server code
Meteor.publish('posts', function() {
Counts.publish(this, 'postsCount', Posts.find());
return Posts.find({}, { limit: 10 });
});
Subscribe on the client:
Meteor.subscribe('posts');
Then just call Counts.get('postsCount'); to get counter number. For example in your helper:
Template.posts.helpers({
postsCount: function() {
return Counts.get('postsCount');
}
});

How do I use the results of a subscription for additional subscriptions at the route level?

I have a template that displays documents from three different collections Cars, CarPaints, and CarPaintTypes. I know I need all these upfront at the Router level. The template will show a Car document, all the CarPaints that reference that Car, and all the CarPaintTypes that reference the returned CarPaints respectively (think nested list). The route to the template takes an id from the URL that represents Car._id.
Both the Cars collection and CarPaints collection make use of the Car._id as a field (it's the native _id of the Cars collection and a field in the CarPaints collection) so that's easy. However, the CarPaintTypes uses the CarPaint._id as a reference to what CarPaint it belongs to.
So I have three publications:
Meteor.publish('car', function(carId) {
return Cars.find({_id: carId});
});
Meteor.publish('carPaints', function(carId) {
return CarPaints.find({carId: carId});
});
Meteor.publish('carPaintTypes', function(carPaintId) {
return CarPaintTypes.find({carPaintId: carPaintId});
});
My route looks like:
this.route('car', {
path: '/car/:_id',
waitOn: function() {
return [Meteor.subscribe('car', this.params._id),
Meteor.subscribe('carPaints', this.params._id)];
// Can't figure out how to subscribe to or publish
// the carPaintTypes using all the results of what gets
// returned by 'carPaints'
}
});
My question is CarPaintTypes doesn't have the Car._id as a field, just the CarPaint._id to reference to a CarPaint document. Where and how I do take the results of the subscription to carPaints and pass each carPaint document that's returned to a subscription to carPaintTypes? Or is there a way to combine them all in the publication? Is it better to do it later on in my helpers? I figure since I know what I need at the route level, all the subscription calls should be in the route code.
You can grab all 3 cursors inside Meteor.publish method and simply return them:
Meteor.publish('carThings', function(carId){
var carPaint = CarPaints.findOne({carId:carId});
return [
Cars.find({_id: carId}),
CarPaints.find({carId: carId}),
CarPaintTypes.find({carPaintId: carPaint._id});
]
})
On client:
this.route('car', {
path: '/car/:_id',
waitOn: function() {
return [Meteor.subscribe('carThings', this.params._id)]
}
}
With Kuba Wyrobek's help, I figured it out. For what I was trying to achieve, the publish looks like this:
Meteor.publish('carThings', function(carId){
var carPaints = CarPaints.find({carId: carId}).fetch();
return [
Cars.find({_id: carId}),
CarPaints.find({carId: carId}),
CarPaintTypes.find({carPaintId: {$in: _.pluck(carPaints, "_id")}})
];
});
I didn't get that you could do manipulations inside your publication blocks. This is super cool and flexible. Thanks for your help.

Change publicated collection fields value

hope my first question here is not a stupid one.
Lets say we want to build a chat application with meteor, with logedin and anonymous users. The chat should be filled like that:
var post = {
userId: user._id, // is empty if anonymous user
submitted: new Date().getTime(),
text: chat_message
});
var postId = Posts.insert(post);
The publication could looks like this to make sure that the userId is not transfered
Meteor.publish('getTheChat', function() {
return Post.find({}, {fields: {userId: false});
});
But is there a way to add a field in the returned collection dynamically?
The userId should not be published but a status like "Your_Post","another_logedin_user" or "an_anonymous_user". By having that, I could include some css, so the chat looks a little bit more like WhatsApp or iMessage.
The logic inside the publish method could be something like
if (userId == this.userId) {
status = "Your_Post";
} else if (userId != null) {
status = "another_logedin_user";
} else {
status = "an_anonymous_user";
}
You see, the publication should include different values when called from different users. Is there a way in Meteor.publish?
Thanks for any insight or suggestions
Thank you both for your ideas! But as I had to find out (just for my inward peace) how it is possible inside the publish method server sided, I came, with the help of David's link, to this solution -maybe it will help someone later:
Meteor.publish('getTheChat', function(postId) {
var currentUserId = this.userId;
var ownerUserId = Posts.findOne({'_id':postId}).userId;
var findOptions = {}; // in my final coding these differ for 'chat room owners' and 'normal users'
var transform = function(post) {
if (currentUserId && post.userId == currentUserId) {
post.userId = "posted by you";
} else if (post.userId == null) {
post.userId = "anonym posted";
} else if (post.userId == ownerUserId) {
post.userId = "posted by owner";
} else {
post.userID = "posted by another loged in";
return post;
};
var self = this;
var handle = Posts.find(findOptions).observe({
added: function (document) {
self.added('posts', document._id, transform(document));
},
changed: function (newDocument, oldDocument) {
self.changed('posts', document._id, transform(newDocument));
},
removed: function (oldDocument) {
self.removed('posts', oldDocument._id);
}
});
self.ready();
self.onStop(function(){
handle.stop();
});
By having this I am finally able to overwrite values dynamically.
It looks like you need to add a transform on your Posts collection. You can do this in your publish function (as seen here), but server-side transforms are computationally inefficient and tricky to write. Though they are necessary in cases where only the server could perform the action - e.g. signed URLs. In your case, I'd recommend a standard collection transform which is a filter applied after the documents are fetched.
Unfortunately, this kind of transform would require the userId on the client. I've never seen a case where simply publishing a id could cause a security issue. If you believe this is the case with your app, I'm very interested to know why. If you can overcome this restriction, keep reading...
You can read about transforms in the documentation on collections, and you can see an example on evented mind. Personally I like to use the collection-helpers package for this.
If you try collection-helpers, your transform could look like:
Posts.helpers({
status: function() {
if (this.userId === Meteor.userId()) {
return 'Your_Post';
} else if (this.userId != null) {
return 'another_logedin_user';
} else {
return 'an_anonymous_user';
}
}
});
And then you could use it in your template like:
{{#each posts}}
<p>{{text}} - <span class='status'>{{status}}</span></p>
{{/each}}
Of course, you can also use template helpers to achieve the same result but transforms are more easily reusable across your application.
Sadly, this has been a huge issue for me too, and I am sorry to say, it is not technically possible to just add a field on the publisher's query and use it conveniently in your view. BUT, I have a solution that may work for you. It will also give you an idea of how complex it can become as soon as you want to keep some reactive data private in Meteor.
Server side:
First, create two different publishers: one for the current user's posts, one for all the others:
Meteor.publish('getTheOthersChat', function() {
return Post.find({$ne:{userId: this.userId}}, {fields: {userId: false});
});
Meteor.publish('getTheOwnChat', function() {
return Post.find({userId: this.userId});
});
Client/router side:
Then, subscribe to both of these: what this will do is include the post's userId only when it is the own user's id. If not, it'll be undefined.
Then, we still need to identify the case "anonymously posted" vs "posted by user". For this, you can add another field during the post creation, for example is_anonymous, which you then set to true or false depending on the case if the user is logged in or not. The check would then be:
if (userId) {
status = "Your_Post";
} else if (is_anonymous === false) {
status = "another_logedin_user";
} else {
status = "an_anonymous_user";
}
This solution should work. I know, it is sad to have to come to this kind of means. It makes Meteor look clunky and impractical for tasks that should be dead easy. Such a shame for such a cool framework!

How to get a published collection's total count, regardless of a specified limit, on the client?

I'm using the meteor-paginated-subscription package in my app. On the server, my publication looks like this:
Meteor.publish("posts", function(limit) {
return Posts.find({}, {
limit: limit
});
});
And on the client:
this.subscriptionHandle = Meteor.subscribeWithPagination("posts", 10);
Template.post_list.events = {
'click #load_more': function(event, template) {
template.subscriptionHandle.loadNextPage();
}
};
This works well, but I'd like to hide the #load_more button if all the data is loaded on the client, using a helper like this:
Template.post_list.allPostsLoaded = function () {
allPostsLoaded = Posts.find().count() <= this.subscriptionHandle.loaded();
Session.set('allPostsLoaded', allPostsLoaded);
return allPostsLoaded;
};
The problem is that Posts.find().count() is returning the number of documents loaded on the client, not the number available on the server.
I've looked through the Telescope project, which also uses the meteor-paginated-subscription package, and I see code that does what I want to do:
allPostsLoaded: function(){
allPostsLoaded = this.fetch().length < this.loaded();
Session.set('allPostsLoaded', allPostsLoaded);
return allPostsLoaded;
}
But I'm not sure if it's actually working. Porting their code into mine does not work.
Finally, it does look like Mongo supports what I want to do. The docs say that, by default, cursor.count() ignores the effects of limit.
Seems like all the pieces are there, but I'm having trouble putting them together.
None of the answers do what you really want becase none provide solution that is reactive.
This package does exactly what you want and also reactive.
publish-counts
I think you can see the demo: counts-by-room in meteor doc
It can help you publish the counts of your posts at server and get it at client
You can simply write this:
// server: publish the current size of your post collection
Meteor.publish("counts-by-room", function () {
var self = this;
var count = 0;
var initializing = true;
var handle = Posts.find().observeChanges({
added: function (id) {
count++;
if (!initializing)
self.changed("counts", 'postCounts', {count: count});
},
removed: function (id) {
count--;
self.changed("counts", postCounts, {count: count});
}
});
initializing = false;
self.added("counts", 'postCounts', {count: count});
self.ready();
self.onStop(function () {
handle.stop();
});
});
// client: declare collection to hold count object
Counts = new Mongo.Collection("counts");
// client: subscribe to the count for posts
Tracker.autorun(function () {
Meteor.subscribe("postCounts");
});
// client: simply use findOne, you can get the count object
Counts.findOne()
The idea of sub.loaded() is to help you with exactly this problem.
Posts.count() isn't going to return the right thing because, as you've guessed, on the client, Meteor has no way of knowing the real number of posts that live on the server. But what the client knows is how many posts it's tried to load. That's what that .loaded() tells you, and is why the line this.fetch().length < this.loaded() will tell you if there are more posts on the server or not.
What I would do is write a Meteor server side method that retrieves the count like so:
Meteor.methods({
getPostsCount: function () {
return Posts.find().count();
}
});
Then call it on the client, in observe to make it reactive:
function updatePostCount() {
Meteor.call('getPostsCount', function (err, count) {
Session.set('postCount', count);
});
}
Posts.find().observe({
added: updatePostCount,
removed: updatePostCount
});
Although this question is old, I thought I would provide an answer that ended up working for me. I did not create the solution, I found the basis for it here (so credit where credit is due): Discover Meteor
Anyway, in my case I was trying to get "size" of the database from client side, so I can determine when to hide the "load more" -button. I was using template level subscriptions. Oh and for this solution to work, you need to add reactive-var -package. Here is my (in short):
/*on the server we define the method which returns
the number of posts in total in the database*/
if(Meteor.isServer){
Meteor.methods({
postsTotal: function() {
return PostsCollection.find().count();
}
});
}
/*In the client side we first create the reactive variable*/
if(Meteor.isClient){
Template.Posts.onCreated(function() {
var self = this;
self.totalPosts = new ReactiveVar();
});
/*then in my case, when the user clicks the load more -button,
we call the postsTotal-method and set the returned value as
the value of the totalPosts-reactive variable*/
Template.Posts.events({
'click .load-more': function (event, instance){
Meteor.call('postsTotal', function(error, result){
instance.totalPosts.set(result);
});
}
});
}
Hope this helps someone (I recommend checking the link first). For template level subscriptions, I used this as my guide Discover Meteor - template level subscriptions. This was my first stacked-post and I am just learning Meteor, so please have mercy...:D
Ouch this post is old, anyway maybe it will help someone.
I had exactly the same issue. I managed to solve it with 2 simple lines...
Remember the :
handle = Meteor.subscribeWithPagination('posts', 10);
Well I used in client handle.loaded() and Posts.find().count(). Because when they are different it means that all the posts are loaded. So here is my code :
"click #nextPosts":function(event){
event.preventDefault();
handle.loadNextPage();
if(handle.loaded()!=Posts.find().count()){
$("#nextPosts").fadeOut();
}
}
I had the same problem, and using the publish-counts package didn't work with the subs-manager package. I created a package that can set a reactive server-to-client session, and keep the document count in this session. You can find an example here:
https://github.com/auweb/server-session/#getting-document-count-on-the-client-before-limit-is-applied
I'm doing something like this:
On cliente
Template.postCount.posts = function() {
return Posts.find();
};
Then you create a template:
<template name="postCount">
{{posts.count}}
</template>
Then, whatever you want to show the counter: {{> postCount}}
Much easier than any solution i have seen.

Resources