Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing a query in Mongo

Tags:

mongodb

This is the case: A webshop in which I want to configure which items should be listed in the sjop based on a set of parameters. I want this to be configurable, because that allows me to experiment with different parameters also change their values easily.

I have a Product collection that I want to query based on multiple parameters. A couple of these are found here:

within product:

"delivery" : {
    "maximum_delivery_days" : 30,
    "average_delivery_days" : 10,
    "source" : 1,
    "filling_rate" : 85,
    "stock" : 0
}

but also other parameters exist.

An example of such query to decide whether or not to include a product could be:

"$or" : [
        {
            "delivery.stock" : 1
        },
        {
            "$or" : [
                    {
                            "$and" : [
                                    {
                                            "delivery.maximum_delivery_days" : {
                                                    "$lt" : 60
                                            }
                                    },
                                    {
                                            "delivery.filling_rate" : {
                                                    "$gt" : 90
                                            }
                                    }
                            ]
                    },
                    {
                            "$and" : [
                                    {
                                            "delivery.maximum_delivery_days" : {
                                                    "$lt" : 40
                                            }
                                    },
                                    {
                                            "delivery.filling_rate" : {
                                                    "$gt" : 80
                                            }
                                    }
                            ]
                    },
                    {
                            "$and" : [
                                    {
                                            "delivery.delivery_days" : {
                                                    "$lt" : 25
                                            }
                                    },
                                    {
                                            "delivery.filling_rate" : {
                                                    "$gt" : 70
                                            }
                                    }
                            ]
                    }
            ]
        }
]

Now to make this configurable, I need to be able to handle boolean logic, parameters and values. So, I got the idea, since such query itself is JSON, to store it in Mongo and have my Java app retrieve it. Next thing is using it in the filter (e.g. find, or whatever) and work on the corresponding selection of products. The advantage of this approach is that I can actually analyse the data and the effectiveness of the query outside of my program.

I would store it by name in the database. E.g.

{ 
    "name": "query1",
    "query": { the thing printed above starting with "$or"... }
}

using:

db.queries.insert({
    "name" : "query1",
    "query": { the thing printed above starting with "$or"... }
})

Which results in:

2016-03-27T14:43:37.265+0200 E QUERY    Error: field names cannot start with $ [$or]
    at Error (<anonymous>)
    at DBCollection._validateForStorage (src/mongo/shell/collection.js:161:19)
    at DBCollection._validateForStorage (src/mongo/shell/collection.js:165:18)
    at insert (src/mongo/shell/bulk_api.js:646:20)
    at DBCollection.insert (src/mongo/shell/collection.js:243:18)
    at (shell):1:12 at src/mongo/shell/collection.js:161

But I CAN STORE it using Robomongo, but not always. Obviously I am doing something wrong. But I have NO IDEA what it is. If it fails, and I create a brand new collection and try again, it succeeds. Weird stuff that goes beyond what I can comprehend.

But when I try updating values in the "query", changes are not going through. Never. Not even sometimes. I can however create a new object and discard the previous one. So, the workaround is there.

db.queries.update(
    {"name": "query1"},
    {"$set": { 
            ... update goes here ...
        }
    }
)

doing this results in:

WriteResult({
        "nMatched" : 0,
        "nUpserted" : 0,
        "nModified" : 0,
        "writeError" : {
                "code" : 52,
                "errmsg" : "The dollar ($) prefixed field '$or' in 'action.$or' is not valid for storage."
        }
})

seems pretty close to the other message above.

Needles to say, I am pretty clueless about what is going on here, so I hope some of the wizzards here are able to shed some light on the matter

like image 547
dexter Avatar asked Mar 27 '16 13:03

dexter


People also ask

Is MongoDB good for query?

MongoDB is designed to make data easy to access, and rarely to require joins or transactions, but when you need to do complex querying, it's more than up to the task. The MongoDB Query API allows you to query deep into documents, and even perform complex analytics pipelines with just a few lines of declarative code.

What is Save () in MongoDB?

MongoDB's update() and save() methods are used to update document into a collection. The update() method updates the values in the existing document while the save() method replaces the existing document with the document passed in save() method.

Can we write SQL query in MongoDB?

Please note that MongoDB does not natively support SQL features. The SQL query is validated and translated into a MongoDB query and executed by NoSQLBooster for MongoDB. The Equivalent MongoDB Query can be viewed in the console.

Can we store data in MongoDB?

The storage engine is the primary component of MongoDB responsible for managing data. MongoDB provides a variety of storage engines, allowing you to choose one most suited to your application.


2 Answers

I think the error message contains the important info you need to consider:

QUERY Error: field names cannot start with $

Since you are trying to store a query (or part of one) in a document, you'll end up with attribute names that contain mongo operator keywords (such as $or, $ne, $gt). The mongo documentation actually references this exact scenario - emphasis added

Field names cannot contain dots (i.e. .) or null characters, and they must not start with a dollar sign (i.e. $)...


I wouldn't trust 3rd party applications such as Robomongo in these instances. I suggest debugging/testing this issue directly in the mongo shell.

My suggestion would be to store an escaped version of the query in your document as to not interfere with reserved operator keywords. You can use the available JSON.stringify(my_obj); to encode your partial query into a string and then parse/decode it when you choose to retrieve it later on: JSON.parse(escaped_query_string_from_db)

like image 86
Lix Avatar answered Oct 29 '22 07:10

Lix


Your approach of storing the query as a JSON object in MongoDB is not viable.

You could potentially store your query logic and fields in MongoDB, but you have to have an external app build the query with the proper MongoDB syntax.

MongoDB queries contain operators, and some of those have special characters in them.

There are rules for mongoDB filed names. These rules do not allow for special characters. Look here: https://docs.mongodb.org/manual/reference/limits/#Restrictions-on-Field-Names

The probable reason you can sometimes successfully create the doc using Robomongo is because Robomongo is transforming your query into a string and properly escaping the special characters as it sends it to MongoDB.

This also explains why your attempt to update them never works. You tried to create a document, but instead created something that is a string object, so your update conditions are probably not retrieving any docs.

like image 42
BigDataKid Avatar answered Oct 29 '22 08:10

BigDataKid