Mongodb: How to order a "select in" in same order as elements of the array - node.js

I'm performing this mongo query:
db.mytable.find({id:{$in:["1","2", "3", "4" ]}});
It returns all results in a strange order, as it follows:
4,3,2,1
I need to retrieve all results in same order as it was defined in the query array.
1,2,3,4
Is it possible ?

There is indeed no guarantee about the order of results returned from your query, but you could do a sort afterwards with the result. Two examples, the first one with the order you wanted, the second one reversed.
const arr = ["1", "2", "3", "4" ];
db.collection.find({ id: { $in: arr }})
.then(result => {
let sorted = arr.map(i => result.find(j => j.id === i));
console.log(sorted) // 1, 2, 3, 4
let reversed = arr.reverse().map(i => result.find(j => j.id === i));
console.log(reversed) // 4, 3, 2, 1
});
In case you want to do real MongoDB ID lookups, use db.collection.find({ _id: { $in: arr }}) and .map(i => result.find(j => j._id == i)) (Notice the two equal signs instead of three)

A couple of things to note:
1.) MongoDB, like most databases, makes no guarantees about the order of results returned from your query unless you use a call to sort(). If you really want to guarantee that your query result is returned in a a specific order, you'll need to specify that specific sort order.
2.) In general, the most recently updated/moved document will show up at the end of your result set but there are still no guarantees. MongoDB uses "natural order" for its native ordering of objects and although this is very close to the order of insertion, it is not guaranteed to be the same.
3.) Indexed fields will behave differently. It's worth pointing out that it looks like your query is using id and not _id. The former, _id would be indexed by default and id would not be indexed unless you've explicitly added an index to that field.
You can read more about MongoDB's sorting and ordering here:
http://www.mongodb.org/display/DOCS/Sorting+and+Natural+Order

you can write a query like this :
db.mytable.find({id:{$in:["1","2", "3", "4" ]}}).sort({id:1})
To have your results ORDER BY id ASC
Source : MongoDB Advanced Queries
But if you just want to order the results based on your $in array, try to sort your $in array in the reverse order, the result regarding to the first element of your $in array is likely to appear as the last element of the results

Related

Is there a way to search In Firebase firestore without saving another field in lowercase for case-insensitive search? [duplicate]

This question already has answers here:
Cloud Firestore Case Insensitive Sorting Using Query
(3 answers)
Are Cloud Firestore queries still case sensitive?
(1 answer)
Closed 1 year ago.
To support case-insensitive or any other canonicalization do we need to write a separate field that contains the canonicalized version and query against that??.
For example:
db.collection("users").where("name", "==", "Dan")
db.collection("users").where("name_lowercase", "==", "dan")
What I would do:
Before querying (maybe client-side): convert the query term in two or more variations (10 variations is maximum). For example, the search term "dan" (String) becomes an array of ["dan", "DAN", "Dan"]
Then I would do a "in" query, where I would search all of those variations in the same name field.
The "in" query type supports up to 10 equality (==) clauses with a logical "OR" operator. (documentation here)
This way, you can keep only one field "name" and query with possible variations on it.
It would look like this:
let query_variations = ["dan", "DAN", "Dan"]; // TODO: write a function that converts the query string into this kind of Array
let search = await db.collection("users").where("name", "in", query_variations).get();
In short, yes.
This is because Cloud Firestore (and the Firebase Realtime Database, when enabled) are indexed databases based on the values of each property in a document.
Rather than search through hundreds (if not thousands and thousands) of documents for matches, the index of the relevant property is queried for matching document IDs.
Consider the following "database" and it's index based on the name in the documents:
const documents = {
"docId1": {
name: "dan"
},
"docId2": {
name: "dan"
},
"docId3": {
name: "Dan"
},
"docId4": {
name: "Dan"
}
}
const nameIndex = {
"dan": ["docId1, docId2"],
"Dan": ["docId3, docId4"]
}
Instead of calling Object.entries(documents).filter(([id, data]) => data.name === "dan") on the entire list of documents, you can just ask the index instead using nameIndex["dan"] yielding the final results ["docId1, docId2"] near-instantly ready to be retrieved.
Continuing that same example, calling nameIndex["daniel"] gives undefined (no documents with that name) which can quickly be used to say that the data doesn't exist in the database).
Firestore introduced composite indexes, which allows you to index across multiple properties such as "name" and "age" so you can also quickly and efficiently search documents where the name is "Dan" but they are also 42 years of age.
Further reading: The Firebase documentation covers one solution for text-based search here.

Differentiating missing documents in MongoDB find()

When I run the following query I am getting the document that matches as normal which is "LON" in this case.
But is there any way that I can make the response seperately return the values that didn't match or found, which is "BUJ" in this case. Instead of running a for loop for individual values.
ports = [
"LON",
"BUJ"
];
findDatas = async(coll, values, key) => {
let datas = await coll.find({[key] : values});
// let datas = await coll.find().where(key).in(values);
console.log(datas)
}
findDatas(airportsModel, ports, "iata_code")
In my DB I only have "LON" which mean "BUJ" is not found. So is there any way to make mongo to tell that the given values haven't been found? along with the found ones.
This code dynamically creates a $match stage to find the documents, the uses $facet to split into 2 pipelines, the first returns the documents, the second uses a $group stage created from the input array to count how many documents match each element. The result should be a document with 2 fields: documents and counts
matchdata={};
matchdata[key]={"$in":ports};
groupdata = {_id:null};
ports.forEach(function(p){
groupdata[p] = {"$sum":{"$cond":[{"$eq":["$" + key, p ]},1,0]}}
});
db.coll.aggregate([
{$match:matchdata},
{$facet:{
documents:[{$match:{}}],
counts:[{$group: groupdata},{$project:{_id:0}}]
}}
])

Compare values inside same subdocument for findOne() [MongoDB]

I have a database full of objects that look ~exactly like this (simplified for clarity):
{
"_id": "GIFT100",
"price": 100,
"priceHistory": [
100, 110
],
"update": 1444183299242
}
What I'm trying to do is create a query document for MongoJS (or MongoDB and I can figure out the rest) that looks for the fact that priceHistory[0] < priceHistory[1].
I would want my query document to return the above record as a result. Alternatively, I could change my document code to compare price < priceHistory[0] but I believe this still leads to the same problem (comparing values inside the same document).
Any help would be appreciated, I've exhausted my Google-foo.
Edit:
I want to return a set of records that indicate a price drop since our last scan (performed daily). Basically a set of "sale" items from a data source I don't control.
You can use the $where clause, but be careful--it's slow, it cannot use your indexes, and it will perform a full table scan. Pass on whatever Javascript you want to use for comparison:
db.collection.findOne({$where: "priceHistory[0] < priceHistory[1]"})
Additionally, you can skip the $where statement if that's the only thing you're querying by:
db.collection.findOne("priceHistory[0] < priceHistory[1]")

Couchdb - How get results in reversed order by date and without id field

I'm testing some Couchdb features and I want get results with a reversed order by insertion date, querying by "i" field
A sample doc:
{
"_id": "970c3a0fdbb23dde47fb4075091a4d2b",
"_rev": "1-54448147611ff5e89189bb44e58c1521",
"doc_type": "Test",
"e": "3/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/0/36/2",
"d": "64/183/329/2/360/10/13/47/6/351/331/320/355/342/7/335/47/18/30/56/18/323/351/325/323/218/163/155/155/155/155",
"f": "1399305161/1399305185/1399305194/1399305254/1399305314/1399305374/1399305434/1399305447/1399305506/1399305566/1399305626/1399305668/1399305727/1399305787/1399305847/1399305908/1399305963/1399305970/1399306022/1399306068/1399306078/1399306100/1399306159/1399306219/1399306279/1399306308/1399306321/1399306379/1399306439/1399306493/1399306506",
"i": "3566120224",
"dated": "1399305161",
"v": "0/5/6/32/63/63/51/16/35/60/0/10/64/31/64/48/14/31/6/55/60/50/0/0/21/5/34/0/0/0/0",
"date": "2014-05-05T15:52:42Z"
}
My view:
function(doc) {
if(doc.i && doc.date){
emit([doc.i,doc.date],1); // 1 to test only
}
}
I'm testing it with:
myview?startkey=["3566120224"]&endkey=["3566120224",{}]&reversed=true
But I'm getting the data with a date order not reversed
{"total_rows":545,"offset":508,"rows":[
{"id":"407ee687674b783601ce6d7da906515e","key":["3566120224","2014-05-05T14:11:01Z"],"value":1},
{"id":"407ee687674b783601ce6d7da9062b51","key":["3566120224","2014-05-05T14:15:21Z"],"value":1},
{"id":"407ee687674b783601ce6d7da905f4d9","key":["3566120224","2014-05-05T14:19:41Z"],"value":1},
{"id":"407ee687674b783601ce6d7da905b4e1","key":["3566120224","2014-05-05T14:24:01Z"],"value":1},
{"id":"407ee687674b783601ce6d7da905733c","key":["3566120224","2014-05-05T14:28:22Z"],"value":1},
{"id":"407ee687674b783601ce6d7da904e7ea","key":["3566120224","2014-05-05T14:32:42Z"],"value":1},
{"id":"407ee687674b783601ce6d7da9043709","key":["3566120224","2014-05-05T14:37:02Z"],"value":1},
{"id":"407ee687674b783601ce6d7da9039896","key":["3566120224","2014-05-05T14:41:22Z"],"value":1},
{"id":"407ee687674b783601ce6d7da90303be","key":["3566120224","2014-05-05T14:45:43Z"],"value":1},
{"id":"407ee687674b783601ce6d7da90239ae","key":["3566120224","2014-05-05T14:50:03Z"],"value":1},
{"id":"407ee687674b783601ce6d7da9018442","key":["3566120224","2014-05-05T14:54:23Z"],"value":1},
{"id":"407ee687674b783601ce6d7da90104f0","key":["3566120224","2014-05-05T14:58:43Z"],"value":1},
{"id":"407ee687674b783601ce6d7da9007b67","key":["3566120224","2014-05-05T15:03:04Z"],"value":1},
{"id":"90bb394f7a4a581ff4dc78bfaffff448","key":["3566120224","2014-05-05T15:07:24Z"],"value":1},
{"id":"90bb394f7a4a581ff4dc78bfafff368e","key":["3566120224","2014-05-05T15:11:44Z"],"value":1},
{"id":"90bb394f7a4a581ff4dc78bfaffe7e65","key":["3566120224","2014-05-05T15:16:05Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb4075091f8e5c","key":["3566120224","2014-05-05T15:24:45Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb4075091f6241","key":["3566120224","2014-05-05T15:29:05Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb4075091f254a","key":["3566120224","2014-05-05T15:33:26Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb4075091ed01b","key":["3566120224","2014-05-05T15:37:46Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb4075091e5f42","key":["3566120224","2014-05-05T15:42:06Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb4075091dd992","key":["3566120224","2014-05-05T15:46:26Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb4075091d3853","key":["3566120224","2014-05-05T15:50:47Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb4075091c9a3c","key":["3566120224","2014-05-05T15:55:07Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb4075091bf465","key":["3566120224","2014-05-05T15:59:27Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb4075091ba442","key":["3566120224","2014-05-05T16:03:47Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb4075091ad482","key":["3566120224","2014-05-05T16:08:08Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb4075091a2130","key":["3566120224","2014-05-05T16:12:28Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb40750919a6ef","key":["3566120224","2014-05-05T16:16:48Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb407509192479","key":["3566120224","2014-05-05T16:21:08Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb40750918a977","key":["3566120224","2014-05-05T16:25:29Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb40750917b468","key":["3566120224","2014-05-05T16:29:49Z"],"value":1},
{"id":"970c3a0fdbb23dde47fb407509170583","key":["3566120224","2014-05-05T16:34:09Z"],"value":1}
]}
I have two times the same data(date & dated[ms date]), 1399305161 that is = 2014-05-05T15:52:42Z
thinking that I could order the results with a data type more easy to parse for couchdb, but didn't work using the dated field
Also I don't need the id field, how can exclude it from the results?
If you look here, you can see that reverse is not a supported query option. To reverse the data, you want to do:
myview?endkey=["3566120224"]&startkey=["3566120224",{}]&descending=false
If you don't want the ID field, you can just return the reduced value at the highest grouping level:
myview?endkey=["3566120224"]&startkey=["3566120224",{}]&descending=false&group_level=2
To remove the doc.i just write:
function(doc) {
if(doc.i && doc.date){
emit([doc.date],1); // just removed doc.i
}
}
To reverse the results:
You can either change the results so that you use the descending option. I'd think that is bad practice though as it depends on the client using this feature.
Assuming it's the default use of the view, I'd opt to returning the date as integer. Date.parse(doc.date).valueof() should do the trick of returning the epoch time as integer or double. If you then return this with a '-' (minus) the view will by default be sorted in descending order. This is assuming you don't need the formatted date in the key.

How to implement "partial match" in couchdb query

I have a Couchdb that stores documents each of each has a prefix field. Prefixes are unique so they can actually be used as IDs
Say:
_id=1 {prefix="AAABBBCCC", ...}
_id=2 {prefix="AAABBBDDD", ...}
_id=3 {prefix="AAABBE", ...}
_id=4 {prefix="AAAFF", ...}
I need to query these documents retrieving an appropriate document (always one full match on the prefix) using a key that is longer but completly matches the prefix. Prefix length varies, key length is constant.
query_key = AAABBBCCC123 => _id1
query_key = AAABBBDDD456 => _id2
query_key = AAABBEEEEEEE => _id3
query_key = AAABxxxxxxxx => Null
Any idea how can this be done in Couch?
Make a view emitting doc.prefix. Then query descending with startkey set to your query key with limit=1. The resulting prefix might be yours but you have to confirm.
You can either confirm the prefix in the client, or with a _list function. A _list function probably does not help with performance so I would consider doing it in the client, unless you have many clients in many languages, and you can standardize on a single URL to query with the same output.
The map function should look like this
function(doc) {
emit(doc.prefix, doc);
}
and you should search for documents with the substring function in the key.
Like this:
_design/doc/_view/viewname?key=QUERY_KEY.substring(0, FIXED_KEY_LENGTH)

Resources