Return list of posts in order of time 'liked' as stored in User collection array - json

Okay, so here's my problem. I now have an array like this that is added to when a user 'likes' a post, which also captures the time that they have liked it.
"liked_times" : [
{
"postId" : "5CeN95hYZNK5uzR9o",
"likedAt" : ISODate("2015-09-28T02:55:08.803Z")
},
{
"postId" : "vN7uZ2d6FSfzYJLmm",
"likedAt" : ISODate("2015-09-28T02:55:26.118Z")
},
{
"postId" : "b5JEtHb9hCsQPeEQB",
"likedAt" : ISODate("2015-09-28T02:55:31.359Z")
}
]
We used to just capture the postId, but updated to getting the time as well with this post How would I return the order of MongoDB Posts by time Favourited by user?
So then we used to be able to retrieve the posts with:
Posts.find({ _id: { $in: user.liked } },
{sort: {createdAt: -1} });
But now I'm trying to work out how I would return the posts a user has liked in the order that he liked them using this new data. I'm sure there must be a relatively easy way.
Edit as per comments: I also have a simple array or list of docs with just the postId
"liked" : [
"D4turxiezBvPtNjDr",
"Hk2YpqgwRM4QCgsLv",
"vN7uZ2d6FSfzYJLmm",
"beNdpXhYLnKygD3yd",
"EBMKgrD4DjZxkxvfY"
],
When a post is liked by the user the Id is added to the end of this list. However when I return posts with return Quotes.find({ _id: { $in: user.liked } }; They are not ordered in the same order (I'm thinking due to a Minimongo local cache behaviour).
Is there another way I should be calling these docs to order them in the specific order that the array stores them? Or should I just go ahead and try to solve this using the likedAt times programatically in the code?

Related

Trying to get filtered response from query with multiple terms from elasticsearch

As the title states, im trying to make a query that doesnt return the entire document, but only certain fields, but with multiple exact terms.
Im using Guzzle from laravel to contruct my query:
$response = $client->post('cvr-permanent/_search', [
'auth' => ['USERNAME', 'PASSWORD'],
'json' => [
"_source" => ["Vrvirksomhed.attributter", "Vrvirksomhed.deltagerRelation.organisationer.medlemsData.attributter"],
"query" => [
"bool"=> [
"must"=> [
[
"term"=> [
"Vrvirksomhed.cvrNummer" => $vat
]
]],
"must_not"=> [ ],
"should"=> [ ]
]
],
"from"=> 0,
"size"=> 10,
"sort"=> [ ]
]
]);
I want the data from the Vrvirksomhed.cvrNummer and the data i want is where Vrvirksomhed.attributter.type => "KAPITAL" and Vrvirksomhed.deltagerRelation.deltager.navne and where Vrvirksomhed.deltagerRelation.organisation.attributter.type = "EJERANDEL_PROCENT"
Im very confused about how to make this query work because it is multiple terms but not really. Also very new to elasticsearch.
I tried the "terms" but couldnt really get it to work.
The query i have made above, return way too much data i dont need, and not all the data i DO need.
Hope you can help
**EDIT
Something like this maybe, but translated to elasticsearch
SELECT attributter.type": "KAPITAL" AND deltagerRelation.deltager.navne AND deltagerRelation.organisation.attributter.type": "EJERANDEL_PROCENT FROM Vrvirksomhed WHERE cvrNummer = $vat
***EDIT
Hopefully more clarification:
Okay, sorry ill try to make it clearer. The object i want is a company with a certain vat number. So Vrvirksomhed.cvrNummer is that, and that has to be the term. It returns a gigantic object with so many arrays in arrays. I do not want all of this data but only some of it. The data i need from this big object, is the object in the array Vrvirksomhed.attributter that has the type : "KAPITAL field, and not all of the attributter. Then i want Vrvirksomhed.deltagerRelation.deltager.navne which i can get by just putting it in the _source because i want all of these objects. But then i want Vrvirksomhed. deltagerRelation.organisation.attributter that again is a bunch of objects in the array attributter but i only want the ones with the type : "EJERANDEL_PROCENT
So i can´t really add them as additional "terms" because the only real term is the "cvrNummer", everything else is just filtering the response. I tried with filters etc, but to no avail
Heres a pastebin so you can see the clusterfuck i am dealing with. THis is what i have been able to sort it to so far, with putting the things in _source but without the extra "filtering" of "KAPITAL" and "EJERANDEL_PROCENT"
https://pastebin.com/b8hWWz1R
You want to get only documents which match several conditions, and you need only a subset of fields from those documents, correct?
In SQL (taking some liberties with the field names and structure), your query would be something like:
SELECT cvrNummer
FROM Vrvirksomhed
WHERE attributter_type = 'KAPITAL'
AND deltagerRelation_deltager_navne = 'you left this out in your question'
AND deltagerRelation_organisation_attributter_type = 'EJERANDEL_PROCENT'
As explained in the Elasticsearch Guide†, the equivalent to this in Elasticsearch is a query with a bool clause that contains all your conditions, and a _source parameter which says what fields you want to get back in the response. Something like the following:
{
"_source": ["cvrNummer"]
"query": {
"bool": {
"must": [
{ "term": "attributter.type": "KAPITAL" },
{ "term": "deltagerRelation.deltager.navne": "you left this out in your question" },
{ "term": "deltagerRelation.organisation.attributter.type": "EJERANDEL_PROCENT" }
]
}
}
}
† Do note that the syntax in this guide is for Elasticsearch 2.x. The current version is 7.x, and many things have changed since then!
See https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-bool-query.html for how to construct a bool query using the new syntax;
see https://www.elastic.co/guide/en/elasticsearch/reference/current/term-level-queries.html for how to use the term-level queries, which you probably want;
also see https://www.elastic.co/guide/en/elasticsearch/reference/current/query-filter-context.html and consider using filter context, since you probably don't care about the score of your query.

Is it possible to order intermediate relation table using sequelize?

I have the following scenario, my application has two entities: box and items with N to N relationship. I am using sequelize with MySQL.
I am using pseudocode to represent the tables:
Box {
id: Integer primary key
name: String
}
Item {
id: Integer primary key
name: String
}
I have set up the schemas with relations hasMany in both directions using the following through relation:
Box.hasMany(Item, { through: Box_Item });
Item.hasMany(Box, { through: Box_Item });
Box_Item {
id_box: Integer,
id_item: Integer,
item_order: Integer
}
With primary_key(id_box, id_item).
I tested it and I can call myBox.getItems() on my instance object myBox and easily get all the items it has.
I can make calls as
BoxModel.findOne({
where: { id: 1 },
include: [{ model: ItemModel }]
});
And it automatically understands there is a relation between the models through Box_Item and get everything correctly, except that I'm not getting the results sorted by item_order field. This field is a number from 1 to N that represents the item order inside that box.
I tried
BoxModel.findOne({
where: { id: 1 },
include: [
{
model: ItemModel,
order: 'item_order'
}
]
});
But it seems sequelizejs does not support order inside include yet (checked on their github repo).
I tried to force
BoxModel.findOne({
where: { id: 1 },
order: '`box_model`.`item_order`'
include: [ { model: ItemModel } ]
})
looking through the query sequelize creates but it just put the ORDER BY in two different places (inside INNER JOIN and at the end of the query, don't know why...) and I got an error.
So I searched for this on stackoverflow (1), found a few questions but I don't get a good way for doing that using the ORM.
How could I get the items sorted by item_order field when asking for specific box items?
After a few days trying to get it done I found an answer on stackoverflow that helped me.
After creating the relationships between Box and Item I can easily call on an instance:
myBox.getItems({
order: '`box_model`.`item_order`'
});
And then I get the result I'm expecting. But I had to look through the query sequelize is creating based on the models and get the correct field based on their renaming rules.
If you want you can pass the as parameter and rename your tables.

Sequelize include (how to structure query)?

I have a query I'm trying to perform based on a one to many relationship.
As an example there is a model called Users and one called Projects.
Users hasMany Projects
Projects have many types which are stored in a type (enum) column. There are 4 different types that potentially a user may have that I want to load. The catch is I want to include the most recent project record (createdAt column) for all networks that potentially will be there. I have not found a way to structure the query for it to work as an include. I have however found a way to do a raw query which does what I want.
I am looking for a way without having to do a raw query. By doing the raw query I have to map the returned results to users I've returned from the other method, or I have to do a simple include and then trim off all the results that are not the most recent. The latter is fine, but I see this getting slower as a user will have many projects and it will keep growing steadily.
This allow serialize a json for anywhere action about a model. Read it, very well
sequelize-virtual-fields
// define models
var Person = sequelize.define('Person', { name: Sequelize.STRING });
var Task = sequelize.define('Task', {
name: Sequelize.STRING,
nameWithPerson: {
type: Sequelize.VIRTUAL,
get: function() { return this.name + ' (' + this.Person.name + ')' }
attributes: [ 'name' ],
include: [ { model: Person, attributes: [ 'name' ] } ],
order: [ ['name'], [ Person, 'name' ] ]
}
});
// define associations
Task.belongsTo(Person);
Person.hasMany(Task);
// activate virtual fields functionality
sequelize.initVirtualFields();

data modules belongs to user MEANJS

Im really new to MEANJS and after some days of google research i can't find a good way and a good tutorial to learn how to make a kind of articles module (like the one in MEANJS starter) belongs to a user.
At now my crud module work well but all the data is displayed for all users. I just want that the user can add article (here this is have an another name but anyway) and only this user can see this article.
I find your question here and you look like to have achieve a kind of what i'm trying. So if you have any tips or any ressources to help me, i'm up ! Of course i have read the mongoose docs part about population and i'm sure this is it, but alone this is hard. I've already tried to link my users module and article module but don't work.
Thanks for your time man, and have a happy coding day :)
Greettings from Paris.
If you check mongodb entry for articles it already holds the user ObjectId that created the article:
> db.articles.find()
{ "_id" : ObjectId("55c7751dbafe1a306b6ce54d"), "user" : ObjectId("55c774f9bafe1a306b6ce54c"), "content" : "My latest test. another info", "title" : "Test", "created" : ISODate("2015-08-09T15:43:25.467Z"), "__v" : 0 }
{ "_id" : ObjectId("55c7c2edd4bc5f277b775ba9"), "user" : ObjectId("55c774f9bafe1a306b6ce54c"), "content" : "ATESTE", "title" : "TESTE", "created" : ISODate("2015-08-09T21:15:25.762Z"), "__v" : 0 }
>
The reason that all articles are loaded when you click "List Articles" menu entry is because the list implementation for articles in articles.server.controller.js does not restrict the find to load only the logged user articles, the default implementation loads all articles from mongodb:
/**
* List of Articles
*/
exports.list = function (req, res) {
Article.find().sort('-created').populate('user', 'displayName').exec(function (err, articles) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(articles);
}
});
};
I've never tried that but I believe the easiest way to do what you want would be to change that call to have a criteria and load only the articles from the logged in user, something like:
/**
* List of Articles
*/
exports.list = function (req, res) {
Article.find({user: req.user}).sort('-created').populate('user', 'displayName').exec(function (err, articles) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
res.json(articles);
}
});
};
This, or something like this(I didn't test it) should now bring only the logged in user created articles. Check details of mongoose query here.

How to represent the data for threaded comments(along with comment voting) in mongodb?

For my blog, I want to incorporate my own commenting system without relying on the default wordpress commenting system. I need the comments module to utilize mongodb instead of mysql and the module needs support for the following:
Threaded comments
Comment Voting
The votes on comments need to be aggregated per each comment author for the entire blog.
In this context, What is best way to represent the data in mongodb?
Just store the comments as you want them represented on your blog. You want threaded/nested comments? Then store them in a nested fashion:
postId: {
comments: [
{
id: "47cc67093475061e3d95369d" // ObjectId
title: "Title of comment",
body: "Comment body",
timestamp: 123456789,
author: "authorIdentifier",
upVotes: 11,
downVotes: 2,
comments: [
{
id: "58ab67093475061e3d95a684"
title: "Nested comment",
body: "Hello, this is a nested/threaded comment",
timestamp: 123456789,
author: "authorIdentifier",
upVotes: 11,
downVotes: 2,
comments: [
// More nested comments
]
}
]
},
{
// Another top-level comment
}
]
}
The postId refers to the blog post to which the comments belong and has been used as the key (or _id in MongoDB) of the document. Each comment has a unique id, in order to vote or comment on individual comments.
To get the aggregated votes, you'll need to write map-reduce functions somewhere along these lines:
function map() {
mapRecursive(this.comments)
}
function mapRecursive(comments) {
comments.forEach(
function (c) {
emit(comment.author, { upVotes: c.upVotes, downVotes: c.downVotes });
mapRecursive(c.comments);
}
);
}
function reduce(key, values) {
var upVotes = 0;
var downVotes = 0;
values.forEach(
function(votes) {
upVotes += votes.upVotes;
downVotes += votes.downVotes;
}
);
return { upVotes: upVotes, downVotes: downVotes };
}
I haven't tested these functions and they don't check for null values either. That's up to you :)
How about:
A single collection for all comments. Each comment object holds a reference to a parent comment object for threading and of course the a reference to the parent blog post.
Each comment object also has numeric vote count that can be updated using mongo's atomic updates.
Each specific vote by a user would then be a reference to the comment's id in the user's object directly.