lokijs - is there a way to make a compound unique index in loki? - lokijs

Is there a way to specify a compound unique index in loki ? I tried something like:
db.addCollection('contents', {unique: ['id', 'wsID']});
but this appears to make 2 different unique indexes.. it's the combination of the two that I'd like to make unique.
Many thanks for any suggestions.

I did something like this by making a surrogate key:
db.addCollection('contents', {unique: 'mySurrogateKey'});
When adding records to the collection, you can create a custom property and set it to be a simple concatenation:
record.mySurrogateKey = `${record.id}${record.wsID}`
collection.insert(record)

Related: https://github.com/techfort/LokiJS/issues/450
What I really use
import SparkMD5 from "spark-md5";
import stringify from "fast-json-stable-stringify";
public getTemplateId(t: IDbTemplate) {
const {front, back, css, js} = t;
return SparkMD5.hash(stringify({front, back, css, js}));
}
const tHook = (t: IDbTemplate) => {
t.key = this.getTemplateId(t);
};
this.template.on("pre-insert", tHook);
this.template.on("pre-update", tHook);

Related

Apply filtering with an array of value instead of one value for a filter in Algolia

I have in my index a list of object, each of them has an objectID value.
On some search, i want to filter OUT a certain number of them, using there objectID.
For the moment it works with one value as a string, i would like to know how to do for multiple value.
filters = 'NOT objectID:' + objectIDToFilter;
This work for one object, what can i do to apply this for an array of ObjectID. because :
filters = 'NOT objectID:' + arrayObjectID;
does not work.
I was thinking of generating a huge string with an arrayId.map with all my 'NOT objectID:1 AND NOT objectID: 2 ...' but i wanted to know if there is a cleaner way to do it.
I unfortunately misunderstood the line in algolia doc :
Array Attributes: Any attribute set up as an array will match the filter as soon as one of the values in the array match.
This apparently refers to the value itself in Algolia and not the filter
So i did not found a solution on algolia doc, i went for the long string, hope there is no limits on how much filter we can add on a query (found nothing about that).
Here is what i did if someone need it :
let filters = `NOT objectID:${userID}`;
blockedByUsers.map((blockedByUser) => {
filters = filters + ` AND NOT objectID:${blockedByUser}`;
});
If you need to add multiple but don't have a starting point like i do, you can't start the query with an AND , a solution i found to bypass that:
let filters = `NOT objectID:${blockedByUsers[0]}`;
blockedByUsers.map((blockedByUser, i) => {
if (i > 0) filters = filters + ` AND NOT objectID:${blockedByUser}`;
});
There is probably a cleaner solution, but this work. If you have found other solution for that problems i'll be happy to see :)

Firestore. How to create query based on user conditions

I want to create query. I'm using FirestoreRecyclerAdapter. Initialising adapter I got error:
FAILED_PRECONDITION: The query requires an index.
How I can create index if eventId in my case is dynamic value?
I'm sure this is basic thing, cause I'm doing all according to manual. As I read in the documentation, sets of data should have crossing sets of keys ie {dataId, true} in both events and users in my case. Please help me where to go for solution.
Query query = mFirebaseFirestore.collection(EVENT_INFO).document(eventId)
.collection(USERS)
.whereEqualTo(FIELD_AGE, guestAge)
.whereEqualTo(EVENTS_ATTENDED + "."+ eventId, true)
.orderBy(FIELD_FAMILYNAME, Query.Direction.ASCENDING);
At that moment there is no such an options to do things beautifull with Firestore. So we should think of hiding items based on custom criteria inside onBindViewHolder method in our FirestoreRecyclerAdapter. Like follows:
protected void onBindViewHolder(GuestHolder holder, int position, GuestItem guest) {
Map<String, Boolean> map = guest.getEventsAttended();
if (map != null && map.containsKey(eventId)) {
holder.itemView.setVisibility(View.VISIBLE);
} else {
holder.itemView.setVisibility(View.GONE);
holder.itemView.setLayoutParams(new RecyclerView.LayoutParams(0, 0));
}}
Hope this helps someone.

Checking if an Index exists in mongodb

Is there a command that i can use via javascript in mongo shell that can be used to check if the particular index exists in my mongodb. I am building a script file that would create indexes. I would like that if I run this file multiple number of times then the indexes that already exists are not recreated.
I can use db.collection.getIndexes() to get the collection of all the indexes in my db and then build a logic to ignore the ones that already exists but i was wondering if there is command to get an index and then ignore a script that creates the index. Something like:
If !exists(db.collection.exists("indexname"))
{
create db.collectionName.CreateIndex("IndexName")
}
Creating indexes in MongoDB is an idempotent operation. So running db.names.createIndex({name:1}) would create the index only if it didn't already exist.
The deprecated (as of MongoDB 3.0) alias for createIndex() is ensureIndex() which is a bit clearer on what createIndex() actually does.
Edit:
Thanks to ZitRo for clarifying in comments that calling createIndex() with the same name but different options than an existing index will throw an error MongoError: Index with name: **indexName** already exists with different options as explained in this question.
If you have other reasons for checking, then you can access current index data one of two ways:
As of v3.0, we can use db.names.getIndexes() where names is the name of the collection. Docs here.
Before v3.0, you can access the system.indexes collection and do a find as bri describes below.
Use db.system.indexes and search on it.
If, for example, you have an index called 'indexname', you can search for it like this:
db.system.indexes.find({'name':'indexname'});
If you need to search for that index on a specific collection,then you need to use the ns property (and, it would be helpful to have the db name).
db.system.indexes.find({'name':'indexname', 'ns':'dbname.collection'});
Or, if you absolutely hate including the db name...
db.system.indexes.find({'name':'indexname', 'ns': {$regex:'.collection$'}});
Pulling that together...
So, you're finished check would be:
if(db.system.indexes.find({name:'indexname',ns:{$regex:'.collection$'}}).count()==0) {
db.collection.createIndex({blah:1},{name:'indexname'})
}
Using nodeJS MongoDB driver version 2.2:
const MongoClient = require('mongodb').MongoClient;
exports.dropOldIndexIfExist = dropOldIndexIfExist;
async function dropOldIndexIfExist() {
try {
const mongoConnection = MongoClient.connect('mongodb://localhost:27017/test');
const indexName = 'name_1';
const isIndexExist = await mongoConnection.indexExists(indexName);
if (isIndexExist === true) {
await mongoConnection.dropIndex(indexName);
}
} catch (err) {
console.error('dropOldIndexIfExist', err.message);
throw err;
}
}
I've created a custom method in c# to check if the index exists, using mongo driver:
public bool IndexExists<TDocument>(
IMongoCollection<TDocument> collection, string name)
{
var indexes = collection.Indexes.List().ToList();
var indexNames = indexes
.SelectMany(index => index.Elements)
.Where(element => element.Name == "name")
.Select(name => name.Value.ToString());
return indexNames.Contains(name);
}
maybe we can use something like https://docs.mongodb.com/v3.2/reference/method/db.collection.getIndexes/#db.collection.getIndexes to check if the collection have an index equal to something ?
if yes then drop and add the new one or add the new one directly
In my case i did as follows.
DBCollection yourcollectionName = mt.getCollection("your_collection");
if (yourcollectionName.getIndexInfo() == null || yourcollectionName.getIndexInfo().isEmpty()) {
DBObject indexOptions = new BasicDBObject();
indexOptions.put("pro1", 1);
indexOptions.put("pro2", 1);
yourcollectionName.createIndex(indexOptions, "name_of_your_index", true);
}
Here is a Python 3.5+ and pymongo >= 4.1 (type hints) function that I wrote which checks to see if the index name exists (other details about the index are omitted).
from pymongo import MongoClient
from pymongo.collection import Collection
def check_collection_indexes(db: MongoClient, collection_name: str, index_name: str) -> bool:
coll: Collection = db[collection_name]
indexes: dict = coll.index_information()
# assume false
found = False
# get length of the index name for substring
l = len(index_name)
for k in indexes.keys():
# Substring the keys and check for match
if k[:l] == index_name:
found = True
break
else:
found = False
return found
If the index exists it will return True, otherwise you can use the False output to call another function that creates/recreates the indexes.

CouchDB Map/Reduce view query from Ektorp

I'm trying to execute a query from java against a Map/Reduce view I have created on the CouchDB.
My map function looks like the following:
function(doc) {
if(doc.type == 'SPECIFIC_DOC_TYPE_NAME' && doc.userID){
for(var g in doc.groupList){
emit([doc.userID,doc.groupList[g].name],1);
}
}
}
and Reduce function:
function (key, values, rereduce) {
return sum(values);
}
The view seems to be working when executed from the Futon interface (without keys specified though).
What I'm trying to do is to count number of some doc types belonging to a single group. I want to query that view using 'userID' and name of the group as a keys.
I'm using Ektorp library for managing CouchDB data, if I execute this query without keys it returns the scalar value, otherwise it just prints an error saying that for reduce query group=true must be specified.
I have tried the following:
ViewQuery query = createQuery("some_doc_name");
List<String> keys = new ArrayList<String>();
keys.add(grupaName);
keys.add(uzytkownikID);
query.group(true);
query.groupLevel(2);
query.dbPath(db.path());
query.designDocId(stdDesignDocumentId);
query.keys(keys);
ViewResult r = db.queryView(query);
return r.getRows().get(0).getValueAsInt();
above example works without 'keys' specified.
I have other queries working with ComplexKey like eg:
ComplexKey key = ComplexKey.of(userID);
return queryView("list_by_userID",key);
but this returns only a list of type T (List) - using CouchDbRepositorySupport of course - and cannot be used with reduce type queries (from what I know).
Is there any way to execute the query with reduce function specified and a complex key with 2 or more values using Ektorp library? Any examples highly appreciated.
Ok, I've found the solution using trial and error approach:
public int getNumberOfDocsAssigned(String userID, String groupName) {
ViewQuery query = createQuery("list_by_userID")
.group(true)
.dbPath(db.path())
.designDocId(stdDesignDocumentId)
.key(new String[]{userID,groupName});
ViewResult r = db.queryView(query);
return r.getRows().get(0).getValueAsInt();
}
So, the point is to send the complex key (not keys) actually as a single (but complex) key containing the String array, for some reason method '.keys(...)' didn't work for me (it takes a Collection as an argument). (for explanation on difference between .key() and .keys() see Hendy's answer)
This method counts all documents assigned to the specific user (specified by 'userID') and specific group (specified by 'groupName').
Hope that helps anybody executing map/reduce queries for retrieving scalar values from CouchDB using Ektorp query.
Addition to Kris's answer:
Note that ViewQuery.keys() is used when you want to query for documents matching a set of keys, not for finding document(s) with a complex key.
Like Kris's answer, the following samples will get document(s) matching the specified key (not "keys")
viewQuery.key("hello"); // simple key
viewQuery.key(documentSlug); // simple key
viewQuery.key(new String[] { userID, groupName }); // complex key, using array
viewQuery.key(ComplexKey.of(userID, groupName)); // complex key, using ComplexKey
The following samples, on the other hand, will get document(s) matching the specified keys, where each key may be either a simple key or a complex key:
// simple key: in essence, same as using .key()
viewQuery.keys(ImmutableSet.of("hello"));
viewQuery.keys(ImmutableSet.of(documentSlug1));
// simple keys
viewQuery.keys(ImmutableSet.of("hello", "world"));
viewQuery.keys(ImmutableSet.of(documentSlug1, documentSlug2));
// complex key: in essence, same as using .key()
viewQuery.keys(ImmutableSet.of(
new String[] { "hello", "world" } ));
viewQuery.keys(ImmutableSet.of(
new String[] { userID1, groupName1 } ));
// complex keys
viewQuery.keys(ImmutableSet.of(
new String[] { "hello", "world" },
new String[] { "Mary", "Jane" } ));
viewQuery.keys(ImmutableSet.of(
new String[] { userID1, groupName1 },
new String[] { userID2, groupName2 } ));
// a simple key and a complex key. while technically possible,
// I don't think anybody actually does this
viewQuery.keys(ImmutableSet.of(
"hello",
new String[] { "Mary", "Jane" } ));
Note: ImmutableSet.of() is from guava library.
new Object[] { ... } seems to have same behavior as ComplexKey.of( ... )
Also, there are startKey() and endKey() for querying using partial key.
To send an empty object {}, use ComplexKey.emptyObject(). (only useful for partial key querying)

Filter array using NSPredicate and obtains new object composed by some elements in the query

I've got an array like that
Word array (
{
translation = (
{
name = Roma;
lang = it;
},
{
name = Rome;
lang = en;
}
);
type = provenance;
value = RMU;
},
{
translation = (
{
name = "Milano";
lang = it;
},
{
name = "Milan";
lang = en;
}
);
type = destination;
value = MIL;
},)
The idea is to filter it using an NSPredicate and receive and an array of dictionaries based on the lang key, I'd like to get something like this made by filtering for lang == it,
Word array (
{
name = Roma;
lang = it;
type = provenance;
value = RMU;
},
{
name = "Milano";
lang = it;
type = destination;
value = MIL;
})
I can't simplify the data because it comes from a "JSON" service.
I've tried different predicates using SUBQUERY but none of them works, documentation about SUBQUERY is pretty poor, I'm missing something, probably the problem is that I'd like to receive an object that is really different from the source.
Of course I'm able to obtain that structure enumerating, I'm wondering if there is a shorter solution
This answer from Dave DeLong link to SUBQUERY explanation gave a me a lot of hints about SUBQUERY, but I'm not able to find a solution to my problem.
Can someone give me a hints about?
You can't do this with a predicate. (Well, you could, but it would be stupidly complex, difficult to understand and maintain, and in the end it would be easier to write the code yourself)
NSPredicate is for extracting a subset of data from an existing set. It only* does filtering, because a predicate is simply a statement that evaluates to true or false. If you have a collection and filter it with a predicate, then what happens is the collection starts iterating over its elements and asks the predicate: "does this pass your test?" "does this pass your test?" "does this pass your test?"... Every time that the predicate answers "yes this passes my test", the collection adds that object to a new collection. It is that new collection that is returned from the filter method.
THUS:
NSPredicate does not (easily) allow for merging two sets of data (which is what you're asking for). It is possible (because you can do pretty much anything with a FUNCTION() expression), but it makes for inherently unreadable predicates.
SO:
Don't use NSPredicate to merge your dataset. Do it yourself.