Mongo roblox API wrapper issue - mongodb

I'm using Roblox's Mongo API wrapper
https://devforum.roblox.com/t/rbx-to-mongo-a-mongodb-data-api-wrapper/1661288
However, I'm having some issues with it.
Here is my data source for mongo:
My Data Source
I'm trying to get a table of all "tokens" in a table
Lua code
It just returns an empty table.

Per the additional information in the comments, it doesn't look like you have a matching document in the database.
Currently your query is looking for a document that is similar to the following:
{
_id: 1,
name: "tokens"
}
But the document from the screenshot resembles the following instead:
{
_id: 6,
tokens: [ 1, 2, 4 ]
}
You can see a demonstration of that in this Mongo Playground.
The changes that you need to make depend on what you are trying to do. If you are trying to find a document that has a specific value in the tokens array, then you may be looking for a filter similar to { tokens: 4 }. If instead you just want to retrieve a document that has a tokens field, then you may be looking for a filter such as { tokens: { $exists: true } }.

Related

Querying MongoDB subdocuments without respecting key order

I want to query against a subdocument. The problem is, that MongoDB seems to respect the order of the object keys. So if I do the following query, I won't receive any results:
db.getCollection('test').find({docId:'tLDmtdeYuG9DiGGrL',optimizeParams:{"deviceType":"mobile","daytime":"night"}})
If I change the order of optimizeParams, I will get a result:
db.getCollection('test').find({docId:'tLDmtdeYuG9DiGGrL',optimizeParams:{"daytime":"night","deviceType":"mobile"}})
Now a soultion would be to use dot notation, but in this case, I will receive ALL documents which contains both keys. But I only want the documents that ONLY have both keys (no others):
db.getCollection('test').find({docId:'tLDmtdeYuG9DiGGrL',"optimizeParams.deviceType":"mobile","optimizeParams.daytime":"night"})
Is there a way how I can execute a query without respecting the key order?
Even if there was a way to respect they key orders, I would recommend not to use it. JSON documents are meant to not respect the key orders. You should be designing your schema with this in mind.
For your particular use case, you could do this if you're using version 3.6 or higher:
db.test.aggregate([{
$match: {
"optimizeParams.daytime": "night",
"optimizeParams.deviceType": "mobile"
},
},{
$addFields: {
count: {
$size: {
$objectToArray: "$optimizeParams"
}
}
}
}, {
$match: {
count: 2
}
}])
This answer helped me with the above solution. Please keep indexes in mind when using it.
Although this could be done, "find documents with only these two keys" seems to be irrelevant from any application's perspective. There must be something meaningful that this situation represents. You could use another field to indicate that meaning, and use that to filter instead.

Error while in update query in MongoDB

What I'm trying to achieve in here is to update a given set of documents that contain a given set of fields, either one, another or both.
I have two questions actually for this matter...
Can MongoDB process fields that aren't present in the document being processed? Like what I'm trying to achieve here, sum two fields, and if one is null just sum 0.
And the other one is the error that I'm getting in the query...
db.getCollection('my_collection').update({'$or': [{
'a_field': 1}, {
'another_field': 1
}
]},
{'$set': {
'another_field': {
'$sum': [
'$a_field', '$another_field'
]
}
},
'$unset': {'a_field': ''}})
Giving me the following error...
The dollar ($) prefixed field '$sum' in 'another_field.$sum' is not valid for storage.
There is no $sum UPDATE operator in mongodb and hence the error. The one you are confused with is an aggregation operator and can only be used to query data in a fashion.
Also, the operator you are looking at is $inc, however you CANNOT update the document in the fashion you are trying to. There is an OPEN ticket for this feature to mongodb. Ticket
Hope this clarifies
See this answer for more on achieving what you are trying to do.

Updating a deeply nested object in mongodb

I'm trying to update the favorite Boolean to true in the following model.
I only need a success/failure return, so I've been trying update with a projection. However, I can't seem to get any further than selecting articles, but am not able to change the favorite value given the title name.
I am using the mongoDB node.js driver
Thanks!
_id: id,
news: [
{
name: bbc,
articles: [
{
title: 'flashpoint', favorite: false
}
]
}
]
You're actually hitting one of the limitations of MongoDB, a Ticket on their Jira was opened in 2010 regarding updating items in deeply nested arrays, and has just been implemented, stated for release with Mongo 3.5.12. If you're using an older version, the simplest solution would be to go for a second collection containing your articles (with references on the first collection), so you can update them directly.

Reverse JSON query: find all queries in a collection matching an object

I am designing a generic notification subscription system where user can specify a compound rule at the time of subscription in terms of MongoDB query, or more generally, json query. The subscription data is stored in MongoDB collection. For example,
{ "userId": 1, "rule": {"p1": "a"} }
{ "userId": 2, "rule": {"p1": "a", "p2": "b"} }
{ "userId": 3, "rule": {"p3": {$gt: 3} } }
Later when an event in the form of a json object, such as the following, arrives, I want to find all user rules the event maches:
{"p1": "a", "p3": 4}
The above event should match rules specified by userId 1 and 3 in the example. The event object doesn't have to be stored in MongoDB.
Although I can probably meet the requirement by writing a loop at application layer. For efficiency I really want to implement it at db layer, preferably allow distributed (sharded) execution due to volume and latency requirement.
Is it achievable? Any help is appreciated. In fact, I am open to other NOSQL dbs as long as supporting dynamic event schema and there is a way to specify compound rule.
What you are trying to achieve is not possible, at least in MongoDB.
If you reason about how a query engine works, you will realize that this has not a straightforward solution.
On high-level terms, the engine will generate a condition object from your query that then will get evaluated against each document in the set that will result in a boolean value which determines if the document belongs to the result set or not.
In your case you want to do the other way round, you want to generate a condition object based on the document and then apply it to something (e.g an object) that you give it.
Even if it were possible, the cost of doing this on the DB would be too high as it would require to compile an expression function for each object and execute it and there would be no way to optimize the execution of the query.
It is more reasonable to actually do that outside the database, where you could have the expression functions already created.
You cant store "Comparison Query Operators" in a mongo database, but you can do this:
{ "userId": 1, "rule": {"p1": "a"} }
{ "userId": 2, "rule": {"p1": "a", "p2": "b"} }
{ "userId": 3, "rule": {"p3": {"value": 3, "operator":"gt"} } }
You store value AND OPERATOR, in string form, and you can make a query like this:
db.test.find({"rule.p3.comparator":"gt", "rule.p3.value":{$lt:4}})
Notice, if your "operator" is "gt", you must use $lt (the opposite comparison operator) in the query
Your complete example is something like this:
db.test.find({$or:[{"rule.p3.comparator":"gt", "rule.p3.value":{$lt:4}}, {"rule.p1":"a"}]})
This query match userId 1 and 3 like you want
Update: Following solution doesn't work. Problem with mongodb is that it doesn't use NodeJs to run map-reduce javascript, nor support any package manager. So it's hard to use any 3rd party libraries.
My own proposed solution, which hasn't been confirmed :
Compose query conforming to json-query syntax
upon arrival of evt, call MongoDB mapReduce function on user rules collection to invoke jsonQuery in mapper
var jsonQuery = require('json-query')
var mapper = function(evt) {
function map() {
if(jsonQuery(this.rule, {data: evt}){
emit(this.userId);
}
}
return map;
};
db.userRules.mapReduce(mapper(evt), ...);
The reason to compose query into json-query syntax instead of MongoDB query syntax is only json-query offers the jsonQuery method that tries to match one rule to one object. For above code to meet the requirements in question, following assumptions have to be met:
MongoDB can execute mapReduce on distributed nodes
In mapReduce I can use external library such as json-query, which implies the library code has to be distributed to all MongoDB nodes, perhaps as a result of closure.

MongoDB - Manipulating multi-level arrays in a document

I am currently building an app with Meteor and MongoDB. I have a 3 level document structure with array in array:
{
_id: "shtZFiTeHrPKyJ8vR",
description: "Some title",
categories: [{
id: "shtZFiTeHrPKyJ8vR",
name: "Foo",
options: [{
id: "shtZFiTeHrPKyJ8vR",
name: "bar",
likes: ["abc", "bce"]
}]
}]
}
Now, the document could be manipulated at any level. Means:
description could be changed
categories can be added / removed / renamed
options can be added / removed / renamed
users can like options, so they must be added or removed
1 and 2 is quite easy. It is also relatively easy to add or remove a new option:
MyCollection.update({ _id: id, "categories.id": categoryId }, {
$push: {
"categories.$.options": {
id: Random.id
name: optionName
}
}
});
But manipulating the options hash requires to do that on javascript objects. That means I first need to find my document, iterate over the options and then write them back.
At least that's what I am doing right now. But I don't like that approach.
What I was thinking about is splitting the collection, at least to put the likes into it's own collection referencing the origin document.
Or is there another way? I don't really like both of my possible solutions.
For this kind of query one would normally use a the Mongo position operator. Although from the docs.
Nested Arrays
The positional $ operator cannot be used for queries
which traverse more than one array, such as queries that traverse
arrays nested within other arrays, because the replacement for the $
placeholder is a single value
Thus the only way to natively do what you want is by using specific indexes.
db.test.update({},{$pull:{"categories.0.options.0.likes":"abc"}})
Unfortunately Mongo does not allow to easily get the index of a match nested document.
I would normally say that once your queries become that difficult it's probably a good idea to revisit the way you store data. Also with that many arrays to which you will be pushing data, Mongo will probably be relocating a lot of documents. This is definitely something that you want to minimize.
So at this point you will need to separate your data out into different documents and even collections.
Your first documents would look like this:
{
_id: "shtZFiTeHrPKyJ8vR",
description: "Some title",
categories: [{
id: "shtZFiTeHrPKyJ8vR",
name: "Foo",
options: ["shtZFiTeHrPKyJ8vR"]
}]
}
This way you can easily add/remove options as you mentioned in your question. You would then need a second collection with documents that represent each option.
{
_id: "shtZFiTeHrPKyJ8vR",
name: "bar",
likes: ["abc", "bce"]
}
You can learn more about references here. This is similar to what you mentioned in your comment. The benefit of this is that you are already reducing the potential amount of relocation. Depending on how you use your data you may even be reducing network usage.
Now doing updates on the likes is easy.
MyCollection.update({ _id: id}, {
$push: {likes: "value"}
});
This does, however, require you to make two queries to the db. Although on the flip side you do a lot less on the client side and a lot less bandwidth is used.
Some other questions you need to ask yourself is if that depth of nesting is really needed. There might be an easier way to go about achieving your goal that doesn't require it to become so complicated.